Sei sulla pagina 1di 69

Introduction to PyQt4 toolkit

About this tutorial


This is an introductory PyQt4 tutorial. The purpose of this tutorial is to get you started with the PyQt4 toolkit. The tutorial has been created and tested on Linux.

About PyQt
PyQt is a toolkit for creating G I applications. It is a blending of python progra!!ing language and the successfull Qt library. Qt library is one of the !ost powerful libraries on this planet. If not the !ost powerful. The official ho!e site for PyQt is on www.ri"erbankco!puting.co.uk It was de"eloped by Phil Tho!pson. PyQt is i!ple!ented as a set of python !odules. It has o"er #$$ classes and al!ost %$$$ functions and !ethods. It is a !ultiplatfor! toolkit. It runs on all !a&or operating syste!s. Including nix' (indows and )ac. PyQt is dual licenced. *e"elopers can choose between GPL and co!!ercial licence. Pre"iously' GPL "ersion was a"ailable only on nix. +tarting fro! PyQt "ersion 4' GPL licence is a"ailable on all supported platfor!s. ,ecause there are a lot of classes a"ailable' they ha"e been di"ided into se"eral !odules.

-igure. PyQt4 )odules The Qt/ore !odule contains the core non0gui functionality. This !odule is used for working with ti!e' files and directories' "arious data types' strea!s' urls' !i!e types' threads or processes. The QtGui !odule contains the graphical co!ponents and related classes. These include for exa!ple buttons' windows' status bars' toolbars' sliders' bit!aps' colors' fonts etc. The Qt1etwork !odule contains the classes for network progra!!ing. These classes allow to

write T/P2IP and *P clients and ser"ers. They !ake the network progra!!ing easier and !ore portable. The Qt3!l contains classes for working with x!l files. This !odule pro"ides i!ple!entation for both +A3 and *4) APIs. The Qt+"g !odule pro"ides classes for displaying the contents of +5G files. +calable 5ector Graphics 6+5G7 is a language for describing two0di!ensional graphics and graphical applications in 3)L. The Qt4penGL !odule is used for rendering #* and 8* graphics using the 4penGL library. The !odule enables sea!less integration of the Qt G I libary and the 4penGL library. The Qt+9l !odule pro"ides classes for working with databases.

Python
Python is a successful scripting language. It was initially de"eloped by Guido "an :ossu!. It was first released in ;<<;. Python was inspired by A,/ and =askell progra!!ing languages. Python is a high le"el' general purpose' !ultiplatfor!' interpreted language. +o!e prefer to call it a dyna!ic language. It is easy to learn. Python is a !ini!alistic language. 4ne of it>s !ost "isible features is that it does not use se!icolons nor brackets. Python uses intendation instead. The !ost recent "ersion of python is 8.?' which was released in +epte!ber 8$$%. Today' Python is !aintained by a large group of "olunteers worldwide. The TI4,@ Progra!!ing /o!!unity Index gi"es us a theoretical usage of "arious progra!!ing languages. Aa"a rules. The /BB language is detroned. ,ut /BB will continue to be used in it>s footholds for the co!ing decades and ther see!s to be no real threat for it. (e can clearly see specialiCation a!ong progra!!ing languages. Aa"a is used !ainly in enterprise pro&ects and portables' / is the king in syste! progra!!ing 64+' de"ice dri"ers' s!all apps7' P=P rules a!ong s!all to !ediu! siCe web sites' Aa"asript is used on the client site of a web application. Position ; 8 # 4 ? % D G < ;$ / P=P /BB Perl /F Python Aa"a+cript :uby Language Aa"a :atings 8;.DE ;4.<E ;$.8E <.<E ?.4E #.4E #.$E 8.DE 8.$E

5isual ,asic ;$.DE

Python is currently nu!ber G. The :uby language has !ade into the toplist. The closest co!petitors to Python are :uby and Perl.

Python toolkits
-or creating graphical user interfaces' python progra!!ers can choose a!ong three decent options. PyGTH' wxPython and PyQt. (hich toolkit to choose' depends on the circu!stances. There is also another IoptionI' called TkInter. A"oid.

-irst progra!s in PyQt4 toolkit


In this part of the PyQt4 tutorial we will learn so!e basic functionality. The explanation will be slow' as if we would talk to a child. The first steps of a child are awkward' so are the "ery first atte!pts of a newbie progra!!er. :e!e!ber' there are no stupid people. There are only laCy people and people' that are not persistent enough.

+i!ple exa!ple
The code exa!ple is "ery si!plistic. It only shows a s!all window. Jet we can do a lot with this window. (e can resiCe it. )axi!iCe it. )ini!iCe it. This re9uires a lot of coding. +o!eone already coded this functionality. ,ecause it repeats in !ost applications' there is no need to code it o"er again +o it has been hidden fro! a progra!!er. PyQt is a high le"el toolkit. If we would code in a lower le"el toolkit' the following code exa!ple could easily ha"e doCens of lines.
#!/usr/bin/python # simple.py import sys from PyQt4 import QtGui app = QtGui.QApplication(sys.argv !i"get = QtGui.Q#i"get( !i"get.resi$e(%&'( )&' !i"get.set#in"o!*itle(+simple+ !i"get.sho!( sys.e,it(app.e,ec-( import sys from PyQt4 import QtGui

=ere we pro"ide the necessary i!ports. The basic G I widgets are located in QtGui !odule.
app = QtGui.QApplication(sys.argv

@"ery PyQt4 application !ust create an application ob&ect. The application ob&ect is located in the QtGui !odule. The sys.arg" para!eter is a list of argu!ents fro! the co!!and line. Python scripts can be run fro! the shell. It is a way' how we can control the startup of our scripts.
!i"get = QtGui.Q#i"get(

The Q(idget widget is the base class of all user interface ob&ects in PyQt4. (e pro"ide the default constructor for Q(idget. The default constructor has no parent. A widget with no parent is called a window.
!i"get.resi$e(%&'( )&'

The resiCe67 !ethod resiCes the widget. It is 8?$px wide and ;?$px high.
!i"get.set#in"o!*itle(+simple+

=ere we set the title for our window. The title is shown in the titlebar.
!i"get.sho!(

The show67 !ethod displays the widget on the screen.


sys.e,it(app.e,ec-(

-inally' we enter the !ainloop of the application. The e"ent handling starts fro! this point. The !ainloop recei"es e"ents fro! the window syste! and dispatches the! to the application widgets. The !ainloop ends' if we call the exit67 !ethod or the !ain widget is destroyed. The sys.exit67 !ethod ensures a clean exit. The en"iron!ent will be infor!ed' how the application ended. Jou wonder why the execK67 !ethod has the underscoreL @"erything has a !eaning. This is ob"iously because the exec is a python keyword. And thus' execK67 was used instead.

-igure. +i!ple

An application icon
The application icon is a s!all i!age' which is usually displayed in the top left corner of the titlebar. In the following exa!ple we will show' how we do it in PyQt4. (e will also introduce so!e new !ethods.
#!/usr/bin/python # icon.py import sys from PyQt4 import QtGui class .con(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+.con+ self.set#in"o!.con(QtGui.Q.con(+icons/!eb.png+ app = QtGui.QApplication(sys.argv icon = .con( icon.sho!( sys.e,it(app.e,ec-(

The pre"ious exa!ple was coded in a procedural style. Python progra!!ing language supports both procedural and ob&ect oriented progra!!ing styles. Progra!!ing in PyQt4 !eans progra!!ing in 44P.
class .con(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent

The three !ost i!portant things in ob&ect oriented progra!!ing are classes' data and !ethods. =ere we create a new class called Icon. The Icon class inherits fro! QtGui.Q(idget class. This !eans' that we !ust call two constructors. The first one for the Icon class and the second one for the inherited class.
self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+.con+ self.set#in"o!.con(QtGui.Q.con(+icons/!eb.png+

All three classes ha"e been inherited fro! the QtGui.Q(idget class. The setGeo!etry67 does two things. It locates the window on the screen and sets the siCe of the window. The first two para!eters are the x and y positions of the window. The third is the width and the fourth is the height of the window. The last !ethod sets the application icon. To do this' we ha"e created a QIcon ob&ect. The QIcon recei"es the path to our icon to be displayed.

-igure. Icon

+howing a tooltip
(e can pro"ide a balloon help for any of our widgets.
#!/usr/bin/python # tooltip.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class *ooltip(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+*ooltip+ self.set*ool*ip(+*his is a 3b4Q#i"get3/b4 !i"get+ QtGui.Q*ool*ip.set5ont(QtGui.Q5ont(+6l"7nglish+( )' app = QtGui.QApplication(sys.argv tooltip = *ooltip( tooltip.sho!( sys.e,it(app.e,ec-(

In this exa!ple' we show a tooltip for a Q(idget widget.


self.set*ool*ip(+*his is a 3b4Q#i"get3/b4 !i"get+

To create a tooltip' we call the setTooltip67 !ethod. (e can use rich text for!atting.
QtGui.Q*ool*ip.set5ont(QtGui.Q5ont(+6l"7nglish+( )'

,ecause the default QToolTip font looks bad' we change it.

-igure. Tooltip

/losing a window
The ob"ious way to how to close a window is to click on the x !ark on the titlebar. In the next exa!ple' we will show' how we can progra!atically close our window. (e will briefly touch signals and slots. The following is the constructor of a QPush,utton' that we will use in our exa!ple.
QPush8utton(string te,t( Q#i"get parent = 0one

The text para!eter is a text that will be displayed on the button. The parent is the ancestor' onto which we place our button. In our case it is Q(idget.
#!/usr/bin/python # 9uitbutton.py import sys from PyQt4 import QtGui( Qt2ore class Quit8utton(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+.con+ 9uit = QtGui.QPush8utton(+2lose+( self 9uit.setGeometry()'( )'( :'( 1& self.connect(9uit( Qt2ore.;.G0A<(+clic=e"( + ( QtGui.9App( Qt2ore.;<6*(+9uit( + app = QtGui.QApplication(sys.argv 9b = Quit8utton( 9b.sho!( sys.e,it(app.e,ec-( 9uit = QtGui.QPush8utton(+2lose+( self 9uit.setGeometry()'( )'( :'( 1&

(e create a push button and position it on the Q(idget &ust like we ha"e positioned the Q(idget on the screen.
self.connect(9uit( Qt2ore.;.G0A<(+clic=e"( + ( QtGui.9App( Qt2ore.;<6*(+9uit( +

The e"ent processing syste! in PyQt4 is built with the signal M slot !echanis!. If we click on the button' the signal clicked67 is e!itted. The slot can be a PyQt slot or any python callable. The Qt/ore.Q4b&ect.connect67 !ethod connects signals with slots. In our case the slot is a predefined PyQt 9uit67 slot. The co!!unication is done between two ob&ects. The sender and the recei"er. The sender is the push button' the recei"er is the application ob&ect.

-igure. 9uit button

)essage ,ox
,y default' if we click on the x button on the titlebar' the Q(idget is closed. +o!eti!es we want to !odify this default beha"iour. -or exa!ple' if we ha"e a file opened in an editor to which we did so!e changes. (e show a !essage box to confir! the action.
#!/usr/bin/python # messagebo,.py import sys from PyQt4 import QtGui class >essage8o,(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+message bo,+ "ef close7vent(self( event / reply = QtGui.Q>essage8o,.9uestion(self( +>essage+( ?Are you sure to 9uit@?( QtGui.Q>essage8o,.Aes( QtGui.Q>essage8o,.0o

if reply == QtGui.Q>essage8o,.Aes/ event.accept( else/ event.ignore( app = QtGui.QApplication(sys.argv 9b = >essage8o,( 9b.sho!( sys.e,it(app.e,ec-(

If we close the Q(idget' the Q/lose@"ent is generated. To !odify the widget beha"iour we need to rei!ple!ent the close@"ent67 e"ent handler.
reply = QtGui.Q>essage8o,.9uestion(self( +>essage+( ?Are you sure to 9uit@?( QtGui.Q>essage8o,.Aes( QtGui.Q>essage8o,.0o

(e show a !essage box with two buttons. Jes and 1o. The first string appears on the titlebar. The second string is the !essage text displayed by the dialog. The return "alue is stored in the reply "ariable.
if reply == QtGui.Q>essage8o,.Aes/ event.accept( else/ event.ignore(

=ere we test the return "alue. If we clicked Jes button' we accept the e"ent which leads to the closure of the widget and to the ter!ination of the application. 4therwise we ignore the close e"ent.

-igure. !essage box

/entering window on the screen


The following script shows' how we can center a window on the desktop screen.
#!/usr/bin/python # center.py import sys from PyQt4 import QtGui class 2enter(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent

self.set#in"o!*itle(+center+ self.resi$e(%&'( )&' self.center( "ef center(self / screen = QtGui.QBes=top#i"get( .screenGeometry( si$e = self.geometry( self.move((screen.!i"th( Csi$e.!i"th( /%( (screen.height( C si$e.height( /% app = QtGui.QApplication(sys.argv 9b = 2enter( 9b.sho!( sys.e,it(app.e,ec-( self.resi$e(%&'( )&'

=ere we resiCe the Q(idget to be 8?$px wide and ;?$px heigh.


screen = QtGui.QBes=top#i"get( .screenGeometry(

(e figure out the screen resolution of our !onitor.


si$e = self.geometry(

=ere we get the siCe of our Q(idget.


self.move((screen.!i"th( Csi$e.!i"th( /%( (screen.height( Csi$e.height( /%

=ere we !o"e the window to the center of the screen.

)enus and Toolbars in PyQt4


)ain (indow
The Q)ain(indow class pro"ides a !ain application window. This enables to create the classic application skeleton with a statusbar' toolbars and a !enubar.

+tatusbar
The statusbar is a widget that si used for displaying status infor!ation.
#!/usr/bin/python # statusbar.py import sys from PyQt4 import QtGui class >ain#in"o!(QtGui.Q>ain#in"o! / "ef --init--(self / QtGui.Q>ain#in"o!.--init--(self

self.resi$e(%&'( )&' self.set#in"o!*itle(+statusbar+ self.status8ar( .sho!>essage(+Dea"y+ app = QtGui.QApplication(sys.argv main = >ain#in"o!( main.sho!( sys.e,it(app.e,ec-(

self.status8ar( .sho!>essage(+Dea"y+

To get the statusbar' we call the status,ar67 !ethod of the QApplication class. The show)essage67 displays !essage on the statusbar.

)enubar
A !enubar is one of the !ost "isible parts of the G I application. It is a group of co!!ands located in "arious !enus. (hile in console applications you had to re!e!ber all those arcane co!!ands' here we ha"e !ost of the co!!ands grouped into logical parts. There are accepted standards that further reduce the a!ount of ti!e spending to learn a new application.
#!/usr/bin/python # menubar.py import sys from PyQt4 import QtGui( Qt2ore class >ain#in"o!(QtGui.Q>ain#in"o! / "ef --init--(self / QtGui.Q>ain#in"o!.--init--(self self.resi$e(%&'( )&' self.set#in"o!*itle(+menubar+ e,it = QtGui.QAction(QtGui.Q.con(+icons/e,it.png+ ( +7,it+( self e,it.set;hortcut(+2trlEQ+ e,it.set;tatus*ip(+7,it application+ self.connect(e,it( Qt2ore.;.G0A<(+triggere"( + ( Qt2ore.;<6*(+close( + self.status8ar( menubar = self.menu8ar( file = menubar.a"">enu(+F5ile+ file.a""Action(e,it app = QtGui.QApplication(sys.argv main = >ain#in"o!( main.sho!( sys.e,it(app.e,ec-(

menubar = self.menu8ar( file = menubar.a"">enu(+F5ile+ file.a""Action(e,it

-irst we create a !enubar with the !enu,ar67 !ethod of the Q)ain(indow class. Then we add a !enu with the Add)enu67 !ethod. In the end we plug the action ob&ect into the file !enu.

Toolbar
)enus group all co!!ands that we can use in an application. Toolbars pro"ide a 9uick access to the !ost fre9uently used co!!ands.
#!/usr/bin/python # toolbar.py import sys from PyQt4 import QtGui( Qt2ore class >ain#in"o!(QtGui.Q>ain#in"o! / "ef --init--(self / QtGui.Q>ain#in"o!.--init--(self self.resi$e(%&'( )&' self.set#in"o!*itle(+toolbar+ self.e,it = QtGui.QAction(QtGui.Q.con(+icons/e,it.png+ ( +7,it+( self self.e,it.set;hortcut(+2trlEQ+ self.connect(self.e,it( Qt2ore.;.G0A<(+triggere"( + ( Qt2ore.;<6*(+close( + self.toolbar = self.a""*ool8ar(+7,it+ self.toolbar.a""Action(self.e,it app = QtGui.QApplication(sys.argv main = >ain#in"o!( main.sho!( sys.e,it(app.e,ec-( self.e,it = QtGui.QAction(QtGui.Q.con(+icons/e,it.png+ ( +7,it+( self self.e,it.set;hortcut(+2trlEQ+

G I applications are controlled with co!!ands. These co!!ands can be launched fro! a !enu' a context !enu' a toolbar or with a shortcut. PyQt si!plifies de"elop!ent with the introduction of actions. An action ob&ect can ha"e !enu text' an icon' a shortcut' status text' I(hat>s ThisLI text and a tooltip. In our exa!ple' we define an action ob&ect with an icon' a tooltip and a shortcut.
self.connect(self.e,it( Qt2ore.;.G0A<(+triggere"( + ( Qt2ore.;<6*(+close( +

=ere we connect the action>s triggered67 signal to the predefined close67 signal.
self.toolbar = self.a""*ool8ar(+7,it+ self.toolbar.a""Action(self.e,it

=ere we create a toolbar and plug and action ob&ect into it.

-igure. toolbar

Putting it together
In the last exa!ple of this section' we will create a !enubar' toolbar and a statusbar. (e will also create a central widget.
#!/usr/bin/python # main!in"o!.py import sys from PyQt4 import QtGui( Qt2ore class >ain#in"o!(QtGui.Q>ain#in"o! / "ef --init--(self / QtGui.Q>ain#in"o!.--init--(self self.resi$e(1&'( %&' self.set#in"o!*itle(+main!in"o!+ te,t7"it = QtGui.Q*e,t7"it( self.set2entral#i"get(te,t7"it e,it = QtGui.QAction(QtGui.Q.con(+icons/e,it.png+ ( +7,it+( self e,it.set;hortcut(+2trlEQ+ e,it.set;tatus*ip(+7,it application+ self.connect(e,it( Qt2ore.;.G0A<(+triggere"( + ( Qt2ore.;<6*(+close( + self.status8ar( menubar = self.menu8ar( file = menubar.a"">enu(+F5ile+ file.a""Action(e,it toolbar = self.a""*ool8ar(+7,it+ toolbar.a""Action(e,it app = QtGui.QApplication(sys.argv main = >ain#in"o!( main.sho!( sys.e,it(app.e,ec-(

te,t7"it = QtGui.Q*e,t7"it( self.set2entral#i"get(te,t7"it

=ere we create a text edit widget. (e set it to be the central widget of the Q)ain(indow. The central widget will occupy all space that is left.

-igure. !ainwindow

Layout !anage!ent in PyQt4


I!portant thing in progra!!ing is the layout !anage!ent. Layout !anage!ent is the way how we place the widgets on the window. The !anage!ent can be done in two ways. (e can use absolute positioning or layout classes.

Absolute positioning
The progra!!er specifies the position and the siCe of each widget in pixels. (hen you use absolute positioning' you ha"e to understand se"eral things.

the siCe and the position of a widget do not change' if you resiCe a window applications !ight look different on "arious platfor!s changing fonts in your application !ight spoil the layout if you decide to change your layout' you !ust co!pletely redo your layout' which is tedious and ti!e consu!ing

#!/usr/bin/python # absolute.py import sys

from PyQt4 import QtGui class Absolute(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+2ommunication+ label = QtGui.Q<abel(+2oul"nG+t+( self label.move()&( )' label = QtGui.Q<abel(+care+( self label.move(1&( 4' label = QtGui.Q<abel(+less+( self label.move(&&( :& label = QtGui.Q<abel(+An"+( self label.move())&( :& label = QtGui.Q<abel(+then+( self label.move()1&( 4& label = QtGui.Q<abel(+you+( self label.move())&( %& label = QtGui.Q<abel(+=isse"+( self label.move()4&( )' label = QtGui.Q<abel(+me+( self label.move(%)&( )' self.resi$e(%&'( )&' app = QtGui.QApplication(sys.argv 9b = Absolute( 9b.sho!( sys.e,it(app.e,ec-(

(e si!ply call the !o"e67 !ethod to position our widgets. In our case QLabel0s. (e position the! by pro"iding the x and the y coordinates. The beginning of the coordinate syste! is at the left top corner. The x "alues grow fro! left to right. The y "alues grow fro! top to botto!.

-igure. absolute positioning

,ox Layout
Layout !anage!ent with layout classes is !uch !ore flexible and practical. It is the preferred way to place widgets on a window. The basic layout classes are Q=,oxLayout and Q5,oxLayout. They line up widgets horiContally and "ertically. I!agine that we wanted to place two buttons in the right botto! corner. To create such a layout' we will use one horiContal and one "ertical box. To create the neccessary space' we will add a stretch factor.
#!/usr/bin/python # bo,layout.py import sys from PyQt4 import QtGui class 8o,<ayout(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+bo, layout+ o= = QtGui.QPush8utton(?6H? cancel = QtGui.QPush8utton(?2ancel? hbo, = QtGui.QI8o,<ayout( hbo,.a"";tretch() hbo,.a""#i"get(o= hbo,.a""#i"get(cancel vbo, = QtGui.QJ8o,<ayout( vbo,.a"";tretch() vbo,.a""<ayout(hbo, self.set<ayout(vbo, self.resi$e(1''( )&' app = QtGui.QApplication(sys.argv 9b = 8o,<ayout( 9b.sho!( sys.e,it(app.e,ec-( o= = QtGui.QPush8utton(?6H? cancel = QtGui.QPush8utton(?2ancel?

=ere we create two push buttons.


hbo, = QtGui.QI8o,<ayout( hbo,.a"";tretch() hbo,.a""#i"get(o= hbo,.a""#i"get(cancel

(e create a horiContal box layout. Add a stretch factor and both buttons.
vbo, = QtGui.QJ8o,<ayout( vbo,.a"";tretch() vbo,.a""<ayout(hbo,

To create the necessary layout' we put a horiContal lauout into a "ertical one.
self.set<ayout(vbo,

-inally' we set the !ain layout of the window.

-igure. box layout

QGridLayout
The !ost uni"ersal layout class is the grid layout. This layout di"ides the space into rows and colu!ns. To create a grid layout' we use the QGridLayout class.
#!/usr/bin/python # gri"layout.py import sys from PyQt4 import QtGui class Gri"<ayout(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+gri" layout+ names = K+2ls+( +8c=+( ++( +2lose+( +L+( +M+( +N+( +/+( +4+( +&+( +:+( +O+( +)+( +%+( +1+( +C+( +'+( +.+( +=+( +E+P gri" = QtGui.QGri"<ayout( Q = ' pos = K('( ' ( ('( ) ( ('( % ( ('( 1 ( ()( ' ( ()( ) ( ()( % ( ()( 1 ( (%( ' ( (%( ) ( (%( % ( (%( 1 ( (1( ' ( (1( ) ( (1( % ( (1( 1 (

(4( ' ( (4( ) ( (4( % ( (4( 1 P for i in names/ button = QtGui.QPush8utton(i if Q == %/ gri".a""#i"get(QtGui.Q<abel(++ ( '( % else/ gri".a""#i"get(button( posKQPK'P( posKQPK)P Q = Q E ) self.set<ayout(gri"

app = QtGui.QApplication(sys.argv 9b = Gri"<ayout( 9b.sho!( sys.e,it(app.e,ec-(

In our exa!ple' we create a grid of buttons. To fill one gap' we add one QLabel widget.
gri" = QtGui.QGri"<ayout(

=ere we create a grid layout.


if Q == %/ gri".a""#i"get(QtGui.Q<abel(++ ( '( % else/ gri".a""#i"get(button( posKQPK'P( posKQPK)P

To add a widget to a grid' we call the add(idget67 !ethod. The argu!ents are the widget' the row and the colu!n nu!ber.

-igure. grid layout (idgets can span !ultiple colu!ns or rows in a grid. In the next exa!ple we illustrate this.
#!/usr/bin/python

# gri"layout%.py import sys from PyQt4 import QtGui class Gri"<ayout%(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+gri" layout+ title = QtGui.Q<abel(+*itle+ author = QtGui.Q<abel(+Author+ revie! = QtGui.Q<abel(+Devie!+ title7"it = QtGui.Q<ine7"it( author7"it = QtGui.Q<ine7"it( revie!7"it = QtGui.Q*e,t7"it( gri" = QtGui.QGri"<ayout( gri".set;pacing()' gri".a""#i"get(title( )( ' gri".a""#i"get(title7"it( )( ) gri".a""#i"get(author( %( ' gri".a""#i"get(author7"it( %( ) gri".a""#i"get(revie!( 1( ' gri".a""#i"get(revie!7"it( 1( )( &( ) self.set<ayout(gri" self.resi$e(1&'( 1'' app = QtGui.QApplication(sys.argv 9b = Gri"<ayout%( 9b.sho!( sys.e,it(app.e,ec-(

gri" = QtGui.QGri"<ayout( gri".set;pacing()'

(e create a grid layout and set spacing between widgets.


gri".a""#i"get(revie!7"it( 1( )( &( )

If we add a widget to a grid' we can pro"ide row span and colu!n span of the widget. In our case' we !ake the re"iew@dit widget span ? rows.

@"ents and +ignals in PyQt4


In this part of the PyQt4 progra!!ing tutorial' we will explore e"ents and singnals occuring in applications.

@"ents
@"ents are an i!portant part in any G I progra!. @"ents are generated by users or by the syste!. (hen we call the application>s execK67 !ethod' the application enters the !ain loop. The !ain loop fetches e"ents and sends the! to the ob&ects. Trolltech has introduced a uni9ue signal and slot !echanis!.

+ignals M +lots
+ignals are e!itted' when users click on the button' drag a slider etc. +ignals can be e!itted also by the en"iron!ent. -or exa!ple' when a clock ticks. A slot is a !ethod' that reacts to a signal. In python' a slot can be any python callable.
#!/usr/bin/python # sigslot.py import sys from PyQt4 import QtGui( Qt2ore class ;ig;lot(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+signal F slot+ lc" = QtGui.Q<2B0umber(self sli"er = QtGui.Q;li"er(Qt2ore.Qt.Iori$ontal( self vbo, = QtGui.QJ8o,<ayout( vbo,.a""#i"get(lc" vbo,.a""#i"get(sli"er self.set<ayout(vbo, self.connect(sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( lc"( Qt2ore.;<6*(+"isplay(int + self.resi$e(%&'( )&' app = QtGui.QApplication(sys.argv 9b = ;ig;lot( 9b.sho!( sys.e,it(app.e,ec-(

In our exa!ple' we display an lcd nu!ber and a slider. (e change the lcd nu!ber by dragging the slider.
self.connect(sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( lc"( Qt2ore.;<6*(+"isplay(int +

=ere we connect a "alue/hanged67 signal of the slider to the display67 slot of the lcd nu!ber. The connect !ethod has four para!eters. The sender is an ob&ect that sends a

signal. The signal is the signal' which is e!itted. The recei"er is the ob&ect' that recei"es the signal. -inally the slot is the !ethod' that reacts to the signal.

-igure. signal M slot

:ei!ple!enting e"ent handler


@"ents in PyQt are processed !ainly by rei!ple!enting e"ent handlers .
#!/usr/bin/python # escape.py import sys from PyQt4 import QtGui( Qt2ore class 7scape(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+escape+ self.resi$e(%&'( )&' self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( + "ef =eyPress7vent(self( event / if event.=ey( == Qt2ore.Qt.Hey-7scape/ self.close( app = QtGui.QApplication(sys.argv 9b = 7scape( 9b.sho!( sys.e,it(app.e,ec-(

In our exa!ple' we rei!ple!ent the keyPress@"ent67 e"ent handler.


"ef =eyPress7vent(self( event / if event.=ey( == Qt2ore.Qt.Hey-7scape/ self.close(

If we click the escape button' we close the application.

@!itting signals
4b&ects created fro! Qt/ore.Q4b&ect can e!it signals. If we click on the button' a clicked67 signal is generated. In the following exa!ple we will see' how we can e!it signals.
#!/usr/bin/python # emit.py import sys from PyQt4 import QtGui( Qt2ore class 7mit(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+emit+ self.resi$e(%&'( )&' self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( + "ef mousePress7vent(self( event / self.emit(Qt2ore.;.G0A<(+close7mitApp( + app = QtGui.QApplication(sys.argv 9b = 7mit( 9b.sho!( sys.e,it(app.e,ec-(

(e create a new signal called close@!itApp67. This signal is e!itted' during a !ouse press e"ent.
"ef mousePress7vent(self( event / self.emit(Qt2ore.;.G0A<(+close7mitApp( +

@!itting a signal with the e!it67 !ethod.


self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( +

=ere we connect the !anually created close@!itApp67 signal with the close67 slot.

-igure. grid layout8

@"ents and +ignals in PyQt4


In this part of the PyQt4 progra!!ing tutorial' we will explore e"ents and singnals occuring in applications.

@"ents
@"ents are an i!portant part in any G I progra!. @"ents are generated by users or by the syste!. (hen we call the application>s execK67 !ethod' the application enters the !ain loop. The !ain loop fetches e"ents and sends the! to the ob&ects. Trolltech has introduced a uni9ue signal and slot !echanis!.

+ignals M +lots
+ignals are e!itted' when users click on the button' drag a slider etc. +ignals can be e!itted also by the en"iron!ent. -or exa!ple' when a clock ticks. A slot is a !ethod' that reacts to a signal. In python' a slot can be any python callable.
#!/usr/bin/python # sigslot.py import sys from PyQt4 import QtGui( Qt2ore class ;ig;lot(QtGui.Q#i"get / "ef --init--(self( parent=0one /

QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+signal F slot+ lc" = QtGui.Q<2B0umber(self sli"er = QtGui.Q;li"er(Qt2ore.Qt.Iori$ontal( self vbo, = QtGui.QJ8o,<ayout( vbo,.a""#i"get(lc" vbo,.a""#i"get(sli"er self.set<ayout(vbo, self.connect(sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( lc"( Qt2ore.;<6*(+"isplay(int + self.resi$e(%&'( )&' app = QtGui.QApplication(sys.argv 9b = ;ig;lot( 9b.sho!( sys.e,it(app.e,ec-(

In our exa!ple' we display an lcd nu!ber and a slider. (e change the lcd nu!ber by dragging the slider.
self.connect(sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( lc"( Qt2ore.;<6*(+"isplay(int +

=ere we connect a "alue/hanged67 signal of the slider to the display67 slot of the lcd nu!ber. The connect !ethod has four para!eters. The sender is an ob&ect that sends a signal. The signal is the signal' which is e!itted. The recei"er is the ob&ect' that recei"es the signal. -inally the slot is the !ethod' that reacts to the signal.

-igure. signal M slot

:ei!ple!enting e"ent handler


@"ents in PyQt are processed !ainly by rei!ple!enting e"ent handlers .
#!/usr/bin/python

# escape.py import sys from PyQt4 import QtGui( Qt2ore class 7scape(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+escape+ self.resi$e(%&'( )&' self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( + "ef =eyPress7vent(self( event / if event.=ey( == Qt2ore.Qt.Hey-7scape/ self.close( app = QtGui.QApplication(sys.argv 9b = 7scape( 9b.sho!( sys.e,it(app.e,ec-(

In our exa!ple' we rei!ple!ent the keyPress@"ent67 e"ent handler.


"ef =eyPress7vent(self( event / if event.=ey( == Qt2ore.Qt.Hey-7scape/ self.close(

If we click the escape button' we close the application.

@!itting signals
4b&ects created fro! Qt/ore.Q4b&ect can e!it signals. If we click on the button' a clicked67 signal is generated. In the following exa!ple we will see' how we can e!it signals.
#!/usr/bin/python # emit.py import sys from PyQt4 import QtGui( Qt2ore class 7mit(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.set#in"o!*itle(+emit+ self.resi$e(%&'( )&' self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( + "ef mousePress7vent(self( event / self.emit(Qt2ore.;.G0A<(+close7mitApp( + app = QtGui.QApplication(sys.argv

9b = 7mit( 9b.sho!( sys.e,it(app.e,ec-(

(e create a new signal called close@!itApp67. This signal is e!itted' during a !ouse press e"ent.
"ef mousePress7vent(self( event / self.emit(Qt2ore.;.G0A<(+close7mitApp( +

@!itting a signal with the e!it67 !ethod.


self.connect(self( Qt2ore.;.G0A<(+close7mitApp( + ( Qt2ore.;<6*(+close( +

=ere we connect the !anually created close@!itApp67 signal with the close67 slot.

*ialogs in PyQt4
*ialog windows or dialogs are an indispensable part of !ost !odern G I applications. A dialog is defined as a con"ersation between two or !ore persons. In a co!puter application a dialog is a window which is used to ItalkI to the application. A dialog is used to input data' !odify data' change the application settings etc. *ialogs are i!portant !eans of co!!unication between a user and a co!puter progra!. There are essentially two types of dialogs. Predefined dialogs and custo! dialogs.

Predefined *ialogs
QInput*ialog
The QInput*ialog pro"ides a si!ple con"enience dialog to get a single "alue fro! the user. The input "alue can be a string' a nu!ber or an ite! fro! a list.
#!/usr/bin/python # input"ialog.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class .nputBialog(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( 1&'( M' self.set#in"o!*itle(+.nputBialog+

self.button = QtGui.QPush8utton(+Bialog+( self self.button.set5ocusPolicy(Qt2ore.Qt.0o5ocus self.button.move(%'( %' self.connect(self.button( Qt2ore.;.G0A<(+clic=e"( + ( self.sho!Bialog self.set5ocus( self.label = QtGui.Q<ine7"it(self self.label.move()1'( %% "ef sho!Bialog(self / te,t( o= = QtGui.Q.nputBialog.get*e,t(self( +.nput Bialog+( +7nter your name/+ if o=/ self.label.set*e,t(unico"e(te,t app = QtGui.QApplication(sys.argv icon = .nputBialog( icon.sho!( app.e,ec-(

The exa!ple has a button and a line edit widget. The button shows the input dialog for getting text "alues. The entered text will be displayed in the line edit widget.
te,t( o= = QtGui.Q.nputBialog.get*e,t(self( +.nput Bialog+( +7nter your name/+

This line displays the input dialog. The first string is a dialog title' the second one is a !essage within the dialog. The dialog returns the entered text and a boolean "alue. If we clicked ok button' the boolean "alue is true' otherwise false.

-igure. Input *ialog

Q/olor*ialog
The Q/olor*ialog pro"ides a dialog widget for specifying colors.
#!/usr/bin/python

# color"ialog.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 2olorBialog(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent color = QtGui.Q2olor('( '( ' self.setGeometry(1''( 1''( %&'( )M' self.set#in"o!*itle(+2olorBialog+ self.button = QtGui.QPush8utton(+Bialog+( self self.button.set5ocusPolicy(Qt2ore.Qt.0o5ocus self.button.move(%'( %' self.connect(self.button( Qt2ore.;.G0A<(+clic=e"( + ( self.sho!Bialog self.set5ocus( self.!i"get = QtGui.Q#i"get(self self.!i"get.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S color.name( self.!i"get.setGeometry()1'( %%( )''( )'' "ef sho!Bialog(self / col = QtGui.Q2olorBialog.get2olor( if col.isJali"( / self.!i"get.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S col.name( app = QtGui.QApplication(sys.argv c" = 2olorBialog( c".sho!( app.e,ec-(

The application exa!ple shows a push button and a Q(idget. The widget background is set to black color. sing the Q/olor*ialog' we can change its background.
color = QtGui.Q2olorBialog.get2olor(

This line will pop up the Q/olor*ialog.


if col.isJali"( / self.!i"get.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S col.name(

(e check if the color is "alid. If we click on the cancel button' no "alid color is returned. If the color is "alid' we change the background color using stylesheets.

-igure. /olor dialog

Q-ont*ialog
The Q-ont*ialog is a dialog widget for selecting font.
#!/usr/bin/python # font"ialog.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 5ontBialog(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent hbo, = QtGui.QI8o,<ayout( self.setGeometry(1''( 1''( %&'( ))' self.set#in"o!*itle(+5ontBialog+ button = QtGui.QPush8utton(+Bialog+( self button.set5ocusPolicy(Qt2ore.Qt.0o5ocus button.move(%'( %' hbo,.a""#i"get(button

self.connect(button( Qt2ore.;.G0A<(+clic=e"( + ( self.sho!Bialog self.label = QtGui.Q<abel(+Hno!le"ge only matters+( self self.label.move()1'( %' hbo,.a""#i"get(self.label( ) self.set<ayout(hbo, "ef sho!Bialog(self / font( o= = QtGui.Q5ontBialog.get5ont( if o=/ self.label.set5ont(font app = QtGui.QApplication(sys.argv c" = 5ontBialog( c".sho!( app.e,ec-(

In our exa!ple' we ha"e a button and a label. (ith Q-ont*ialog' we change the font of the label.
hbo,.a""#i"get(self.label( )

(e !ake the label resiCable. It is necessary' because when we select a different font' the text !ay beco!e larger. 4therwise the label !ight not be fully "isible.
font( o= = QtGui.Q5ontBialog.get5ont(

=ere we pop up the font dialog.


if o=/ self.label.set5ont(font

If we clicked ok' the font of the label was changed.

-igure. -ont dialog

Q-ile*ialog
The Q-ile*ialog is a dialog that allows users to select files or directories. The files can be selected for both opening a sa"ing.
#!/usr/bin/python # openfile"ialog.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 6pen5ile(QtGui.Q>ain#in"o! / "ef --init--(self( parent=0one / QtGui.Q>ain#in"o!.--init--(self( parent self.setGeometry(1''( 1''( 1&'( 1'' self.set#in"o!*itle(+6pen5ile+ self.te,t7"it = QtGui.Q*e,t7"it( self.set2entral#i"get(self.te,t7"it self.status8ar( self.set5ocus( e,it = QtGui.QAction(QtGui.Q.con(+open.png+ ( +6pen+( self

e,it.set;hortcut(+2trlE6+ e,it.set;tatus*ip(+6pen ne! 5ile+ self.connect(e,it( Qt2ore.;.G0A<(+triggere"( + ( self.sho!Bialog menubar = self.menu8ar( file = menubar.a"">enu(+F5ile+ file.a""Action(e,it "ef sho!Bialog(self / filename = QtGui.Q5ileBialog.get6pen5ile0ame(self( +6pen file+( +/home+ file=open(filename "ata = file.rea"( self.te,t7"it.set*e,t("ata app = QtGui.QApplication(sys.argv c" = 6pen5ile( c".sho!( app.e,ec-(

The exa!ple shows a !enubar' centrally set text edit widget and a statusbar. The statusbar is shown only for desing purposes. The the !enu ite! shows the Q-ile*ialog which is used to select a file. The contents of the file are loaded into the text edit widget.
class 6pen5ile(QtGui.Q>ain#in"o! / ... self.te,t7"it = QtGui.Q*e,t7"it( self.set2entral#i"get(self.te,t7"it

The exa!ple is based on the Q)ain(indow widget' because we centrally set the text edit widget. This is easily done with the Q)ain(indow widget' without resorting to layouts.
filename = QtGui.Q5ileBialog.get6pen5ile0ame(self( +6pen file+( +/home+

(e pop up the Q-ile*ialog. The first string in the get4pen-ile1a!e !ethod is the caption. The second string specifies the dialog working directory. ,y default' the file filter is set to All files 6N7.
file=open(filename "ata = file.rea"( self.te,t7"it.set*e,t("ata

The selected file na!e is read and the contents of the file are set to the text edit widget.

-igure. -ile dialog

PyQt4 (idgets
(idgets are basic building blocks of an application. The PyQt4 progra!!ing toolkit has a wide range of "arious widgets. ,uttons' check boxes' sliders' list boxes etc. @"erything a progra!!er needs for his &ob. In this section of the tutorial' we will describe se"eral useful widgets.

Q/heck,ox
Q/heck,ox is a widget that has two states. 4n and 4ff. It is a box with a label. (hene"er a checkbox is checked or cleared it e!its the signal state/hanged67.
#!/usr/bin/python # chec=bo,.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 2hec=8o,(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+2hec=bo,+ self.cb = QtGui.Q2hec=8o,(+;ho! title+( self self.cb.set5ocusPolicy(Qt2ore.Qt.0o5ocus

self.cb.move()'( )' self.cb.toggle( U self.connect(self.cb( Qt2ore.;.G0A<(+state2hange"(int + ( self.change*itle "ef change*itle(self( value / if self.cb.is2hec=e"( / self.set#in"o!*itle(+2hec=bo,+ else/ self.set#in"o!*itle(++ app = QtGui.QApplication(sys.argv icon = 2hec=8o,( icon.sho!( app.e,ec-(

In our exa!ple' we will create a checkbox that will toggle the window title.
self.cb = QtGui.Q2hec=8o,(+;ho! title+( self

This is the Q/heck,ox constructor.


self.cb.set5ocusPolicy(Qt2ore.Qt.0o5ocus

(e connect the user defined changeTitle67 !ethod to the state/hanged67 signal. The changeTitle67 !ethod will toggle the window title.
self.connect(self.cb( Qt2ore.;.G0A<(+state2hange"(int + ( self.change*itle

,y default' the Q/heck,ox accepts focus. It is represented by a thin rectangle o"er the checkbox label. The rectangle looks awful' so I disable it by setting the widget focus policy to Qt.1o-ocus.
self.cb.toggle( U

(e set the window title' so we !ust also check the checkbox. ,y default' the window title is not set and the check box is unchecked.

-igure. Q/heck,ox

Toggle,utton
PyQt4 has no widget for a Toggle,utton. To create a Toggle,utton' we use a

QPush,utton in a special !ode. Toggle,utton is a button that has two states. Pressed and not pressed. Jou toggle between these two states by clicking on it. There are situations where this functionality fits well.
#!/usr/bin/python # togglebutton.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class *oggle8utton(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.color = QtGui.Q2olor('( '( ' self.setGeometry(1''( 1''( %M'( )L' self.set#in"o!*itle(+*oggle8utton+ self.re" = QtGui.QPush8utton(+De"+( self self.re".set2hec=able(*rue self.re".move()'( )' self.connect(self.re"( Qt2ore.;.G0A<(+clic=e"( + ( self.setDe" self.green = QtGui.QPush8utton(+Green+( self self.green.set2hec=able(*rue self.green.move()'( :' self.connect(self.green( Qt2ore.;.G0A<(+clic=e"( + ( self.setGreen self.blue = QtGui.QPush8utton(+8lue+( self self.blue.set2hec=able(*rue self.blue.move()'( ))' self.connect(self.blue( Qt2ore.;.G0A<(+clic=e"( + ( self.set8lue self.s9uare = QtGui.Q#i"get(self self.s9uare.setGeometry()&'( %'( )''( )'' self.s9uare.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S self.color.name( QtGui.QApplication.set;tyle(QtGui.Q;tyle5actory.create(+cleanloo=s+ "ef setDe"(self / if self.re".is2hec=e"( / self.color.setDe"(%&& else/ self.color.setDe"(' self.s9uare.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S self.color.name( "ef setGreen(self / if self.green.is2hec=e"( / self.color.setGreen(%&& else/ self.color.setGreen(' self.s9uare.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S

self.color.name( "ef set8lue(self / if self.blue.is2hec=e"( / self.color.set8lue(%&& else/ self.color.set8lue(' self.s9uare.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S self.color.name(

app = QtGui.QApplication(sys.argv tb = *oggle8utton( tb.sho!( app.e,ec-(

In our exa!ple' we create three Toggle,uttons. (e also create a Q(idget. (e set the background color of the Q(idget to black. The togglebuttons will toggle the red' green and blue parts of the color "alue. The background color will depend on which togglebuttons we ha"e pressed.
self.color = QtGui.Q2olor('( '( '

This is the initial color "alue. 1o red' green and blue e9uals to black. Theoretically speaking' black is not a color after all.
self.re" = QtGui.QPush8utton(+De"+( self self.re".set2hec=able(*rue

To create a Toggle,utton' we create a QPush,utton and !ake it checkable by calling set/heckable67 !ethod.
self.connect(self.re"( Qt2ore.;.G0A<(+clic=e"( + ( self.setDe"

(e connect a clicked67 signal to our user defined !ethod.


QtGui.QApplication.set;tyle(QtGui.Q;tyle5actory.create(+cleanloo=s+

I ha"e set the style of the application to cleanlooks. I did it' because the default style for linux' plasti9ue has a design bug. Jou cannot easily tell whether the Toggle,utton is pressed or not. /leanLooks style is better.
if self.re".is2hec=e"( / self.color.setDe"(%&& else/ self.color.setDe"('

(e check' whether the button is pressed and change the color "alue accordingly.
self.s9uare.set;tyle;heet(?Q#i"get R bac=groun"Ccolor/ Ss T? S self.color.name(

To change the background color' we use stylesheets.

-igure. Toggle,utton

Q+lider' QLabel
Q+lider is a widget that has a si!ple handle. This handle can be pulled back and forth. This way we are choosing a "alue for a specific task. +o!eti!es using a slider is !ore natural' than si!ply pro"iding a nu!ber or using a spin box. QLabel displays text or i!age. In our exa!ple we will show one slider and one label. This ti!e' the label will display an i!age. The slider will control the label.
#!/usr/bin/python # sli"erClabel.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class ;li"er<abel(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+;li"er<abel+ self.sli"er = QtGui.Q;li"er(Qt2ore.Qt.Iori$ontal( self self.sli"er.set5ocusPolicy(Qt2ore.Qt.0o5ocus self.sli"er.setGeometry(1'( 4'( )''( 1' self.connect(self.sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( self.changeJalue self.label = QtGui.Q<abel(self self.label.setPi,map(QtGui.QPi,map(+mute.png+ self.label.setGeometry():'( 4'( M'( 1' "ef changeJalue(self( value / pos = self.sli"er.value( if pos == '/ self.label.setPi,map(QtGui.QPi,map(+mute.png+

elif pos 4 ' an" pos 3= 1'/ self.label.setPi,map(QtGui.QPi,map(+min.png+ elif pos 4 1' an" pos 3 M'/ self.label.setPi,map(QtGui.QPi,map(+me".png+ else/ self.label.setPi,map(QtGui.QPi,map(+ma,.png+ app = QtGui.QApplication(sys.argv icon = ;li"er<abel( icon.sho!( app.e,ec-(

In our exa!ple we si!ulate a "olu!e control. ,y dragging the handle of a slider' we change a i!age on the label.
self.sli"er = QtGui.Q;li"er(Qt2ore.Qt.Iori$ontal( self

=ere we create a horiContal Q+lider.


self.label = QtGui.Q<abel(self self.label.setPi,map(QtGui.QPi,map(+mute.png+

(e create a Qlabel. And set an initial !ute i!age to it.


self.connect(self.sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( self.changeJalue

(e connect the "alue/hanged signal to the user defined change5alue67 !ethod.


pos = self.sli"er.value(

(e get the position of the slider by calling the "alue67 !ethod. (e change the i!age on the label accordingly.

-igure. +lider and Label

QProgress,ar
A progress bar is a widget that is used' when we process lengthy tasks. It is ani!ated so that the user knows' that our task is progressing. The QProgress,ar widget pro"ides a horiContal or "ertical progress bar in PyQt4 toolkit. The task

is di"ided into steps. The progra!!er can set the !ini!u! and !axi!u! "alues for the progress bar. The default "alues are $' <<.
#!/usr/bin/python # progressbar.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class Progress8ar(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+Progress8ar+ self.pbar = QtGui.QProgress8ar(self self.pbar.setGeometry(1'( 4'( %''( %& self.button = QtGui.QPush8utton(+;tart+( self self.button.set5ocusPolicy(Qt2ore.Qt.0o5ocus self.button.move(4'( M' self.connect(self.button( Qt2ore.;.G0A<(+clic=e"( + ( self.on;tart self.timer = Qt2ore.Q8asic*imer( self.step = 'U "ef timer7vent(self( event / if self.step 4= )''/ self.timer.stop( return self.step = self.step E ) self.pbar.setJalue(self.step "ef on;tart(self / if self.timer.isActive( / self.timer.stop( self.button.set*e,t(+;tart+ else/ self.timer.start()''( self self.button.set*e,t(+;top+ app = QtGui.QApplication(sys.argv icon = Progress8ar( icon.sho!( app.e,ec-(

In our exa!ple we ha"e a horiContal progress bar and a push button. The push button starts and stops the progress bar.
self.pbar = QtGui.QProgress8ar(self

QProgress,ar constructor.
self.timer = Qt2ore.Q8asic*imer(

To acti"ate the progress bar' we use the ti!er ob&ect.


self.timer.start()''( self

To launch the ti!er e"ents' we call the start67 !ethod. This !ethod has two para!eters. The ti!eout and the ob&ect' which will recei"e the e"ents.
"ef timer7vent(self( event / if self.step 4= )''/ self.timer.stop( return self.step = self.step E ) self.pbar.setJalue(self.step

@ach Q4b&ect and its descendants has a Q4b&ect.ti!er@"ent e"ent handler. In order to react to ti!er e"ents' we rei!ple!ent the e"ent handler.

-igure. Progress,ar

Q/alendar(idget
The Q/alendar(idget pro"ides a !onthly based calendar widget. It allows a user to select a date in a si!ple and intuiti"e way.
#!/usr/bin/python # calen"ar.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 2alen"ar(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( 1&'( 1'' self.set#in"o!*itle(+2alen"ar+ self.cal = QtGui.Q2alen"ar#i"get(self

self.cal.setGri"Jisible(*rue self.cal.move(%'( %' self.connect(self.cal( Qt2ore.;.G0A<(+selection2hange"( + ( self.sho!Bate self.label = QtGui.Q<abel(self "ate = self.cal.selecte"Bate( self.label.set*e,t(str("ate.toPyBate( self.label.move()1'( %:' "ef sho!Bate(self / "ate = self.cal.selecte"Bate( self.label.set*e,t(str("ate.toPyBate( app = QtGui.QApplication(sys.argv icon = 2alen"ar( icon.sho!( app.e,ec-(

The exa!ple has a calendar widget and a label widget. The currently selected date is displayed in the label widget.
self.cal = QtGui.Q2alen"ar#i"get(self

(e construct a calendar widget.


self.connect(self.cal( Qt2ore.;.G0A<(+selection2hange"( + ( self.sho!Bate

If we select a date fro! the widget' a selection/hanged67 signal is e!itted. (e connect this !ethod to the user defined show*ate67 !ethod.
"ef sho!Bate(self / "ate = self.cal.selecte"Bate( self.label.set*e,t(str("ate.toPyBate(

(e retrie"e the selected date calling the selected*ate67 !ethod. Then we transfor! the date ob&ect into string and set it to the label widget.

-igure. /alendar widget

*rag and *rop in PyQt4


In this part of the PyQt4 tutorial' we will talk about drag M drop operations. In co!puter graphical user interfaces' drag0and0drop is the action of 6or support for the action of7 clicking on a "irtual ob&ect and dragging it to a different location or onto another "irtual ob&ect. In general' it can be used to in"oke !any kinds of actions' or create "arious types of associations between two abstract ob&ects. 6(ikipedia7 *rag and drop functionality is one of the !ost "isible aspects of the graphical user interface. *rag and drop operation enables users to do co!plex things intuiti"ely. sually' we can drag and drop two things. *ata or so!e graphical ob&ects. If we drag an i!age fro! one application to another' we drag and drop binary data. If we drag a tab in -irefox and !o"e it to another place' we drag and drop a graphical co!ponent.

+i!ple *rag and *rop


In the first exa!ple' we will ha"e a QLine@dit and a QPush,utton. (e will drag plain text fro! the line edit widget and drop it onto the button widget.
#!/usr/bin/python # "rag"rop.py import sys

from PyQt4 import QtGui class 8utton(QtGui.QPush8utton / "ef --init--(self( title( parent / QtGui.QPush8utton.--init--(self( title( parent self.setAcceptBrops(*rue "ef "rag7nter7vent(self( event / if event.mimeBata( .has5ormat(+te,t/plain+ / event.accept( else/ event.ignore( "ef "rop7vent(self( event / self.set*e,t(event.mimeBata( .te,t( class BragBrop(QtGui.QBialog / "ef --init--(self( parent=0one / QtGui.QBialog.--init--(self( parent self.resi$e(%M'( )&' self.set#in"o!*itle(+;imple Brag F Brop+ e"it = QtGui.Q<ine7"it(++( self e"it.setBrag7nable"(*rue e"it.move(1'( :& button = 8utton(?8utton?( self button.move()L'( :& screen = QtGui.QBes=top#i"get( .screenGeometry( si$e = self.geometry( self.move((screen.!i"th( Csi$e.!i"th( /%( (screen.height( Csi$e.height( /% app = QtGui.QApplication(sys.argv icon = BragBrop( icon.sho!( app.e,ec-(

+i!ple drag M drop operation.


class 8utton(QtGui.QPush8utton / "ef --init--(self( title( parent / QtGui.QPush8utton.--init--(self( title( parent

In order to drop text on the QPush,utton widget' we !ust rei!ple!ent so!e !ethods. +o we create our own ,utton class' which will inherit fro! the QPush,utton class.
self.setAcceptBrops(*rue

(e enable drop e"ents for the widget.


"ef "rag7nter7vent(self( event / if event.mimeBata( .has5ormat(+te,t/plain+ / event.accept( else/ event.ignore(

-irst we rei!ple!ent the drag@nter@"ent67 !ethod. (e infor! about the data type' we will accept. In our case it is plain text.
"ef "rop7vent(self( event / self.set*e,t(event.mimeBata( .te,t(

,y rei!ple!enting the drop@"ent67 !ethod' we will define' what we will do upon the drop e"ent. =ere we change the text of the button widget.
e"it = QtGui.Q<ine7"it(++( self e"it.setBrag7nable"(*rue

The QLine@dit widget has a built0in support for drag operations. All we need to do is to call set*rag@nabled67 !ethod to acti"ate it.

-igure. +i!ple *rag M *rop

*rag M drop a button widget


In the following exa!ple' we will de!onstrate' how to drag M drop a button widget.
#!/usr/bin/python # "ragbutton.py import sys from PyQt4 import QtGui from PyQt4 import Qt2ore class 8utton(QtGui.QPush8utton / "ef --init--(self( title( parent / QtGui.QPush8utton.--init--(self( title( parent "ef mouse>ove7vent(self( event / if event.buttons( return != Qt2ore.Qt.Dight8utton/

mimeBata = Qt2ore.Q>imeBata( "rag = QtGui.QBrag(self "rag.set>imeBata(mimeBata "rag.setIot;pot(event.pos(

C self.rect( .top<eft(

"ropAction = "rag.start(Qt2ore.Qt.>oveAction

if "ropAction == Qt2ore.Qt.>oveAction/ self.close( "ef mousePress7vent(self( event / QtGui.QPush8utton.mousePress7vent(self( event if event.button( == Qt2ore.Qt.<eft8utton/ print +press+

class Brag8utton(QtGui.QBialog / "ef --init--(self( parent=0one / QtGui.QBialog.--init--(self( parent self.resi$e(%M'( )&' self.set#in"o!*itle(+2lic= or >ove+ self.setAcceptBrops(*rue self.button = 8utton(+8utton+( self self.button.move()''( :& screen = QtGui.QBes=top#i"get( .screenGeometry( si$e = self.geometry( self.move((screen.!i"th( Csi$e.!i"th( /%( (screen.height( Csi$e.height( /% "ef "rag7nter7vent(self( event / event.accept( "ef "rop7vent(self( event / position = event.pos( button = 8utton(+8utton+( self button.move(position button.sho!( event.setBropAction(Qt2ore.Qt.>oveAction event.accept( app = QtGui.QApplication(sys.argv "b = Brag8utton( "b.sho!( app.e,ec-(

In our code exa!ple' we ha"e a QPush,utton on the window. If we click on the button with a left !ouse button' we print >press> to the console. ,y right clicking and !o"ing the button' we perfor! a drag M drop operation on the button widget.
class 8utton(QtGui.QPush8utton / "ef --init--(self( title( parent / QtGui.QPush8utton.--init--(self( title( parent

(e create a ,utton class' which will deri"e fro! the QPush,utton. (e also

rei!ple!ent two !ethods of the QPush,utton. !ouse)o"e@"ent67 and !ousePress@"ent67. The !ouse)o"e@"ent67 !ethod is the place' where the drag M drop operation begins.
if event.buttons( return != Qt2ore.Qt.Dight8utton/

=ere we decide' that we can perfor! drag M drop only with a right !ouse button. The left !ouse button is reser"ed for clicking on the button.
mimeBata = Qt2ore.Q>imeBata( "rag = QtGui.QBrag(self "rag.set>imeBata(mimeBata "rag.setIot;pot(event.pos(

C self.rect( .top<eft(

=ere we create a Q*rag ob&ect.


"ropAction = "rag.start(Qt2ore.Qt.>oveAction if "ropAction == Qt2ore.Qt.>oveAction/ self.close(

The start67 !ethod of the drag ob&ect starts the drag M drop operation. If we perfor! a !o"e drop action' we destroy the button widget. Technically' we destroy a widget on the current position and recreate it on a new one.
"ef mousePress7vent(self( event / QtGui.QPush8utton.mousePress7vent(self( event if event.button( == Qt2ore.Qt.<eft8utton/ print +press+

(e print >press> to the console' if we left click on the button with the !ouse. 1otice that we call !ousePress@"ent67 !ethod on the parent as well. 4therwise we would not see the button being pushed.
position = event.pos( button = 8utton(+2lose+( self button.move(position button.sho!(

In the drop@"ent67 !ethod we code' what happens after we release the !ouse button and finish the drop operation. In our exa!ple' we create a new ,utton widget at the current position of the !ouse pointer.
event.setBropAction(Qt2ore.Qt.>oveAction event.accept(

(e specify the type of the drop action. In our case it is a !o"e action.

*rawing in PyQt4
*rawing is used' when we want to change or enhance an existing widget. 4r if we

are creating a custo! widget fro! scratch. To do the drawing' we use the drawing API pro"ided by the PyQt4 toolkit. The drawing is done within the paint@"ent67 !ethod. The drawing code is placed between the begin67 and end67 !ethods of the QPainter ob&ect.

*rawing text
(e begin with drawing so!e unicode text onto the window client area.
#!/usr/bin/python # "ra!te,t.py import sys from PyQt4 import QtGui( Qt2ore class Bra!*e,t(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+Bra! *e,t+ self.te,t = u+Gu'4)bGu'41&Gu'41% Gu'4)"Gu'41MGu'41aGu'41eGu'41bGu'41'G Gu'41&Gu'41%Gu'41MGu'44L Gu'4%%Gu'41eGu'41bGu'44)Gu'44%Gu'41eGu'41N/ GnG Gu'4)'Gu'41"Gu'41"Gu'41' Gu'4)aGu'41'Gu'44'Gu'41&Gu'41"Gu'41MGu'41"Gu'41'+

"ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self paint.setPen(QtGui.Q2olor():M( 14( 1 paint.set5ont(QtGui.Q5ont(+Becorative+( )' paint."ra!*e,t(event.rect( ( Qt2ore.Qt.Align2enter( self.te,t paint.en"( app = QtGui.QApplication(sys.argv "t = Bra!*e,t( "t.sho!( app.e,ec-(

In our exa!ple' we draw so!e text in aCbuka. The text is "ertically and horiContally aligned.
"ef paint7vent(self( event /

*rawing is done within a paint e"ent.


paint = QtGui.QPainter( paint.begin(self ... paint.en"(

The QPainter class is responsible for all the low0le"el painting. All the painting !ethods go between begin67 and end67 !ethods.
paint.setPen(QtGui.Q2olor():M( 14( 1 paint.set5ont(QtGui.Q5ont(+Becorative+( )'

=ere we define pen and font' which we use to draw the text.
paint."ra!*e,t(event.rect( ( Qt2ore.Qt.Align2enter( self.te,t

The drawText67 !ethod actually draws text on the window.

-igure. *rawing Text

*rawing points
A point is the !ost si!ple graphics ob&ect' that can be drawn. It is a s!all spot on the window.
#!/usr/bin/python # points.py import sys( ran"om from PyQt4 import QtGui( Qt2ore class Points(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %&'( )&' self.set#in"o!*itle(+Points+ "ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self paint.setPen(Qt2ore.Qt.re" si$e = self.si$e( for i in range()''' / , = ran"om.ran"int()( si$e.!i"th( C) y = ran"om.ran"int()( si$e.height( C) paint."ra!Point(,( y paint.en"(

app = QtGui.QApplication(sys.argv "t = Points( "t.sho!( app.e,ec-(

In our exa!ple' we draw rando!ly ;$$$ red points on the client area.
paint.setPen(Qt2ore.Qt.re"

(e set the pen to red color. (e use a predefined color constant.


si$e = self.si$e(

@ach ti!e we resiCe the window' a paint e"ent is generated. (e get the current siCe of the window with the siCe67 !ethod.
paint."ra!Point(,( y

(e draw the point with the drawPoint67 !ethod.

-igure. Points

/olors
A color is an ob&ect representing a co!bination of :ed' Green' and ,lue 6:G,7 intensity "alues. 5alid :G, "alues are in the range $ to 8??. (e can define a color in "arious ways. The !ost co!!on are :G, deci!al "alues or hexadeci!al "alues. (e can also use an :G,A "alue' which stands for :ed' Green' ,lue' Alpha. =ere we add so!e extra infor!ation' regarding transparency. Alpha "alue of 8?? defines full opacity' $ is for full transparency' eg the color is in"isible.
#!/usr/bin/python # colors.py import sys( ran"om from PyQt4 import QtGui( Qt2ore class 2olors(QtGui.Q#i"get / "ef --init--(self( parent=0one /

QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( 1&'( %M' self.set#in"o!*itle(+2olors+ "ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self color = QtGui.Q2olor('( '( ' color.set0ame"2olor(+#"4"4"4+ paint.setPen(color paint.set8rush(QtGui.Q2olor(%&&( '( '( M' paint."ra!Dect()'( )&( N'( :' paint.set8rush(QtGui.Q2olor(%&&( '( '( ):' paint."ra!Dect()1'( )&( N'( :' paint.set8rush(QtGui.Q2olor(%&&( '( '( %&& paint."ra!Dect(%&'( )&( N'( :' paint.set8rush(QtGui.Q2olor()'( ):1( %( && paint."ra!Dect()'( )'&( N'( :' paint.set8rush(QtGui.Q2olor():'( )''( '( %&& paint."ra!Dect()1'( )'&( N'( :' paint.set8rush(QtGui.Q2olor(:'( )''( :'( %&& paint."ra!Dect(%&'( )'&( N'( :' paint.set8rush(QtGui.Q2olor(&'( &'( &'( %&& paint."ra!Dect()'( )N&( N'( :' paint.set8rush(QtGui.Q2olor(&'( )&'( &'( %&& paint."ra!Dect()1'( )N&( N'( :' paint.set8rush(QtGui.Q2olor(%%1( )1&( )N( %&& paint."ra!Dect(%&'( )N&( N'( :' paint.en"( app = QtGui.QApplication(sys.argv "t = 2olors( "t.sho!( app.e,ec-(

In our exa!ple' we draw < colored rectangles. The first row shows a red color' with different alpha "alues.
color = QtGui.Q2olor('( '( ' color.set0ame"2olor(+#"4"4"4+

=ere we define a color using hexadeci!al notation.


paint.set8rush(QtGui.Q2olor(%&&( '( '( M' paint."ra!Dect()'( )&( N'( :' U

=ere we define a brush and draw a rectangle. A brush is an ele!entary graphics

ob&ect' which is used to draw the background of a shape. The draw:ect67 !ethod accepts four para!eter. The first two are x' y "alues on the axis. The third and fourth para!eters are width and height of the rectangle. The !ethod draws a rectangle using current pen and current brush.

-igure. /olors

QPen
QPen is an ele!entary graphics ob&ect. It is used to draw lines' cur"es and outlines of rectangles' ellipses' polygons or other shapes.
#!/usr/bin/python # penstyles.py import sys from PyQt4 import QtGui( Qt2ore class Pen;tyles(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( %M'( %L' self.set#in"o!*itle(+penstyles+ "ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self pen = QtGui.QPen(Qt2ore.Qt.blac=( %( Qt2ore.Qt.;oli"<ine paint.setPen(pen paint."ra!<ine(%'( 4'( %&'( 4'

pen.set;tyle(Qt2ore.Qt.Bash<ine paint.setPen(pen paint."ra!<ine(%'( M'( %&'( M' pen.set;tyle(Qt2ore.Qt.BashBot<ine paint.setPen(pen paint."ra!<ine(%'( )%'( %&'( )%' pen.set;tyle(Qt2ore.Qt.Bot<ine paint.setPen(pen paint."ra!<ine(%'( ):'( %&'( ):' pen.set;tyle(Qt2ore.Qt.BashBotBot<ine paint.setPen(pen paint."ra!<ine(%'( %''( %&'( %'' pen.set;tyle(Qt2ore.Qt.2ustomBash<ine pen.setBashPattern(K)( 4( &( 4P paint.setPen(pen paint."ra!<ine(%'( %4'( %&'( %4' paint.en"( app = QtGui.QApplication(sys.argv "t = Pen;tyles( "t.sho!( app.e,ec-(

In our exa!ple' we draw six lines. The lines are drawn in six different pen styles. There are fi"e predefined pen styles. (e can create also custo! pen styles. The last line is drawn using custo! pen style.
pen = QtGui.QPen(Qt2ore.Qt.blac=( %( Qt2ore.Qt.;oli"<ine

(e create a QPen ob&ect. The color is black. The width is set to 8 pixels' so that we can see the differences between the pen styles. The Qt/ore.Qt.+olidLine is one of the predefined pen styles.
pen.set;tyle(Qt2ore.Qt.2ustomBash<ine pen.setBashPattern(K)( 4( &( 4P paint.setPen(pen

=ere we define a custo! pen style. (e set a Qt/ore.Qt./usto!*ashLine pen style and call a set*ashPattern67 !ethod. The list of nu!bers defines a style. There !ust be an e"en nu!ber of nu!bers. 4dd nu!bers define a dash' e"en nu!bers space. The greater the nu!ber' the greater the space or the dash. 4ur pattern is ;px dash 4px space ?px dash 4px space etc.

-igure. Pen +tyles

Q,rush
Q,rush is an ele!entary graphics ob&ect. It is used to paint the background of graphics shapes' such as rectangles' ellipses or polygons. A brush can be of three different types. A predefined brush a gradien or a texture pattern.
#!/usr/bin/python # brushes.py import sys from PyQt4 import QtGui( Qt2ore class 8rushes(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.setGeometry(1''( 1''( 1&&( %M' self.set#in"o!*itle(+8rushes+ "ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self brush = QtGui.Q8rush(Qt2ore.Qt.;oli"Pattern paint.set8rush(brush paint."ra!Dect()'( )&( N'( :' brush.set;tyle(Qt2ore.Qt.Bense)Pattern paint.set8rush(brush paint."ra!Dect()1'( )&( N'( :' brush.set;tyle(Qt2ore.Qt.Bense%Pattern paint.set8rush(brush paint."ra!Dect(%&'( )&( N'( :'

brush.set;tyle(Qt2ore.Qt.Bense1Pattern paint.set8rush(brush paint."ra!Dect()'( )'&( N'( :' brush.set;tyle(Qt2ore.Qt.Biag2rossPattern paint.set8rush(brush paint."ra!Dect()'( )'&( N'( :' brush.set;tyle(Qt2ore.Qt.Bense&Pattern paint.set8rush(brush paint."ra!Dect()1'( )'&( N'( :' brush.set;tyle(Qt2ore.Qt.Bense:Pattern paint.set8rush(brush paint."ra!Dect(%&'( )'&( N'( :' brush.set;tyle(Qt2ore.Qt.IorPattern paint.set8rush(brush paint."ra!Dect()'( )N&( N'( :' brush.set;tyle(Qt2ore.Qt.JerPattern paint.set8rush(brush paint."ra!Dect()1'( )N&( N'( :' brush.set;tyle(Qt2ore.Qt.8BiagPattern paint.set8rush(brush paint."ra!Dect(%&'( )N&( N'( :' paint.en"( app = QtGui.QApplication(sys.argv "t = 8rushes( "t.sho!( app.e,ec-(

In our exa!ple' we draw six different rectangles.


brush = QtGui.Q8rush(Qt2ore.Qt.;oli"Pattern paint.set8rush(brush paint."ra!Dect()'( )&( N'( :'

(e define a brush ob&ect. +et it to the painter ob&ect. And draw the rectangle calling the draw:ect67 !ethod.

-igure. ,rushes

/usto! (idgets in PyQt4


=a"e you e"er looked at an application and wondered' how a particular gui ite! was createdL Probably e"ery wannabe progra!!er has. Then you were looking at a list of widgets pro"ided by your fa"ourite gui library. ,ut you couldn>t find it. Toolkits usually pro"ide only the !ost co!!on widgets like buttons' text widgets' sliders etc. 1o toolkit can pro"ide all possible widgets. There are actually two kinds of toolkits. +partan toolkits and hea"y weight toolkits. The -LTH toolkit is a kind of a spartan toolkit. It pro"ides only the "ery basic widgets and assu!es' that the progra!e!er will create the !ore co!plicated ones hi!self. PyQt4 is a hea"y weight one. It has lots of widgets. Jet it does not pro"ide the !ore specialiCed widgets. -or exa!ple a speed !eter widget' a widget that !easures the capacity of a /* to be burned 6found e.g. in nero7. Toolkits also don>t ha"e usually charts. Progra!!ers !ust create such widgets by the!sel"es. They do it by using the drawing tools pro"ided by the toolkit. There are two possibilities. A progra!!er can !odify or enhance an existing widget. 4r he can create a custo! widget fro! scratch.

,urning widget
This is a widget that we can see in 1ero' H#, or other /*2*5* burning software.
#!/usr/bin/python # burning.py import sys from PyQt4 import QtGui( Qt2ore

class #i"get(QtGui.Q<abel / "ef --init--(self( parent / QtGui.Q<abel.--init--(self( parent self.set>inimum;i$e()( 1' self.parent = parent self.num = KL&( )&'( %%&( 1''( 1L&( 4&'( &%&( :''( :L&P "ef paint7vent(self( event / paint = QtGui.QPainter( paint.begin(self font = QtGui.Q5ont(+;erif+( L( QtGui.Q5ont.<ight paint.set5ont(font si$e = self.si$e( ! = si$e.!i"th( h = si$e.height( c! = self.parent.c! step = int(roun"(! / )'.' till = int(((! / L&'.' full = int(((! / L&'.' O c! O L''

if c! 4= L''/ paint.setPen(QtGui.Q2olor(%&&( %&&( %&& paint.set8rush(QtGui.Q2olor(%&&( %&&( )M4 paint."ra!Dect('( '( full( h paint.setPen(QtGui.Q2olor(%&&( )L&( )L& paint.set8rush(QtGui.Q2olor(%&&( )L&( )L& paint."ra!Dect(full( '( tillCfull( h else/ paint.setPen(QtGui.Q2olor(%&&( %&&( %&& paint.set8rush(QtGui.Q2olor(%&&( %&&( )M4 paint."ra!Dect('( '( till( h pen = QtGui.QPen(QtGui.Q2olor(%'( %'( %' ( )( Qt2ore.Qt.;oli"<ine paint.setPen(pen paint.set8rush(Qt2ore.Qt.0o8rush paint."ra!Dect('( '( !C)( hC) Q = ' for i in range(step( )'Ostep( step / paint."ra!<ine(i( '( i( & metrics = paint.font>etrics( f! = metrics.!i"th(str(self.numKQP paint."ra!*e,t(iCf!/%( h/%( str(self.numKQP Q = Q E ) paint.en"( class 8urning(QtGui.Q#i"get / "ef --init--(self( parent=0one / QtGui.Q#i"get.--init--(self( parent self.c! = L& self.sli"er = QtGui.Q;li"er(Qt2ore.Qt.Iori$ontal( self

self.sli"er.set5ocusPolicy(Qt2ore.Qt.0o5ocus self.sli"er.setDange()( L&' self.sli"er.setJalue(L& self.sli"er.setGeometry(1'( 4'( )&'( 1' self.!i" = #i"get(self self.connect(self.sli"er( Qt2ore.;.G0A<(+value2hange"(int + ( self.changeJalue hbo, = QtGui.QI8o,<ayout( hbo,.a""#i"get(self.!i" vbo, = QtGui.QJ8o,<ayout( vbo,.a"";tretch() vbo,.a""<ayout(hbo, self.set<ayout(vbo, self.setGeometry(1''( 1''( 1''( %%' self.set#in"o!*itle(+8urning+ "ef changeJalue(self( event / self.c! = self.sli"er.value( self.!i".repaint( app = QtGui.QApplication(sys.argv "t = 8urning( "t.sho!( app.e,ec-(

In our exa!ple' we ha"e a Q+lider and a custo! widget. The slider controls the custo! widget. This widget shows graphically the total capacity of a !ediu! and the free space a"ailable to us. The !ini!u! "alue of our custo! widget is ;' the !axi!u! is D?$. If we reach "alue D$$' we begin drawing in red colour. This nor!ally indicates o"erburning. The burning widget is placed at the botto! of the window. This is achie"ed using one Q=,oxLayout and one Q5,oxLayout
class #i"get(QtGui.Q<abel / "ef --init--(self( parent / QtGui.Q<abel.--init--(self( parent

The burning widget it based on the QLabel widget.


self.set>inimum;i$e()( 1'

(e change the !ini!u! siCe 6height7 of the widget. The default "alue is a bit s!all for us.
font = QtGui.Q5ont(+;erif+( L( QtGui.Q5ont.<ight paint.set5ont(font

(e use a s!aller font than the default one. That better suits our needs.
si$e = self.si$e( ! = si$e.!i"th( h = si$e.height(

c! = self.parent.c! step = int(roun"(! / )'.' till = int(((! / L&'.' full = int(((! / L&'.' O c! O L''

(e draw the widget dyna!ically. The greater the window' the greater the burning widget. And "ice "ersa. That is why we !ust calculate the siCe of the widget onto which we draw the custo! widget. The till para!eter deter!ines the total siCe to be drawn. This "alue co!es fro! the slider widget. It is a proportion of the whole area. The full para!eter deter!ines the point' where we begin to draw in red color. 1otice the use of floating point arith!etics. This is to achie"e greater precision. The actual drawing consists of three steps. (e draw the yellow or red and yellow rectangle. Then we draw the "ertical lines' which di"ide the widget into se"eral parts. -inally' we draw the nu!bers' which indicate the capacity of the !ediu!.
metrics = paint.font>etrics( f! = metrics.!i"th(str(self.numKQP paint."ra!*e,t(iCf!/%( h/%( str(self.numKQP

(e use font !etrics to draw the text. (e !ust know the width of the text in order to center it around the "ertical line.

-igure. The burning widget

The Tetris ga!e in PyQt4


/reating a co!puter ga!e is "ery challenging. +ooner or later' a progra!!er will want to create a co!puter ga!e one day. In fact' !any people beca!e interested in progra!!ing' because they played ga!es and wanted to create their own. /reating a co!puter ga!e will "astly help i!pro"ing your progra!!ing skills.

Tetris
The tetris ga!e is one of the !ost popular co!puter ga!es e"er created. The original ga!e was designed and progra!!ed by a russian progra!!er Alexey Pa&itno" in ;<G?. +ince then' tetris is a"ailable on al!ost e"ery co!puter platfor! in lots of "ariations. @"en !y !obile phone has a !odified "ersion of the tetris ga!e. Tetris is called a falling block puCCle ga!e. In this ga!e' we ha"e se"en different shapes called tetro!inoes. +0shape' O0shape' T0shape' L0shape' Line0 shape' )irroredL0shape and a +9uare0shape. @ach of these shapes is for!ed with four s9uares. The shapes are falling down the board. The ob&ect of the tetris ga!e is to !o"e and rotate the shapes' so that they fit as !uch as possible. If we !anage to for! a row' the row is destroyed and we score. (e play the tetris ga!e until we top out.

-igure. Tetro!inoes PyQt4 is a toolkit designed to create applications. There are other libraries which are targeted at creating co!puter ga!es. 1e"ertheless' PyQt4 and other application toolkits can be used to create ga!es. The following exa!ple is a !odified "ersion of the tetris ga!e' a"ailable with PyQt4 installation files.

The de"elop!ent
(e do not ha"e i!ages for our tetris ga!e' we draw the tetro!inoes using the drawing API a"ailable in the PyQt4 progra!!ing toolkit. ,ehind e"ery co!puter ga!e' there is a !athe!atical !odel. +o it is in tetris. +o!e ideas behind the ga!e.

(e use Qt/ore.Q,asicTi!er67 to create a ga!e cycle The tetro!inoes are drawn The shapes !o"e on a s9uare by s9uare basis 6not pixel by pixel7 )athe!atically a board is a si!ple list of nu!bers

#!/usr/bin/python # tetris.py

import sys import ran"om from PyQt4 import Qt2ore( QtGui class *etris(QtGui.Q>ain#in"o! / "ef --init--(self / QtGui.Q>ain#in"o!.--init--(self self.setGeometry(1''( 1''( )M'( 1M' self.set#in"o!*itle(+*etris+ self.tetrisboar" = 8oar"(self self.set2entral#i"get(self.tetrisboar" self.statusbar = self.status8ar( self.connect(self.tetrisboar"( Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( self.statusbar( Qt2ore.;<6*(?sho!>essage(Q;tring ? self.tetrisboar".start( self.center( "ef center(self / screen = QtGui.QBes=top#i"get( .screenGeometry( si$e = self.geometry( self.move((screen.!i"th( Csi$e.!i"th( /%( (screen.height( Csi$e.height( /% class 8oar"(QtGui.Q5rame / 8oar"#i"th = )' 8oar"Ieight = %% ;pee" = 1'' "ef --init--(self( parent / QtGui.Q5rame.--init--(self( parent self.timer = Qt2ore.Q8asic*imer( self.is#aitingAfter<ine = 5alse self.curPiece = ;hape( self.ne,tPiece = ;hape( self.curV = ' self.curA = ' self.num<inesDemove" = ' self.boar" = KP self.set5ocusPolicy(Qt2ore.Qt.;trong5ocus self.is;tarte" = 5alse self.isPause" = 5alse self.clear8oar"( self.ne,tPiece.setDan"om;hape( "ef shapeAt(self( ,( y / return self.boar"K(y O 8oar".8oar"#i"th "ef set;hapeAt(self( ,( y( shape / self.boar"K(y O 8oar".8oar"#i"th "ef s9uare#i"th(self / return self.contentsDect( .!i"th( E ,P

E ,P = shape / 8oar".8oar"#i"th

"ef s9uareIeight(self / return self.contentsDect( .height( "ef start(self / if self.isPause"/ return self.is;tarte" = *rue self.is#aitingAfter<ine = 5alse self.num<inesDemove" = ' self.clear8oar"(

/ 8oar".8oar"Ieight

self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( str(self.num<inesDemove" self.ne!Piece( self.timer.start(8oar".;pee"( self "ef pause(self / if not self.is;tarte"/ return self.isPause" = not self.isPause" if self.isPause"/ self.timer.stop( self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( ?pause"? else/ self.timer.start(8oar".;pee"( self self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( str(self.num<inesDemove" self.up"ate( "ef paint7vent(self( event / painter = QtGui.QPainter(self rect = self.contentsDect( boar"*op = rect.bottom( C 8oar".8oar"Ieight O self.s9uareIeight(

for i in range(8oar".8oar"Ieight / for Q in range(8oar".8oar"#i"th / shape = self.shapeAt(Q( 8oar".8oar"Ieight C i C ) if shape != *etrominoes.0o;hape/ self."ra!;9uare(painter( rect.left( E Q O self.s9uare#i"th( ( boar"*op E i O self.s9uareIeight( ( shape if self.curPiece.shape( != *etrominoes.0o;hape/ for i in range(4 / , = self.curV E self.curPiece.,(i y = self.curA C self.curPiece.y(i self."ra!;9uare(painter( rect.left( E , O self.s9uare#i"th( ( boar"*op E (8oar".8oar"Ieight C y C ) O self.s9uareIeight( ( self.curPiece.shape( "ef =eyPress7vent(self( event / if not self.is;tarte" or self.curPiece.shape( QtGui.Q#i"get.=eyPress7vent(self( event return =ey = event.=ey( == *etrominoes.0o;hape/

if =ey == Qt2ore.Qt.Hey-P/ self.pause( return if self.isPause"/ return elif =ey == Qt2ore.Qt.Hey-<eft/ self.try>ove(self.curPiece( self.curV C )( self.curA elif =ey == Qt2ore.Qt.Hey-Dight/ self.try>ove(self.curPiece( self.curV E )( self.curA elif =ey == Qt2ore.Qt.Hey-Bo!n/ self.try>ove(self.curPiece.rotate"Dight( ( self.curV( self.curA elif =ey == Qt2ore.Qt.Hey-Wp/ self.try>ove(self.curPiece.rotate"<eft( ( self.curV( self.curA elif =ey == Qt2ore.Qt.Hey-;pace/ self."ropBo!n( elif =ey == Qt2ore.Qt.Hey-B/ self.one<ineBo!n( else/ QtGui.Q#i"get.=eyPress7vent(self( event "ef timer7vent(self( event / if event.timer."( == self.timer.timer."( / if self.is#aitingAfter<ine/ self.is#aitingAfter<ine = 5alse self.ne!Piece( else/ self.one<ineBo!n( else/ QtGui.Q5rame.timer7vent(self( event "ef clear8oar"(self / for i in range(8oar".8oar"Ieight O 8oar".8oar"#i"th / self.boar".appen"(*etrominoes.0o;hape "ef "ropBo!n(self / ne!A = self.curA !hile ne!A 4 '/ if not self.try>ove(self.curPiece( self.curV( ne!A C ) / brea= ne!A C= ) self.pieceBroppe"( "ef one<ineBo!n(self / if not self.try>ove(self.curPiece( self.curV( self.curA C ) / self.pieceBroppe"( "ef pieceBroppe"(self / for i in range(4 / , = self.curV E self.curPiece.,(i y = self.curA C self.curPiece.y(i self.set;hapeAt(,( y( self.curPiece.shape( self.remove5ull<ines( if not self.is#aitingAfter<ine/ self.ne!Piece( "ef remove5ull<ines(self / num5ull<ines = ' ro!s*oDemove = KP

for i in range(8oar".8oar"Ieight / n = ' for Q in range(8oar".8oar"#i"th / if not self.shapeAt(Q( i == *etrominoes.0o;hape/ n = n E ) if n == )'/ ro!s*oDemove.appen"(i ro!s*oDemove.reverse( for m in ro!s*oDemove/ for = in range(m( 8oar".8oar"Ieight / for l in range(8oar".8oar"#i"th / self.set;hapeAt(l( =( self.shapeAt(l( = E ) num5ull<ines = num5ull<ines E len(ro!s*oDemove if num5ull<ines 4 '/ self.num<inesDemove" = self.num<inesDemove" E num5ull<ines self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( str(self.num<inesDemove" self.is#aitingAfter<ine = *rue self.curPiece.set;hape(*etrominoes.0o;hape self.up"ate( "ef ne!Piece(self / self.curPiece = self.ne,tPiece self.ne,tPiece.setDan"om;hape( self.curV = 8oar".8oar"#i"th / % E ) self.curA = 8oar".8oar"Ieight C ) E self.curPiece.minA( if not self.try>ove(self.curPiece( self.curV( self.curA / self.curPiece.set;hape(*etrominoes.0o;hape self.timer.stop( self.is;tarte" = 5alse self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( ?Game over?

"ef try>ove(self( ne!Piece( ne!V( ne!A / for i in range(4 / , = ne!V E ne!Piece.,(i y = ne!A C ne!Piece.y(i if , 3 ' or , 4= 8oar".8oar"#i"th or y 3 ' or y 4= 8oar".8oar"Ieight/ return 5alse if self.shapeAt(,( y != *etrominoes.0o;hape/ return 5alse self.curPiece = ne!Piece self.curV = ne!V self.curA = ne!A self.up"ate( return *rue "ef "ra!;9uare(self( painter( ,( y( shape / color*able = K',''''''( ',22::::( ',::22::( ',::::22( ',2222::( ',22::22( ',::2222( ',BAAA''P color = QtGui.Q2olor(color*ableKshapeP

painter.fillDect(, E )( y E )( self.s9uare#i"th( self.s9uareIeight( C %( color

C %(

painter.setPen(color.light( painter."ra!<ine(,( y E self.s9uareIeight( C )( ,( y painter."ra!<ine(,( y( , E self.s9uare#i"th( C )( y painter.setPen(color."ar=( painter."ra!<ine(, E )( y E self.s9uareIeight( C )( , E self.s9uare#i"th( C )( y E self.s9uareIeight( painter."ra!<ine(, E self.s9uare#i"th( C )( y E self.s9uareIeight( C )( , E self.s9uare#i"th( class *etrominoes(obQect / 0o;hape = ' X;hape = ) ;;hape = % <ine;hape = 1 *;hape = 4 ;9uare;hape = & <;hape = : >irrore"<;hape = L class ;hape(obQect / coor"s*able = ( (('( ' ( (('( C) ( (('( C) ( (('( C) ( ((C)( ' ( (('( ' ( ((C)( C) ( (()( C) (

C ) C )( y E )

('( ('( ('( ('( ('( ()( ('( ('(

' ( ' ( ' ( ' ( ' ( ' ( C) ( C) (

('( ' ( (C)( ' ( ()( ' ( ('( ) ( ()( ' ( ('( ) ( ('( ' ( ('( ' (

('( ' (C)( ) ()( ) ('( % ('( ) ()( ) ('( ) ('( )

( ( ( ( ( ( (

"ef --init--(self / self.coor"s = KK'('P for i in range(4 P self.piece;hape = *etrominoes.0o;hape self.set;hape(*etrominoes.0o;hape "ef shape(self / return self.piece;hape "ef set;hape(self( shape / table = ;hape.coor"s*ableKshapeP for i in range(4 / for Q in range(% / self.coor"sKiPKQP = tableKiPKQP self.piece;hape = shape "ef setDan"om;hape(self / self.set;hape(ran"om.ran"int()( L "ef ,(self( in"e, / return self.coor"sKin"e,PK'P "ef y(self( in"e, / return self.coor"sKin"e,PK)P

"ef setV(self( in"e,( , / self.coor"sKin"e,PK'P = , "ef setA(self( in"e,( y / self.coor"sKin"e,PK)P = y "ef minV(self / m = self.coor"sK'PK'P for i in range(4 / m = min(m( self.coor"sKiPK'P return m "ef ma,V(self / m = self.coor"sK'PK'P for i in range(4 / m = ma,(m( self.coor"sKiPK'P return m "ef minA(self / m = self.coor"sK'PK)P for i in range(4 / m = min(m( self.coor"sKiPK)P return m "ef ma,A(self / m = self.coor"sK'PK)P for i in range(4 / m = ma,(m( self.coor"sKiPK)P return m "ef rotate"<eft(self / if self.piece;hape == *etrominoes.;9uare;hape/ return self result = ;hape( result.piece;hape = self.piece;hape for i in range(4 / result.setV(i( self.y(i result.setA(i( Cself.,(i return result "ef rotate"Dight(self / if self.piece;hape == *etrominoes.;9uare;hape/ return self result = ;hape( result.piece;hape = self.piece;hape for i in range(4 / result.setV(i( Cself.y(i result.setA(i( self.,(i return result app = QtGui.QApplication(sys.argv tetris = *etris(

tetris.sho!( sys.e,it(app.e,ec-(

I ha"e si!plified the ga!e a bit' so that it is easier to understand. The ga!e starts i!!ediately' after it is launched. (e can pause the ga!e by pressing the p key. The space key will drop the tetris piece i!!ediately to the botto!. The ga!e goes at constant speed' no acceleration is i!ple!ented. The score is the nu!ber of lines' that we ha"e re!o"ed.
self.statusbar = self.status8ar( self.connect(self.tetrisboar"( Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( self.statusbar( Qt2ore.;<6*(?sho!>essage(Q;tring ?

(e create a statusbar' where we will display !essages. (e will display three possible !essages. The nu!ber of lines alredy re!o"ed. The paused !essage and the ga!e o"er !essage.
... self.curV = ' self.curA = ' self.num<inesDemove" = ' self.boar" = KP ...

,efore we start the ga!e cycle' we initialiCe so!e i!portant "ariables. The self.board "ariable is a list of nu!bers fro! $ ... D. It represents the position of "arious shapes and re!ains of the shapes on the board.
for Q in range(8oar".8oar"#i"th / shape = self.shapeAt(Q( 8oar".8oar"Ieight C i C ) if shape != *etrominoes.0o;hape/ self."ra!;9uare(painter( rect.left( E Q O self.s9uare#i"th( ( boar"*op E i O self.s9uareIeight( ( shape

The painting of the ga!e is di"ided into two steps. In the first step' we draw all the shapes' or re!ains of the shapes' that ha"e been dropped to the botto! of the board. All the s9uares are re!e!berd in the self.board list "ariable. (e access it using the shapeAt67 !ethod.
if self.curPiece.shape( != *etrominoes.0o;hape/ for i in range(4 / , = self.curV E self.curPiece.,(i y = self.curA C self.curPiece.y(i self."ra!;9uare(painter( rect.left( E , O self.s9uare#i"th( ( boar"*op E (8oar".8oar"Ieight C y C ) O self.s9uareIeight( ( self.curPiece.shape(

The next step is drawing of the actual piece' that is falling down.
elif =ey == Qt2ore.Qt.Hey-<eft/ self.try>ove(self.curPiece( self.curV C )( self.curA elif =ey == Qt2ore.Qt.Hey-Dight/ self.try>ove(self.curPiece( self.curV E )( self.curA

In the keyPress@"ent we check for pressed keys. If we press the right arrow

key' we try to !o"e the piece to the right. (e say try' because the piece !ight not be able to !o"e.
"ef try>ove(self( ne!Piece( ne!V( ne!A / for i in range(4 / , = ne!V E ne!Piece.,(i y = ne!A C ne!Piece.y(i if , 3 ' or , 4= 8oar".8oar"#i"th or y 3 ' or y 4= 8oar".8oar"Ieight/ return 5alse if self.shapeAt(,( y != *etrominoes.0o;hape/ return 5alse self.curPiece = ne!Piece self.curV = ne!V self.curA = ne!A self.up"ate( return *rue

In the try)o"e67 !ethod we try to !o"e our shapes. If the shape is at the edge of the board or is ad&acent to so!e other piece' we return false. 4therwise we place the current falling piece to a new position.
"ef timer7vent(self( event / if event.timer."( == self.timer.timer."( / if self.is#aitingAfter<ine/ self.is#aitingAfter<ine = 5alse self.ne!Piece( else/ self.one<ineBo!n( else/ QtGui.Q5rame.timer7vent(self( event

In the ti!er e"ent' we either create a new piece' after the pre"ious one was dropped to the botto!' or we !o"e a falling piece one line down.
"ef remove5ull<ines(self / num5ull<ines = ' ro!s*oDemove = KP for i in range(8oar".8oar"Ieight / n = ' for Q in range(8oar".8oar"#i"th / if not self.shapeAt(Q( i == *etrominoes.0o;hape/ n = n E ) if n == )'/ ro!s*oDemove.appen"(i ro!s*oDemove.reverse( for m in ro!s*oDemove/ for = in range(m( 8oar".8oar"Ieight / for l in range(8oar".8oar"#i"th / self.set;hapeAt(l( =( self.shapeAt(l( = E )

...

If the piece hits the botto!' we call the re!o"e-ullLines67 !ethod. -irst we

find out all full lines. And we re!o"e the!. (e do it by !o"ing all lines abo"e the current full line to be re!o"ed one line down. 1otice' that we re"erse the order of the lines to be re!o"ed. 4therwise' it would not work correctly. In our case we use a nai"e gra"ity. This !eans' that the pieces !ay be floating abo"e e!pty gaps.
"ef ne!Piece(self / self.curPiece = self.ne,tPiece self.ne,tPiece.setDan"om;hape( self.curV = 8oar".8oar"#i"th / % E ) self.curA = 8oar".8oar"Ieight C ) E self.curPiece.minA( if not self.try>ove(self.curPiece( self.curV( self.curA / self.curPiece.set;hape(*etrominoes.0o;hape self.timer.stop( self.is;tarte" = 5alse self.emit(Qt2ore.;.G0A<(?message*o;tatusbar(Q;tring ? ( ?Game over?

The newPiece67 !ethod creates rando!ly a new tetris piece. If the piece cannot go into it>s initial position' the ga!e is o"er.

The +hape class sa"es infor!ation about the tetris piece.


self.coor"s = KK'('P for i in range(4 P

pon creation we create an e!pty coordinates list. The list will sa"e the coordinates of the tetris piece. -or exa!ple' these tuples 6$' 0;7' 6$' $7' 6;' $7' 6;' ;7 represent a rotated +0shape. The following diagra! illustrates the shape.

-igure. /oordinates (hen we draw the current falling piece' we draw it at self.cur3' self.curJ position. Then we look at the coordinates table and draw all the four s9uares.

-igure. Tetris

Potrebbero piacerti anche