2015-05-22 8 views
10

Sto lavorando con un TextView/TextBuffer Gtk nel mio progetto in cui l'utente è in grado di digitare il testo RTF (grassetto/corsivo/sottolineato)) selezionando i pulsanti di commutazione corretti.Gtk3 TextBuffer.serialize() restituisce il testo con i tag di formato, anche quando non c'è visivamente nessuno

Il problema è che se applico la sottolineatura o il flag di Pango in corsivo in testo all'interno di TextView, disattivi il corsivo/sottolineatura e ne scrivo un po 'e poi ottengo il testo con quei flag tramite TextBuffer.serialize(), il testo non formattato (visibilmente non formattato in TextView) viene restituito con i tag Underline/Italic che lo circondano.

Potete vedere questo qui: (Nota, ho semplificato i tag alle loro controparti HTML con BeautifulSoup per migliorare la leggibilità, ma l'attuale posizioni/tipo non sono stati modificati a tutti.)

Ecco il codice (richiede GTK3 e BS4 per python3 per essere installato):

import gi 
gi.require_version('Gtk', '3.0') 
from gi.repository import Gtk, Gdk, Pango 

import smtplib, mimetypes 
from bs4 import BeautifulSoup 

class Handler(): 

    def __init__(self): 
     global html 
     self.state = 0 

    def onDeleteWindow(self, *args): 
     Gtk.main_quit(*args) 

    def onSendClicked(self, button): 
     start, end = textBodyBuffer.get_bounds() 
     self.content = textBodyBuffer.get_text(start, end, True) 

     # Below is the serialization code for exporting with format tags 
     format = textBodyBuffer.register_serialize_tagset() 
     exported = textBodyBuffer.serialize(textBodyBuffer, format, start, end) 

     exported = exported.decode("latin-1") 

     exported = exported.split('<text_view_markup>', 1) 
     del exported[0] 
     exported[0] = '<text_view_markup>' + str(exported[0]) 

     exported = exported[0].split('</tags>', 1) 
     del exported[0] 

     exported = exported[0].split('</text_view_markup>', 1) 
     exported = str(exported[0]).replace('\n', ' ') 

     soup = BeautifulSoup(exported) 

     soupTags = soup.find_all('apply_tag') 

     for tag in soupTags: 

      if tag['name'] == 'bold': 
       tag.name = 'b' 
       del tag['name'] 
      elif tag['name'] == 'italic': 
       tag.name = 'em' 
       del tag['name'] 
      elif tag['name'] == 'underline': 
       tag.name = 'u' 
       del tag['name'] 

     print (soup) 

    def bold(self, button): 
     global tags_on 
     name = button.get_name() 
     if button.get_active():    # Button is "down"/enabled 
      tags_on.append('bold') 
     elif button.get_active() != True: # Button is "up"/disabled 
      del tags_on[tags_on.index('bold')] 

    def italic(self, button): 
     global tags_on 
     name = button.get_name() 
     if button.get_active():    # Button is "down"/enabled 
      tags_on.append('italic') 
     elif button.get_active() != True: # Button is "up"/disabled 
      del tags_on[tags_on.index('italic')] 

    def underline(self, button): 
     global tags_on 
     name = button.get_name() 
     if button.get_active():    # Button is "down"/enabled 
      tags_on.append('underline') 
     elif button.get_active() != True: # Button is "up"/disabled 
      del tags_on[tags_on.index('underline')] 

    def alignToggled(self, radiobutton): 
     pass 

    def undo(self, button): 
     pass 

    def redo(self, button): 
     pass 

    def keyHandler(self, widget, event): 
     global html 
     if Gdk.ModifierType.CONTROL_MASK & event.state: 
      if Gdk.keyval_name(event.keyval) == 'q': # Quit the program 
       w.destroy() 
       Gtk.main_quit() 

def get_iter_position(buffer): 
    return buffer.get_iter_at_mark(buffer.get_insert()) 

def text_inserted(buffer, iter, char, length): 

    global tags_on 

    if len(tags_on) >= 0: 
     iter.backward_chars(length) 

     for tag in tags_on: 
      w.queue_draw() 
      if tag == 'bold': 
       buffer.apply_tag(tag_bold, get_iter_position(buffer), iter) 
      elif tag == 'italic': 
       buffer.apply_tag(tag_italic, get_iter_position(buffer), iter) 
      elif tag == 'underline': 
       buffer.apply_tag(tag_underline, get_iter_position(buffer), iter) 


if __name__ == '__main__': 

    global text, html 
    # Gtk tag globals 
    global tag_bold, tag_italic, tag_underline, tags_on 

    tags_on = [] 

    text = '' 
    html = '<html><body><p>' 

    builder = Gtk.Builder() 
    builder.add_from_file('editor.glade') 
    builder.connect_signals(Handler()) 

    buttonSend = builder.get_object('buttonSend') 

    textBody = builder.get_object('textviewBody') 
    textBodyBuffer = textBody.get_buffer() 

    textBodyBuffer.connect_after('insert-text', text_inserted) 
    tag_bold = textBodyBuffer.create_tag("bold", weight=Pango.Weight.BOLD) 
    tag_italic = textBodyBuffer.create_tag("italic", style=Pango.Style.ITALIC) 
    tag_underline = textBodyBuffer.create_tag("underline", underline=Pango.Underline.SINGLE) 

    w = builder.get_object('window1') 
    w.show_all() 

    Gtk.main() 

Ecco il file editor.glade:

<?xml version="1.0" encoding="UTF-8"?> 
<!-- Generated with glade 3.16.1 --> 
<interface> 
    <requires lib="gtk+" version="3.10"/> 
    <object class="GtkImage" id="image1"> 
    <property name="visible">True</property> 
    <property name="can_focus">False</property> 
    <property name="icon_name">mail-send</property> 
    </object> 
    <object class="GtkWindow" id="window1"> 
    <property name="can_focus">False</property> 
    <property name="title" translatable="yes">Keyboard Mail - Edit Message</property> 
    <property name="modal">True</property> 
    <property name="type_hint">dialog</property> 
    <signal name="delete-event" handler="onDeleteWindow" swapped="no"/> 
    <signal name="key-press-event" handler="keyHandler" swapped="no"/> 
    <child> 
     <object class="GtkBox" id="box2"> 
     <property name="visible">True</property> 
     <property name="can_focus">False</property> 
     <property name="orientation">vertical</property> 
     <child> 
      <object class="GtkBox" id="box1"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      <property name="orientation">vertical</property> 
      <child> 
       <object class="GtkButton" id="buttonSend"> 
       <property name="label" translatable="yes">Send</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="image">image1</property> 
       <property name="relief">none</property> 
       <property name="image_position">top</property> 
       <property name="always_show_image">True</property> 
       <signal name="clicked" handler="onSendClicked" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">False</property> 
       <property name="position">1</property> 
       </packing> 
      </child> 
      </object> 
      <packing> 
      <property name="expand">False</property> 
      <property name="fill">False</property> 
      <property name="position">1</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkToolbar" id="toolbar1"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      <property name="toolbar_style">icons</property> 
      <property name="show_arrow">False</property> 
      <child> 
       <object class="GtkToggleToolButton" id="buttonBold"> 
       <property name="name">bold</property> 
       <property name="visible">True</property> 
       <property name="can_focus">False</property> 
       <property name="tooltip_text" translatable="yes">Make the selected text bold</property> 
       <property name="icon_name">format-text-bold</property> 
       <signal name="toggled" handler="bold" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="homogeneous">True</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkToggleToolButton" id="buttonItalic"> 
       <property name="name">italic</property> 
       <property name="visible">True</property> 
       <property name="can_focus">False</property> 
       <property name="icon_name">format-text-italic</property> 
       <signal name="toggled" handler="italic" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="homogeneous">True</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkToggleToolButton" id="buttonUnderline"> 
       <property name="name">underline</property> 
       <property name="visible">True</property> 
       <property name="can_focus">False</property> 
       <property name="icon_name">format-text-underline</property> 
       <signal name="toggled" handler="underline" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="homogeneous">True</property> 
       </packing> 
      </child> 
      </object> 
      <packing> 
      <property name="expand">False</property> 
      <property name="fill">True</property> 
      <property name="position">3</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkBox" id="box5"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      <property name="orientation">vertical</property> 
      <child> 
       <object class="GtkTextView" id="textviewBody"> 
       <property name="width_request">500</property> 
       <property name="height_request">100</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="hexpand">True</property> 
       <property name="vexpand">True</property> 
       <property name="wrap_mode">word</property> 
       <property name="left_margin">5</property> 
       <property name="right_margin">5</property> 
       </object> 
       <packing> 
       <property name="expand">True</property> 
       <property name="fill">True</property> 
       <property name="position">0</property> 
       </packing> 
      </child> 
      </object> 
      <packing> 
      <property name="expand">False</property> 
      <property name="fill">True</property> 
      <property name="position">3</property> 
      </packing> 
     </child> 
     </object> 
    </child> 
    </object> 
</interface> 

Qualcuno sa perché l'istruzione TextBuffer.serialize() (riga 23) restituisca i caratteri visibilmente non formattati all'interno dei tag underline/italic?

Non riesco a trovare alcun motivo quando questo si verifica, sembra decidere in modo casuale di restituire il testo con i tag o meno.

Modifica: Ho seguito il codice passo dopo passo con pdb (debugger Python) e non ho ancora visto nulla che possa causare questo.

Edit: modello interessante che ho capito - se si attiva sia corsivo e sottolineato in una sola volta, quindi digitare alcuni, poi girare entrambi fuori in una sola volta, e il tipo, la chiamata serialize() restituisce la stringa corretta.

Tuttavia, se li si applica uno alla volta, digitando un bit per ogni nuovo tag, viene restituito in modo errato.

+0

@ace Fatto. Ho aggiunto uno snippet di codice in linea. Il problema è che mettere il file .glade renderebbe la domanda troppo lunga, quindi ho dovuto mantenere il collegamento a una pagina PasteBin per quello. –

+0

@ace Ah. Proverò a ridurlo un po 'di più. L'ho già abbattuto un po ', ma vedrò di più che posso tagliare. –

+0

@ace Fatto. È un esempio più appropriato? –

risposta

4

Sono stato in grado di ricreare il problema con uno minimal example in C. Pertanto sospetto fortemente che si tratti di un bug in GTK. L'interrogante quindi opened a bugreport upstream.

Come soluzione, suggerirei di provare a implementare la serializzazione da soli. forward_to_tag_toggle dovrebbe essere utile a tal fine. Si noti che non è possibile utilizzare il percorso ovvio dell'utilizzo di register_serialize_format per registrare il proprio serializzatore e quindi chiamare serializzare, poiché mentre un GtkTextBufferSerializeFunc deve restituire una stringa, nel repository GObject it is apparently recorded as a function returning a single integer. (A causa di un altro bug.) Invece, esegui la serializzazione interamente dal tuo codice, ad esempio prendi un iter di avvio e poi attraversa il buffer di testo.

+0

Ah, quindi è molto probabilmente non la mia programmazione - buono. Non riuscivo a vedere nulla di diverso tra il mio codice in grassetto e il mio codice corsivo o sottolineato, a parte le ovvie differenze tra i nomi delle bandiere e delle variabili Pango. Grazie per esserti preso la briga di fare quell'esempio - cercherò di farlo da solo. –

+0

Inoltre, forward_to_tag_toggle è inutile per me, dal momento che è PyGtk invece di Gtk 3. Non c'è nulla nei documenti di Gtk3 su 'forward_to_tag_toggle' :( –

+1

[Sicuramente esiste] (https://developer.gnome.org/gtk3/ stable/GtkTextIter.html # gtk-text-iter-forward-to-tag-toggle)!! – Phillip

Problemi correlati