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