Dies ist die Support Website des Buches:
Das Python Praxisbuch
Der große Profi-Leitfaden für Programmierer
Farid Hajji
Addison Wesley / Pearson Education
ISBN 978-3-8273-2543-3 (Sep 2008), 1298 Seiten.
16. GUI-Toolkits¶
wxPython¶
Screenshots:
URLs:
Erste Schritte in wxPython¶
Wie finde ich mich in wxPython zurecht?¶
#!/usr/bin/env python
# wx_app1.py -- A first wxPython application.
import wx
app = wx.App()
frame = wx.Frame(None, wx.ID_ANY, title="A frame", size=(400, 200))
frame.Show(True)
app.MainLoop()
Screenshots:
Ein Fenster mit einem Button¶
#!/usr/bin/env python
# wx_app2.py -- A window with one button.
import wx
app = wx.App()
frame = wx.Frame(None, wx.ID_ANY, title="A frame", size=(400, 200))
button = wx.Button(frame, wx.ID_EXIT, label="Exit")
button.Bind(event=wx.EVT_BUTTON, handler=lambda evt: frame.Close())
frame.Show(True)
app.MainLoop()
#!/usr/bin/env python
# wx_app2a.py -- A window with one button, using inheritance
import wx
class FrameWithOneButton(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
button = wx.Button(self, wx.ID_EXIT, label="Exit")
button.Bind(event=wx.EVT_BUTTON, handler=lambda evt: self.Close())
if __name__ == '__main__':
app = wx.App()
bframe = FrameWithOneButton(None, id=wx.ID_ANY,
title="A frame with one button",
size=(400, 200))
bframe.Show(True)
app.MainLoop()
Screenshots:
Sizer oder: ein Fenster mit zwei Buttons¶
#!/usr/bin/env python
# wx_app3.py -- A window with two buttons
import wx
class FrameWithTwoButtons(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
self.panel = wx.Panel(self, wx.ID_ANY)
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
button1 = wx.Button(self.panel, wx.ID_EXIT, label="Exit 1")
button1.Bind(event=wx.EVT_BUTTON, handler=lambda evt: self.Close())
self.sizer.Add(button1, wx.EXPAND)
button2 = wx.Button(self.panel, wx.ID_EXIT, label="Exit 2")
button2.Bind(event=wx.EVT_BUTTON, handler=lambda evt: self.Close())
self.sizer.Add(button2, wx.EXPAND)
self.panel.SetSizer(self.sizer)
self.sizer.Fit(self)
if __name__ == '__main__':
app = wx.App()
bframe = FrameWithTwoButtons(None, id=wx.ID_ANY,
title="A frame with two buttons",
size=(400, 200))
bframe.Show(True)
app.MainLoop()
Screenshots:
Ein Button und ein Dialogfenster¶
#!/usr/bin/env python
# wx_app4.py -- A button and a dialog window
import wx
class FrameWithButtonAndDialog(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
button = wx.Button(self, wx.ID_ANY, label="Click me")
button.Bind(event=wx.EVT_BUTTON, handler=self.OnClick)
def OnClick(self, evt):
dialog = wx.MessageDialog(self, message="Thank you for clicking me",
caption="Button clicked",
style=wx.OK | wx.ICON_INFORMATION)
dialog.ShowModal()
if __name__ == '__main__':
app = wx.App()
bdframe = FrameWithButtonAndDialog(None, id=wx.ID_ANY,
title="A button and a dialog",
size=(400, 200))
bdframe.Show(True)
app.MainLoop()
Screenshots:
Ein Dialogfenster als Passwortabfrage¶
#!/usr/bin/env python
# wx_app5.py -- Querying a password with a TextEntryDialog window
import wx
class QueryFrame(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
button = wx.Button(self, wx.ID_ANY, label="Click me")
button.Bind(event=wx.EVT_BUTTON, handler=self.OnClick)
def OnClick(self, evt):
dialog = wx.TextEntryDialog(self, message="Password, please?",
caption="Password query",
style=wx.OK | wx.CANCEL | wx.TE_PASSWORD)
dialog.SetValue("Your password here...")
result = dialog.ShowModal()
if result == wx.ID_OK:
passwd = dialog.GetValue()
elif result == wx.ID_CANCEL:
passwd = "No password supplied"
else:
passwd = "Eh?"
dialog.Destroy()
wx.MessageDialog(self, message="Your password: " + passwd,
caption="Query result", style=wx.OK).ShowModal()
if __name__ == '__main__':
app = wx.App()
qframe = QueryFrame(None, id=wx.ID_ANY,
title="A query frame", size=(400, 200))
qframe.Show(True)
app.MainLoop()
Screenshots:
Weitere Widgets¶
Screenshots:
Responsive GUIs¶
Das Problem des eingefrorenen GUI¶
#!/usr/bin/env python
# wx_app7.py -- simulating a non-responsive GUI
import time
import wx
class NonResponsiveGUI(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
self.sb = wx.StatusBar(self)
self.sb.SetStatusText("Ready")
self.SetStatusBar(self.sb)
self.button = wx.Button(self, id=100, label="Start slow_op")
self.Bind(event=wx.EVT_BUTTON, handler=self.handler, id=100)
self.count = 0L
def handler(self, evt):
"Simulate a slow operation"
time.sleep(10)
self.count = self.count + 1L
self.sb.SetStatusText(str(self.count))
if __name__ == '__main__':
app = wx.App()
frame = NonResponsiveGUI(None, title='Freezing GUI',
size=(400, 100))
frame.Show(True)
app.MainLoop()
Screenshots:
Timer¶
#!/usr/bin/env python
# wx_app7a.py -- simulating a responsive GUI with wx.CallLater
import wx
from wx_app7 import NonResponsiveGUI
class CallLaterGUI(NonResponsiveGUI):
def __init__(self, protect=True, *args, **kw):
NonResponsiveGUI.__init__(self, *args, **kw)
self.protect = protect
def handler(self, evt):
"Simulate a slow operation"
if self.protect:
self.button.Enable(False)
self.timer = wx.CallLater(10000, self.OnTimer)
def OnTimer(self):
self.count = self.count + 1L
self.sb.SetStatusText(str(self.count))
if self.protect:
self.button.Enable(True)
if __name__ == '__main__':
import sys
protectValue = (sys.argv[1] == 'True')
app = wx.App()
frame = CallLaterGUI(protect=protectValue,
parent=None, title='CallLater GUI', size=(400, 100))
frame.Show(True)
app.MainLoop()
#!/usr/bin/env python
# wx_app7b.py -- simulating a responsive GUI with wx.Timer and wx.EVT_TIMER
import wx
from wx_app7 import NonResponsiveGUI
class EVT_TIMER_GUI(NonResponsiveGUI):
def __init__(self, *args, **kw):
NonResponsiveGUI.__init__(self, *args, **kw)
self.Bind(wx.EVT_TIMER, self.OnTimer)
def handler(self, evt):
"Simulate a slow operation"
self.button.Enable(False)
self.timer = wx.Timer(self)
self.timer.Start(10000)
def OnTimer(self, evt):
self.timer.Stop()
self.count = self.count + 1L
self.sb.SetStatusText(str(self.count))
self.button.Enable(True)
if __name__ == '__main__':
app = wx.App()
frame = EVT_TIMER_GUI(None, title='EVT_TIMER GUI', size=(400, 100))
frame.Show(True)
app.MainLoop()
#!/usr/bin/env python
# wx_app7c.py -- simulating a responsive GUI with deriving from wx.Timer
import wx
from wx_app7 import NonResponsiveGUI
class NotifyTimerGUI(NonResponsiveGUI):
def __init__(self, *args, **kw):
NonResponsiveGUI.__init__(self, *args, **kw)
def handler(self, evt):
"Simulate a slow operation"
self.button.Enable(False)
self.timer = NotifyTimer(self)
self.timer.Start(10000)
def OnTimer(self):
self.timer.Stop()
self.count = self.count + 1L
self.sb.SetStatusText(str(self.count))
self.button.Enable(True)
class NotifyTimer(wx.Timer):
def __init__(self, theFrame):
wx.Timer.__init__(self)
self.theFrame = theFrame
def Notify(self):
"Will be triggered with the timer ticks"
self.theFrame.OnTimer()
if __name__ == '__main__':
app = wx.App()
frame = NotifyTimerGUI(None, title='NotifyTimer GUI', size=(400, 100))
frame.Show(True)
app.MainLoop()
Delayed Results¶
#!/usr/bin/env python
# wx_app7d.py -- simulating a responsive GUI with DelayedResults
import time
import wx
import wx.lib.delayedresult as delayedresult
from wx_app7 import NonResponsiveGUI
class DelayedResultsGUI(NonResponsiveGUI):
def __init__(self, protect=True, *args, **kw):
NonResponsiveGUI.__init__(self, *args, **kw)
self.protect = protect
def handler(self, evt):
"Called when self.button is clicked"
if self.protect:
self.button.Enable(False)
delayedresult.startWorker(self.consumeResult, self.produceResult)
def produceResult(self):
"Simulates a slow operation running in its own thread"
time.sleep(10)
return self.count + 1L
def consumeResult(self, delayedResult):
"Collects results from worker thread, running in main thread"
self.count = delayedResult.get()
self.sb.SetStatusText(str(self.count))
if self.protect:
self.button.Enable(True)
if __name__ == '__main__':
import sys
protectValue = (sys.argv[1] == 'True')
app = wx.App()
frame = DelayedResultsGUI(protect=protectValue,
parent=None, title='DelayedResults GUI',
size=(400, 100))
frame.Show(True)
app.MainLoop()
#!/usr/bin/env python
# wx_app8.py -- simulating a responsive GUI with DelayedResults and Cancel
import time
import wx
import wx.lib.delayedresult as delayedresult
class DelayedResultsCancelGUI(wx.Frame):
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
self.count = 0L
self.is_running = False
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.sb = wx.StatusBar(self)
self.sb.SetStatusText("Ready")
self.SetStatusBar(self.sb)
self.panel = wx.Panel(self, wx.ID_ANY)
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.buttonStart = wx.Button(self.panel, id=100, label="Start Thread")
self.buttonStop = wx.Button(self.panel, id=101, label="Stop Thread")
self.buttonStart.Enable(True)
self.buttonStop.Enable(False)
self.sizer.Add(self.buttonStart, wx.EXPAND)
self.sizer.Add(self.buttonStop, wx.EXPAND)
self.Bind(event=wx.EVT_BUTTON, handler=self.handlerStart, id=100)
self.Bind(event=wx.EVT_BUTTON, handler=self.handlerStop, id=101)
self.panel.SetSizer(self.sizer)
self.sizer.Fit(self)
def OnClose(self, evt):
self.is_running = False
self.Destroy()
def handlerStart(self, evt):
"Called when buttonStart is clicked"
self.is_running = True
self.buttonStart.Enable(False)
self.buttonStop.Enable(True)
delayedresult.startWorker(self.consumeResult, self.produceResult)
def handlerStop(self, evt):
"Called when buttonStop is clicked"
self.is_running = False
self.buttonStop.Enable(False)
def produceResult(self):
"Simulates a slow operation running in its own thread"
time.sleep(10)
if self.is_running:
return self.count + 1L
else:
return "Aborted"
def consumeResult(self, delayedResult):
"Collects results from worker thread, running in main thread"
result = delayedResult.get()
if result != "Aborted":
self.count = result
self.sb.SetStatusText(str(result))
self.buttonStart.Enable(True)
self.buttonStop.Enable(False)
if __name__ == '__main__':
app = wx.App()
frame = DelayedResultsCancelGUI(None, title='DelayedResultsCancel GUI',
size=(400, 100))
frame.Show(True)
app.MainLoop()
Screenshots:
Threads¶
Das folgenden Programm wird stückweise in diesem Abschnitt vorgestellt. Hier nochmal als Ganzes:
#!/usr/bin/env python
# wx_app9.py -- Multiple Python Threads and one GUI
from counterthread import CounterThread
import wx
import wx.lib.newevent
# This creates a new Event class and a EVT binder function
(UpdateTextEvent, EVT_UPDATE_TEXT) = wx.lib.newevent.NewEvent()
class CounterGUI(wx.Frame):
def __init__(self, numThreads=5, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.fields = [] # a list of wx.TextCtrl fields for the threads
self.buttons = [] # a list of (startButton, stopButton) objects
self.numThreads = numThreads # number of worker threads
self.threads = self.createThreads() # a list of worker threads
self.BuildGUI()
self.Bind(EVT_UPDATE_TEXT, self.OnUpdate)
def createThreads(self):
threads = []
for i in range(self.numThreads):
thr = CounterThread(self.callbackFn, 3+i)
thr.setName("<Thread %d>" % (i,))
threads.append(thr)
return threads
def BuildGUI(self):
self.panel = wx.Panel(self, wx.ID_ANY)
self.sizer = wx.GridSizer(self.numThreads, 4, 2, 10) # rows, cols,
# vgap, hgab
for i in range(self.numThreads):
textfield = wx.TextCtrl(self.panel, id=wx.ID_ANY)
textfield.Enable(False) # read only text field
self.fields.append(textfield)
buttonStart = wx.Button(self.panel, label="Start Thread")
self.Bind(wx.EVT_BUTTON, handler=self.startThread,
source=buttonStart)
buttonStop = wx.Button(self.panel, label="Stop Thread")
self.Bind(wx.EVT_BUTTON, handler=self.stopThread,
source=buttonStop)
buttonStop.Enable(False)
self.buttons.append((buttonStart, buttonStop))
self.sizer.Add(wx.StaticText(self.panel, label="Thread %d" % (i,)))
self.sizer.Add(textfield, wx.EXPAND)
self.sizer.Add(buttonStart)
self.sizer.Add(buttonStop)
self.panel.SetSizer(self.sizer)
self.sizer.Fit(self)
def callbackFn(self, theThread, theCounter):
"Called to update the GUI. Runs in calling worker thread."
threadID = self.threads.index(theThread)
evt = UpdateTextEvent(threadNum=threadID, value=theCounter)
wx.PostEvent(self, evt)
def OnUpdate(self, evt):
"Updates the GUI. Runs in the main GUI thread."
threadID = evt.threadNum
counterValue = evt.value
self.fields[threadID].SetValue(str(counterValue))
def startThread(self, evt):
startButton = evt.GetEventObject()
buttonID = [lst[0] for lst in self.buttons].index(startButton)
thread = self.threads[buttonID]
startButton.Enable(False) # can't start thread anew
self.buttons[buttonID][1].Enable(True) # enable stop button
thread.start()
def stopThread(self, evt):
stopButton = evt.GetEventObject()
buttonID = [lst[1] for lst in self.buttons].index(stopButton)
thread = self.threads[buttonID]
stopButton.Enable(False)
thread.stop() # may take some seconds
# to be picked up by thread
if __name__ == '__main__':
import sys
number_of_threads = int(sys.argv[1])
app = wx.App()
frame = CounterGUI(parent=None, title='Threads controlling a GUI',
numThreads=number_of_threads)
frame.Show(True)
app.MainLoop()
#!/usr/bin/env python
# counterthread.py -- A Thread which acts like a counter and a timer.
from threading import Thread
import time
class CounterThread(Thread):
def __init__(self, callbackFn, pace=1):
Thread.__init__(self)
self.callbackFn = callbackFn
self.pace = pace
self.counter = 0L
self.keepGoing = True
def run(self):
while self.keepGoing:
time.sleep(self.pace)
self.counter = self.counter + 1L
self.callbackFn(self, self.counter)
def stop(self):
self.keepGoing = False
Screenshots:
Externe Prozesse¶
Das folgende Programm wird stückweise in diesem Abschnitt vorgestellt. Hier nochmal als Ganzes:
#!/usr/bin/env python
# wx_app10.py -- running a program in a wxPython GUI
import wx
import wx.richtext as rt
class RunProcess(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.size = kwargs.get('size', ((1024,768)))
self.process = None
self.pid = None
self.createControls()
self.bindEvents()
self.createLayout()
def createControls(self):
"Create the UI controls / widgets for the GUI shell"
self.normalfont = wx.Font(pointSize=10,
family=wx.FONTFAMILY_TELETYPE,
style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL)
self.panel = wx.Panel(self)
self.command = wx.TextCtrl(self.panel, value='telnet localhost 80')
self.command.SetFont(self.normalfont)
self.runBtn = wx.Button(self.panel, label='Run!')
self.output = rt.RichTextCtrl(self.panel, value='',
style=rt.RE_MULTILINE | rt.RE_READONLY)
self.output.SetFont(self.normalfont)
self.input = wx.TextCtrl(self.panel, value='',
style=wx.TE_PROCESS_ENTER)
self.input.SetFont(self.normalfont)
self.closeBtn = wx.Button(self.panel, label='Close stream')
self.ctrlCBtn = wx.Button(self.panel, label='Ctrl-C')
self.kill9Btn = wx.Button(self.panel, label='Kill -9')
self.input.Enable(False) # will be enabled in OnRunBtn
self.closeBtn.Enable(False) # will be enabled in OnRunBtn
self.ctrlCBtn.Enable(False) # will be enabled in OnRunBtn
self.kill9Btn.Enable(False) # will be enabled in OnRunBtn
self.command.SetFocus()
def bindEvents(self):
"Associate events to callback functions"
self.Bind(wx.EVT_IDLE, self.OnIdle)
self.Bind(wx.EVT_END_PROCESS, self.OnProcessEnded)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.Bind(wx.EVT_BUTTON, self.OnRunBtn, self.runBtn)
self.Bind(wx.EVT_BUTTON, self.OnCloseStream, self.closeBtn)
self.Bind(wx.EVT_BUTTON, self.OnCtrlC, self.ctrlCBtn)
self.Bind(wx.EVT_BUTTON, self.OnKill9, self.kill9Btn)
self.Bind(wx.EVT_TEXT_ENTER, self.OnSendText, self.input)
def createLayout(self):
"Layout the UI widgets into a nice shell"
runbox = wx.BoxSizer(wx.HORIZONTAL)
runbox.Add(self.command, 1, wx.EXPAND | wx.ALL)
runbox.Add(self.runBtn, 0)
inpbox = wx.BoxSizer(wx.HORIZONTAL)
inpbox.Add(self.input, 1, wx.EXPAND | wx.ALL)
inpbox.Add(self.closeBtn, 0)
inpbox.Add(self.ctrlCBtn, 0)
inpbox.Add(self.kill9Btn, 0)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(runbox, 0, wx.EXPAND | wx.ALL)
vbox.Add(self.output, 1, wx.EXPAND | wx.ALL)
vbox.Add(inpbox, 0, wx.EXPAND | wx.ALL)
self.panel.SetSizer(vbox)
vbox.Fit(self)
self.SetSize(self.size)
def OnRunBtn(self, evt):
"The user wants to start a new process"
cmdline = self.command.GetValue().strip()
self.process = wx.Process(self)
self.process.Redirect()
self.pid = wx.Execute(cmdline, wx.EXEC_ASYNC, self.process)
self.command.Enable(False)
self.runBtn.Enable(False)
self.input.Enable(True)
self.closeBtn.Enable(True)
self.ctrlCBtn.Enable(True)
self.kill9Btn.Enable(True)
self.output.SetValue('')
self.input.SetFocus()
def OnSendText(self, evt):
"The user wants to send a line to the process"
text = self.input.GetValue()
self.input.SetValue('')
self.output.BeginBold()
self.output.AppendText(text + '\n')
self.output.EndBold()
self.process.GetOutputStream().write(text + '\n')
self.input.SetFocus()
def OnCloseStream(self, evt):
"The user wants to close the stream to the process"
if self.process is not None:
self.process.CloseOutput()
self.input.Enable(False)
def OnCtrlC(self, evt):
"The user clicked on Ctrl-C; send SIGINT to process"
if self.process is not None:
wx.Process.Kill(pid=self.pid, sig=wx.SIGINT,
flags=wx.KILL_NOCHILDREN)
def OnKill9(self, evt):
"The user clicked on Kill -9; send SIGKILL to process"
if self.process is not None:
wx.Process.Kill(pid=self.pid, sig=wx.SIGKILL,
flags=wx.KILL_NOCHILDREN)
def OnIdle(self, evt):
"When idling, read from the process"
if self.process is not None:
for stream in (self.process.GetInputStream(),
self.process.GetErrorStream()):
if stream.CanRead():
text = stream.read()
self.output.AppendText(text)
def OnCloseWindow(self, evt):
"The window is closing; destroy the process"
if self.process is not None:
self.process.Destroy()
self.process = None
self.Destroy()
def OnProcessEnded(self, evt):
"The process terminated; react accordingly"
stream = self.process.GetInputStream()
if stream.CanRead():
text = stream.read()
self.output.AppendText(text)
self.process.Destroy()
self.process = None
self.pid = None
wx.MessageDialog(self, message='Process ended, pid:%s, exitcode:%s' % \
(evt.GetPid(), evt.GetExitCode()),
caption='Process ended',
style=wx.OK | wx.ICON_INFORMATION).ShowModal()
self.command.Enable(True)
self.runBtn.Enable(True)
self.input.Enable(False)
self.closeBtn.Enable(False)
self.ctrlCBtn.Enable(False)
self.kill9Btn.Enable(False)
self.command.SetFocus()
if __name__ == '__main__':
app = wx.App()
frame = RunProcess(parent=None, title='Run process')
frame.Show(True)
app.MainLoop()
Screenshots:
Integration mit Twisted¶
#!/usr/bin/env python
# wxdemo.py -- Integrating Twisted and wxPython event loops with wxreactor
# Copyright (c) 2001-2006 Twisted Matrix Laboratories.
# Adapted from ${twisted_src}/doc/core/examples/wxdemo.py
import sys
import wx
from twisted.python import log
from twisted.internet import wxreactor
wxreactor.install()
# import twisted.internet.reactor only after installing wxreactor:
from twisted.internet import reactor, defer
class MyFrame(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, size=(300,200), *args, **kwargs)
menu = wx.Menu()
menu.Append(wx.ID_EXIT, "E&xit", "Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(menu, "&File")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.DoExit, id=wx.ID_EXIT)
# make sure reactor.stop() is used to stop event loop:
self.Bind(wx.EVT_CLOSE, lambda evt: reactor.stop())
def DoExit(self, event):
reactor.stop()
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, id=wx.ID_ANY, title="Hello, world")
frame.Show(True)
self.SetTopWindow(frame)
# look, we can use twisted calls!
reactor.callLater(10, self.tenSecondsPassed)
return True
def tenSecondsPassed(self):
print "ten seconds passed"
def demo():
log.startLogging(sys.stdout)
# register the wxApp instance with Twisted:
app = MyApp(0)
reactor.registerWxApp(app)
# start the event loop:
reactor.run()
if __name__ == '__main__':
demo()
Screenshots:
Schnelle Entwicklung mit RAD-Tools¶
wxGlade¶
URLs:
Screenshots:
- Der wxGlade Desktop
- Einen Titel im Properties Fenster setzen
- Der Objekt-Baum hat sich verändert
- Einen Text für die StatusBar
- Der Menü-Editor
- Das Frame mit einem GridSizer unterteilen
- Der Desktop sieht jetzt so aus
- Und nun generieren wir den Code
- Die Anwendung ausführen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# generated by wxGlade 0.6.3 on Fri Jun 06 02:40:48 2008
import wx
# begin wxGlade: extracode
# end wxGlade
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
# begin wxGlade: MyFrame.__init__
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.themainpanel = wx.Panel(self, -1)
self.label_name = wx.StaticText(self.themainpanel, -1, "Username")
self.text_ctrl_username = wx.TextCtrl(self.themainpanel, -1, "")
self.label_password = wx.StaticText(self.themainpanel, -1, "Password")
self.text_ctrl_password = wx.TextCtrl(self.themainpanel, -1, "",
style=wx.TE_PASSWORD)
self.button_OK = wx.Button(self.themainpanel, wx.ID_OK, "")
self.button_Cancel = wx.Button(self.themainpanel, wx.ID_CANCEL, "")
self.thestatusbar = self.CreateStatusBar(2, 0)
# Menu Bar
self.themenubar = wx.MenuBar()
self.fileMenu = wx.Menu()
self.newMenu = wx.MenuItem(self.fileMenu, wx.ID_NEW,
"&New", "", wx.ITEM_NORMAL)
self.fileMenu.AppendItem(self.newMenu)
self.openMenu = wx.MenuItem(self.fileMenu, wx.ID_OPEN,
"&Open", "", wx.ITEM_NORMAL)
self.fileMenu.AppendItem(self.openMenu)
self.saveMenu = wx.MenuItem(self.fileMenu, wx.ID_SAVE,
"&Save", "", wx.ITEM_NORMAL)
self.fileMenu.AppendItem(self.saveMenu)
self.saveMenu = wx.MenuItem(self.fileMenu, wx.ID_SAVEAS,
"Save &As...", "", wx.ITEM_NORMAL)
self.fileMenu.AppendItem(self.saveMenu)
self.fileMenu.AppendSeparator()
self.quitMenu = wx.MenuItem(self.fileMenu, wx.ID_EXIT,
"&Quit", "", wx.ITEM_NORMAL)
self.fileMenu.AppendItem(self.quitMenu)
self.themenubar.Append(self.fileMenu, "&File")
self.helpMenu = wx.Menu()
self.themenubar.Append(self.helpMenu, "&Help")
self.SetMenuBar(self.themenubar)
# Menu Bar end
self.__set_properties()
self.__do_layout()
# end wxGlade
def __set_properties(self):
# begin wxGlade: MyFrame.__set_properties
self.SetTitle("My first wxGlade Demo")
self.button_OK.SetDefault()
self.thestatusbar.SetStatusWidths([-1, 80])
# statusbar fields
thestatusbar_fields = ["Created with wxGlade!", "Ready"]
for i in range(len(thestatusbar_fields)):
self.thestatusbar.SetStatusText(thestatusbar_fields[i], i)
# end wxGlade
def __do_layout(self):
# begin wxGlade: MyFrame.__do_layout
sizer_1 = wx.BoxSizer(wx.VERTICAL)
grid_sizer_1 = wx.GridSizer(3, 2, 2, 3)
grid_sizer_1.Add(self.label_name, 0, 0, 0)
grid_sizer_1.Add(self.text_ctrl_username, 0, 0, 0)
grid_sizer_1.Add(self.label_password, 0, 0, 0)
grid_sizer_1.Add(self.text_ctrl_password, 0, 0, 0)
grid_sizer_1.Add(self.button_OK, 0, 0, 0)
grid_sizer_1.Add(self.button_Cancel, 0, 0, 0)
self.themainpanel.SetSizer(grid_sizer_1)
sizer_1.Add(self.themainpanel, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
# end wxGlade
# end of class MyFrame
if __name__ == "__main__":
app = wx.PySimpleApp(0)
wx.InitAllImageHandlers()
frame_1 = MyFrame(None, -1, "")
app.SetTopWindow(frame_1)
frame_1.Show()
app.MainLoop()
<?xml version="1.0"?>
<!-- generated by wxGlade 0.6.3 on Fri Jun 06 02:40:44 2008 -->
<application path="E:\Documents and Settings\farid\myApp.py" name="" class="" option="0" language="python" top_window="frame_1" encoding="UTF-8" use_gettext="0" overwrite="1" use_new_namespace="1" for_version="2.8" is_template="0">
<object class="MyFrame" name="frame_1" base="EditFrame">
<style>wxDEFAULT_FRAME_STYLE</style>
<title>My first wxGlade Demo</title>
<menubar>1</menubar>
<statusbar>1</statusbar>
<object class="wxBoxSizer" name="sizer_1" base="EditBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<border>0</border>
<option>1</option>
<object class="wxPanel" name="themainpanel" base="EditPanel">
<style>wxTAB_TRAVERSAL</style>
<object class="wxGridSizer" name="grid_sizer_1" base="EditGridSizer">
<hgap>3</hgap>
<rows>3</rows>
<cols>2</cols>
<vgap>2</vgap>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxStaticText" name="label_name" base="EditStaticText">
<attribute>1</attribute>
<label>Username</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxTextCtrl" name="text_ctrl_username" base="EditTextCtrl">
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxStaticText" name="label_password" base="EditStaticText">
<attribute>1</attribute>
<label>Password</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxTextCtrl" name="text_ctrl_password" base="EditTextCtrl">
<style>wxTE_PASSWORD</style>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_OK" base="EditButton">
<stockitem>OK</stockitem>
<default>1</default>
<label>&OK</label>
</object>
</object>
<object class="sizeritem">
<border>0</border>
<option>0</option>
<object class="wxButton" name="button_Cancel" base="EditButton">
<stockitem>CANCEL</stockitem>
<label>&Cancel</label>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="wxStatusBar" name="thestatusbar" base="EditStatusBar">
<fields>
<field width="-1">Created with wxGlade!</field>
<field width="80">Ready</field>
</fields>
</object>
<object class="wxMenuBar" name="themenubar" base="EditMenuBar">
<menus>
<menu name="fileMenu" label="&File">
<item>
<label>&New</label>
<id>wx.ID_NEW</id>
<name>newMenu</name>
</item>
<item>
<label>&Open</label>
<id>wx.ID_OPEN</id>
<name>openMenu</name>
</item>
<item>
<label>&Save</label>
<id>wx.ID_SAVE</id>
<name>saveMenu</name>
</item>
<item>
<label>Save &As...</label>
<id>wx.ID_SAVEAS</id>
<name>saveMenu</name>
</item>
<item>
<label>---</label>
<id>---</id>
<name>---</name>
</item>
<item>
<label>&Quit</label>
<id>wx.ID_EXIT</id>
<name>quitMenu</name>
</item>
</menu>
<menu name="helpMenu" itemid="wx.ID_HELP" label="&Help">
</menu>
</menus>
</object>
</object>
</application>
XRC-Ressourcen¶
<?xml version="1.0" encoding="UTF-8"?>
<!-- generated by wxGlade 0.6.3 on Fri Jun 06 03:05:06 2008 -->
<resource version="2.3.0.1">
<object class="wxFrame" name="frame_1" subclass="MyFrame">
<style>wxDEFAULT_FRAME_STYLE</style>
<title>My first wxGlade Demo</title>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<option>1</option>
<flag>wxEXPAND</flag>
<object class="wxPanel" name="themainpanel">
<style>wxTAB_TRAVERSAL</style>
<object class="wxGridSizer">
<hgap>3</hgap>
<rows>3</rows>
<cols>2</cols>
<vgap>2</vgap>
<object class="sizeritem">
<object class="wxStaticText" name="label_name">
<label>Username</label>
</object>
</object>
<object class="sizeritem">
<object class="wxTextCtrl" name="text_ctrl_username">
</object>
</object>
<object class="sizeritem">
<object class="wxStaticText" name="label_password">
<label>Password</label>
</object>
</object>
<object class="sizeritem">
<object class="wxTextCtrl" name="text_ctrl_password">
<style>wxTE_PASSWORD</style>
</object>
</object>
<object class="sizeritem">
<object class="wxButton" name="wxID_OK">
<default>1</default>
</object>
</object>
<object class="sizeritem">
<object class="wxButton" name="wxID_CANCEL">
</object>
</object>
</object>
</object>
</object>
</object>
<object class="wxStatusBar" name="thestatusbar">
<fields>2</fields>
<widths>-1, 80</widths>
</object>
<object class="wxMenuBar" name="themenubar">
<object class="wxMenu" name="fileMenu">
<label>_File</label>
<object class="wxMenuItem" name="newMenu">
<label>_New</label>
</object>
<object class="wxMenuItem" name="openMenu">
<label>_Open</label>
</object>
<object class="wxMenuItem" name="saveMenu">
<label>_Save</label>
</object>
<object class="wxMenuItem" name="saveMenu">
<label>Save _As...</label>
</object>
<object class="separator"/>
<object class="wxMenuItem" name="quitMenu">
<label>_Quit</label>
</object>
</object>
<object class="wxMenuItem" name="helpMenu">
<label>_Help</label>
</object>
</object>
</object>
</resource>
URLs:
- XRCed als alleinstehende Application (ist auch Bestandteil von wxPython)
- Beispiel für wx.wrc
PyQt4¶
PyQt4 installieren¶
URLs:
Installation unter Unix¶
Installation unter Windows¶
Screenshots:
- Erst muß die Qt-Bibliothek installiert werden
- Dann soll MinGW installiert werden (haben wir aber schon im ersten Kapitel)
- Diese Fehlermeldung kann man getrost wegklicken
Erste Schritte in PyQt4¶
Screenshots:
Wie finde ich mich in PyQt4 und Qt4 zurecht?¶
Screenshots:
Ein Fenster mit einem Button¶
#!/usr/bin/env python
# pyqt4_tutorial_t2.py -- PyQt tutorial 2
# From: /usr/local/share/examples/py-qt4/tutorial/t2.py
import sys
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication(sys.argv)
quit = QtGui.QPushButton("Quit")
quit.resize(75, 30)
quit.setFont(QtGui.QFont("Times", 18, QtGui.QFont.Bold))
QtCore.QObject.connect(quit, QtCore.SIGNAL("clicked()"),
app, QtCore.SLOT("quit()"))
quit.show()
sys.exit(app.exec_())
Screenshots:
Geometrie, Signale und Slots¶
#!/usr/bin/env python
# pyqt4_tutorial_t5.py -- PyQt tutorial 5
# From: /usr/local/share/examples/py-qt4/tutorial/t5.py
import sys
from PyQt4 import QtCore, QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
quit = QtGui.QPushButton("Quit")
quit.setFont(QtGui.QFont("Times", 18, QtGui.QFont.Bold))
lcd = QtGui.QLCDNumber(2)
slider = QtGui.QSlider(QtCore.Qt.Horizontal)
slider.setRange(0, 99)
slider.setValue(0)
self.connect(quit, QtCore.SIGNAL("clicked()"),
QtGui.qApp, QtCore.SLOT("quit()"))
self.connect(slider, QtCore.SIGNAL("valueChanged(int)"),
lcd, QtCore.SLOT("display(int)"))
layout = QtGui.QVBoxLayout()
layout.addWidget(quit);
layout.addWidget(lcd);
layout.addWidget(slider);
self.setLayout(layout);
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
Screenshots:
Responsive GUIs¶
Ein HTTP-Downloader¶
Das folgende Programm wird stückweise in diesem Abschnitt eingeführt und erklärt. Hier steht es nochmal als Ganzes:
#!/usr/bin/env python
# pyqt4_network_http.py -- PyQt4 port of the network/http example from Qt v4.x
# From: /usr/local/share/examples/py-qt4/network/http.py
import sys
from PyQt4 import QtCore, QtGui, QtNetwork
class HttpWindow(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.urlLineEdit = QtGui.QLineEdit("http://www.ietf.org/iesg/1rfc_index.txt")
self.urlLabel = QtGui.QLabel(self.tr("&URL:"))
self.urlLabel.setBuddy(self.urlLineEdit)
self.statusLabel = QtGui.QLabel(self.tr("Please enter the URL of a file "
"you want to download."))
self.quitButton = QtGui.QPushButton(self.tr("Quit"))
self.downloadButton = QtGui.QPushButton(self.tr("Download"))
self.downloadButton.setDefault(True)
self.progressDialog = QtGui.QProgressDialog(self)
self.http = QtNetwork.QHttp(self)
self.outFile = None
self.httpGetId = 0
self.httpRequestAborted = False
self.connect(self.urlLineEdit, QtCore.SIGNAL("textChanged(QString &)"),
self.enableDownloadButton)
self.connect(self.http, QtCore.SIGNAL("requestFinished(int, bool)"),
self.httpRequestFinished)
self.connect(self.http, QtCore.SIGNAL("dataReadProgress(int, int)"),
self.updateDataReadProgress)
self.connect(self.http, QtCore.SIGNAL("responseHeaderReceived(QHttpResponseHeader &)"),
self.readResponseHeader)
self.connect(self.progressDialog, QtCore.SIGNAL("canceled()"),
self.cancelDownload)
self.connect(self.downloadButton, QtCore.SIGNAL("clicked()"),
self.downloadFile)
self.connect(self.quitButton, QtCore.SIGNAL("clicked()"),
self, QtCore.SLOT("close()"))
topLayout = QtGui.QHBoxLayout()
topLayout.addWidget(self.urlLabel)
topLayout.addWidget(self.urlLineEdit)
buttonLayout = QtGui.QHBoxLayout()
buttonLayout.addStretch(1)
buttonLayout.addWidget(self.downloadButton)
buttonLayout.addWidget(self.quitButton)
mainLayout = QtGui.QVBoxLayout()
mainLayout.addLayout(topLayout)
mainLayout.addWidget(self.statusLabel)
mainLayout.addLayout(buttonLayout)
self.setLayout(mainLayout)
self.setWindowTitle(self.tr("HTTP"))
self.urlLineEdit.setFocus()
def enableDownloadButton(self):
self.downloadButton.setEnabled(not self.urlLineEdit.text().isEmpty())
def downloadFile(self):
url = QtCore.QUrl(self.urlLineEdit.text())
fileInfo = QtCore.QFileInfo(url.path())
fileName = QtCore.QString(fileInfo.fileName())
if QtCore.QFile.exists(fileName):
QtGui.QMessageBox.information(self, self.tr("HTTP"), self.tr(
"There already exists a file called %1 "
"in the current directory.").arg(fileName))
return
self.outFile = QtCore.QFile(fileName)
if not self.outFile.open(QtCore.QIODevice.WriteOnly):
QtGui.QMessageBox.information(self, self.tr("HTTP"),
self.tr("Unable to save the file %1: %2.")
.arg(fileName).arg(self.outFile.errorString()))
self.outFile = None
return
if url.port() != -1:
self.http.setHost(url.host(), url.port())
else:
self.http.setHost(url.host(), 80)
if not url.userName().isEmpty():
self.http.setUser(url.userName(), url.password())
self.httpRequestAborted = False
self.httpGetId = self.http.get(url.path(), self.outFile)
self.progressDialog.setWindowTitle(self.tr("HTTP"))
self.progressDialog.setLabelText(self.tr("Downloading %1.").arg(fileName))
self.downloadButton.setEnabled(False)
def cancelDownload(self):
self.statusLabel.setText(self.tr("Download canceled."))
self.httpRequestAborted = True
self.http.abort()
self.downloadButton.setEnabled(True)
def readResponseHeader(self, responseHeader):
if responseHeader.statusCode() != 200:
QtGui.QMessageBox.information(self, self.tr("HTTP"),
self.tr("Download failed: %1.")
.arg(responseHeader.reasonPhrase()))
self.httpRequestAborted = True
self.progressDialog.hide()
self.http.abort()
return
def updateDataReadProgress(self, bytesRead, totalBytes):
if self.httpRequestAborted:
return
self.progressDialog.setMaximum(totalBytes)
self.progressDialog.setValue(bytesRead)
def httpRequestFinished(self, requestId, error):
if self.httpRequestAborted:
if self.outFile is not None:
self.outFile.close()
self.outFile.remove()
self.outFile = None
self.progressDialog.hide()
return
if requestId != self.httpGetId:
return
self.progressDialog.hide()
self.outFile.close()
if error:
self.outFile.remove()
QtGui.QMessageBox.information(self, self.tr("HTTP"),
self.tr("Download failed: %1.")
.arg(self.http.errorString()))
else:
fileName = QtCore.QFileInfo(QtCore.QUrl(self.urlLineEdit.text()).path()).fileName()
self.statusLabel.setText(self.tr("Downloaded %1 to current directory.").arg(fileName))
self.downloadButton.setEnabled(True)
self.outFile = None
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
httpWin = HttpWindow()
sys.exit(httpWin.exec_())
Screenshots:
Schnelle Entwicklung mit RAD-Tools¶
Qt Designer¶
Screenshots:
<ui version="4.0" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>DemoDialog</class>
<widget class="QDialog" name="DemoDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<string>PyUIC4 Demo Dialog</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="2" column="0" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item rowspan="3" row="0" column="1" >
<widget class="QListWidget" name="list" />
</item>
<item row="1" column="0" >
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="button1" >
<property name="text" >
<string>Add items</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button2" >
<property name="text" >
<string>Clear list</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2" >
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>131</width>
<height>31</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton" >
<property name="text" >
<string>OK</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<pixmapfunction></pixmapfunction>
<resources/>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>DemoDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>369</x>
<y>256</y>
</hint>
<hint type="destinationlabel" >
<x>96</x>
<y>254</y>
</hint>
</hints>
</connection>
<connection>
<sender>button2</sender>
<signal>clicked()</signal>
<receiver>list</receiver>
<slot>clear()</slot>
<hints>
<hint type="sourcelabel" >
<x>92</x>
<y>112</y>
</hint>
<hint type="destinationlabel" >
<x>279</x>
<y>123</y>
</hint>
</hints>
</connection>
</connections>
</ui>
.ui-Dateien laden¶
Aus pyqt4_demo.ui
wird mit uic
die Datei pyqt4_demo.cpp
erzeugt:
/********************************************************************************
** Form generated from reading ui file 'demo.ui'
**
** Created: Mon Jun 9 04:34:50 2008
** by: Qt User Interface Compiler version 4.3.4
**
** WARNING! All changes made in this file will be lost when recompiling ui file!
********************************************************************************/
#ifndef UI_DEMO_H
#define UI_DEMO_H
#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QDialog>
#include <QtGui/QGridLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QListWidget>
#include <QtGui/QPushButton>
#include <QtGui/QSpacerItem>
#include <QtGui/QVBoxLayout>
class Ui_DemoDialog
{
public:
QGridLayout *gridLayout;
QSpacerItem *spacerItem;
QSpacerItem *spacerItem1;
QListWidget *list;
QVBoxLayout *vboxLayout;
QPushButton *button1;
QPushButton *button2;
QHBoxLayout *hboxLayout;
QSpacerItem *spacerItem2;
QPushButton *okButton;
void setupUi(QDialog *DemoDialog)
{
if (DemoDialog->objectName().isEmpty())
DemoDialog->setObjectName(QString::fromUtf8("DemoDialog"));
DemoDialog->resize(400, 300);
gridLayout = new QGridLayout(DemoDialog);
#ifndef Q_OS_MAC
gridLayout->setSpacing(6);
#endif
#ifndef Q_OS_MAC
gridLayout->setMargin(9);
#endif
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
spacerItem = new QSpacerItem(20, 40, QSizePolicy::Minimum,
QSizePolicy::Expanding);
gridLayout->addItem(spacerItem, 2, 0, 1, 1);
spacerItem1 = new QSpacerItem(20, 40, QSizePolicy::Minimum,
QSizePolicy::Expanding);
gridLayout->addItem(spacerItem1, 0, 0, 1, 1);
list = new QListWidget(DemoDialog);
list->setObjectName(QString::fromUtf8("list"));
gridLayout->addWidget(list, 0, 1, 3, 1);
vboxLayout = new QVBoxLayout();
#ifndef Q_OS_MAC
vboxLayout->setSpacing(6);
#endif
vboxLayout->setMargin(0);
vboxLayout->setObjectName(QString::fromUtf8("vboxLayout"));
button1 = new QPushButton(DemoDialog);
button1->setObjectName(QString::fromUtf8("button1"));
vboxLayout->addWidget(button1);
button2 = new QPushButton(DemoDialog);
button2->setObjectName(QString::fromUtf8("button2"));
vboxLayout->addWidget(button2);
gridLayout->addLayout(vboxLayout, 1, 0, 1, 1);
hboxLayout = new QHBoxLayout();
#ifndef Q_OS_MAC
hboxLayout->setSpacing(6);
#endif
hboxLayout->setMargin(0);
hboxLayout->setObjectName(QString::fromUtf8("hboxLayout"));
spacerItem2 = new QSpacerItem(131, 31, QSizePolicy::Expanding,
QSizePolicy::Minimum);
hboxLayout->addItem(spacerItem2);
okButton = new QPushButton(DemoDialog);
okButton->setObjectName(QString::fromUtf8("okButton"));
hboxLayout->addWidget(okButton);
gridLayout->addLayout(hboxLayout, 3, 0, 1, 2);
retranslateUi(DemoDialog);
QObject::connect(okButton, SIGNAL(clicked()), DemoDialog, SLOT(accept()));
QObject::connect(button2, SIGNAL(clicked()), list, SLOT(clear()));
QMetaObject::connectSlotsByName(DemoDialog);
} // setupUi
void retranslateUi(QDialog *DemoDialog)
{
DemoDialog->setWindowTitle(QApplication::translate("DemoDialog",
"PyUIC4 Demo Dialog", 0, QApplication::UnicodeUTF8));
button1->setText(QApplication::translate("DemoDialog",
"Add items", 0, QApplication::UnicodeUTF8));
button2->setText(QApplication::translate("DemoDialog",
"Clear list", 0, QApplication::UnicodeUTF8));
okButton->setText(QApplication::translate("DemoDialog",
"OK", 0, QApplication::UnicodeUTF8));
Q_UNUSED(DemoDialog);
} // retranslateUi
};
namespace Ui {
class DemoDialog: public Ui_DemoDialog {};
} // namespace Ui
#endif // UI_DEMO_H
Und pyuic4
erzeugt daraus die Datei pyqt4_demo.py
, die so
aussieht:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'demo.ui'
#
# Created: Mon Jun 09 04:45:23 2008
# by: PyQt4 UI code generator 4.3.4-snapshot-20080328
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
class Ui_DemoDialog(object):
def setupUi(self, DemoDialog):
DemoDialog.setObjectName("DemoDialog")
DemoDialog.resize(QtCore.QSize(QtCore.QRect(0,0,400,300).size())
.expandedTo(DemoDialog.minimumSizeHint()))
self.gridlayout = QtGui.QGridLayout(DemoDialog)
self.gridlayout.setMargin(9)
self.gridlayout.setSpacing(6)
self.gridlayout.setObjectName("gridlayout")
spacerItem = QtGui.QSpacerItem(20,40,QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.gridlayout.addItem(spacerItem,2,0,1,1)
spacerItem1 = QtGui.QSpacerItem(20,40,QtGui.QSizePolicy.Minimum,
QtGui.QSizePolicy.Expanding)
self.gridlayout.addItem(spacerItem1,0,0,1,1)
self.list = QtGui.QListWidget(DemoDialog)
self.list.setObjectName("list")
self.gridlayout.addWidget(self.list,0,1,3,1)
self.vboxlayout = QtGui.QVBoxLayout()
self.vboxlayout.setMargin(0)
self.vboxlayout.setSpacing(6)
self.vboxlayout.setObjectName("vboxlayout")
self.button1 = QtGui.QPushButton(DemoDialog)
self.button1.setObjectName("button1")
self.vboxlayout.addWidget(self.button1)
self.button2 = QtGui.QPushButton(DemoDialog)
self.button2.setObjectName("button2")
self.vboxlayout.addWidget(self.button2)
self.gridlayout.addLayout(self.vboxlayout,1,0,1,1)
self.hboxlayout = QtGui.QHBoxLayout()
self.hboxlayout.setMargin(0)
self.hboxlayout.setSpacing(6)
self.hboxlayout.setObjectName("hboxlayout")
spacerItem2 = QtGui.QSpacerItem(131,31,QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Minimum)
self.hboxlayout.addItem(spacerItem2)
self.okButton = QtGui.QPushButton(DemoDialog)
self.okButton.setObjectName("okButton")
self.hboxlayout.addWidget(self.okButton)
self.gridlayout.addLayout(self.hboxlayout,3,0,1,2)
self.retranslateUi(DemoDialog)
QtCore.QObject.connect(self.okButton,QtCore.SIGNAL("clicked()"),
DemoDialog.accept)
QtCore.QObject.connect(self.button2,QtCore.SIGNAL("clicked()"),
self.list.clear)
QtCore.QMetaObject.connectSlotsByName(DemoDialog)
def retranslateUi(self, DemoDialog):
DemoDialog.setWindowTitle(QtGui.QApplication.translate("DemoDialog",
"PyUIC4 Demo Dialog", None, QtGui.QApplication.UnicodeUTF8))
self.button1.setText(QtGui.QApplication.translate("DemoDialog",
"Add items", None, QtGui.QApplication.UnicodeUTF8))
self.button2.setText(QtGui.QApplication.translate("DemoDialog",
"Clear list", None, QtGui.QApplication.UnicodeUTF8))
self.okButton.setText(QtGui.QApplication.translate("DemoDialog",
"OK", None, QtGui.QApplication.UnicodeUTF8))
#!/usr/bin/env python
# pyqt4_pyuic_load_ui1.py -- dynamically load demo.py
# From: /usr/local/share/examples/py-qt4/pyuic/load_ui1.py
import sys
from PyQt4 import QtGui, uic
app = QtGui.QApplication(sys.argv)
widget = uic.loadUi("demo.ui")
widget.show()
app.exec_()
#!/usr/bin/env python
# pyqt4_pyuic_load_ui2.py -- Load demo.ui and merge it into a `QDialog` class
# From: /usr/local/share/examples/py-qt4/pyuic/load_ui2.py
import sys
from PyQt4 import QtCore, QtGui, uic
class DemoImpl(QtGui.QDialog):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
uic.loadUi("demo.ui", self)
@QtCore.pyqtSignature("")
def on_button1_clicked(self):
for s in "This is a demo".split(" "):
self.list.addItem(s)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
widget = DemoImpl()
widget.show()
app.exec_()
#!/usr/bin/env python
# pyqt4_pyuic_compile_on_the_fly.py -- load and compile demo.ui into a class
import sys
from PyQt4 import QtCore, QtGui, uic
app = QtGui.QApplication(sys.argv)
form_class, base_class = uic.loadUiType("demo.ui")
class DemoImpl(QtGui.QDialog, form_class):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
self.setupUi(self)
@QtCore.pyqtSignature("")
def on_button1_clicked(self):
for s in "This is a demo".split(" "):
self.list.addItem(s)
if __name__ == '__main__':
form = DemoImpl()
form.show()
app.exec_()
Tkinter¶
Erste Schritte in Tkinter¶
#!/usr/bin/env python
# tksimple.py -- A simple 2 button application
import Tkinter
from Tkconstants import *
def build_gui():
root = Tkinter.Tk()
button1 = Tkinter.Button(root, text="Click Me!", command=button_pressed)
button2 = Tkinter.Button(root, text="Exit", command=root.destroy)
button1.pack(side=RIGHT, fill=BOTH, expand=True)
button2.pack(side=RIGHT, fill=BOTH, expand=True)
return root
def button_pressed():
print "Button pressed!"
root = build_gui()
root.mainloop()
Screenshots:
Wie findet man sich in Tkinter zurecht?¶
URLs:
- http://wiki.python.org/moin/TkInter
- http://tkinter.unpythonic.net/wiki/
- http://www.pythonware.com/library/tkinter/introduction/index.htm
- http://effbot.org/zone/wck.htm
- http://www.astro.washington.edu/owen/TkinterSummary.html
- Ein GUI-Builder für Tkinter
Note
Die Python-Shell IDLE ist in Tkinter geschrieben. Daher lassen sich im IDLE-Quellcode eine Menge guter Anwendungsbeispiele für Tkinter finden. Übrigens: da IDLE Teil der Python Standard Library ist, lassen sich Komponenten daraus ebenfalls für eigene Projekte selbstverständlich wiederverwenden. (15.08.2008)
Text-basierte GUIs¶
pythondialog¶
URLs:
- Die Website der dialog Bibliothek
- Die Website von pythondialog (aber mit
easy_install pythondialog
installieren)
#!/usr/bin/env python
# pydialog_input_password.py -- inputbox, passwordbox, msgbox
import dialog
def verify_credentials(d):
"Get username / password, and verify credentials"
(code, username) = d.inputbox("What is your name?")
(code, password) = d.passwordbox("What is your password?")
if username == "Falken" and password == "CPE 1704 TKS":
d.msgbox("You authenticated successfully.")
return True
else:
d.msgbox("Sorry. Authorized personnel only.")
return False
if __name__ == '__main__':
d = dialog.Dialog(dialog="cdialog")
result = verify_credentials(d)
print "Credentials:", result
#!/usr/bin/env python
# pydialog_yesno.py -- yes/no box
import dialog
the_question = '''You are about to nuke the planet.
Do you really, absolutely without any doubts want to do it? Do you
realize that there is no way to undo this action? Please confirm that
you intend to nuke the whole planet now!'''
d = dialog.Dialog(dialog="cdialog")
result = d.yesno(text=the_question, width=40, height=12)
print "Result:", result == d.DIALOG_OK
#!/usr/bin/env python
# pydialog_fselect.py -- A file selector box
import dialog
d = dialog.Dialog(dialog="cdialog")
(code, path) = d.fselect(filepath='/usr/local/lib/python2.5/site-packages/',
height=10, width=50)
ret = d.msgbox(text="You selected (%d, %s)" % (code, path), width=50)
Screenshots:
Low-level APIs¶
Ein Blick unter die Haube: Rohe Events¶
GUI-Toolkits vereinfachen eine komplexe API¶
Signatur der Funktion XCreateWindow
:
extern Window XCreateWindow(
Display* /* display */,
Window /* parent */,
int /* x */,
int /* y */,
unsigned int /* width */,
unsigned int /* height */,
unsigned int /* border_width */,
int /* depth */,
unsigned int /* class */,
Visual* /* visual */,
unsigned long /* valuemask */,
XSetWindowAttributes* /* attributes */
);
Signatur der Funktion CreateWindow
:
HWND CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);