2017年8月28日 星期一

Tkinter Button 範例





from tkinter import Tk, Button

def goodbye_world():
    print ("Goodbye World!\nWait, I changed my mind!")
    button.configure(text = "Hello World!", command=hello_world)

def hello_world():
    print ("Hello World!\nWait, I changed my mind!")
    button.configure(text = "Goodbye World!", command=goodbye_world)

root = Tk()
button = Button(root, text="Hello World!", command=hello_world)
button.pack()

root.mainloop()

Tkinter intro


Tkinter intro




In this video, we begin discussion of the tkinter module. The tkinter module is a wrapper around tk, which is a wrapper around tcl, which is what is used to create windows and graphical user interfaces. Here, we show how simple it is to create a very basic window in just 8 lines. We get a window that we can resize, minimize, maximize, and close! The tkinter module's purpose is to generate GUIs. Python is not very popularly used for this purpose, but it is more than capable of doing it.
Let's walk through each step to making a tkinter window:
Simple enough, just import everything from tkinter.
from tkinter import *
Here, we are creating our class, Window, and inheriting from the Frame class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
Then we define the settings upon initialization. This is the master widget.
class Window(Frame):

    def __init__(self, master=None):
        Frame.__init__(self, master)               
        self.master = master
The above is really all we need to do to get a window instance started.
Root window created. Here, that would be the only window, but you can later have windows within windows.
root = Tk()
Then we actually create the instance.
app = Window(root)
Finally, show it and begin the mainloop.
root.mainloop()
The above code put together should spawn you a window that looks like:
tkinter python 3 tutorial
Pretty neat, huh? Obviously there is much more to cover.
The next tutorial: 



Tkinter 攝氏溫度轉華視溫度



# -*- coding: UTF-8 -*-
from tkinter import *
from tkinter import ttk

def calculate(*args):
    try:
        value = float(Cels.get())
        Fahr.set( value * (9/5) +32)
    except ValueError:
        pass
    
root = Tk()
root.title("******   攝氏溫度 轉 華式溫度   *******")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)

Cels = StringVar()
Fahr = StringVar()

feet_entry = ttk.Entry(mainframe, width=7, textvariable=Cels)
feet_entry.grid(column=2, row=1, sticky=(W, E))

ttk.Label(mainframe, textvariable=Fahr).grid(column=2, row=2, sticky=(W, E))
ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=1, row=3, sticky=E)
ttk.Button(mainframe, text="Quit", command= quit).grid(column=2, row=3, sticky=E)


ttk.Label(mainframe, text="請輸入 攝氏溫度 == ").grid(column=1, row=1, sticky=E)
ttk.Label(mainframe, text="     相當於    ").grid(column=1, row=2, sticky=E)
ttk.Label(mainframe, text="華式溫度").grid(column=3, row=2, sticky=W)

for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)

feet_entry.focus()
root.bind('<Return>', calculate)

root.mainloop()

2017年8月27日 星期日

Message Widget

The syntax of a message widget: 

w = Message ( master, option, ... ) 


The Options in Detail

OptionMeaning
anchorThe position, where the text should be placed in the message widget: N, NE, E, SE, S, SW, W, NW, or CENTER. The Default is CENTER.
aspectAspect ratio, given as the width/height relation in percent. The default is 150, which means that the message will be 50% wider than it is high. Note that if the width is explicitly set, this option is ignored.
backgroundThe background color of the message widget. The default value is system specific.
bgShort for background.
borderwidthBorder width. Default value is 2.
bdShort for borderwidth.
cursorDefines the kind of cursor to show when the mouse is moved over the message widget. By default the standard cursor is used.
fontMessage font. The default value is system specific.
foregroundText color. The default value is system specific.
fgSame as foreground.
highlightbackgroundTogether with highlightcolor and highlightthickness, this option controls how to draw the highlight region.
highlightcolorSee highlightbackground.
highlightthicknessSee highlightbackground.
justifyDefines how to align multiple lines of text. Use LEFT, RIGHT, or CENTER. Note that to position the text inside the widget, use the anchor option. Default is LEFT.
padxHorizontal padding. Default is -1 (no padding).
padyVertical padding. Default is -1 (no padding).
reliefBorder decoration. The default is FLAT. Other possible values are SUNKEN, RAISED, GROOVE, and RIDGE.
takefocusIf true, the widget accepts input focus. The default is false.
textMessage text. The widget inserts line breaks if necessary to get the requested aspect ratio. (text/Text)
textvariableAssociates a Tkinter variable with the message, which is usually a StringVar. If the variable is changed, the message text is updated.
widthWidget width given in character units. A suitable width based on the aspect setting is automatically chosen, if this option is not given.

Python 的內建 GUI 模組 Tkinter 建立視窗 -----Message Widget



from tkinter import *
master = Tk()
whatever_you_do = "Whatever you do will be insignificant, but it is very important that you do it.\n(Mahatma Gandhi)"
msg = Message(master, text = whatever_you_do)
msg.config(bg='lightgreen', font=('times', 24, 'italic'))
msg.pack( )
mainloop( )

Python 的內建 GUI 模組 Tkinter 建立視窗 ----Dynamical Content in a Label



import tkinter as tk

counter = 0 
def counter_label(label):
  def count():
    global counter
    counter += 1
    label.config(text=str(counter))
    label.after(1000, count)
  count()


root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg="green")
label.pack()
counter_label(label)
button = tk.Button(root, text='Stop', width=25, command=root.destroy)
button.pack()
root.mainloop()

Python 的內建 GUI 模組 Tkinter 建立視窗----Colorized Labels in various fonts







from tkinter import *

root = Tk()

Label(root, 
text="Red Text in Times Font",
fg = "red",
font = "Times").pack()
Label(root, 
text="Green Text in Helvetica Font",
fg = "light green",
bg = "dark green",
font = "Helvetica 16 bold italic").pack()
Label(root, 
text="Blue Text in Verdana bold",
fg = "blue",
bg = "yellow",
font = "Verdana 10 bold").pack()

root.mainloop()

Python Course

Tkinter

Hello Tkinter Label

Hello Tkinter with Python
We will start our tutorial with one of the easiest widgets of Tk (Tkinter), i.e. a label. A Label is a Tkinter Widget class, which is used to display text or an image. The label is a widget that the user just views but not interact with.

There is hardly any book or introduction into a programming language, which doesn't start with the "Hello World" example. We will draw on tradition but will slightly modify the output to "Hello Tkinter" instead of "Hello World".

The following Python script uses Tkinter to create a window with the text "Hello Tkinter". You can use the Python interpretor to type this script line after line, or you can save it in a file, for example "hello.py":

from Tkinter import *
# if you are working under Python 3, comment the previous line and comment out the following line
#from tkinter import *

root = Tk()

w = Label(root, text="Hello Tkinter!")
w.pack()

root.mainloop()

Starting our example

If we save the script under the name hello.py, we can start it like this:
$ python hello.py
If you run the command under the Gnome and Linux, the window the window will look like this:

Tk widgets

Under Windows it appears in the Windows look and feel:

Hello Tkinter Windows

Explanation

The Tkinter module, containing the Tk toolkit, has always to be imported. In our example, we import everything from Tkinter by using the asterisk symbol ("*") into our module's namespace:
from Tkinter import *
To initialize Tkinter, we have to create a Tk root widget, which is a window with a title bar and other decoration provided by the window manager. The root widget has to be created before any other widgets and there can only be one root widget.
root = Tk()
The next line of code contains the Label widget. The first parameter of the Label call is the name of the parent window, in our case "root". So our Label widget is a child of the root widget. The keyword parameter "text" specifies the text to be shown:
w = Label(root, text="Hello Tkinter!")
The pack method tells Tk to fit the size of the window to the given text.
w.pack()
The window won't appear until we enter the Tkinter event loop:
root.mainloop()
Our script will remain in the event loop until we close the window.

Using Images in Labels

As we have already mentioned, labels can contain text and images. The following example contains two labels, one with a text and the other one with an image.

from Tkinter import *

root = Tk()
logo = PhotoImage(file="../images/python_logo_small.gif")
w1 = Label(root, image=logo).pack(side="right")
explanation = """At present, only GIF and PPM/PGM
formats are supported, but an interface 
exists to allow additional image file
formats to be added easily."""
w2 = Label(root, 
           justify=LEFT,
           padx = 10, 
           text=explanation).pack(side="left")
root.mainloop()


If you start this script, it will look like this using Ubuntu Linux with Gnome desktop:

Label with included image

The "justify" parameter can be used to justify a text on the LEFT, RIGHT or CENTER. padx can be used to add additional horizontal padding around a text label. The default padding is 1 pixel. pady is similar for vertical padding. The previous example without justify (default is centre) and padx looks like this:

Label with included image

You want the text drawn on top of the image? No problem! We need just one label and use the image and the text option at the same time. By default, if an image is given, it is drawn instead of the text. To get the text as well, you have to use the compound option. If you set the compound option to CENTER the text will be drawn on top of the image:
from Tkinter import *

root = Tk()
logo = PhotoImage(file="../images/python_logo_small.gif")
explanation = """At present, only GIF and PPM/PGM
formats are supported, but an interface 
exists to allow additional image file
formats to be added easily."""
w = Label(root, 
          compound = CENTER,
          text=explanation, 
          image=logo).pack(side="right")

root.mainloop()


Text on top of image in label

We can have the image on the right side and the text left justified with a padding of 10 pixel on the left and right side by changing the Label command like this:

w = Label(root, 
          justify=LEFT,
          compound = LEFT,
          padx = 10, 
          text=explanation, 
          image=logo).pack(side="right")


If the compound option is set to BOTTOM, LEFT, RIGHT, or TOP, the image is drawn correspondingly to the bottom, left, right or top of the text.

Colorized Labels in various fonts

Some Tk widgets, like the label, text, and canvas widget, allow you to specify the fonts used to display text. This can be achieved by setting the attribute "font". typically via a "font" configuration option. You have to consider that fonts are one of several areas that are not platform-independent.

The attribute fg can be used to have the text in another colour and the attribute bg can be used to change the background colour of the label.

from Tkinter import *

root = Tk()

Label(root, 
   text="Red Text in Times Font",
   fg = "red",
   font = "Times").pack()
Label(root, 
   text="Green Text in Helvetica Font",
   fg = "light green",
   bg = "dark green",
   font = "Helvetica 16 bold italic").pack()
Label(root, 
   text="Blue Text in Verdana bold",
   fg = "blue",
   bg = "yellow",
   font = "Verdana 10 bold").pack()

root.mainloop()
The result looks like this:

Colored Labels with different fonts

Dynamical Content in a Label

The following script shows an example, where a label is dynamically incremented by 1 until the stop button is pressed:

import Tkinter as tk

counter = 0 
def counter_label(label):
  def count():
    global counter
    counter += 1
    label.config(text=str(counter))
    label.after(1000, count)
  count()
 
 
root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg="green")
label.pack()
counter_label(label)
button = tk.Button(root, text='Stop', width=25, command=root.destroy)
button.pack()
root.mainloop()
The result of the previous script looks like this:

Dynamic content in Label


Python 的內建 GUI 模組 Tkinter 建立視窗 --ListBox



#!/usr/bin/python
# -*- coding: UTF-8 -*-

from tkinter import *           # 導入 Tkinter 庫
root = Tk()                     # 創建視窗物件的背景色
                                # 創建兩個列表
li     = ['C','python','php','html','SQL','java']
movie  = ['CSS','jQuery','Bootstrap']
listb  = Listbox(root)          #  創建兩個列表組件
listb2 = Listbox(root)
for item in li:                 # 第一個小部件插入資料
    listb.insert(0,item)

for item in movie:              # 第二個小部件插入資料
    listb2.insert(0,item)

listb.pack()                    # 將小部件放置到主窗口中
listb2.pack()
root.mainloop()                 # 進入消息迴圈

計算機的程式碼。用tkinter提供GUI介面。

計算機的程式碼。用tkinter提供GUI介面。



from tkinter import *

def frame(root, side):
    w = Frame(root)
    w.pack(side=side, expand=YES, fill=BOTH)
    return w

def button(root, side, text, command=None):
    w = Button(root, text=text, command=command)
    w.pack(side=side,expand=YES, fill=BOTH)
    return w

class Calculator(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.pack(expand=YES, fill=BOTH)
        self.master.title('Simple Calculator')
        self.master.iconname("calc1")

        display = StringVar()
        Entry(self, relief=SUNKEN, textvariable=display).pack(side=TOP, expand=YES, fill=BOTH)

        for key in ("123", "456", "789", "-0."):
            keyF = frame(self, TOP)
            for char in key:
                button(keyF, LEFT, char, lambda w=display, s='%s'%char: w.set(w.get() + s))

        opsF = frame(self, TOP)
        for char in "+-*/=":
            if char == '=':
                btn = button(opsF, LEFT, char)
                btn.bind('<ButtonRelease-1>', lambda e, s = self, w=display: s.calc(w), '+')
            else:
                btn = button(opsF, LEFT, char, lambda w=display, c=char: w.set(w.get() + ' '+c+' '))
        clearF = frame(self, BOTTOM)
        button(clearF, LEFT, 'Clr', lambda w=display: w.set(''))
    def calc(self, display):
        try:
            display.set( eval(display.get()) )
        except ValueError:
            display.set("ERROR")

if __name__ == '__main__':
    Calculator().mainloop()



2017年8月26日 星期六

python tkinter--英呎轉公尺



#-*- coding: utf-8 -*-

from tkinter import *
from tkinter import ttk

def calculate(*args):
    try:
        value = float(feet.get())
        meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0)
    except ValueError:
        pass
   
root = Tk()
root.title("英呎轉 公尺 Feet to Meters")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)

feet = StringVar()
meters = StringVar()

feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet)
feet_entry.grid(column=2, row=1, sticky=(W, E))

ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E))
ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W)

ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W)
ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E)
ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W)

for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)

feet_entry.focus()
root.bind('<Return>', calculate)

root.mainloop()

A First (Real) Example---Pyhon tkinter


A First (Real) Example

With that out of the way, let's try a slightly more useful example, which will give you an initial feel for what the code behind a Tk program looks like.

Design

The example we'll use is a simple GUI tool that will convert a number of feet to the equivalent number of meters. If we were to sketch this out, it might look something like this:

A sketch of our feet to meters conversion program.
So it looks like we have a short text entry widget that will let us type in the number of feet, and a 'Calculate' button that will get the value out of that entry, perform the calculation, and then put the resulting number of meters on the screen just below where the entry is. We've also got three static labels ("feet", "is equivalent to", and "meters") which help our user figure out how to use the interface.
In terms of layout, things seem to naturally divide into three columns and three rows:

The layout of our user interface, which follows a 3 x 3 grid.

Code

Now here is the Tcl/Tk code to create this program.
package require Tk

wm title . "Feet to Meters"
grid [ttk::frame .c -padding "3 3 12 12"] -column 0 -row 0 -sticky nwes
grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1

grid [ttk::entry .c.feet -width 7 -textvariable feet] -column 2 -row 1 -sticky we
grid [ttk::label .c.meters -textvariable meters] -column 2 -row 2 -sticky we
grid [ttk::button .c.calc -text "Calculate" -command calculate] -column 3 -row 3 -sticky w

grid [ttk::label .c.flbl -text "feet"] -column 3 -row 1 -sticky w
grid [ttk::label .c.islbl -text "is equivalent to"] -column 1 -row 2 -sticky e
grid [ttk::label .c.mlbl -text "meters"] -column 3 -row 2 -sticky w

foreach w [winfo children .c] {grid configure $w -padx 5 -pady 5}
focus .c.feet
bind . <Return> {calculate}

proc calculate {} {  
   if {[catch {
       set ::meters [expr {round($::feet*0.3048*10000.0)/10000.0}]
   }]!=0} {
       set ::meters ""
   }
}

Now here is the RubyTk code to create this program.
require 'tk'
require 'tkextlib/tile'

root = TkRoot.new {title "Feet to Meters"}
content = Tk::Tile::Frame.new(root) {padding "3 3 12 12"}.grid( :sticky => 'nsew')
TkGrid.columnconfigure root, 0, :weight => 1; TkGrid.rowconfigure root, 0, :weight => 1

$feet = TkVariable.new; $meters = TkVariable.new
f = Tk::Tile::Entry.new(content) {width 7; textvariable $feet}.grid( :column => 2, :row => 1, :sticky => 'we' )
Tk::Tile::Label.new(content) {textvariable $meters}.grid( :column => 2, :row => 2, :sticky => 'we');
Tk::Tile::Button.new(content) {text 'Calculate'; command {calculate}}.grid( :column => 3, :row => 3, :sticky => 'w')

Tk::Tile::Label.new(content) {text 'feet'}.grid( :column => 3, :row => 1, :sticky => 'w')
Tk::Tile::Label.new(content) {text 'is equivalent to'}.grid( :column => 1, :row => 2, :sticky => 'e')
Tk::Tile::Label.new(content) {text 'meters'}.grid( :column => 3, :row => 2, :sticky => 'w')

TkWinfo.children(content).each {|w| TkGrid.configure w, :padx => 5, :pady => 5}
f.focus
root.bind("Return") {calculate}

def calculate
  begin
     $meters.value = (0.3048*$feet*10000.0).round()/10000.0
  rescue
     $meters.value = ''
  end
end

Tk.mainloop

Now here is the Perl code to create this program.
use Tkx;

Tkx::wm_title(".", "Feet to Meters");
Tkx::ttk__frame(".c",  -padding => "3 3 12 12");
Tkx::grid( ".c", -column => 0, -row => 0, -sticky => "nwes");
Tkx::grid_columnconfigure( ".", 0, -weight => 1); 
Tkx::grid_rowconfigure(".", 0, -weight => 1);

Tkx::ttk__entry(".c.feet", -width => 7, -textvariable => \$feet);
Tkx::grid(".c.feet", -column => 2, -row => 1, -sticky => "we");
Tkx::ttk__label(".c.meters", -textvariable => \$meters);
Tkx::grid(".c.meters", -column => 2, -row => 2, -sticky => "we");
Tkx::ttk__button(".c.calc", -text => "Calculate", -command => sub {calculate();});
Tkx::grid(".c.calc", -column => 3, -row => 3, -sticky => "w");

Tkx::grid( Tkx::ttk__label(".c.flbl", -text => "feet"), -column => 3, -row => 1, -sticky => "w");
Tkx::grid( Tkx::ttk__label(".c.islbl", -text => "is equivalent to"), -column => 1, -row => 2, -sticky => "e");
Tkx::grid( Tkx::ttk__label(".c.mlbl", -text => "meters"), -column => 3, -row => 2, -sticky => "w");

foreach (Tkx::SplitList(Tkx::winfo_children(".c"))) {
    Tkx::grid_configure($_, -padx => 5, -pady => 5);
}
Tkx::focus(".c.feet");
Tkx::bind(".", "<Return>", sub {calculate();});

sub calculate {
   $meters = int(0.3048*$feet*10000.0+.5)/10000.0 || '';
}

Tkx::MainLoop();
As we'll see in the next chapter, there's another, more object-oriented way to do exactly the same thing. Are we surprised?
Now here is the Python code to create this program.
from tkinter import *
from tkinter import ttk

def calculate(*args):
    try:
        value = float(feet.get())
        meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0)
    except ValueError:
        pass
    
root = Tk()
root.title("Feet to Meters")

mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)

feet = StringVar()
meters = StringVar()

feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet)
feet_entry.grid(column=2, row=1, sticky=(W, E))

ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E))
ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W)

ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W)
ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E)
ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W)

for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)

feet_entry.focus()
root.bind('<Return>', calculate)

root.mainloop()
And the resulting user interface:

Screenshot of our completed feet to meters user interface (on Mac OS X, Windows and Linux).

Construire une interface graphique pas à pas en Python avec Tkinter et wxPython


Etape 1 : Importer le toolkit

Importons le module dont nous avons besoin:

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program."

Tkinter fait partie de la distribution standard de Python. On s'attend donc à ce qu'il soit présent. Nous nous contentons de l'importer.

wxPython ne fait pas partie de la distribution standard de Python et doit être téléchargé et installé séparément. Il est plus convenable de prévenir l'utilisateur que notre programme nécessite wxPython mais qu'il n'est pas installé, et où le trouver (d'où le bloc try/except ImportError).



Etape 2 : Créer une classe

C'est mieux de mettre notre application dans une classe:

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):

En Tkinter, on hérite de Tkinter.Tk qui est la classe de base pour les fenêtres standards.
En wxPython, on dérive de wx.Frame qui est la classe de base pour les fenêtres standards.




Etape 3 : Le constructeur

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)

simpleapp_tk dérive de Tkinter.Tk, nous devons donc appeller le constructeur de Tkinter.Tk (Tkinter.Tk.__init__()).
simpleapp_wx dérive de wx.Frame, nous devons donc appeller le constructeur de wx.Frame (wx.Frame.__init__()).

Une interface graphique est une hiérarchie d'objets: Un bouton peut être contenu dans un panneau qui est contenu dans un onglet qui est contenu dans une fenêtre, etc.
Donc chaque élément de l'interface graphique (widget) possède un parent (le widget qui le contient, généralement).
C'est pour cela que chacun de nos constructeurs a un paramètre parent.
Garder la référence du parent est utile quand il faut montrer/masquer des groupes de widgets, les redessiner à l'écran ou tout simplement les détruire quand la fenêtre est fermée.

L'objet wx.Frame a deux paramètres supplémentaires: id (un identifiant du widget, que nous n'utiliserons pas) et title (le titre de la fenêtre).





Etape 4 : Garder une référence de notre parent

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent


C'est une bonne habitude à prendre de garder une référence de notre parent quand on créé un widget.



Etape 5 : Initialiser l'interface graphique

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        pass   
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.Show(True)


Il est généralement préférable de garder la portion de code qui créé les éléments graphique (boutons, champs texte...) séparée de la logique du programme.
C'est pour cela que nous créons une méthode initialize(). Nous allons crées tous nos widgets dans cette méthode.

Pour le moment, la version Tkinter ne contient rien (d'où l'instruction pass qui ne fait rien.)
La version wxPython contient self.Show(True) pour obliger la fenêtre à apparaître (sans quoi elle reste cachée après sa création). (Ceci n'est pas nécessaire avec Tkinter car Tkinter affiche automatiquement tous les widgets créés).





Etape 6 : Création du main

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        pass

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')


Maintenant que nous avons notre classe, utilisons-la !
Nous créons un main qui sera exécuté quand le programme sera lancé à partir de la ligne de commande.

Avec Tkinter, nous instancions notre classe (app=simpleapp_tk()). On ne lui donne aucun parent (None), car c'est le premier élément graphique que nous créons.
On donne également un titre à notre fenêtre (app.title()).

Avec wxPython, est il obligatoire d'instancier un objet wx.App() avant de créer des éléments graphiques. C'est pour cela que nous faisons app=wx.App().
Ensuite nous instancions notre classe (frame=simpleapp_wx()). Nous ne lui donnons également aucun parent (None), car c'est le premier élément graphique que nous créons.
On utilise -1 pour laisser wxPython choisir un identifiant lui-même. Et nous donnons à notre fenêtre son titre: 'my application'.




Etape 7 : Boucler !

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        pass

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

Maintenant, nous demandons à nos deux programme de boucler avec .mainloop()

Qu'est-ce que ça signifie ?

Cela veut dire que chaque programme va maintenant boucler sans fin, en attente d'évènements (l'utilisateur qui clique sur un bouton, presse une touche, le système d'exploitation qui demande à notre application de quitter, etc.)
Les boucles Tkinter et wxPython recevront ces évènements et agiront en conséquence.
C'est de la programmation évènementielle (Car chaque programme ne fait rien d'autre qu'attendre des évènements, et ne réagit que quand il en reçoit un.)



Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !
Ils afficheront une fenêtre vide.

Maintenant, fermez ces fenêtres et revenons au code source pour y ajouter nos widgets.



Etape 8 : Gestionnaire de layout

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()
        self.SetSizerAndFit(sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Il y a plusieurs manières de mettre les widgets dans une fenêtre (ou un autre conteneur): Les ajouter horizontalement, les empiler verticalement, etc.

Il existe donc différentes classes (appellées gestionnaires de layout, ou layout managers) capables de placer les widgets dans les conteneurs de différentes manières. Certains sont plus souples que d'autres.
Chaque conteneur (fenêtre, panneau, onglet, dialogue...) peut avoir son propre gestionnaire de layout.

Je recommande le gestionnaire grid (grille). C'est une simple grille où vous positionnez vos widgets dans des cases à la manière d'un tableur (Excel, OpenOffice Calc...)
Par exemple: Mettre le bouton à la colonne 2, ligne 1. Mettre une checkbox colonne 5, ligne 3. etc.
Si vous ne devez en apprendre qu'un, prenez celui-là.


Avec Tkinter, l'appel à self.grid() va automatiquement créer un le gestionnaire de layout grille et demander à notre fenêtre de l'utiliser.
Avec wxPython, nous créons explicitement le gestionnaire avec sizer=wx.GridBagSizer() et nous demandons à notre fenêtre de l'utiliser (self.SetSizerAndFit(sizer)).


Maintenant ajoutons des widgets.



Etape 9 : Ajouter un champ texte

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)

        self.SetSizerAndFit(sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

Pour ajouter un widgets, vous devez toujours:
  • d'abord créer le widget
  • puis l'ajouter à un conteneur.

D'abord, créons le widget:

Avec Tkinter, on créé le widget Entry (self.entry=Tkinter.Entry())
Avec wxPython, on créé le widget TextCtrl (self.entry=wx.TextCtrl())
Dans les deux cas, on passe self comme parent, car notre fenêtre sera le parent de ces widgets: Ils apparaîtront dans notre fenêtre.

wxPython.TextCtrl a 2 paramètres supplémentaires: -1 (pour que wxPython choisisse automatiquement un identifiant), et le texte lui-même (u'Enter text here.').
(Pour le texte dans Tkinter.Entry, nous verrons cela plus tard.)

Notez que nous conservons une référence au widget que nous venons de créer (self.entry=...) car nous aurons besoin d'y accéder plus tard, dans d'autres méthodes de notre application.


Maintenant il est temps de l'ajouter au conteneur:

Avec Tkinter, on appelle la méthode .grid() sur notre widget. Nous indiquons où le mettre dans la grille (column=0, row=0).
Si la cellule doit s'agrandir, on peut demander aux widgets qu'elle contient de rester collés à un des bords de la cellule. C'est option sticky='EW'.
(E=east (gauche), W=West (droite), N=North (haut), S=South (bas))
Nous avons spécifié 'EW', ce qui veut dire que notre widget essaiera de rester collé aux bords gauche et droit de sa cellule.
(C'est l'un de nos buts: Que le champ texte s'agrandisse quand on redimensionne la fenêtre horizontalement.)


Avec wxPython, nous appellons la méthode .Add() sur le conteneur (la fenêtre). Nous lui passons le widget que nous venons de créer (self.entry) et ses coordonnées (0,0).
(1,1) est l'étendue de la cellule: Dans notre cas, notre cellule ne déborde pas sur les cellule voisines (d'où le (1,1)).
wx.EXPAND demande au gestionnaire de fenêtres d'agrandir notre champ texte si sa cellule est agrandie.




Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !
Nous avons une fenêtre avec un simple champ texte. Vous pouvez déjà taper du texte dedans.





Hé !  Le champ texte ne s'agrandit pas quand je redimensionne la fenêtre !  Vous avez menti !
Restez calme, il y a une excellente raison à cela: Nous avons demandé à nos champs texte de s'agrandir si la cellule ou colonne qui les contient change de taille, mais nous n'avons pas encore demandé à notre gestionnaire de layout lui-même d'élargir les colonnes (et donc les cellules) si la fenêtre est redimensionnée. Nous verrons cela plus tard.




Etape 10 : Ajouter le bouton

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')

        button = Tkinter.Button(self,text=u"Click me !")
        button.grid(column=1,row=0)

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))

        self.SetSizerAndFit(sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

C'est facile dans les deux cas: Créer le bouton, et l'ajouter.

Notez que dans ce cas, on ne conserve pas de référence au bouton (car nous n'aurons pas besoin de lire ou modifier sa valeur par la suite).


Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !
Nous avons une fenêtre avec un champ texte et un bouton.






Etape 11 : Ajouter le label

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')

        button = Tkinter.Button(self,text=u"Click me !")
        button.grid(column=1,row=0)

        label = Tkinter.Label(self,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))

        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        self.SetSizerAndFit(sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

Nous créons également un label:
  • C'est un objet Label en Tkinter.
  • C'est un objet StaticText en wxPython.

Pour la couleur: On prend un texte blanc sur fond bleu.
  • Avec Tkinter, c'est fg="white",bg="blue".
  • Avec wxPython, il faut appeller deux méthodes (SetForegroundColour and SetBackgroundColour)

Pour l'alignement du texte:
  • Avec Tkinteranchor="w" signifie que le texte doit être aligné à gauche (w=west=ouest) dans le label.
  • Avec wxPython, le texte est aligné par défaut à gauche. Nous n'avons donc rien à faire.

Concernant le placement du label dans la grille:
  • Avec Tkinter, c'est à nouveau la méthode .grid(), mais cette fois la cellule doit déborder sur sa voisine de droite (afin que le label apparaisse sous le champ texte (colonne 0) et le bouton (colonne 1).): C'est le paramètre columnspan=2.
  • Nous faisons la même chose avec wxPython en spécifiant (1,2) (ce qui veut dire: s'étirer d'une cellule verticalement et 2 cellule hozitontalement).

Pour l'expansion du label:
  • De nouveau, nous utilisons sticky="EW" pour Tkinter
  • Et nous utilisons wx.EXPAND pour wxPython.


Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !

Ok. 3 widgets. Et ensuite ?




Etape 12 : Activer le redimensionnement automatique

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')

        button = Tkinter.Button(self,text=u"Click me !")
        button.grid(column=1,row=0)

        label = Tkinter.Label(self,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')

        self.grid_columnconfigure(0,weight=1)

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))

        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Maintenant nous allons demander à notre gestionnaire de layout de changer la taille de ses colonnes et lignes quand la fenêtre est redimensionée.
Enfin... pas les lignes. Et seulement la première colonne (0).

C'est AddGrowableCol() pour wxPython.
C'est grid_columnconfigure() pour Tkinter.

Notez qu'il y a des paramètres supplémentaires. Par exemple, certaines colonnes peuvent s'agrandir plus que d'autre. C'est à ça que sert le paramètre weight: partager l'espace disponible dans des proportions différentes pour les différentes colonnes/lignes. (Dans notre cas, on ne fait rien de spécial: On met 1).




Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !
Maintenant essayez de redimensionner la fenêtre.
Vous voyez ?
Le champ texte et le label bleu se redimensionnent automatiquement en conséquences pour s'adapter à la largeur de la fenêtre.


Ceci dit, quand on agrandit la fenêtre verticalement, ça n'est pas très beau:

Bad resizing in Tkinter    Bad resizing in wxPython

Ajoutons donc une contrainte pour que l'utilisateur ne puisse agrandir la fenêtre que horizontalement.





Etape 13 : Ajouter une contrainte

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')

        button = Tkinter.Button(self,text=u"Click me !")
        button.grid(column=1,row=0)

        label = Tkinter.Label(self,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))

        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.Show(True)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

Pour empêcher la fenêtre d'être redimensionnée verticalement:
  • Avec Tkinter, on utilise .resizable(True,False).
  • Avec wxPython, vous pouvez spécifier les hauteurs et largeurs minimales et maximales de votre fenêtre.
    Nous réglons les hauteurs minimales et maximales à la hauteur actuelle de notre fenêtre (self.GetSize().y) de manière ce qu'il ne soit pas possible de la redimensionner verticalement.
    Nous laissons -1, -1 pour la largeur afin que la fenêtre puisse être librement redimensionnée horizontalement (-1 veut dire "pas de limite").


Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !

Essayez maintenant de redimensionner la fenêtre.




Etape 14 : Ajouter des gestionnaires d'évènements

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')
        self.entry.bind("<Return>", self.OnPressEnter)

        button = Tkinter.Button(self,text=u"Click me !",
                                command=self.OnButtonClick
)
        button.grid(column=1,row=0)

        label = Tkinter.Label(self,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)

    def OnButtonClick(self):
        print "You clicked the button !"

    def OnPressEnter(self,event):
        print "You pressed enter !"

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.entry)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, button)


        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.Show(True)

    def OnButtonClick(self,event):
        print "You clicked the button !"

    def OnPressEnter(self,event):
        print "You pressed enter !"

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Les gestionnaires d'évènements sont des méthodes qui sont appellées quand quelquechose se passe dans l'interface graphique (bouton cliqué, etc.)
Nous allons lier ces gestionnaires d'évènement à des widgets spécifiques pour des évènements spécifiques.

Nous allons donc faire quelquechose quand le bouton est cliqué, ou la touche ENTREE est pressée dans le champ texte.
  • On créé une méthode OnButtonClick() qui sera appellée quand le bouton est cliqué.
  • On créé une méthode OnPressEnter() qui sera appellée quand la touche ENTREE sera pressée dans le champ texte.

On lie ("bind") ensuite ces méthodes aux widgets:

Pour le bouton:
  • Avec Tkinter, on ajoute simplement command=self.OnButtonClick au bouton.
  • Avec wxPython, on utilise la méthode .Bind():
    button est le widget sur lequel on veut "attraper" un évènement (notre bouton).
    wx.EVT_BUTTON est le type d'évènement qu'on veut attraper (clic sur un bouton).
    self.OnButtonClick est la méthode qui sera appellée si le bouton est cliqué.
Pour le champ texte:
  • Avec Tkinter, on utilise la méthode .bind()
    "<Return>" est la touche que nous voulons "attraper".
    self.OnPressEnter est la méthode qui sera appellée si cet évènement se produit.
  • Avec wxPython, c'est à nouveau la méthode .Bind() que nous utilisons, mais cette fois sur l'évènement wx.EVT_TEXT_ENTER.


Ainsi:
  • Cliquer sur le bouton appellera la méthode OnButtonClick().
  • Presser ENTREE dans le champs texte appellera la méthode OnPressEnter().



Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !

Entrez un peu de texte, et pressez ENTREE ou cliquez sur le bouton: Du texte apparaîtra.
(Tkinter l'affichera dans la console (ou fenêtre MS-Dos) ; wxPython affichera une popup.)





Etape 15 : Modifier le label

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entry = Tkinter.Entry(self)
        self.entry.grid(column=0,row=0,sticky='EW')
        self.entry.bind("<Return>", self.OnPressEnter)

        button = Tkinter.Button(self,text=u"Click me !",
                                command=self.OnButtonClick)
        button.grid(column=1,row=0)

        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)

    def OnButtonClick(self):
        self.labelVariable.set("You clicked the button !")

    def OnPressEnter(self,event):
        self.labelVariable.set("You pressed enter !")if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.entry)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, button)


        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.Show(True)

    def OnButtonClick(self,event):
        self.label.SetLabel("You clicked the button !")

    def OnPressEnter(self,event):
        self.label.SetLabel("You pressed enter !")

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Maintenant modifions le label bleu:
  • Avec wxPython, c'est facile: on appelle simplement la méthode .SetLabel() sur notre label (self.label)
  • Avec Tkinter, c'est un peu plus compliqué. Vous devez:
    • créer une variable spéciale Tktinter qui contiendra la valeur (self.labelVariable = Tkinter.StringVar())
    • lier cette variable au widget (textvariable=self.labelVariable)
    • puis utiliser les méthodes set() or get() sur la variable Tkinter pour modifier ou lire sa valeur (self.labelVariable.set("You clicked the button !"))
Avec Tkinter, chaque fois que vous voulez lire ou modifier une valeur dans un widget (champ texte, label, case à cocher, bouton radio...), vous devez créer une variable Tkinter et la lier au widget. Il existe plusieurs types de variables Tkinter (StringVar, IntVar, DoubleVar, BooleanVar).
Par exemple, on utilisera typiquement une variable Tkinter BooleanVar pour une case à cocher.

Avec wxPython, vous appelez directement une méthode pour lire/modifier la valeur du widget.


Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !

Pressez ENTREE ou cliquez sur le bouton: Le label bleu est modifié.


Etape 16 : Afficher la valeur

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky='EW')
        self.entry.bind("<Return>", self.OnPressEnter)
        self.entryVariable.set(u"Enter text here.")

        button = Tkinter.Button(self,text=u"Click me !",
                                command=self.OnButtonClick)
        button.grid(column=1,row=0)

        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')
        self.labelVariable.set(u"Hello !")

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)

    def OnButtonClick(self):
        self.labelVariable.set( self.entryVariable.get()+" (You clicked the button)" )

    def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+" (You pressed ENTER)" )

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.entry)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, button)


        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.Show(True)

    def OnButtonClick(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You clicked the button)" )

    def OnPressEnter(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You pressed ENTER)" )

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()

Maintenant lisons la valeur du champ texte pour l'afficher dans le label bleu:
  • Avec wxPython, on appelle self.entry.GetValue()
  • Avec Tkinter, nous devons à nouveau créer une variable Tkinter dont nous lirons la valeur avec .get().

Comme nous avons maintenant une variable Tkinter pour accéder au texte du champs et au label, on peut également mettre les valeurs par défaut ("Enter text here." et "Hello !")


Lancez-le !  A cet instant, vous pouvez lancer les deux programmes: Ils fonctionnent !

Entrez un peu de texte dans le champ, et pressez ENTREE ou cliquez sur le bouton: Le label bleu va afficher le texte que vous avez tapé.




Etape 17 : Petit raffinement

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky='EW')
        self.entry.bind("<Return>", self.OnPressEnter)
        self.entryVariable.set(u"Enter text here.")

        button = Tkinter.Button(self,text=u"Click me !",
                                command=self.OnButtonClick)
        button.grid(column=1,row=0)

        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')
        self.labelVariable.set(u"Hello !")

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

    def OnButtonClick(self):
        self.labelVariable.set( self.entryVariable.get()+" (You clicked the button)" )
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

    def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+" (You pressed ENTER)" )
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.entry)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, button)


        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)
        self.Show(True)

    def OnButtonClick(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You clicked the button)" )
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)

    def OnPressEnter(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You pressed ENTER)" )
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Voici un petit raffinement: Le champ texte sera automatiquement re-sélectionné après que l'utilisateur ait pressé ENTREE ou cliqué sur le bouton. Il pourra ainsi taper immédiatement un nouveau texte dans le champ (en remplaçant le texte existant).

Nous commençons par donner le focus au champ texte (focus_set() et SetFocus()), puis nous sélectionnons le texte (selection_range() et SetSelection()).






Etape 18 : Correction d'un comportement de Tkinter

#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable)
        self.entry.grid(column=0,row=0,sticky='EW')
        self.entry.bind("<Return>", self.OnPressEnter)
        self.entryVariable.set(u"Enter text here.")

        button = Tkinter.Button(self,text=u"Click me !",
                                command=self.OnButtonClick)
        button.grid(column=1,row=0)

        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,
                              anchor="w",fg="white",bg="blue")
        label.grid(column=0,row=1,columnspan=2,sticky='EW')
        self.labelVariable.set(u"Hello !")

        self.grid_columnconfigure(0,weight=1)
        self.resizable(True,False)
        self.update()
        self.geometry(self.geometry())       
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

    def OnButtonClick(self):
        self.labelVariable.set( self.entryVariable.get()+" (You clicked the button)" )
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

    def OnPressEnter(self,event):
        self.labelVariable.set( self.entryVariable.get()+" (You pressed ENTER)" )
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('my application')
    app.mainloop()
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-

try:
    import wx
except ImportError:
    raise ImportError,"The wxPython module is required to run this program"

class simpleapp_wx(wx.Frame):
    def __init__(self,parent,id,title):
        wx.Frame.__init__(self,parent,id,title)
        self.parent = parent
        self.initialize()

    def initialize(self):
        sizer = wx.GridBagSizer()

        self.entry = wx.TextCtrl(self,-1,value=u"Enter text here.")
        sizer.Add(self.entry,(0,0),(1,1),wx.EXPAND)
        self.Bind(wx.EVT_TEXT_ENTER, self.OnPressEnter, self.entry)

        button = wx.Button(self,-1,label="Click me !")
        sizer.Add(button, (0,1))
        self.Bind(wx.EVT_BUTTON, self.OnButtonClick, button)


        self.label = wx.StaticText(self,-1,label=u'Hello !')
        self.label.SetBackgroundColour(wx.BLUE)
        self.label.SetForegroundColour(wx.WHITE)
        sizer.Add( self.label, (1,0),(1,2), wx.EXPAND )

        sizer.AddGrowableCol(0)
        self.SetSizerAndFit(sizer)
        self.SetSizeHints(-1,self.GetSize().y,-1,self.GetSize().y );
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)
        self.Show(True)

    def OnButtonClick(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You clicked the button)" )
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)

    def OnPressEnter(self,event):
        self.label.SetLabel( self.entry.GetValue() + " (You pressed ENTER)" )
        self.entry.SetFocus()
        self.entry.SetSelection(-1,-1)

if __name__ == "__main__":
    app = wx.App()
    frame = simpleapp_wx(None,-1,'my application')
    app.MainLoop()


Tkinter a une particularité: Il essaie constamment d'adapter la taille de la fenêtre à ce qu'elle contient. Ça part d'un bon sentiment, mais ce n'est pas toujours ce que l'on souhaite.
Par exemple, entrez un looooooooooooooooooong texte dans le champ, et pressez ENTREE: La fenêtre d'agrandit.
Entrez à nouveau un texte court et pressez ENTREE: La fenêtre rétrécit.

Généralement, on ne veut pas que la fenêtre passe son temps à changer de taille toute seule. Les utilisateurs n'aiment pas ça.

C'est pour cela que nous figeons la taille de la fenêtre à sa propre taille (self.geometry(self.geometry())).
De cette manière, Tkinter cessera d'essayer tout le temps d'adapter la fenêtre à son contenu.
(Et nous faisons un update() pour être sûr que Tkinter a terminé le rendu des différents widgets qu'il contient et qu'il a terminé de déterminer leur taille.)


wxPython/wxWidgets n'a pas ce comportement, nous n'avons donc rien à faire de spécial.


C'est terminé !


Nous avons maintenant notre application.   :-)


Vous pouvez télécharger les sources des deux programmes:  simpleapp_tk.py   simpleapp_wx.py


Maintenant, quelques remarques.


Outils RAD et coordonnées pixel

Il peut paraître lourd de construire des interfaces graphiques de cette manière, mais quand on a compris le principe, il est assez simple d'ajouter des éléments et de les assembler dans des conteneurs (onglets, panneau redimensionnables, scrollables...). Au final, c'est très souple.

Si vous préférez les environnement RAD dans le genre de VB (VisualBasic), vous pouvez vous tourner vers Boa Constructor (conçu pour wxPython). Mais j'apprécie de moins en moins de genre d'outils.
Ils tendent à générer du code inutile, lourd, et certains environnements n'arrivent parfois même pas à relire le code (Est-ce qu'il y a des utilisateurs de VisualStudio.Net dans la salle ? Je n'aime pas que le designer me viande complètement mon interface, qu'il me fasse disparaître sans raison des évènements, qu'il ne comprenne pas les bases du HTML et CSS ou que le designer me fasse une faute de protection générale en ré-ouvrant un projet.  :-@  ).

Créer une interface graphique sans un RAD est un peu plus long, mais vous avez un bien meilleur contrôle sur ce qui est fait, et surtout vous pouvez utiliser des gestionnaire de layout de type grille (grid). (La plupart des environnements RAD ne travaillent qu'en coordonnées pixel fixes.)

Pourquoi éviter les coordonnés fixes en pixels ?
  • Est-ce que vous avez déjà rencontré ces logiciels où une partie du texte est illisible parcequ'il dépasse d'un widget ? Ce sont les coordonnées pixel en action !
    Si vous utilisez les coordonnées pixels, votre interface graphique risque d'être inutilisable avec des polices de taille différentes.
    Avec un gestionnaire de type grid, les widgets s'adaptent.
  • Vous n'avez jamais été énervé par ces logiciels avec une taille de fenêtre ridiculement minuscule alors que vous avez un écran 1600x1200 ? C'est encore les coordonnées pixel en action.
    Avec un gestionnaire de type grid, les utilisateurs peuvent redimensionner la fenêtre pour profiter de votre logiciel en plein écran.

Interface graphique non bloquante ?

Vous devez garder à l'esprit que lorsqu'un gestionnaire d'évènement est en cours d'exécution, la boucle d'évènement du toolkit ne tourne plus et ne traite plus les évènements qui arrivent (Les nouveaux évènements sont alors placés dans une file d'attente.)
Donc l'interface graphique est complètement bloquée tant que le gestionnaire d'évènement n'a pas terminé son travail.
(Les boutons et menus ne répondent pas, et la fenêtre ne peut même pas être déplacée ou redimensionnée. Je suis certain que vous avez déjà rencontré ce genre de logiciel.)


Vous avez deux solutions à ce problème:
  • Effectuer seulement des actions courtes dans les gestionnaires d'évènement.
  • Utilliser les threads.

La programmation par multi-threads n'est pas triviale. Elle peut même devenir un cauchemard à débuguer. Mais c'est le prix à payer si vous voulez créer un logiciel qui répond bien au sollicitations de l'utilisateur. (Par exemple, mon programme webGobbler possède un thread dédié exclusivement à l'interface graphique, ce qui fait que le logiciel répond bien à l'utilisateur même quand il est en train de faire des traitements sur les images ou qu'il attend la réponse de requêtes réseau.)
La plupart des utilisateurs attendent des logiciels récents qu'il soient non bloquants.

Attention: La plupart des toolkits graphique ne sont pas thread-safe. Cela veut dire que si deux threads essaient de modifier la valeur d'un même widget, cela peut planter toute votre application (et même générer une faute de protection générale). Vous devez utiliser des sections critiques et d'autres moyens (queues de messages, etc.)
Python supporte les threads et fournit différents objets pour régler ces problèmes (classe Queue thread-safe, sémaphores, sections critiques...)


Séparer nettement la logique de votre programme de l'interface graphique facilitera grandement le passage au mode multi-threads.
C'est pour cela que je recommande de séparer l'interface graphique et la logique du programme dans ce document.

De plus, cela vous permettra même de créer plusieurs interfaces graphiques pour votre programme si vous le souhaitez !



Tkinter ou wxPython ?


Tkinter / Tcl/tk :

+ Tkinter est fourni en standard avec Python. La plupart des utilisateurs de Python pourront utiliser votre programme tel quel.

- Pas de widgets avancés, même si on peut palier cela avec Pmw (Python Megawidgets).



wxPython / wxWidgets:

+ Utilise l'aspect natif du système d'exploitation quand possible (aspect graphique plus proche de celui du système d'exploitation).

+ Widgets avancés (sélecteur de date, bar d'outils flottantes, fenêtres non-rectangulaires, arbres, bulles d'aide...)

- Ne fait pas partie de la distribution standard de Python. Doit être téléchargé et installé séparément.



Autres toolkit graphiques


Il existe beaucoup d'autres toolkits accessibles en Python, comme GTK (à travers pyGTK), Qt (à travers pyQt), MFC, SWING, Fltk et même des wrappers autour de Tkinter/wxPython eux-mêmes (PythonCard, etc.).

Pour d'autres toolkits, voir:



Créer un fichier EXE


Si votre application utilise Tkinter ou wxPython, il est possible de la packager sous forme d'exécutable (binaire) avec des programmes comme py2exe ou cx_Freeze.
(Même si parfois cela nécessite quelques bidouillages comme celui-là: http://sebsauvage.net/python/snyppets/index.html#tkinter_cxfreeze  )

Voir (en anglais): http://sebsauvage.net/python/snyppets/index.html#py2exe


Par exemple, webGobbler utilise Tkinter et a été packagé avec cx_Freeze et InnoSetup sous forme d'un installeur pour Windows.


A propos de ce document

Ce document a été écrit par Sébastien SAUVAGE, webmaster de http://sebsauvage.net
L'adresse de ce document est http://sebsauvage.net/python/gui/index_fr.html
C'est la traduction française du document: http://sebsauvage.net/python/gui/index.html
Dernière mise à jour: 2006-10-25.