Edit detail for PythonAGlade revision 2 of 1

2
Editor: geon
Time: 2011/10/13 21:13:38 GMT+2
Note: pridavani znacek 2x-3x

changed:
-
.. image:: py25.png
   :align: right

Python a Glade
=================

Jak určitě víte, existuje skvělý nástroj na naklikání GTK+ widgetů - glade http://glade.gnome.org/ (případně glade pro windows http://gladewin32.sourceforge.net/modules/news/). Z něj vyleze XML soubor, který se da použít z mnoha programovacích jazyků a jedním z nich je i Python.
Budu sem dávat svoje postřehy, současně s tím, jak se GTK+ učím. 

První - změna labelu po kliknutí na tlačítko
--------------------------------------------------
| **Cíl:** po kliknutí na tlačítko button1 se změní text labelu label1 na současný text v entry1.
| **Widgety:** window, fixed, label, entry, button (learn1.glade soubor http://www.py.cz/learn1.glade)
 
Zdrojový kód::
  
    # -*- coding: cp1250 -*- 
    #!/usr/bin/python
    
    import os,sys
    import pygtk
    pygtk.require("2.0")  
    import gtk 
    import gtk.glade 
    
    def glade_file_missing(fname):
        "Rucne delane okno, ktere se objevi, kdyz se zapomeme stahnout *.glade soubor"
        
        url="http://www.py.cz/"
        win=gtk.Window(gtk.WINDOW_TOPLEVEL)
        win.connect("destroy", gtk.main_quit)
        win.set_resizable(False)
        win.set_border_width(10)
        win.set_position(gtk.WIN_POS_CENTER)
        win.set_title(u"Upozornění na chybějící soubor")
    
        vbox=gtk.VBox(False, 3)
        win.add(vbox)
        vbox.show()
        hbox=gtk.HBox(False,3)
        vbox.pack_start(hbox)
    
        image=gtk.Image()
        image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
        button=gtk.Button(None,gtk.STOCK_STOP)
        button.connect("clicked",gtk.main_quit)
        label=gtk.Label(u"CHYBÍ SOUBOR "+fname+"\n\n"+url+fname)
    
        hbox.pack_start(image)
        hbox.pack_start(label)
        vbox.pack_start(button)
    
        win.show_all()
    
        gtk.main()
        sys.exit(1)

    class window1Handlers: #vytvorim si tridu jejiz metody budou obsluhovat udalosti widgetu
        """metody jsou obsluha udalosti widgetu, jmeno metody se musi shodovat se jmenem udalosti nastavenym v Glade"""
    
        def on_window1_destroy(widget):
            gtk.main_quit()
    
        def on_button1_clicked(widget):
            window1['label1'].set_text(window1['entry1'].get_text())
    

    class WidgetsWrapper:
        "Zde se zajistí nactení *.glade souboru a svázaní jeho definovaných událostí s funkcemi zde."
        
        def __init__(self,file_name,widget_name,handlers_class):
            self.glade_file=file_name
            self.widgets = gtk.glade.XML (file_name, widget_name)
            self.widgets.signal_autoconnect(handlers_class.__dict__)
    
        def __getitem__(self, key):
            """diky tehle fci jde pouzit jmeno_instance['jmeno_widgetu']"""
            
            return self.widgets.get_widget(key)
    

    # ---- zacatek -----------------
    
    if not os.path.exists("learn1.glade"):
        glade_file_missing("learn1.glade")
    
    window1=WidgetsWrapper("learn1.glade", "window1",window1Handlers)
    gtk.main()

Druhý - minitextový editor s dialogy pro otevření a uložení souboru
--------------------------------------------------------------------
| **Cíl:** obsluha gtk.filechooserdialog-u jako dialogu pro otevření a uložení souboru
| **Widgety:** window, vbox, hbox, alignment, label, button, textview, filechooserdialog, dialog, image ("learn2.glade soubor":http://www.py.cz/learn2.glade)
| V tomto příkladu lze otevřít pouze soubory uložené v ISO nebo UTF kódování - pro zobrazení obsahu souboru ve win-1250 je potreba odkomentovat radek kodu s konverzi na unicode (data=unicode(data,"cp1250"))

Zdrojový kód::

  #!/usr/bin/python
  
  import sys,os
  import pygtk 
  pygtk.require("2.0")  
  import gtk 
  import gtk.glade
  
  def glade_file_missing(fname):
      url="http://www.py.cz/"
      win=gtk.Window(gtk.WINDOW_TOPLEVEL)
      win.connect("destroy", gtk.main_quit)
      win.set_resizable(False)
      win.set_border_width(10)
      win.set_position(gtk.WIN_POS_CENTER)
      win.set_title("Chybi soubor")
      
      vbox=gtk.VBox(False, 3)
      win.add(vbox)
      vbox.show()
      hbox=gtk.HBox(False,3)
      vbox.pack_start(hbox)
      
      image=gtk.Image()
      image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
      button=gtk.Button(None,gtk.STOCK_STOP)
      button.connect("clicked",gtk.main_quit)
      label=gtk.Label("CHYBI SOUBOR "+fname+"\n\n"+url+fname)
      
      hbox.pack_start(image)
      hbox.pack_start(label)
      vbox.pack_start(button)
      
      win.show_all()
      
      gtk.main()
      sys.exit(1)


  if not os.path.exists("learn2.glade"):
      glade_file_missing("learn2.glade")
  
  class window1Handlers:
      """
      metody jsou obsluha udalosti widgetu, jmeno metody se musi shodovat se jmenem udalosti nastavenym v Glade
      window1 - globalni promenna  - instance tridy WidgetsWrapper
      """
      def on_window1_destroy(widget):
          gtk.main_quit()
      
      def on_button1_clicked(widget):
          """otevre dialog pro otevreni souboru a obsah souboru vypise do textview"""
          open_dialog=gtk.glade.XML(window1.glade_file,"open_dialog").get_widget("open_dialog") #nactu si dialog pro otevreni souboru
          
          retval=open_dialog.run()
          if retval==gtk.RESPONSE_OK:
              fname=unicode(open_dialog.get_filename())
              fin=open(fname,"r")
              data=fin.read()
              #data=unicode(data,"cp1250") #odkomentujte tento radek, pokud chcete nacitat soubory v kodovani windows-1250
              fin.close()
              
              window1["label2"].set_text(fname) # zobrazeni soucasneho jmena souboru
              
              textview=window1["textview1"] # textview je widget, ktery zobrazuje obsah gtk.TextBuffer
              text_buffer=gtk.TextBuffer(None) # vytvorim buffer
              textview.set_buffer(text_buffer) # reknu textview ktery buffer ma zobrazovat
              text_buffer.set_text(data) # a zapisu data do bufferu
          
          # po zavolani run() je treba manualne dialog znicit - pokud tuhle metodu nezavolate, dilalog se neukonci, ale uz nebude "aktivni"
          open_dialog.destroy()
          
      def on_button2_clicked(widget):
          """ulozi obsah textview do souboru, ktery je vybran pomoci save dialogu"""
          save_dialog=gtk.glade.XML(window1.glade_file,"save_dialog").get_widget("save_dialog")
          save_dialog.set_filename(window1["label2"].get_text())
          
          retval=save_dialog.run()
          if retval==gtk.RESPONSE_OK:
              fname=unicode(save_dialog.get_filename())
              if os.path.exists(fname): # okud uz soubor existuje, zeptam se, jestli prepsat
                  overwrite_dialog=gtk.glade.XML(window1.glade_file,"dialog_overwrite").get_widget("dialog_overwrite")
                  
                  retval=overwrite_dialog.run()
                  if retval!=gtk.RESPONSE_OK: # pokud nebylo stisknuto tlacitko potvrzujici prepsani
                      overwrite_dialog.destroy() 
                      save_dialog.destroy() #ukoncim oba dialogy
                      return() # a vyskocim z cele metody
                  overwrite_dialog.destroy()
          
              text_buffer=window1['textview1'].get_buffer() # protoze nelze pracovat primo s textview, vytahnu si buffer ktery zobrazuje
              start=text_buffer.get_start_iter() # ke cteni z bufferu je potreba znat zacatek a konec useku, ktery chceme cist - tady mam zacatek
              end=text_buffer.get_end_iter() # a tady konec
          
              data=text_buffer.get_text(start, end, True) # prectu data z bufferu - od pozice start do pozice end, vcetne "hidden" znaku
              open(fname, 'w').write(data) # a zapisu je do souboru
              window1['label2'].set_text(fname) # nastavim label se jmenem souboru, na soubor do jakeho jsem to ulozil
          
          save_dialog.destroy()
  
  class WidgetsWrapper:
      def __init__(self,file_name,widget_name,handlers_class):
          self.glade_file=file_name
          self.widgets = gtk.glade.XML (file_name, widget_name)
          self.widgets.signal_autoconnect(handlers_class.__dict__)
          
      def __getitem__(self, key):
          """diky tehle fci jde pouzit jmeno_instance['jmeno_widgetu']"""
          return self.widgets.get_widget(key)
    
    
  window1=WidgetsWrapper("learn2.glade", "window1",window1Handlers)
  
  gtk.main()

Zdroje
-------------
- Vytvoření jednoduchého dialogu: http://www.writelinux.com/glade/ (anglicky)
- Úvod do práce s Glade 2: http://www.kplug.org/glade_tutorial/glade2_tutorial/glade2_introduction.html (anglicky)


py25.png

Python a Glade

Jak určitě víte, existuje skvělý nástroj na naklikání GTK+ widgetů - glade http://glade.gnome.org/ (případně glade pro windows http://gladewin32.sourceforge.net/modules/news/). Z něj vyleze XML soubor, který se da použít z mnoha programovacích jazyků a jedním z nich je i Python. Budu sem dávat svoje postřehy, současně s tím, jak se GTK+ učím.

První - změna labelu po kliknutí na tlačítko

Cíl: po kliknutí na tlačítko button1 se změní text labelu label1 na současný text v entry1.
Widgety: window, fixed, label, entry, button (learn1.glade soubor http://www.py.cz/learn1.glade)

Zdrojový kód:

# -*- coding: cp1250 -*-
#!/usr/bin/python

import os,sys
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade

def glade_file_missing(fname):
    "Rucne delane okno, ktere se objevi, kdyz se zapomeme stahnout *.glade soubor"

    url="http://www.py.cz/"
    win=gtk.Window(gtk.WINDOW_TOPLEVEL)
    win.connect("destroy", gtk.main_quit)
    win.set_resizable(False)
    win.set_border_width(10)
    win.set_position(gtk.WIN_POS_CENTER)
    win.set_title(u"Upozornění na chybějící soubor")

    vbox=gtk.VBox(False, 3)
    win.add(vbox)
    vbox.show()
    hbox=gtk.HBox(False,3)
    vbox.pack_start(hbox)

    image=gtk.Image()
    image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
    button=gtk.Button(None,gtk.STOCK_STOP)
    button.connect("clicked",gtk.main_quit)
    label=gtk.Label(u"CHYBÍ SOUBOR "+fname+"\n\n"+url+fname)

    hbox.pack_start(image)
    hbox.pack_start(label)
    vbox.pack_start(button)

    win.show_all()

    gtk.main()
    sys.exit(1)

class window1Handlers: #vytvorim si tridu jejiz metody budou obsluhovat udalosti widgetu
    """metody jsou obsluha udalosti widgetu, jmeno metody se musi shodovat se jmenem udalosti nastavenym v Glade"""

    def on_window1_destroy(widget):
        gtk.main_quit()

    def on_button1_clicked(widget):
        window1['label1'].set_text(window1['entry1'].get_text())


class WidgetsWrapper:
    "Zde se zajistí nactení *.glade souboru a svázaní jeho definovaných událostí s funkcemi zde."

    def __init__(self,file_name,widget_name,handlers_class):
        self.glade_file=file_name
        self.widgets = gtk.glade.XML (file_name, widget_name)
        self.widgets.signal_autoconnect(handlers_class.__dict__)

    def __getitem__(self, key):
        """diky tehle fci jde pouzit jmeno_instance['jmeno_widgetu']"""

        return self.widgets.get_widget(key)


# ---- zacatek -----------------

if not os.path.exists("learn1.glade"):
    glade_file_missing("learn1.glade")

window1=WidgetsWrapper("learn1.glade", "window1",window1Handlers)
gtk.main()

Druhý - minitextový editor s dialogy pro otevření a uložení souboru

Cíl: obsluha gtk.filechooserdialog-u jako dialogu pro otevření a uložení souboru
Widgety: window, vbox, hbox, alignment, label, button, textview, filechooserdialog, dialog, image ("learn2.glade soubor":http://www.py.cz/learn2.glade)
V tomto příkladu lze otevřít pouze soubory uložené v ISO nebo UTF kódování - pro zobrazení obsahu souboru ve win-1250 je potreba odkomentovat radek kodu s konverzi na unicode (data=unicode(data,"cp1250"))

Zdrojový kód:

#!/usr/bin/python

import sys,os
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade

def glade_file_missing(fname):
    url="http://www.py.cz/"
    win=gtk.Window(gtk.WINDOW_TOPLEVEL)
    win.connect("destroy", gtk.main_quit)
    win.set_resizable(False)
    win.set_border_width(10)
    win.set_position(gtk.WIN_POS_CENTER)
    win.set_title("Chybi soubor")

    vbox=gtk.VBox(False, 3)
    win.add(vbox)
    vbox.show()
    hbox=gtk.HBox(False,3)
    vbox.pack_start(hbox)

    image=gtk.Image()
    image.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
    button=gtk.Button(None,gtk.STOCK_STOP)
    button.connect("clicked",gtk.main_quit)
    label=gtk.Label("CHYBI SOUBOR "+fname+"\n\n"+url+fname)

    hbox.pack_start(image)
    hbox.pack_start(label)
    vbox.pack_start(button)

    win.show_all()

    gtk.main()
    sys.exit(1)


if not os.path.exists("learn2.glade"):
    glade_file_missing("learn2.glade")

class window1Handlers:
    """
    metody jsou obsluha udalosti widgetu, jmeno metody se musi shodovat se jmenem udalosti nastavenym v Glade
    window1 - globalni promenna  - instance tridy WidgetsWrapper
    """
    def on_window1_destroy(widget):
        gtk.main_quit()

    def on_button1_clicked(widget):
        """otevre dialog pro otevreni souboru a obsah souboru vypise do textview"""
        open_dialog=gtk.glade.XML(window1.glade_file,"open_dialog").get_widget("open_dialog") #nactu si dialog pro otevreni souboru

        retval=open_dialog.run()
        if retval==gtk.RESPONSE_OK:
            fname=unicode(open_dialog.get_filename())
            fin=open(fname,"r")
            data=fin.read()
            #data=unicode(data,"cp1250") #odkomentujte tento radek, pokud chcete nacitat soubory v kodovani windows-1250
            fin.close()

            window1["label2"].set_text(fname) # zobrazeni soucasneho jmena souboru

            textview=window1["textview1"] # textview je widget, ktery zobrazuje obsah gtk.TextBuffer
            text_buffer=gtk.TextBuffer(None) # vytvorim buffer
            textview.set_buffer(text_buffer) # reknu textview ktery buffer ma zobrazovat
            text_buffer.set_text(data) # a zapisu data do bufferu

        # po zavolani run() je treba manualne dialog znicit - pokud tuhle metodu nezavolate, dilalog se neukonci, ale uz nebude "aktivni"
        open_dialog.destroy()

    def on_button2_clicked(widget):
        """ulozi obsah textview do souboru, ktery je vybran pomoci save dialogu"""
        save_dialog=gtk.glade.XML(window1.glade_file,"save_dialog").get_widget("save_dialog")
        save_dialog.set_filename(window1["label2"].get_text())

        retval=save_dialog.run()
        if retval==gtk.RESPONSE_OK:
            fname=unicode(save_dialog.get_filename())
            if os.path.exists(fname): # okud uz soubor existuje, zeptam se, jestli prepsat
                overwrite_dialog=gtk.glade.XML(window1.glade_file,"dialog_overwrite").get_widget("dialog_overwrite")

                retval=overwrite_dialog.run()
                if retval!=gtk.RESPONSE_OK: # pokud nebylo stisknuto tlacitko potvrzujici prepsani
                    overwrite_dialog.destroy()
                    save_dialog.destroy() #ukoncim oba dialogy
                    return() # a vyskocim z cele metody
                overwrite_dialog.destroy()

            text_buffer=window1['textview1'].get_buffer() # protoze nelze pracovat primo s textview, vytahnu si buffer ktery zobrazuje
            start=text_buffer.get_start_iter() # ke cteni z bufferu je potreba znat zacatek a konec useku, ktery chceme cist - tady mam zacatek
            end=text_buffer.get_end_iter() # a tady konec

            data=text_buffer.get_text(start, end, True) # prectu data z bufferu - od pozice start do pozice end, vcetne "hidden" znaku
            open(fname, 'w').write(data) # a zapisu je do souboru
            window1['label2'].set_text(fname) # nastavim label se jmenem souboru, na soubor do jakeho jsem to ulozil

        save_dialog.destroy()

class WidgetsWrapper:
    def __init__(self,file_name,widget_name,handlers_class):
        self.glade_file=file_name
        self.widgets = gtk.glade.XML (file_name, widget_name)
        self.widgets.signal_autoconnect(handlers_class.__dict__)

    def __getitem__(self, key):
        """diky tehle fci jde pouzit jmeno_instance['jmeno_widgetu']"""
        return self.widgets.get_widget(key)


window1=WidgetsWrapper("learn2.glade", "window1",window1Handlers)

gtk.main()

Zdroje