الوثيقة بهيئات أخرىHTMLODT

تعليم البرمجة الرسومية في PyQt4

  • مقالة من تأليف Jan Bodnar
  • قيد الترجمة: فضلا لا تقم بتحرير الصفحة بالكامل. قم بالضغط على زر التحرير مقابل الفصل الذي تريد ترجمته


هذا مدخل لطقم PyQt4. وهو مناسب للمبرمجين المبتدئين والمتوسطين. بعد قراءة هذا المدحل، ستتمكن من عمل تطبيقات PyQt4 غير سطحية. قد ترغب في إلقاء نظرة على مقالة تعلم بايثون أيضا.

مقدمة في طفم ادوات PyQt4

حول هذه المقالة

هذه مقدمة عن PyQt4. الهدف منها هو وضع قدمك على أول الطريق مع طقم أدوات PtQt4 تم كتابة المقال وتجربته على لينكس.

حول PyQt

تعتبر PyQt طقم أدوات لعمل تطبيقات رسومية GUI. وهي عبارة عن دمج بين لغة البرمجة بايثون ومكتبة Qt الناجحة وهذه الأخيرة من أقوى المكتبات على وجه البسيطة. الموقع الرسمي لها هو The official home site for PyQt is on www.riverbankcomputing.co.uk ويطورها فيل ثومسون Phil Thompson.

تم تنفيذ PyQt على شكل طقم من عدة وحدات بايثون Python modules. وهي تحتوي أكثر من 300 صنف class و 6000 وظيفة/طريقة function/method. وهي متعددة المنصات وتعمل على كل الأنظمة الرئيسية بما في ذلك يونكس وويندوز وماك. تخضع PyQt لرخصة مزدوجة حيث يمكن للمطورين الاختيار بين GPL والرخصة تجارية. سابقا كانت رخصة GPL متوفرة فقط على يونكس لكن بدأ من الإصدار الرابعة أصبحت GPL متوفرة على كل المنصات المدعومة.

وحيث أن هناك الكثير من الصنوف Classes المتوفرة تم تقسيمها إلى عدة وحدات Modules.

الوحدات المكونة ل PyQt4

الوحدة QtCore تحتوي الوظائف المركزية غير الرسومية. تستخدم هذه الوحدة في التعامل مع الوقت والملفات والأدلة (المجلدات) وأنواع البيانات المختلفة والسيالات streams وروابط URL وأنواع الملفات MIME والعمليات processes and threads.

الوحدة QtGui فتحتوي على المكونات الرسومية وما يتعلق يها من صنوف. وتشتمل على سبيل المثال على الأزرار والنوافذ وأشرطة الأدوات والحالة والزلاقات والصور النقصية والألوان والخطوط …إلخ.

الوحدة QtSVG بها الصنوف اللازمة لعرض محتويات ملفات SVG. وهي لغة وصفية للرسومات المتجهية ثنائية الأبعاد والتطبيقات الرسومية ب XML

الوحدة QtOpenGL تستخدم لتوليد رسومات ثلاثة الأبعاد أو ثنائية باستخدام مكتبة OpenGL. مما يمكننا من دمج Qt مع OpenGL دون عناء.

الوحدة QtNetwork تحتوي على الصنوف اللازمة لبرمجة الشبكات. هذه الصنوف تسمح بكتابة برامج خوادم severs أو مخدومات clients لبروتوكولات TCP/IP و UDP. مما يجعل برمجية الشبكات أسهل وقابل للنقل لمنصات أخرى.

الصنوف في الوحدة QtXml للعمل على ملفات xml. هذه الوحدة تقدم تطبيق ل SAX و DOM.

الوحدة QtSql تقدم صنوف التعامل مع قواعد البيانات.

بايثون

بايثون لغة نصية ناجحة. بدأ تطويرها Guido van Rossum. وقد اطلقت لاول مرة في عام 1991. بايثون استلهمت من ABC و Haskell. وهي لغة برمجة تفسيرية عالية المستوى وعامة الأغراض ومتعددة المنصات. والبعض يحب أن يسميها اللغة الديناميكية. إنها لغة سهلة التعلم. وهي لغة مصغرة minimalistic. وأحد أوضح مزاياها أنها لا تستعمل الفاصلة المنقوطة ولا الحاصرات. بل تستخدم الإزاحة (المسافة البادئة indentation) الإصدار الأحدث هو 2.5 وقد أطلق في أيلول/سبتمبر 2006. ويقوم اليوم عليها مجموعة كبيرة من المتطوعين حول العالم.

مجتمع مبرمجي TIOBE يقدم ترتيب نظري لمستوى استخدام لغات البرمجة المختلفة. جافا تتربع على عرشها بعد أن خلعت سي++ لكن سي++ ستظل حاضرة بثقلها خلال العقود القادمة ولا يوجد ما يهددها. ونرى تخصص لغات البرمجة فلغة جافا للأعمال enterprise وسهولة النقل portability . وسي هي ملكة برمجة النظم (نظم التشغيل وتعريفات وادوات النظام الصغيرة). ولغة PHP تسيطر على مواقع الويب الصغيرة والمتوسطة وجافاسكربت تستخدم في تطبيقات الويب عند المخدوم client

ترتيب اللغات في 2008 TIOBE
المرتبة اللغة التقييم
1 Java 21.571%
2 C 16.178%
3 (Visual) Basic 10.857%
4 C++ 10.057%
5 PHP 9.349%
6 Python 4.975%
7 Perl 4.694%
8 C# 3.697%
9 Ruby 2.920%
10 JavaScript 2.892%

ونلاحظ أن بايثون تحتل المركز السادس (متقدمة مرتبتين على سي#) وقد كانت قبل عام في المركز الثامن.

الأدوات الرسومية في بايثون

لعمل تطبيقات رسومية يختار مبرمجوا بايثون بين ثلاث خيارات محترمة. وهي PyGTK و wxPython و PyQt، وأيها عليك أن تختار يعتمد على الظروف. وهناك خيار آخر اسمه TkInter تجنبه.

أول البرامج على PyQt4

في هذا الجزء من “تعليم PyQt4” سنتعلم بعض الوظائف الأساسية. وسنطيل الشرح كما لو اننا نتحدث إلى طفل. وكما تكون خطوات الطفل الأولى خرقاء كذلك تكون محاولات المبتدئ في البرمجة. وتذكر، لا يوجد أغبياء لكن يوجد كسالى غير مثابرين.

مثال بسيط

هذا الكود مبسيط جدا. إنه يظهر نافذة صغيرة. نستطيع القيام بالكثير من الأشياء على هذه النافذة، نحجمها أو نكبرها أو نصغرها وهذا يتطلب كتابة كود، لكن هناك شخص ما قد قام بذلك عنا. ولأن هذا يتكرر في كل التطبيقات لا داع لكتابته مرة بعد أخرى فقد تمر إبعاد هذا الجزء من الكود عن ناظر المبرمج. مكتبة أدوات PyQt هي مكتبة عليا. فلو كنا نبرمج في مكتبة دنيا لكان الكود التالي مكونا من عشرات الأسطر.

#!/usr/bin/python
 
# simple.py
 
import sys
from PyQt4 import QtGui
 
app = QtGui.QApplication(sys.argv)
 
widget = QtGui.QWidget()
widget.resize(250, 150)
widget.setWindowTitle('simple')
widget.show()
 
sys.exit(app.exec_())

وإليكم شرح الكود

import sys
from PyQt4 import QtGui

هنا قمنا بعملية الاستيراد import اللازمة. فالودجات 1) الرسومية الأساسية موجودة في وحدة QtGui.

app = QtGui.QApplication(sys.argv)

كر تطبيق على PyQt4 يجب أن ينشئ كائن من نوع تطبيق QApplication وهو موجود في وحدة QtGui المعامل sys.argv هي قائمة بالمعاملات التي تمرر للتطبيق عبر سطر الاوامر. وكما نعلم يمكن تنفيذ نصوص بايثون من الصدفة (سطر الأوامر) فهي طريقة نتحكم فيها ببداية البرنامج.

widget = QtGui.QWidget()

الصنف QWidget هو أساس صنوف base class كل الودجات (الكائنات الرسومية) في PyQt4. وهنا قدمنا على استدعاء الباني constructor الخاص به. والسلوك التلقائي أن لا يحتاج إلى والد. الودجة التي لا والد لها تكون نافذة.

widget.resize(250, 150)

الطريقة resize تحجّم الكائن. فسيكون عرضه 250 وارتفاعه 150 (بيكسل).

 widget.setWindowTitle('simple')

وهنا وضعنا عنوانا لنافذتنا. هذا العنوان يظهر في شريط العنوان.

 widget.show()

الطريقة show تظهر الكائن على الشاشة

sys.exit(app.exec_())

وأخيرا ندخل في الحلقة الرئيسية mainloop للتطبيق. وتبدأ معالجة الأحداث بعد هذه النقطة. الحلقة الرئيسية تستلم الأحداث من نظام النوافذ وترسلها لودجات التطبيق. وتنتهي الحلقة الرئيسية عندما نستدع exit أو عند تدمير الودجة الرئيسية. وقد قمنا باستدعاء sys.exit للتأكد من إنهاء التطبيق بطريقة نظيفة.

وإذا كنت تتسأل لماذا لحقت _ بالطريقة exec ؟ كل شيء له معنى. ببساطة لان exec عبارة عن كلمة مفتاحية في لغة بايثون.

البرنامج البسيط Simple

An application icon

The application icon is a small image, which is usually displayed in the top left corner of the titlebar. In the following example we will show, how we do it in PyQt4. We will also introduce some new methods.

#!/usr/bin/python
 
# icon.py
 
import sys
from PyQt4 import QtGui
 
 
class Icon(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Icon')
        self.setWindowIcon(QtGui.QIcon('icons/web.png'))
 
 
app = QtGui.QApplication(sys.argv)
icon = Icon()
icon.show()
sys.exit(app.exec_())

The previous example was coded in a procedural style. Python programming language supports both procedural and object oriented programming styles. Programming in PyQt4 means programming in OOP.

class Icon(QtGui.QWidget):
   def __init__(self, parent=None):
      QtGui.QWidget.__init__(self, parent)

The three most important things in object oriented programming are classes, data and methods. Here we create a new class called Icon. The Icon class inherits from QtGui.QWidget class. This means, that we must call two constructors. The first one for the Icon class and the second one for the inherited class.

self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('icons/web.png'))

All three classes have been inherited from the QtGui.QWidget class. The setGeometry() does two things. It locates the window on the screen and sets the size of the window. The first two parameters 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 method sets the application icon. To do this, we have created a QIcon object. The QIcon receives the path to our icon to be displayed.

برنامج له أيقونة

Showing a tooltip

We can provide a balloon help for any of our widgets.

#!/usr/bin/python
 
# tooltip.py
 
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
 
 
class Tooltip(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Tooltip')
 
        self.setToolTip('This is a <b>QWidget</b> widget')
        QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))
 
 
app = QtGui.QApplication(sys.argv)
tooltip = Tooltip()
tooltip.show()
app.exec_()

In this example, we show a tooltip for a QWidget widget.

self.setToolTip('This is a <b>QWidget</b> widget')

To create a tooltip, we call the setTooltip() method. We can use rich text formatting.

QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))

Because the default QToolTip font looks bad, we change it.

تلميح Tooltip

Closing a window

The obvious way to how to close a window is to click on the x mark on the titlebar. In the next example, we will show, how we can programatically close our window. We will briefly touch signals and slots.

The following is the constructor of a QPushButton, that we will use in our example.

QPushButton(string text, QWidget parent = None)

The text parameter 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 QWidget.

#!/usr/bin/python
 
# quitbutton.py
 
import sys
from PyQt4 import QtGui, QtCore
 
 
class QuitButton(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Icon')
 
        quit = QtGui.QPushButton('Close', self)
        quit.setGeometry(10, 10, 60, 35)
 
        self.connect(quit, QtCore.SIGNAL('clicked()'),
            QtGui.qApp, QtCore.SLOT('quit()'))
 
 
app = QtGui.QApplication(sys.argv)
qb = QuitButton()
qb.show()
sys.exit(app.exec_())
quit = QtGui.QPushButton('Close', self)
quit.setGeometry(10, 10, 60, 35)

We create a push button and position it on the QWidget just like we have positioned the QWidget on the screen.

self.connect(quit, QtCore.SIGNAL('clicked()'),
     QtGui.qApp, QtCore.SLOT('quit()'))

The event processing system in PyQt4 is built with the signal & slot mechanism. If we click on the button, the signal clicked() is emitted. The slot can be a PyQt slot or any python callable. The QtCore.QObject.connect() method connects signals with slots. In our case the slot is a predefined PyQt quit() slot. The communication is done between two objects. The sender and the receiver. The sender is the push button, the receiver is the application object.

زر الخروج

Message Box

By default, if we click on the x button on the titlebar, the QWidget is closed. Sometimes we want to modify this default behaviour. For example, if we have a file opened in an editor to which we did some changes. We show a message box to confirm the action.

#!/usr/bin/python
 
# messagebox.py
 
import sys
from PyQt4 import QtGui
 
 
class MessageBox(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('message box')
 
 
    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
 
        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
 
app = QtGui.QApplication(sys.argv)
qb = MessageBox()
qb.show()
sys.exit(app.exec_())

If we close the QWidget, the QCloseEvent is generated. To modify the widget behaviour we need to reimplement the closeEvent() event handler.

reply = QtGui.QMessageBox.question(self, 'Message',
     "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

We show a message box with two buttons. Yes and No. The first string appears on the titlebar. The second string is the message text displayed by the dialog. The return value is stored in the reply variable.

 if reply == QtGui.QMessageBox.Yes:
     event.accept()
 else:
    event.ignore()

Here we test the return value. If we clicked Yes button, we accept the event which leads to the closure of the widget and to the termination of the application. Otherwise we ignore the close event.

صندوق Message Box

Centering 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 Center(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('center')
        self.resize(250, 150)
        self.center()
 
    def center(self):
        screen = QtGui.QDesktopWidget().screenGeometry()
        size =  self.geometry()
        self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
 
 
app = QtGui.QApplication(sys.argv)
qb = Center()
qb.show()
sys.exit(app.exec_())
self.resize(250, 150)

Here we resize the QWidget to be 250px wide and 150px heigh.

screen = QtGui.QDesktopWidget().screenGeometry()

We figure out the screen resolution of our monitor.

size =  self.geometry()

Here we get the size of our QWidget.

self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)

Here we move the window to the center of the screen.

Menus and Toolbars in PyQt4

Main Window

The QMainWindow class provides a main application window. This enables to create the classic application skeleton with a statusbar, toolbars and a menubar.

Statusbar

The statusbar is a widget that si used for displaying status information.

#!/usr/bin/python
 
# statusbar.py 
 
import sys
from PyQt4 import QtGui
 
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
 
        self.resize(250, 150)
        self.setWindowTitle('statusbar')
 
        self.statusBar().showMessage('Ready')
 
 
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
self.statusBar().showMessage('Ready')

To get the statusbar, we call the statusBar() method of the QApplication class. The showMessage() displays message on the statusbar.

Menubar

A menubar is one of the most visible parts of the GUI application. It is a group of commands located in various menus. While in console applications you had to remember all those arcane commands, here we have most of the commands grouped into logical parts. There are accepted standards that further reduce the amount of time spending to learn a new application

#!/usr/bin/python
 
# menubar.py 
 
import sys
from PyQt4 import QtGui, QtCore
 
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
 
        self.resize(250, 150)
        self.setWindowTitle('menubar')
 
        exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        exit.setShortcut('Ctrl+Q')
        exit.setStatusTip('Exit application')
        self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
 
        self.statusBar()
 
        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(exit)
 
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
menubar = self.menuBar()
file = menubar.addMenu('&File')
file.addAction(exit)

First we create a menubar with the menuBar() method of the QMainWindow class. Then we add a menu with the AddMenu() method. In the end we plug the action object into the file menu.

Toolbar

Menus group all commands that we can use in an application. Toolbars provide a quick access to the most frequently used commands.

#!/usr/bin/python
 
# toolbar.py 
 
import sys
from PyQt4 import QtGui, QtCore
 
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
 
        self.resize(250, 150)
        self.setWindowTitle('toolbar')
 
        self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        self.exit.setShortcut('Ctrl+Q')
        self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
 
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(self.exit)
 
 
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
self.exit.setShortcut('Ctrl+Q')

GUI applications are controlled with commands. These commands can be launched from a menu, a context menu, a toolbar or with a shortcut. PyQt simplifies development with the introduction of actions. An action object can have menu text, an icon, a shortcut, status text, “What's This?” text and a tooltip. In our example, we define an action object with an icon, a tooltip and a shortcut.

self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))

Here we connect the action's triggered() signal to the predefined close() signal.

self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(self.exit)

Here we create a toolbar and plug and action object into it.

toolbar.jpg

Putting it together

In the last example of this section, we will create a menubar, toolbar and a statusbar. We will also create a central widget.

#!/usr/bin/python
 
# mainwindow.py 
 
import sys
from PyQt4 import QtGui, QtCore
 
class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
 
        self.resize(350, 250)
        self.setWindowTitle('mainwindow')
 
        textEdit = QtGui.QTextEdit()
        self.setCentralWidget(textEdit)
 
        exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        exit.setShortcut('Ctrl+Q')
        exit.setStatusTip('Exit application')
        self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
 
        self.statusBar()
 
        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(exit)
 
        toolbar = self.addToolBar('Exit')
        toolbar.addAction(exit)
 
 
app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
textEdit = QtGui.QTextEdit()
self.setCentralWidget(textEdit)

Here we create a text edit widget. We set it to be the central widget of the QMainWindow. The central widget will occupy all space that is left.

mainwindow.jpg

Layout management in PyQt4

Important thing in programming is the layout management. Layout management is the way how we place the widgets on the window. The management can be done in two ways. We can use absolute positioning or layout classes.

Absolute positioning

The programmer specifies the position and the size of each widget in pixels. When you use absolute positioning, you have to understand several things.

  • the size and the position of a widget do not change, if you resize a window
  • applications might look different on various platforms
  • changing fonts in your application might spoil the layout
  • if you decide to change your layout, you must completely redo your layout, which is tedious and time consuming
#!/usr/bin/python
 
# absolute.py
 
import sys
from PyQt4 import QtGui
 
 
class Absolute(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('Communication')
 
        label = QtGui.QLabel('Couldn\'t', self)
        label.move(15, 10)
 
        label = QtGui.QLabel('care', self)
        label.move(35, 40)
 
        label = QtGui.QLabel('less', self)
        label.move(55, 65)
 
        label = QtGui.QLabel('And', self)
        label.move(115, 65)
 
        label = QtGui.QLabel('then', self)
        label.move(135, 45)
 
        label = QtGui.QLabel('you', self)
        label.move(115, 25)
 
        label = QtGui.QLabel('kissed', self)
        label.move(145, 10)
 
        label = QtGui.QLabel('me', self)
        label.move(215, 10)
 
        self.resize(250, 150)
 
app = QtGui.QApplication(sys.argv)
qb = Absolute()
qb.show()
sys.exit(app.exec_())

We simply call the move() method to position our widgets. In our case QLabel-s. We position them by providing the x and the y coordinates. The beginning of the coordinate system is at the left top corner. The x values grow from left to right. The y values grow from top to bottom.

Box Layout

Layout management with layout classes is much more flexible and practical. It is the preferred way to place widgets on a window. The basic layout classes are QHBoxLayout and QVBoxLayout. They line up widgets horizontally and vertically.

Imagine that we wanted to place two buttons in the right bottom corner. To create such a layout, we will use one horizontal and one vertical box. To create the neccessary space, we will add a stretch factor.

#!/usr/bin/python
 
# boxlayout.py
 
import sys
from PyQt4 import QtGui
 
 
class BoxLayout(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('box layout')
 
        ok = QtGui.QPushButton("OK")
        cancel = QtGui.QPushButton("Cancel")
 
        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(ok)
        hbox.addWidget(cancel)
 
        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
 
        self.setLayout(vbox)
 
        self.resize(300, 150)
 
app = QtGui.QApplication(sys.argv)
qb = BoxLayout()
qb.show()
sys.exit(app.exec_())
ok = QtGui.QPushButton("OK")
cancel = QtGui.QPushButton("Cancel")

Here we create two push buttons.

hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(ok)
hbox.addWidget(cancel)

We create a horizontal box layout. Add a stretch factor and both buttons.

vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)

To create the necessary layout, we put a horizontal lauout into a vertical one.

self.setLayout(vbox)

Finally, we set the main layout of the window.

boxlayout.jpg

QGridLayout

The most universal layout class is the grid layout. This layout divides the space into rows and columns. To create a grid layout, we use the QGridLayout class.

#!/usr/bin/python
 
# gridlayout.py
 
import sys
from PyQt4 import QtGui
 
 
class GridLayout(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('grid layout')
 
        names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/',
            '4', '5', '6', '*', '1', '2', '3', '-',
            '0', '.', '=', '+']
 
        grid = QtGui.QGridLayout()
 
        j = 0
        pos = [(0, 0), (0, 1), (0, 2), (0, 3),
                (1, 0), (1, 1), (1, 2), (1, 3),
                (2, 0), (2, 1), (2, 2), (2, 3),
                (3, 0), (3, 1), (3, 2), (3, 3 ),
                (4, 0), (4, 1), (4, 2), (4, 3)]
 
        for i in names:
            button = QtGui.QPushButton(i)
            if j == 2:
                grid.addWidget(QtGui.QLabel(''), 0, 2)
            else: grid.addWidget(button, pos[j][0], pos[j][1])
            j = j + 1
 
        self.setLayout(grid)
 
 
 
app = QtGui.QApplication(sys.argv)
qb = GridLayout()
qb.show()
sys.exit(app.exec_())

In our example, we create a grid of buttons. To fill one gap, we add one QLabel widget.

grid = QtGui.QGridLayout()

Here we create a grid layout.

 if j == 2:
     grid.addWidget(QtGui.QLabel(''), 0, 2)
 else: grid.addWidget(button, pos[j][0], pos[j][1])

To add a widget to a grid, we call the addWidget() method. The arguments are the widget, the row and the column number.

gridlayout.jpg

Widgets can span multiple columns or rows in a grid. In the next example we illustrate this.

 #!/usr/bin/python
 
# gridlayout2.py
 
import sys
from PyQt4 import QtGui
 
 
class GridLayout2(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('grid layout')
 
        title = QtGui.QLabel('Title')
        author = QtGui.QLabel('Author')
        review = QtGui.QLabel('Review')
 
        titleEdit = QtGui.QLineEdit()
        authorEdit = QtGui.QLineEdit()
        reviewEdit = QtGui.QTextEdit()
 
        grid = QtGui.QGridLayout()
        grid.setSpacing(10)
 
        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)
 
        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)
 
        grid.addWidget(review, 3, 0)
        grid.addWidget(reviewEdit, 3, 1, 5, 1)
 
 
        self.setLayout(grid)
        self.resize(350, 300)
 
 
app = QtGui.QApplication(sys.argv)
qb = GridLayout2()
qb.show()
sys.exit(app.exec_())
grid = QtGui.QGridLayout()
grid.setSpacing(10)

We create a grid layout and set spacing between widgets.

grid.addWidget(reviewEdit, 3, 1, 5, 1)

If we add a widget to a grid, we can provide row span and column span of the widget. In our case, we make the reviewEdit widget span 5 rows.

gridlayout2.jpg

Events and Signals in PyQt4

In this part of the PyQt4 programming tutorial, we will explore events and singnals occuring in applications.

Events

Events are an important part in any GUI program. Events are generated by users or by the system. When we call the application's exec_() method, the application enters the main loop. The main loop fetches events and sends them to the objects. Trolltech has introduced a unique signal and slot mechanism.

Signals & Slots

Signals are emitted, when users click on the button, drag a slider etc. Signals can be emitted also by the environment. For example, when a clock ticks. A slot is a method, 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, QtCore
 
 
class SigSlot(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('signal & slot')
 
        lcd = QtGui.QLCDNumber(self)
        slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
 
        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(slider)
 
        self.setLayout(vbox)
        self.connect(slider,  QtCore.SIGNAL('valueChanged(int)'), lcd, 
		QtCore.SLOT('display(int)') )
 
        self.resize(250, 150)
 
 
app = QtGui.QApplication(sys.argv)
qb = SigSlot()
qb.show()
sys.exit(app.exec_())

In our example, we display an lcd number and a slider. We change the lcd number by dragging the slider.

self.connect(slider,  QtCore.SIGNAL('valueChanged(int)'), lcd, QtCore.SLOT('display(int)') )

Here we connect a valueChanged() signal of the slider to the display() slot of the lcd number.

The connect method has four parameters. The sender is an object that sends a signal. The signal is the signal, which is emitted. The receiver is the object, that receives the signal. Finally the slot is the method, that reacts to the signal.

signalslot.jpg

Reimplementing event handler

Events in PyQt are processed mainly by reimplementing event handlers .

#!/usr/bin/python
 
# escape.py
 
import sys
from PyQt4 import QtGui, QtCore
 
class Escape(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('escape')
        self.resize(250, 150)
        self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
 
 
    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()
 
app = QtGui.QApplication(sys.argv)
qb = Escape()
qb.show()
sys.exit(app.exec_())

In our example, we reimplement the keyPressEvent() event handler.

 def keyPressEvent(self, event):
     if event.key() == QtCore.Qt.Key_Escape:
         self.close()

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

Emitting signals

Objects created from QtCore.QObject can emit signals. If we click on the button, a clicked() signal is generated. In the following example we will see, how we can emit signals.

 
#!/usr/bin/python
 
# emit.py
 
import sys
from PyQt4 import QtGui, QtCore
 
 
class Emit(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setWindowTitle('emit')
        self.resize(250, 150)
        self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
 
    def mousePressEvent(self, event):
        self.emit(QtCore.SIGNAL('closeEmitApp()'))
 
app = QtGui.QApplication(sys.argv)
qb = Emit()
qb.show()
sys.exit(app.exec_())

We create a new signal called closeEmitApp(). This signal is emitted, during a mouse press event.

def mousePressEvent(self, event): self.emit(QtCore.SIGNAL('closeEmitApp()'))

Emitting a signal with the emit() method.

self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )

Here we connect the manually created closeEmitApp() signal with the close() slot.

Dialogs in PyQt4

Dialog windows or dialogs are an indispensable part of most modern GUI applications. A dialog is defined as a conversation between two or more persons. In a computer application a dialog is a window which is used to “talk” to the application. A dialog is used to input data, modify data, change the application settings etc. Dialogs are important means of communication between a user and a computer program.

There are essentially two types of dialogs. Predefined dialogs and custom dialogs.

Predefined Dialogs

QInputDialog

The QInputDialog provides a simple convenience dialog to get a single value from the user. The input value can be a string, a number or an item from a list.

#!/usr/bin/python
 
# inputdialog.py
 
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
 
class InputDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        self.setGeometry(300, 300, 350, 80)
        self.setWindowTitle('InputDialog')
 
        self.button = QtGui.QPushButton('Dialog', self)
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)
 
        self.button.move(20, 20)
        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
        self.setFocus()
 
        self.label = QtGui.QLineEdit(self)
        self.label.move(130, 22)
 
 
    def showDialog(self):
        text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
 
        if ok:
            self.label.setText(unicode(text))
 
 
app = QtGui.QApplication(sys.argv)
icon = InputDialog()
icon.show()
app.exec_()

The example has a button and a line edit widget. The button shows the input dialog for getting text values. The entered text will be displayed in the line edit widget.

text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')

This line displays the input dialog. The first string is a dialog title, the second one is a message within the dialog. The dialog returns the entered text and a boolean value. If we clicked ok button, the boolean value is true, otherwise false.

inputdialog.jpg

QColorDialog

The QColorDialog provides a dialog widget for specifying colors.

#!/usr/bin/python
 
# colordialog.py
 
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
 
 
class ColorDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        color = QtGui.QColor(0, 0, 0)
 
        self.setGeometry(300, 300, 250, 180)
        self.setWindowTitle('ColorDialog')
 
        self.button = QtGui.QPushButton('Dialog', self)
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)
        self.button.move(20, 20)
 
        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
        self.setFocus()
 
        self.widget = QtGui.QWidget(self)
        self.widget.setStyleSheet("QWidget { background-color: %s }"
            % color.name())
        self.widget.setGeometry(130, 22, 100, 100)
 
 
    def showDialog(self):
        color = QtGui.QColorDialog.getColor()
 
        self.widget.setStyleSheet("QWidget { background-color: %s }"
            % color.name())
 
app = QtGui.QApplication(sys.argv)
cd = ColorDialog()
cd.show()
app.exec_()

The application example shows a push button and a QWidget. The widget background is set to black color. Using the QColorDialog, we can change it's background.

color = QtGui.QColorDialog.getColor()

This line will pop up the QColorDialog.

self.widget.setStyleSheet("QWidget { background-color: %s }" % color.name())

We change the background color using stylesheets.

colordialog.jpg

QFontDialog

The QFontDialog is a dialog widget for selecting font.

#!/usr/bin/python
 
# fontdialog.py
 
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
 
 
class FontDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
 
        hbox = QtGui.QHBoxLayout()
 
        self.setGeometry(300, 300, 250, 110)
        self.setWindowTitle('FontDialog')
 
        button = QtGui.QPushButton('Dialog', self)
        button.setFocusPolicy(QtCore.Qt.NoFocus)
        button.move(20, 20)
 
        hbox.addWidget(button)
 
        self.connect(button, QtCore.SIGNAL('clicked()'), self.showDialog)
 
        self.label = QtGui.QLabel