هذا مدخل لطقم PyQt4. وهو مناسب للمبرمجين المبتدئين والمتوسطين.
بعد قراءة هذا المدحل، ستتمكن من عمل تطبيقات PyQt4 غير سطحية.
قد ترغب في إلقاء نظرة على مقالة تعلم بايثون أيضا.
هذه مقدمة عن PyQt4. الهدف منها هو وضع قدمك على أول الطريق مع طقم أدوات PtQt4 تم كتابة المقال وتجربته على لينكس.
تعتبر PyQt طقم أدوات لعمل تطبيقات رسومية GUI. وهي عبارة عن دمج بين لغة البرمجة بايثون ومكتبة Qt الناجحة وهذه الأخيرة من أقوى المكتبات على وجه البسيطة. الموقع الرسمي لها هو
The official home site for PyQt is on www.riverbankcomputing.co.uk
ويطورها فيل ثومسون Phil Thompson.
تم تنفيذ PyQt على شكل طقم من عدة وحدات بايثون Python modules.
وهي تحتوي أكثر من 300 صنف class 1) و 6000 وظيفة/طريقة function/method.
وهي متعددة المنصات وتعمل على كل الأنظمة الرئيسية بما في ذلك يونكس وويندوز وماك.
تخضع PyQt لرخصة مزدوجة حيث يمكن للمطورين الاختيار بين GPL والرخصة تجارية.
سابقا كانت رخصة GPL متوفرة فقط على يونكس لكن بدأ من الإصدار الرابعة أصبحت GPL متوفرة على كل المنصات المدعومة.
وحيث أن هناك الكثير من الصنوف Classes المتوفرة تم تقسيمها إلى عدة وحدات Modules.
الوحدة 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” سنتعلم بعض الوظائف الأساسية. وسنطيل الشرح كما لو اننا نتحدث إلى طفل. وكما تكون خطوات الطفل الأولى خرقاء كذلك تكون محاولات المبتدئ في البرمجة.
وتذكر، لا يوجد أغبياء لكن يوجد كسالى غير مثابرين.
هذا الكود مبسط جدا. إنه يظهر نافذة صغيرة. نستطيع القيام بالكثير من الأشياء على هذه النافذة، نحجمها أو نكبرها أو نصغرها وهذا يتطلب كتابة كود، لكن هناك شخص ما قد قام بذلك عنا. ولأن هذا يتكرر في كل التطبيقات لا داع لكتابته مرة بعد أخرى فقد تم إبعاد هذا الجزء من الكود عن ناظر المبرمج. مكتبة أدوات 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 اللازمة. فالودجات 2) الرسومية الأساسية موجودة في وحدة 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 عبارة عن كلمة مفتاحية في لغة بايثون.
أيقونة التطبيق (البرنامج) هي صورة صغيرة تعرض في زاوية شريط العنوان. وفي المثال التالي سنريكم طريقة القيام بذلك في PyQt4
سنقدم بعض الطرق الجديدة.
#!/usr/bin/python
# icon.py
import sys
from PyQt4 import QtGui
class WinWithIcon(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 = WinWithIcon()
icon.show()
sys.exit(app.exec_())
المثال السابق كتب بطريقة إجرائية. وبايثون تدعم الطريقة الإجرائية Procedural والموجهة للكائنات OOP. البرمجة مع PyQt4 تعني السير بأسلوب OOP.
class WinWithIcon(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
أهم 3 عناصر في البرمجة الموجهة للكائنات هي الصنوف classes والبيانات data والطرق methods. وهنا قمنا بعمل صنف جديد اسمه WinWithIcon يرث الصنف QtGui.QWidget وهذا يعني أننا نستدعي الإنشاءين واحد للصنف WinWithIcon والثاني للصنف الذي ورثه.
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('icons/web.png'))
كل الصنوف الثلاثة ورثت من QtGui.QWidget.
الطريقة setGeometry تقوم بشيئين. تموضع النافذة في مكانها وتحدد حجمها. المعاملان الأولان هما الإحداثيان x و y للنافذة. الثالث هو العرض والرابع هو الارتفاع.
الطريقة الأخيرة (أي الدالة الأخيرة) تحدد الأيقونة. لعمل ذلك قمنا بعمل كائن QIcon وهو يحمّل الأيقونة من المسار المعطى.
نستطيع ان نعرض بالونات المساعدة3) للودجات الخاصة بنا
#!/usr/bin/python
# tooltip.py
import sys
from PyQt4 import QtGui
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_()
فى هذا المثال سنقوم بعرض نص تلميج على ودجة QWidget
self.setToolTip('This is a <b>QWidget</b> widget')
ننشئ التحديد باستدعاء الطريقة setToolTip. نستطيع عمل نص غني بالتنسيق Rich Text
QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))
ولأن الخط التلقائي في QToolTip قبيح نغيّره باستدعاء setFont 4)
الطريقة الواضحة لإغلاق نافذة هي عن طريق الضغط على زر إكس x الموجود بشريط العنوان، سنريك فى المثال التالي كيف تقوم بإغلاق نافذتنا برمجيا.
اخترنا المشيد التالي لصنف QPushButton فى مثالنا5)
QPushButton(string text, QWidget parent = None)
تمثل فيه text النص الظاهر وتمثل parent والد الزر parent الذي فيه سيظر الزر وفي مثالنا هو 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('QuitButton')
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)
انشأنا زر وحددنا موضعه بإستخدام الطريقة setGeometry 6) ، مثلما موضعنا النافذة على الشاشة.
self.connect(quit, QtCore.SIGNAL('clicked()'),
QtGui.qApp, QtCore.SLOT('quit()'))
معالجة الأحداث فى PyQt4 مبنية على آلية الإشارة والتّلم7) signal/slot إذا ضغطنا على زر تُبث emitted إشارة clicked() والتّلم slot الذي تمرر له هي أي تلم مُعرّف في PtQt أو أي كائن قابل للاستدعاء في بايثون python callable.
الطريقة QtCore.QObject.connect()
تربط الإشارة بالتلم. في حالتنا التلم معرف مسبقا في PyQt وهو quit()
الإتصال سيتم بين كائنين الأول المرسل (وهو الزر) والمستقبل (وهو كائن التطبيق application object)
عادة، عند الضغط على علامة إكس x فى شريط العنوان يتم غلق الودجة، أحيانا نرغب في تعديل هذا السلوك. على سبيل المثال، إن كان لدينا ملف مفتوح فى محرر نصوص قمنا ببعض التعديلات عليه؛ عندها علينا عرض رسالة لتأكيد عملية الإغلاق
#!/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_())
عند غلق الودجة يتم توليد الحدث QcloseEvent. لتعديل سلوك الودجة يجب علينا إعادة تعريف معالج حدث ()closeEvent
reply = QtGui.QMessageBox.question(self, 'Message',
"Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
نعرض “صندوف رسالة” له زران “نعم” و “لا” وأول معامل هو السلسلة النصية التي تظهر على شريط العنوان والثاني هو محتوى رسالة صندوق الحوار. الرد يوضع في المتغير reply
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
event.ignore()
هنا نختبر قيمة الرد فإن كان المستخدم قد اختار
“نعم” يتم الموافقة على الحدث مما يؤدي لإغلاق الودجة وإلا يتم تجاهل الحدث.
السكربت التالي يبين طريقة توسيط النافذة على سطح المكتب
#!/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)
هنا نقوم بتحجيم الودجة QWidget إلى 250 بكسل عرض و 150 بكسل ارتفاع
screen = QtGui.QDesktopWidget().screenGeometry()
نحصل على الاستبانة الخاصة بالشاشة
size = self.geometry()
نحصل على الحجم الخاص بالودجة
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
نقوم بنقل النافذة الى منتصف الشاشة بإستخدام الطريقة move
الصنف QMainWindow يقدم نافذة التطبيق الأساسية. وهذا يتيح إنشاء هيكل التطبيق بشرط حالة statusbar وشريط الأدوات وشريط القوائم
شريط الحالة هو ودجة تستخدم فى عرض معلومات عن حالة البرنامج (مثل “تم حفظ الملف”)
#!/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')
للحصول على شريط الحالة نقوم بإستدعاء الطريقة statusBar ونقوم بإستدعاء الطريقة showMessage لعرض رسالة ما على شريط الحالة
شريط القوائم أحد أوضح الأجزاء فى واجهة التطبيق، هو مجموعة من الأوامر commands موزعةً على القوائم. كان لزاما عليك تذكر الأوامر في تطبيقات المحاورة console أما هنا فلا تحتاج لتذكر الأوامر إذ تجمعت إلى أجزاء منطقية.
وهناك معايير معتمدة لتقليل الوقت اللازم لتعلم التطبيقات الجديدة.
#!/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)
أولا أنشأنا شريط القائمة بالطريقة menuBar من الصنف QMainWindow. ثم أضفنا قائمة له بالطريقة AddMenu. وفي النهاية وصلنا كائن العمل action object لقائمة ملف.
في حين يقوم شريط القوائم بتجميع كل الأوامر المستخدمه فى التطبيق، يتفرد شريط الأدوات بتقديم وصول سريع لأكثرها استخداما.
#!/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 بأوامر، تطلق هذه الأوامر من شريط القوائم أو من القوائم السياقية (بالزر الأيمن مثلا) أو شريط الأدوات أو مفتاح مختصر (CTRL+Q مثلا).
تسهّل PyQt التطوير عبر تقديم مفهوم الأعمل actions.
العمل قد يكون له نص ليظهر في القائمة وأيقونة ومفتاح مختصر ونص الحالة ونص “ما هذا ؟” ونص التلميح.
في مثالنا عرّفنا عمل له أيقونة وتلميح واختصار.
- self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
هنا قمنا بربط الإشارة triggered الخاصة بالعمل action إلى التلم المعرف مسبقا close.
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(self.exit)
وهنا أنشأنا الشرط ووصنا العمل إليه.
في آخر امثلة هذا الفصل سنقوم بإنشاء شريط قوائم وشريط ادوات وشريط حالة وودجة مركزية.
#!/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)
هنا نقوم بإضافة ودجة تحرير النصوص ونجعله الودجة المركزية للنافذة QMainWindow، الودجة المركزى سيقوم بشغل كل المساحة المتبقية.
إن إدارة المخطط (توزيع المساحات في واجهة البرنامج)
أمر مهم، فعن طريقها نقوم بتحديد طريقة توزيع الودجات على النافذة (مكانا وحجما).
نستطيع إدارتها بطريقين: الموقع المطلق أو استخدام احد صنوف التخطيط layout classes
وهي أن يحدد المبرمج مكان وحجم كل ودجة بوحدة بكسل، عند استخدامك للتحديد المطلق يجب ان تعلم عدة اشياء:
عند اعادة تحجيم النافذة ، لا يغيّر مكان وحجم الودجة تبعا لذلك
التطبيق قد يختلف شكله على المنصات المختلفة
تغيّر الخطوط قد يفسد تصميم المخطط
إذا قررت تغيّر المخطط، فإنك ستحتاج الى إعادة تنظيمه بالكامل وهو أمر ممل ويأخذ وقتا
#!/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('absolute')
label = QtGui.QLabel('ABC', self)
label.move(15, 10)
label = QtGui.QLabel('DEF', self)
label.move(35, 40)
label = QtGui.QLabel('GHI', self)
label.move(55, 65)
label = QtGui.QLabel('JKL', self)
label.move(115, 65)
label = QtGui.QLabel('MNO', self)
label.move(135, 45)
label = QtGui.QLabel('PQR', self)
label.move(115, 25)
label = QtGui.QLabel('STUV', self)
label.move(145, 10)
label = QtGui.QLabel('WXYZ', self)
label.move(215, 10)
self.resize(250, 150)
app = QtGui.QApplication(sys.argv)
qb = Absolute()
qb.show()
sys.exit(app.exec_())
ببساطة، نقوم بإستدعاء الطريقة move لتحديد موقع كل ودجة.
نحدد الموقع بالإحاثي السيني x والصادي y. نقطة الأصل في نظام الإحداثيات هي الزاوية العليا اليسرى
وتزداد فيه قيم س من اليسار إلى اليمين وقيم ص تزداد من أعلى لأسفل.
إدارة المخطط بصفنوف المخططات أكثر مرونة وعملية. وهى الطريقة المفضلة لوضع ودجات على نافذة.
الصفنوف الأساسية هى QHBoxLayout و QVBoxLayout وهما يرصفان الودجات أفقيا أو عاموديا (على الترتيب).
تخيل أننا نريد وضع زرين فى أسفل يمين النافذة. لعمل مثل هذا المخطط سنستخدم صندوق افقى وصندوق رأسى ولإنشاء المساحة الباقية سنستخدم معامل التمدد 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")
هنا نقوم بإنشاء الزرين من الصنف QPushButton ونقوم بتحدد النص الظاهر عليهم كمعاملات للمشيد.
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(ok)
hbox.addWidget(cancel)
هنا ننشئ صندوق أفقى، ونضيف معامل التمدد بإستخدام الطريقة addStretch ونقوم بإضافة الودجات بإستخدام الطريقة addWidget
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
لإنشاء المخطط النهائي المطلوب، نقوم بإنشاء صندوق عمودي ونضع الأفقي فيه.
self.setLayout(vbox)
أخيرا نقوم بتحديد المخطط الرئيسى للنافذة بإستخدام الطريقة setLayout
اكثر مخطط استخداما هو صنف مخطط الشبكة أو الجدول Grid وفيه يتم تقسيم المساحة إلى صفوف وأعمدة. ونستخدم الصنف QGridLayout لتنفيذ ذلك.
#!/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_())
فى مثالنا سننشئ شبكة من الأزرار. وأحد الأماكن سنحتاج فيها لترك فراغ8) بالودجة QLabel
grid = QtGui.QGridLayout()
هنا ننشئ كائن مخطط الشبكة باسم grid
if j == 2:
grid.addWidget(QtGui.QLabel(''), 0, 2)
else: grid.addWidget(button, pos[j][0], pos[j][1])
نقوم بإضافة الودجات بإستخدام الطريقة addWidget ومعاملاتها هنا هم (الودجة، رقم الصف، رقم العمود)
الودجات تستطيع شغل أكثر من عمود أو صف في الشبكة، كما فى المثال التالي
#!/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)
قمنا بإنشاء grid و بتحديد المسافة بين الودجات بإستخدام الطريقة setSpacing
grid.addWidget(reviewEdit, 3, 1, 5, 1)
إذا قمنا بإضافة ودجة إلى شبكة، نستطيع ان نقوم بتحديد التمدد على الصفوف أو الأعمدة. فى حالتنا هذه قمنا بمد الودجة reviewEdit إلى 5 صفوف
فى هذا الجزء سنستكشف للأحداث والإشارات اللتي تحدث فى التطبيقات.
الأحداث هى جزء هام في أي تطبيق رسومى، وهى تحدث من المستخدم او من النظام. عندما نستدعى طريقة التطبيق exec_()
يدخل التطبيق فى الحلقة الأساسية main loop. وهي بدورها تستخرج الأحداث وترسلها إلى الكائنات. قدمت Trolltech (الآن جزء من Nokia) بتقديم آلية فريدة هي الإشارات والأتلام
الإشارات تحدث عندما يضغط مستخدم على زر او يحرك منزلق .. إلخ، وقد تحدث من البيئة (على سبيل المثال دقات الساعة)
التلم هو طريقة method (الطريقة مصطلح يكافئ الوظيفة أو الدالة في البرمجة الإجرائية) تتفاعل ردا على إشارة. ويمكن أن يكون التلم أي كائن قابل للاستدعاء في بايثون.
#!/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_())
فى مثالنا، قمنا بعرض رقم LCD مع منزلق. ونقوم بتغيير الرقم بناء على سحب المنزلق
self.connect(slider, QtCore.SIGNAL('valueChanged(int)'), lcd, QtCore.SLOT('display(int)') )
هنا قمنا بربط الإشارة valueChanged الخاصة بالمنزلق بالتلم display الخاصة بال LCD
للربط يوجد 4 معاملات المرسل وهو الكائن اللذي أرسل الإشارة ثم الإشارة ثم المستقبل للإشارة ثم أخيرا التلم وهى التي ستتفاعل مع الإشارة.
الأحداث في PyQt يتم معالجتها غالبا بإعادة تعريف “معالج الحدث” 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)
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_())
فى مثالنا، ثمنا بإعادة تعريف معالج الحدث keyPressEvent
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.close()
إذا تم الضغط على زر escape فيتم الخروج من التطبيق
الكائنات المنشئة من الصنف QtCore.QObject (أو أي صنف يرثها) تستطيع بت إشارات، اذا ضغطنا على زر ما فيتم توليد الإشارة clicked. في المثال التالي سنرى كيف يتم إرسال الإشارات
#!/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_())
انشأنا إشارة باسم closeEmitApp وسيتم بثها عند الضغط بالفأرة
def mousePressEvent(self, event): self.emit(QtCore.SIGNAL('closeEmitApp()'))
نقوم ببث الإشارة عبر الطريقة emit وهى تأخذ تعريف الإشارة كمعامل لها
self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
هنا قمنا بربط الإشارة closeEmitApp بالتلم close
نوافذ الحوار Dialog مكون حيوي في التطبيقات الرسومية، الحوار هو محادثة بين شخصين أو أكثر، في التطبيقات الحاسوب فإن صندوق الحوار نافذة من خلالها نتحدث مع التطبيق. يمكن استخدامها فى إدخال بيانات وتعديلها أو التحكم فى إعدادات البرنامج.. الخ. صناديق الحوار وسيلة مهمة في التواصل بين المستخدم والحاسوب.
هناك نوعان منها: المعرفة مسبقا أو المخصصة.
QinputDialog يقدم طريقة مريحة للحصول على قيمة من المستخدم (سلسلة نصية ، رقم ، عنصر من قائمة)
#!/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_()
هذا المثال يشمل زر و line edit. الزر سيقوم بعرض InputDialog للحصول على قيمة، ووضعها فى ال line edit
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
هذا السطر يعرض ال input dialog. ,وذلك بإستخدام الطريقة getText ومعاملاته كالتالى: الوالد، العنوان، الرسالة .. يعيد هذا ال dialog
النص المدخل وقيمة منطقية (مرتبطة بالزر الذى ضغطه المستخدم اذا كان زر OK فإن القيمة ستكون True وإلا فإنها False.
الصنف QColorDialog يتيح لنا تحديد خطوط ما (اختيار خط)
#!/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_()
T
هذا المثال يعرض زر وودجة ذو خلفية سوداء. سنقوم بتحديد الخلفية عن طريق الناتج من استدعاء الطريقة QColorDialog.getColor
color = QtGui.QColorDialog.getColor()
هذا السطر سيقوم بعرض ال QColorDialog وذلك بإستدعاء الطريقة getColor للحصول على لون ما من المستخدم لتغيير لون خلفية الودجة self.widget إليه وذلك بإستخدام الطريقة setStyleSheet
self.widget.setStyleSheet("QWidget { background-color: %s }" % color.name())
We change the background color using stylesheets.
QFontDialog هو صف لإختيار الخطوط
#!/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('Knowledge only matters', self)
self.label.move(130, 20)
hbox.addWidget(self.label, 1)
self.setLayout(hbox)
def showDialog(self):
font, ok = QtGui.QFontDialog.getFont()
if ok:
self.label.setFont(font)
app = QtGui.QApplication(sys.argv)
cd = FontDialog()
cd.show()
app.exec_()
فى مثالنا لدينا زر ونص الساكن ( label) وبإستخدام الصنف QFontDialog نقوم بتغيير الخط على ذلك النص الثابت
hbox.addWidget(self.label, 1)
هنا نجعل ال self.label قابل للتحجيم، وذلك لمرونة التعامل مع الخطوط وإلا قد لايظهر النص على ال self.label بصورة كاملة
font, ok = QtGui.QFontDialog.getFont()
نستخدم الطريقة getFont لعرض الdialog للحصول على الخط
if ok: self.label.setFont(font)
اذا قام المستخدم بضغط الزر OK على الdialog سيتم تغيير الخط على ال self.label “النص الساكن”
الصنف QFileDialog يتيح لنا اختيار ملفات او مجلدات (سواء للفتح او للحفظ)
#!/usr/bin/python
# openfiledialog.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class OpenFile(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('OpenFile')
self.textEdit = QtGui.QTextEdit()
self.setCentralWidget(self.textEdit)
self.statusBar()
self.setFocus()
exit = QtGui.QAction(QtGui.QIcon('open.png'), 'Open', self)
exit.setShortcut('Ctrl+O')
exit.setStatusTip('Open new File')
self.connect(exit, QtCore.SIGNAL('triggered()'), self.showDialog)
menubar = self.menuBar()
file = menubar.addMenu('&File')
file.addAction(exit)
def showDialog(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file',
'/home')
file=open(filename)
data = file.read()
self.textEdit.setText(data)
app = QtGui.QApplication(sys.argv)
cd = OpenFile()
cd.show()
app.exec_()
Th
هذا المثال يعرض شريط وقوائم وودجة مركزى TextEdit “ودجة النصوص” وشريط الحالة.
عنصر القائمة يقوم بعرض QFileDialog لإختيار ملف ما، وعرض محتواه على ودجة النصوص
class OpenFile(QtGui.QMainWindow):
...
self.textEdit = QtGui.QTextEdit()
self.setCentralWidget(self.textEdit)
هذا المثال مبنى على الصنف QMainWidget وذلك لتحديد الودجة المركزى وذلك يتم بسهولة مع الصنف QMainWindow
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file', '/home')
هنا نقوم بعرض ال QFileDialog للحصول على ملف ما للفتح بإستخدام الطريقة getOpenFileName ولها معاملات(الأب ، العنوان، ومسار البداية)، والفلتر المستخدم افتراضيا هو كل الملفات (*)
file=open(filename)
data = file.read()
self.textEdit.setText(data)
يتم قراءة الملف بإستخدام الطريقة read الخاصة بال file object ويتم عرض المحتوى بإستخدام الطريقة setText الخاصة بالودجة textEdit
الودجات هى وحدات البناء للتطبيق، وتقدم لنا PyQt عدد كبير من الودجات المختلفة كالأزرار والمنزلقات والصناديق المنطقية والقوائم.. الخ وكل مايحتاجه المبرمج لعمله. سنقوم بالتعرض لبعض الودجات المفيدة فى هذه الجزئية
QCheckBox هو ودجة بحالتين on “منشط” او off “غير منشط” ويشمل نص للوصف.عند اى تغيير فى الحالة يتم ارسال الإشارة statChanged()
#!/usr/bin/python
# checkbox.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class CheckBox(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Checkbox')
self.cb = QtGui.QCheckBox('Show title', self)
self.cb.setFocusPolicy(QtCore.Qt.NoFocus)
self.cb.move(10, 10)
self.cb.toggle();
self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)
def changeTitle(self, value):
if self.cb.isChecked():
self.setWindowTitle('Checkbox')
else:
self.setWindowTitle('')
app = QtGui.QApplication(sys.argv)
icon = CheckBox()
icon.show()
app.exec_()
فى مثالنا سننشئ اوبجكت من ال CheckBox ليتحكم فى عرض عنوان النافذة.
self.cb = QtGui.QCheckBox('Show title', self)
هذا هو المشيد الخاص بال QCheckBox وفيه النص المرفق معه والأب
self.cb.setFocusPolicy(QtCore.Qt.NoFocus)
الصنف QCheckBox بيقبل حدوث تركيز عليه. قمت هنا بمنع ذلك لمنع ظهور ذلك المستطيل البشع!
نربط السلوت “الطريقة اللتى سنقوم بتعريفها changeTitle” بإشارة تغيير الحالة stateChanged
self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)
def changeTitle(self, value):
if self.cb.isChecked():
self.setWindowTitle('Checkbox')
else:
self.setWindowTitle('')
قمنا بعكس الحالة الخاصة بال checkbox بإستخدام الطريقة toggle()، افتراضيا عنوان النافذة غير ظاهر وال checkbox غير منشط
self.cb.toggle();
We set the window title, so we must also check the checkbox. By default, the window title is not set and the
check box is unchecked.
PyQt لاتشمل ToggleButton “زر بحالتين مشابه لل CheckBox”، لذا لإنشاءه بنستخدم الصنف QPushButton بطريقة خاصة.
#!/usr/bin/python
# togglebutton.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class ToggleButton(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.color = QtGui.QColor(0, 0, 0)
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('ToggleButton')
self.red = QtGui.QPushButton('Red', self)
self.red.setCheckable(True)
self.red.move(10, 10)
self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)
self.green = QtGui.QPushButton('Green', self)
self.green.setCheckable(True)
self.green.move(10, 60)
self.connect(self.green, QtCore.SIGNAL('clicked()'), self.setGreen)
self.blue = QtGui.QPushButton('Blue', self)
self.blue.setCheckable(True)
self.blue.move(10, 110)
self.connect(self.blue, QtCore.SIGNAL('clicked()'), self.setBlue)
self.square = QtGui.QWidget(self)
self.square.setGeometry(150, 20, 100, 100)
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))
def setRed(self):
if self.red.isChecked():
self.color.setRed(255)
else: self.color.setRed(0)
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
def setGreen(self):
if self.green.isChecked():
self.color.setGreen(255)
else: self.color.setGreen(0)
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
def setBlue(self):
if self.blue.isChecked():
self.color.setBlue(255)
else: self.color.setBlue(0)
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
app = QtGui.QApplication(sys.argv)
tb = ToggleButton()
tb.show()
app.exec_()
فى مثالنا قمنا بإنشاء ثلاثة ToggleButtons وانشئنا ودجة وحددنا خلفيته باللون الأسود وال3 ازرار سيقومو بعكس الحالة بين الألوان الأحمر والأخضر والأزرق
self.color = QtGui.QColor(0, 0, 0)
Tهذا هو اللون الأساسى “اسود” “لا أحمر ولا أخضر ولاأزرق”
self.red = QtGui.QPushButton('Red', self)
self.red.setCheckable(True)
لإنشاء Toggle Button نقوم بإنشاء زر عادى من الصنف QPushButton ولكن نقوم بتحديد قابليته للتنشيط بإستخدام الطريقة setCheckable
self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)
ونقوم بربط الإشارة clicked بطريقتنا المعرفة setRed
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))
هنا قمنا بتحديد الإستايل الخاص ل cleanlooks وذلك بإستخدام الطريقة setStyle بالتطبيق بسبب عدم وضوع شكل ال Toggle Button على plastique
if self.red.isChecked(): self.color.setRed(255)
else: self.color.setRed(0)
هنا نختبر اذا كان الزر الخاص باللون الأحمر منشط او لا بإستخدام الطريقة isChecked ونقوم بتحديد اللون
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
قمنا بتغيير لون خلفية الودجة بإستخدام الطريقة setStyleSheet مع تحديد background-color كخاصية ليتم تعديلها
وهكذا مع باقى الأزرار
المنزلق هو ودجة مع مقبض بسيط يمكن سحبه للأمام او للخلف، وذلك لإختيار قيمة ما. قد يكون استخدامه فى بعض الأحيان طبيعيا اكثر من ادخال رقم او استخدام spin box “ودجة مع اسهم لأعلى واسفل لزيادة القيمة وتقليلها”
ال QLabel بيستخدم فى عرض نص ساكن او ربما صورة.
فى مثالنا سنعرض منزلق مع label “نعرض عليه صورة” وسيتم التحكم فيها بإستخدام المنزلق
#!/usr/bin/python
# slider-label.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class SliderLabel(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('SliderLabel')
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
self.slider.setGeometry(30, 40, 100, 30)
self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
self.label = QtGui.QLabel(self)
self.label.setPixmap(QtGui.QPixmap('mute.png'))
self.label.setGeometry(160, 40, 80, 30)
def changeValue(self, value):
pos = self.slider.value()
if pos == 0:
self.label.setPixmap(QtGui.QPixmap('mute.png'))
elif pos > 0 and pos <= 30:
self.label.setPixmap(QtGui.QPixmap('min.png'))
elif pos > 30 and pos < 80:
self.label.setPixmap(QtGui.QPixmap('med.png'))
else:
self.label.setPixmap(QtGui.QPixmap('max.png'))
app = QtGui.QApplication(sys.argv)
icon = SliderLabel()
icon.show()
app.exec_()
فى مثالنا نحاكى اداة التحكم فى الصوت بجذب المقبض الخاص بالمنزل فيتم تغيير الصورة
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
قمنا بإنشاء منزلق افقى
self.label = QtGui.QLabel(self)
self.label.setPixmap(QtGui.QPixmap('mute.png'))
انشأنا Label وحددنا الصورة الظاهرة “بصوت صامت” بإستخدام الطريقة setPixmap التى تأخذ اوبجكت من الصنف QPixmap “المشيد له يحوى مسار الصورة”
self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
هنا قمنا بربط اشارة التغيير فى حالة المنزلق valueChanged الى الطريقة اللتى عرفناها changeValue
def changeValue(self, value):
pos = self.slider.value()
if pos == 0:
self.label.setPixmap(QtGui.QPixmap('mute.png'))
elif pos > 0 and pos <= 30:
self.label.setPixmap(QtGui.QPixmap('min.png'))
elif pos > 30 and pos < 80:
self.label.setPixmap(QtGui.QPixmap('med.png'))
else:
self.label.setPixmap(QtGui.QPixmap('max.png'))
pos = self.slider.value()
هنا نحصل على القيمة الحالية للمنزلق بإستخدام الطريقة value ونغير الصورة بناء عليها.
الصنف QProgressBar هو ودجة يستخدم فى تتبع مدى انتهاء المهمات (مفيدة للمستخدم)، قد يكون افقى او رأسى. يتم تقسيم المهمة الى خطوات يقوم المبرمج بتحديد اقل قيمة واعلى قيمة (الإفتراضى هو 0و 99)
#!/usr/bin/python
# progressbar.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class ProgressBar(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('ProgressBar')
self.pbar = QtGui.QProgressBar(self)
self.pbar.setGeometry(30, 40, 200, 25)
self.button = QtGui.QPushButton('Start', self)
self.button.setFocusPolicy(QtCore.Qt.NoFocus)
self.button.move(40, 80)
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onStart)
self.timer = QtCore.QBasicTimer()
self.step = 0;
def timerEvent(self, event):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 1
self.pbar.setValue(self.step)
def onStart(self):
if self.timer.isActive():
self.timer.stop()
self.button.setText('Start')
else:
self.timer.start(100, self)
self.button.setText('Stop')
app = QtGui.QApplication(sys.argv)
icon = ProgressBar()
icon.show()
app.exec_()
فى مثالنا لدينا progress bar افقى مع زر يتحكم فىه “ايقاف واستمرار”
self.pbar = QtGui.QProgressBar(self)
المشيد الخاص بالQProgressBar
self.timer = QtCore.QBasicTimer()
لتنشيط ال progressbar نستخدم مؤقت
self.timer.start(100, self)
هنا نقوم بتحديد ال timeout والمستقبل للحدث بإستخدام الطريقة start
def timerEvent(self, event):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 1
self.pbar.setValue(self.step)
لكل مايرث QObject له معالج حدث timerEvent وللتعامل مع ذلك الحدث نقوم بإعادة تعريفه
QCalendarWidget تستخدم لتوفير نتيجة شهرية، تتيح للمستخدم اختيار تاريخ ما
#!/usr/bin/python
# calendar.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class Calendar(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Calendar')
self.cal = QtGui.QCalendarWidget(self)
self.cal.setGridVisible(True)
self.cal.move(20, 20)
self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)
self.label = QtGui.QLabel(self)
date = self.cal.selectedDate()
self.label.setText(str(date.toPyDate()))
self.label.move(130, 260)
def showDate(self):
date = self.cal.selectedDate()
self.label.setText(str(date.toPyDate()))
app = QtGui.QApplication(sys.argv)
icon = Calendar()
icon.show()
app.exec_()
هذا المثال يشمل نتيجة وlabel والتاريخ المختار يتم عرضه على ذلك ال label
self.cal = QtGui.QCalendarWidget(self)
ننشئ اوبجكت من QCalendarWidget
self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)
إذا قمنا بإختيار تاريخ ما فسيتم ارسال الإشارة selectionChanged ، لذا سنقوم بربطها بطريقة showData التى سنعرفها لعرض التاريخ على ال label
def showDate(self):
date = self.cal.selectedDate()
self.label.setText(str(date.toPyDate()))
هنا حصلنا على التاريخ بإستخدام الطريقة selectedDate وقمنا بتحويله إلى سلسلة نصية بإستخدام str
فى هذه الجزئية سنتحدث عن السحب والإفلات
In computer graphical user interfaces, drag-and-drop is the action of (or support for the action of) clicking on a virtual object and dragging it to a different location or onto another virtual object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two abstract objects. (Wikipedia)
السحب والإفلات احد اوضح جوانب الواجهات الرسومية. عمليات السحب والإفلات تتيح عمل اشياء معقدة
عادة نستطيع ان نسحب ونفلت نوعين بيانات او بعض الأوبجكتس الرسومية. اذا سحبنا صورة من تطبيق لآخر فإننا نقوم بسحب وإفلات بيانات binary اذا قمنا بسحب نافذة فى متصفح فايرفوكس من مكان لآخر فإننا نقوم بسحب مكون رسومى
فى هذا المثال لدينا QLineEdit و QPushButton اوبجكتس. سنقوم بحسب النص على ودجة النص وإفلاته على الزر
In the first example, we will have a QLineEdit and a QPushButton. We will drag plain text from
the line edit widget and drop it onto the button widget.
#!/usr/bin/python
# dragdrop.py
import sys
from PyQt4 import QtGui
class Button(QtGui.QPushButton):
def __init__(self, title, parent):
QtGui.QPushButton.__init__(self, title, parent)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('text/plain'):
event.accept()
else:
event.ignore()
def dropEvent(self, event):
self.setText(event.mimeData().text())
class DragDrop(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.resize(280, 150)
self.setWindowTitle('Simple Drag & Drop')
edit = QtGui.QLineEdit('', self)
edit.setDragEnabled(True)
edit.move(30, 65)
button = Button("Button", self)
button.move(170, 65)
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2,
(screen.height()-size.height())/2)
app = QtGui.QApplication(sys.argv)
icon = DragDrop()
icon.show()
app.exec_()
class Button(QtGui.QPushButton):
def __init__(self, title, parent):
QtGui.QPushButton.__init__(self, title, parent)
لإتاحة الإفلات على الزر يجب علينا اعادة تعريف بعض الطرق فى الصنف الخاص بنا الذى سيرث من الصنف QPushButton
self.setAcceptDrops(True)
هنا نتيج امكانيه احداث الإفلات على للصف
def dragEnterEvent(self, event):
if event.mimeData().hasFormat('text/plain'):
event.accept()
else:
event.ignore()
اولا نعيد تعريف معالج الحدث dragEnterEvent() لنحدد البيانات اللتى سنقبلها، فى حالتنا هى مجرد نص عادى.
def dropEvent(self, event):
self.setText(event.mimeData().text())
بإعادة تعريف معالج الحدث dropEvent() سنقوم بتعريف ماذا سنفعل اثناء الإفلات. هنا سنقوم بتغير النص الظاهر على الزر.
edit = QtGui.QLineEdit('', self)
edit.setDragEnabled(True)
الصنف QLineEdit لديه دعم داخلى لعمليات السحب. كل ماعلينا هو اتاحتها بإستخدام الطريقة setDragEnabled
فى المثال التالى سنتعرض لكيفية سحب وإفلات زر
#!/usr/bin/python
# dragbutton.py
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class Button(QtGui.QPushButton):
def __init__(self, title, parent):
QtGui.QPushButton.__init__(self, title, parent)
def mouseMoveEvent(self, event):
if event.buttons() != QtCore.Qt.RightButton:
return
mimeData = QtCore.QMimeData()
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
dropAction = drag.start(QtCore.Qt.MoveAction)
if dropAction == QtCore.Qt.MoveAction:
self.close()
def mousePressEvent(self, event):
QtGui.QPushButton.mousePressEvent(self, event)
if event.button() == QtCore.Qt.LeftButton:
print 'press'
class DragButton(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.resize(280, 150)
self.setWindowTitle('Click or Move')
self.setAcceptDrops(True)
self.button = Button('Close', self)
self.button.move(100, 65)
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2,
(screen.height()-size.height())/2)
def dragEnterEvent(self, event):
event.accept()
def dropEvent(self, event):
position = event.pos()
button = Button('Close', self)
button.move(position)
button.show()
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
app = QtGui.QApplication(sys.argv)
db = DragButton()
db.show()
app.exec_()
فى كود المثال، لدينا زر من الصنف QPushButton على النافذة اذا تم الضغط عليه بالزر الأيسر للفأرة سيطبع كلمة press على الكونسول. وفى حال الضغط بالزر الأيمن والتحريك سنقوم بعمل عملية سحب وإفلات على الزر.
class Button(QtGui.QPushButton):
def __init__(self, title, parent):
QtGui.QPushButton.__init__(self, title, parent)
هنا انشأنا صف Button يرث QPushButton
سنقوم ايضا بإعادة تعريف معالجى الأحداث mouseMoveEvent() و mousePressEvent()
حيث mouseMoveEvent هى المكان الذى بدأت عنده عملية السحب والإفلات.
if event.buttons() != QtCore.Qt.RightButton: return
هنا قررنا، اننا سنقوم بعملية السحب والإفلات عن طريق الزر الأيمن للفأرة. الزر الأيسر للفأرة محجوز للضغط على الزر
mimeData = QtCore.QMimeData()
drag = QtGui.QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(event.pos() - self.rect().topLeft())
هنا ننشئ اوبجكت من الصنف QDrag
dropAction = drag.start(QtCore.Qt.MoveAction)
if dropAction == QtCore.Qt.MoveAction:
self.close()
الطريقة start() تبدأ عملية السحب والإفلات. عند سحب الزر فإننا نقوم بحذف الودجة من المكان الأول ونعيد انشاءه بالمكان الجديد
def mousePressEvent(self, event):
QtGui.QPushButton.mousePressEvent(self, event)
if event.button() == QtCore.Qt.LeftButton:
print 'press'
عند الضغط على الزر الأيسر يتم طباعة كلمة press
لاحظ استدعائنا لمعالج الحدث mousePressEvent الخاص بالأب ايضا وإلا لن نرى الزر يتم الضغط عليه.
position = event.pos()
button = Button('Close', self)
button.move(position)
button.show()
فى معالج الحدث dropEvent نكتب ماسيحدث بعد افلات الفأرة، نقل الزر واظهاره.
فى مثالنا، قمنا بإعادة انشاء الزر فى الموقع الجديد عن مؤشر الفأرة
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
نحدد نوع الإفلات، فى حالتنا هذه هو Move
نستخدم الرسم عندما نريد تعديل او تحسين زر موجود او حتى انشاءه من الصفر.
للقيام بالرسم، نستخدم واجهة التطبيق البرمجة API التى تقدمها لكن PyQt4
يتم الرسم داخل الطريقة paintEvent ويوضع كود الرسم بين الطريقتين begin()و end() وهما طريقتين خاصين بال QPainter اوبجكت
نبدأ برسم بعض النصوص على النافذة
We begin with drawing some unicode text onto the window client area.
#!/usr/bin/python
# drawtext.py
import sys
from PyQt4 import QtGui, QtCore
class DrawText(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Draw Text')
self.text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439: \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
paint.setPen(QtGui.QColor(168, 34, 3))
paint.setFont(QtGui.QFont('Decorative', 10))
paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
paint.end()
app = QtGui.QApplication(sys.argv)
dt = DrawText()
dt.show()
app.exec_()
فى مثالنا نكتب بعض النصوص بazbuka. ذلك النص محاذى رأسيا وافقيا
In our example, we draw some text in azbuka. The text is vertically and horizontally aligned.
def paintEvent(self, event):
الرسم يتم فى معالج الحدث paintEvent
paint = QtGui.QPainter()
paint.begin(self)
...
paint.end()
الصنف QPainter مسئول عن كل عمليات الرسم المنخفضة. كل عمليات الرسم تكون بين الطرق begin() و end()
paint.setPen(QtGui.QColor(168, 34, 3))
paint.setFont(QtGui.QFont('Decorative', 10))
هنا نحدد قلما وخطا، لنستخدمهم فى رسم النص
paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
الطريقة drawText() تقوم برسم النص على النافذة
النقطة هى ابسط كائن رسومى، مجرد بقعة صغيرة على النافذة.
#!/usr/bin/python
# points.py
import sys, random
from PyQt4 import QtGui, QtCore
class Points(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Points')
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
paint.setPen(QtCore.Qt.red)
size = self.size()
for i in range(1000):
x = random.randint(1, size.width()-1)
y = random.randint(1, size.height()-1)
paint.drawPoint(x, y)
paint.end()
app = QtGui.QApplication(sys.argv)
dt = Points()
dt.show()
app.exec_()
فى مثالنا نقوم برسم 1000 نقطة حمراء على مساحة العميل
paint.setPen(QtCore.Qt.red)
نقوم بتحديد اللون للقلم وذلك بإستخدام ثابت معرف سابقا red
size = self.size()
عند كل عملية اعادة تحجيم، يتم انشاء الحدث paint
نحصل على المساحة الحالية بإستخدام الطريقة size()
paint.drawPoint(x, y)
نقوم برسم النقطة بإستخدام الطريقة drawPoint()
اللون هو كائن بيعبر عن خليط من الأحمر والأخضر والأزرق RGB بقيم تتراوح بين 0 ل 255 ، نستطيع تعريف اللون بعدة طرق واشهرهاقيم RGB decimal or Hexadicemal ..نستطيع ايضا استخدام RGBA “اختصارا ل Red, Green, Blue, Alpha لنضيف بعض المعلومات الإضافية opacity, transparency
#!/usr/bin/python
# colors.py
import sys, random
from PyQt4 import QtGui, QtCore
class Colors(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 350, 280)
self.setWindowTitle('Colors')
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
color = QtGui.QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')
paint.setPen(color)
paint.setBrush(QtGui.QColor(255, 0, 0, 80))
paint.drawRect(10, 15, 90, 60)
paint.setBrush(QtGui.QColor(255, 0, 0, 160))
paint.drawRect(130, 15, 90, 60)
paint.setBrush(QtGui.QColor(255, 0, 0, 255))
paint.drawRect(250, 15, 90, 60)
paint.setBrush(QtGui.QColor(10, 163, 2, 55))
paint.drawRect(10, 105, 90, 60)
paint.setBrush(QtGui.QColor(160, 100, 0, 255))
paint.drawRect(130, 105, 90, 60)
paint.setBrush(QtGui.QColor(60, 100, 60, 255))
paint.drawRect(250, 105, 90, 60)
paint.setBrush(QtGui.QColor(50, 50, 50, 255))
paint.drawRect(10, 195, 90, 60)
paint.setBrush(QtGui.QColor(50, 150, 50, 255))
paint.drawRect(130, 195, 90, 60)
paint.setBrush(QtGui.QColor(223, 135, 19, 255))
paint.drawRect(250, 195, 90, 60)
paint.end()
app = QtGui.QApplication(sys.argv)
dt = Colors()
dt.show()
app.exec_()
فى مثالنا قمنا بإنشاء 9 مستطيلات ملونة. الصنف الأول مكون من مستطيلات حمراء مع قيم مختلفة لل alpha
color = QtGui.QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')
هنا قمنا بتعريف الرقم بإستخدام hexadecimal
paint.setBrush(QtGui.QColor(255, 0, 0, 80));
paint.drawRect(10, 15, 90, 60)
هنا قمنا بتعريف فرشاة، لرسم خلفية الشكل.
الطريقة drawRect() الخاصة برسم المستطيل تأخذ 4 معاملات x, y للإحداثيات و العرض والإرتفاع للمستطيل.وذلك بإستخدام القلم والفرشاة الحاليين
الصنف QPen القلم كائن رسومى يستخدم فى رسم الخطوط، المنحنيات، محددات المستطيلات .. الخ
#!/usr/bin/python
# penstyles.py
import sys
from PyQt4 import QtGui, QtCore
class PenStyles(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 280, 270)
self.setWindowTitle('penstyles')
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
paint.setPen(pen)
paint.drawLine(20, 40, 250, 40)
pen.setStyle(QtCore.Qt.DashLine)
paint.setPen(pen)
paint.drawLine(20, 80, 250, 80)
pen.setStyle(QtCore.Qt.DashDotLine)
paint.setPen(pen)
paint.drawLine(20, 120, 250, 120)
pen.setStyle(QtCore.Qt.DotLine)
paint.setPen(pen)
paint.drawLine(20, 160, 250, 160)
pen.setStyle(QtCore.Qt.DashDotDotLine)
paint.setPen(pen)
paint.drawLine(20, 200, 250, 200)
pen.setStyle(QtCore.Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
paint.setPen(pen)
paint.drawLine(20, 240, 250, 240)
paint.end()
app = QtGui.QApplication(sys.argv)
dt = PenStyles()
dt.show()
app.exec_()
فى مثالنا، نقوم برسم 6 خطوط بإستخدام 6 انواع من الأقلام. هناك 5 معرفين مسبقا وواحد قمنا بتعريفه.
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
هنا قمنا بإنشاء قلم من الصنف QPen وقمنا بتحديد اللون والسمك “بالبكسلز” واستخدمنا النوع SolidLine كنوع للقلم
pen.setStyle(QtCore.Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
paint.setPen(pen)
هنا قمنا بتعريف نوع جديد من الأقلام بإستخدام setStyle وقمنا بإستدعاء الطريقة setDashPattern() واللتى تأخذ list كمعامل.
يجب ان يكون هناك رقم زوجى، الأرقام الفردية بتحدد الdash والزوجية تحدد المسافة.
نمطنا هو 1بكسل داش 4بكسل مسافة 5بكسل داش 4بكسل مسافة.. الخ
هى كائن رسومى، بيستخدم فى رسم خلفية الأشكال مثل المضلعات وغيرها.
ممكن تكون الفرشاة من 3 انواع، نوع معرف سابقا، gradien or a texture pattern
#!/usr/bin/python
# brushes.py
import sys
from PyQt4 import QtGui, QtCore
class Brushes(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 355, 280)
self.setWindowTitle('Brushes')
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
paint.setBrush(brush)
paint.drawRect(10, 15, 90, 60)
brush.setStyle(QtCore.Qt.Dense1Pattern)
paint.setBrush(brush)
paint.drawRect(130, 15, 90, 60)
brush.setStyle(QtCore.Qt.Dense2Pattern)
paint.setBrush(brush)
paint.drawRect(250, 15, 90, 60)
brush.setStyle(QtCore.Qt.Dense3Pattern)
paint.setBrush(brush)
paint.drawRect(10, 105, 90, 60)
brush.setStyle(QtCore.Qt.DiagCrossPattern)
paint.setBrush(brush)
paint.drawRect(10, 105, 90, 60)
brush.setStyle(QtCore.Qt.Dense5Pattern)
paint.setBrush(brush)
paint.drawRect(130, 105, 90, 60)
brush.setStyle(QtCore.Qt.Dense6Pattern)
paint.setBrush(brush)
paint.drawRect(250, 105, 90, 60)
brush.setStyle(QtCore.Qt.Dense7Pattern)
paint.setBrush(brush)
paint.drawRect(250, 105, 90, 60)
brush.setStyle(QtCore.Qt.HorPattern)
paint.setBrush(brush)
paint.drawRect(10, 195, 90, 60)
brush.setStyle(QtCore.Qt.VerPattern)
paint.setBrush(brush)
paint.drawRect(130, 195, 90, 60)
brush.setStyle(QtCore.Qt.BDiagPattern)
paint.setBrush(brush)
paint.drawRect(250, 195, 90, 60)
paint.end()
app = QtGui.QApplication(sys.argv)
dt = Brushes()
dt.show()
app.exec_()
فى مثالنا نقوم برسم 6 انواع من المستطيلات.
brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
paint.setBrush(brush)
paint.drawRect(10, 15, 90, 60)
قمنا بتعريف كائن من الصنف QBrush ليتم استخدامه فى عملية الرسم بإستخدام الطريقة drawRect
We define a brush object. Set it to the painter object. And draw the rectangle calling the drawRect() method.
هل تسألت كيف تم تنفيذ عنصر ما من الواجهة فى تطبيق ما؟ غالبا كل من يريد ان يصبح مبرمجا فعل، ثم عند النظر لقائمة الودجات اللتى تقدمها لك مكتبتك الرسومية المفضلة، ولكن لاتجدها؟
ادوات تطوير التطبيقات بتقدملك اهم الودجات مثل الأزرار، ويدجات النصوص، المنزلقات،.. الخ ولكن لايوجد من يقدم لك كل شئ.
هناك نوعين من ادوات تطوير التطبيقات، سبارطية (مثل FLTK لاتقدم لك الا الأساسيات ليقوم المبرمج بإنشاء مايريده بنفسه) وثقيلة (مثل PyQt4 مع العديد من الودجات ولكنها ايضا لاتوفر لك كل شئ مثلا ودجة لحرق اسطوانات؟ (مثل النيرو او k3B وغيرهم). وايضا ادوات فى الغالب لاتحتوى اداة تطوير التطبيقات على ال charts
لذيا يجب ان ينشئ المبرمجين هذه الودجات بأنفسهم، وذلك بإستخدام ادوات الرسم المتوفرة فى اداة التطوير.هنالك احتمالين
تعديل وتحسين ودجة موجود بالفعل، او انشاء واحد من الصفر
شبيه بما تراه فى Nero, K3B او غيرهم من برامج حرق الإسطوانات
#!/usr/bin/python
# burning.py
import sys
from PyQt4 import QtGui, QtCore
class Widget(QtGui.QLabel):
def __init__(self, parent):
QtGui.QLabel.__init__(self, parent)
self.setMinimumSize(1, 30)
self.parent = parent
self.num = [75, 150, 225, 300, 375, 450, 525, 600, 675]
def paintEvent(self, event):
paint = QtGui.QPainter()
paint.begin(self)
font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
paint.setFont(font)
size = self.size()
w = size.width()
h = size.height()
cw = self.parent.cw
step = int(round(w / 10.0))
till = int(((w / 750.0) * cw))
full = int(((w / 750.0) * 700))
if cw >= 700:
paint.setPen(QtGui.QColor(255, 255, 255))
paint.setBrush(QtGui.QColor(255, 255, 184))
paint.drawRect(0, 0, full, h)
paint.setPen(QtGui.QColor(255, 175, 175))
paint.setBrush(QtGui.QColor(255, 175, 175))
paint.drawRect(full, 0, till-full, h)
else:
paint.setPen(QtGui.QColor(255, 255, 255))
paint.setBrush(QtGui.QColor(255, 255, 184))
paint.drawRect(0, 0, till, h)
pen = QtGui.QPen(QtGui.QColor(20, 20, 20), 1, QtCore.Qt.SolidLine)
paint.setPen(pen)
paint.setBrush(QtCore.Qt.NoBrush)
paint.drawRect(0, 0, w-1, h-1)
j = 0
for i in range(step, 10*step, step):
paint.drawLine(i, 0, i, 5)
metrics = paint.fontMetrics()
fw = metrics.width(str(self.num[j]))
paint.drawText(i-fw/2, h/2, str(self.num[j]))
j = j + 1
paint.end()
class Burning(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.cw = 75
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
self.slider.setRange(1, 750)
self.slider.setValue(75)
self.slider.setGeometry(30, 40, 150, 30)
self.wid = Widget(self)
self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.wid)
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('Burning')
def changeValue(self, event):
self.cw = self.slider.value()
self.wid.repaint()
app = QtGui.QApplication(sys.argv)
dt = Burning()
dt.show()
app.exec_()
فى مثالنا، لدينا منزلق و ودجة قمنا بإنشاءه خصيصا ويتم التحكم فيه عن طريق المنزلق.هذا الودجة يعرض السعة لوسيط تخزين والمساحة الفارغة فيه لنا.
اقل قيمة مقبولة هى 1 واكبر قيمة هى 750 وحينما يتم تعدى القيمة 700 نقوم بوضع الرسم بلون احمر “ليعبر عن الحرق الزائد”
ودجة الحرق موضوع بأسفل النافذة وذلك عن طريق مخطط افقى QHBoxLayout و QVBoxLayout
class Widget(QtGui.QLabel):
def __init__(self, parent):
QtGui.QLabel.__init__(self, parent)
ودجة الحرق يرث ال QLabel
self.setMinimumSize(1, 30)
نقوم بتعديل اقل مساحة (ارتفاع) الودجة لأن القيمة الأساسية صغيرة بالنسبة لنا
font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
paint.setFont(font)
نستخدم خط اصغر من الإفتراضى ليناسبنا اكثر.
size = self.size()
w = size.width()
h = size.height()
cw = self.parent.cw
step = int(round(w / 10.0))
till = int(((w / 750.0) * cw))
full = int(((w / 750.0) * 700))
نرسم الودجة بصورة ديناميكية، الأكبر النافذة، الأكبر ودجة الحرق والعكس. لذا نحسب المساحة للودجة، المعامل till بيحدد المساحة الكلية للرسم، هذه القيمة تأتى من المنزلق وهى النسبة من المساحة الكلية. المعامل full يحدد النقطة اللتى سنبدأ عندها برسم اللون الأحمر “حرق زائد”. لاحظ استخدام الأعداد الحقيقة لضمان اكبر دقة.
عملية الرسم تضمن 3 خطوات، رسم الأصفر او الأحمر، والمستطيل الأصفر ثم رسم الخطوط الرأسية لتقسم الودجة لعدة اقسام، واخيرا نقوم برسم الأرقام لتعبر عن سعة وسيط التخزين.
metrics = paint.fontMetrics()
fw = metrics.width(str(self.num[j]))
paint.drawText(i-fw/2, h/2, str(self.num[j]))
نستخدم ال font metrics لرسم النص. يجب ان نعلم عرض النص لنقوم بوضعه فى المنتصف حول الخط الأرسى
عمل لعبة كمبيوتر شئ غير سهل، وعاجلا او اجلا سيريد المبرمج عمل لعبة، فى الواقع العديد من الناس اصبحو مهتمين بالبرمجة رغبتهم فى انشاء العاب خاصة بهم.
على كل حال، كتابة لعبة سيفيدك فى تحسين مهاراتك البرمجية
واحدة من اشهر الألعاب على الإطلاق. اللعبة الأصلية صممت وبرمجت بواسطة Alexey Pajitnov فى عام 1985، ومنذ ذلك الحين اصبحت موجودة تقريبا على معظم الأجهزة، حتى الخلوى
تسمى ايضا احجية القطع المتساقطة. فى هذه اللعبة لدينا 7 انواع من القطع مسماه tetominoes وهى S-shape, Z-shape, T-shape, L-shape, Line-shape, MirroredL-shape and a Square-shape.
كل من هذه الأشكال مكون من 4 مربعات. وتتساقط القطع على لوحة. ممكن ازاحة او تدوير القطع ليجعلهم مناسبين بأقصى قدر.
اذا كونا صف فيتم حذفه وإضافته للنتيجة النهائية، تظل اللعبة حتى يصل عدد الصفوف للأعلى
اداة التطوير PyQt4 مخصصة لتطوير التطبيقات مع انه يمكن تطوير العاب بها كما سنرى، فى حين هناك بعض المكتبات موجهة لتطوير العاب الحاسوب.
المثال التالى هو اصدار معدل من لعبة tetris المتاحة مع ملفات تنصيب PyQt4
ليس لدينا صور للعبة، لذا سنقوم برسمها بإستخدام ال APIs الخاصة بالرسم فى PyQt4 ، اى لعبة عبارة عن تمثيل رياضى وكذلك tetris
بعض الأفكار وراء اللعبة
*استخدام QtCore.QBasicTimer() لإدارة اللعبة
*رسم القطع
*القطع تتحرك بالمربع وليس بالبيكسل
*رياضيا، اللوحة ماهى الا مجموعة بسيطة من الأرقام
Some ideas behind the game.
#!/usr/bin/python
# tetris.py
import sys
import random
from PyQt4 import QtCore, QtGui
class Tetris(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setGeometry(300, 300, 180, 380)
self.setWindowTitle('Tetris')
self.tetrisboard = Board(self)
self.setCentralWidget(self.tetrisboard)
self.statusbar = self.statusBar()
self.connect(self.tetrisboard, QtCore.SIGNAL("messageToStatusbar(QString)"),
self.statusbar, QtCore.SLOT("showMessage(QString)"))
self.tetrisboard.start()
self.center()
def center(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width()-size.width())/2,
(screen.height()-size.height())/2)
class Board(QtGui.QFrame):
BoardWidth = 10
BoardHeight = 22
Speed = 300
def __init__(self, parent):
QtGui.QFrame.__init__(self, parent)
self.timer = QtCore.QBasicTimer()
self.isWaitingAfterLine = False
self.curPiece = Shape()
self.nextPiece = Shape()
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.isStarted = False
self.isPaused = False
self.clearBoard()
self.nextPiece.setRandomShape()
def shapeAt(self, x, y):
return self.board[(y * Board.BoardWidth) + x]
def setShapeAt(self, x, y, shape):
self.board[(y * Board.BoardWidth) + x] = shape
def squareWidth(self):
return self.contentsRect().width() / Board.BoardWidth
def squareHeight(self):
return self.contentsRect().height() / Board.BoardHeight
def start(self):
if self.isPaused:
return
self.isStarted = True
self.isWaitingAfterLine = False
self.numLinesRemoved = 0
self.clearBoard()
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
str(self.numLinesRemoved))
self.newPiece()
self.timer.start(Board.Speed, self)
def pause(self):
if not self.isStarted:
return
self.isPaused = not self.isPaused
if self.isPaused:
self.timer.stop()
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "paused")
else:
self.timer.start(Board.Speed, self)
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
str(self.numLinesRemoved))
self.update()
def paintEvent(self, event):
painter = QtGui.QPainter(self)
rect = self.contentsRect()
boardTop = rect.bottom() - Board.BoardHeight * self.squareHeight()
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoes.NoShape:
self.drawSquare(painter,
rect.left() + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
if self.curPiece.shape() != Tetrominoes.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
def keyPressEvent(self, event):
if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
QtGui.QWidget.keyPressEvent(self, event)
return
key = event.key()
if key == QtCore.Qt.Key_P:
self.pause()
return
if self.isPaused:
return
elif key == QtCore.Qt.Key_Left:
self.tryMove(self.curPiece, self.curX - 1, self.curY)
elif key == QtCore.Qt.Key_Right:
self.tryMove(self.curPiece, self.curX + 1, self.curY)
elif key == QtCore.Qt.Key_Down:
self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
elif key == QtCore.Qt.Key_Up:
self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
elif key == QtCore.Qt.Key_Space:
self.dropDown()
elif key == QtCore.Qt.Key_D:
self.oneLineDown()
else:
QtGui.QWidget.keyPressEvent(self, event)
def timerEvent(self, event):
if event.timerId() == self.timer.timerId():
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.newPiece()
else:
self.oneLineDown()
else:
QtGui.QFrame.timerEvent(self, event)
def clearBoard(self):
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoes.NoShape)
def dropDown(self):
newY = self.curY
while newY > 0:
if not self.tryMove(self.curPiece, self.curX, newY - 1):
break
newY -= 1
self.pieceDropped()
def oneLineDown(self):
if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
self.pieceDropped()
def pieceDropped(self):
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.setShapeAt(x, y, self.curPiece.shape())
self.removeFullLines()
if not self.isWaitingAfterLine:
self.newPiece()
def removeFullLines(self):
numFullLines = 0
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.shapeAt(j, i) == Tetrominoes.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.setShapeAt(l, k, self.shapeAt(l, k + 1))
numFullLines = numFullLines + len(rowsToRemove)
if numFullLines > 0:
self.numLinesRemoved = self.numLinesRemoved + numFullLines
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"),
str(self.numLinesRemoved))
self.isWaitingAfterLine = True
self.curPiece.setShape(Tetrominoes.NoShape)
self.update()
def newPiece(self):
self.curPiece = self.nextPiece
self.nextPiece.setRandomShape()
self.curX = Board.BoardWidth / 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
if not self.tryMove(self.curPiece, self.curX, self.curY):
self.curPiece.setShape(Tetrominoes.NoShape)
self.timer.stop()
self.isStarted = False
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "Game over")
def tryMove(self, newPiece, newX, newY):
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.shapeAt(x, y) != Tetrominoes.NoShape:
return False
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.update()
return True
def drawSquare(self, painter, x, y, shape):
colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
color = QtGui.QColor(colorTable[shape])
painter.fillRect(x + 1, y + 1, self.squareWidth() - 2,
self.squareHeight() - 2, color)
painter.setPen(color.light())
painter.drawLine(x, y + self.squareHeight() - 1, x, y)
painter.drawLine(x, y, x + self.squareWidth() - 1, y)
painter.setPen(color.dark())
painter.drawLine(x + 1, y + self.squareHeight() - 1,
x + self.squareWidth() - 1, y + self.squareHeight() - 1)
painter.drawLine(x + self.squareWidth() - 1,
y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
class Tetrominoes(object):
NoShape = 0
ZShape = 1
SShape = 2
LineShape = 3
TShape = 4
SquareShape = 5
LShape = 6
MirroredLShape = 7
class Shape(object):
coordsTable = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
((0, -1), (0, 0), (1, 0), (1, 1)),
((0, -1), (0, 0), (0, 1), (0, 2)),
((-1, 0), (0, 0), (1, 0), (0, 1)),
((0, 0), (1, 0), (0, 1), (1, 1)),
((-1, -1), (0, -1), (0, 0), (0, 1)),
((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
self.coords = [[0,0] for i in range(4)]
self.pieceShape = Tetrominoes.NoShape
self.setShape(Tetrominoes.NoShape)
def shape(self):
return self.pieceShape
def setShape(self, shape):
table = Shape.coordsTable[shape]
for i in range(4):
for j in range(2):
self.coords[i][j] = table[i][j]
self.pieceShape = shape
def setRandomShape(self):
self.setShape(random.randint(1, 7))
def x(self, index):
return self.coords[index][0]
def y(self, index):
return self.coords[index][1]
def setX(self, index, x):
self.coords[index][0] = x
def setY(self, index, y):
self.coords[index][1] = y
def minX(self):
m = self.coords[0][0]
for i in range(4):
m = min(m, self.coords[i][0])
return m
def maxX(self):
m = self.coords[0][0]
for i in range(4):
m = max(m, self.coords[i][0])
return m
def minY(self):
m = self.coords[0][1]
for i in range(4):
m = min(m, self.coords[i][1])
return m
def maxY(self):
m = self.coords[0][1]
for i in range(4):
m = max(m, self.coords[i][1])
return m
def rotatedLeft(self):
if self.pieceShape == Tetrominoes.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.setX(i, self.y(i))
result.setY(i, -self.x(i))
return result
def rotatedRight(self):
if self.pieceShape == Tetrominoes.SquareShape:
return self
result = Shape()
result.pieceShape = self.pieceShape
for i in range(4):
result.setX(i, -self.y(i))
result.setY(i, self.x(i))
return result
app = QtGui.QApplication(sys.argv)
tetris = Tetris()
tetris.show()
sys.exit(app.exec_())
قمت بتبسيط اللعبة قليلا لتصبح اسهل للفهم. تبدأ اللعبة مباشرة بعد تشغيلها، نستطيع ايقافها بالضغط على الزر p وزر المسافة سيقوم بإنزال القطعة فورا الى الأسفل. السرعة ثابتة فى اللعبة، لم يتم اضافة اى تسريع. يتم حساب النتيجة بعدد الأسطر اللتى تم حذفها
self.statusbar = self.statusBar()
self.connect(self.tetrisboard, QtCore.SIGNAL("messageToStatusbar(QString)"),
self.statusbar, QtCore.SLOT("showMessage(QString)"))
بننشئ شريط حالة، لنعرض الرسائل(عدد الأسطر المنتهية، اللعبة متوقفة، انتهاء اللعبة)
...
self.curX = 0
self.curY = 0
self.numLinesRemoved = 0
self.board = []
...
قبل ان نبدأ اللعبة، يجب ان نجهز بعض المتغيرات الهامة. مثل اللوحة وهى عبارة عن مجموعة الأرقام من 0 الى 7 تمثل المركز للقطع والبقايا. وعدد الأسطر/الصفوف المحذوفه.. الخ
for j in range(Board.BoardWidth):
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
if shape != Tetrominoes.NoShape:
self.drawSquare(painter,
rect.left() + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
عملية الرسم مقسمه لخطوتين:
الخطوة الأولى: رسم كل القطع او البقايا اللتى سقطت بأسفل اللوحة، كل المربعات يتم تذكرها فى اللوحة self.board ونصل إليها بإستخدام الطريقة shapeAt()
if self.curPiece.shape() != Tetrominoes.NoShape:
for i in range(4):
x = self.curX + self.curPiece.x(i)
y = self.curY - self.curPiece.y(i)
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
الخطوة الثانية: رسم القطعة الساقطة
The next step is drawing of the actual piece, that is falling down.
elif key == QtCore.Qt.Key_Left:
self.tryMove(self.curPiece, self.curX - 1, self.curY)
elif key == QtCore.Qt.Key_Right:
self.tryMove(self.curPiece, self.curX + 1, self.curY)
فى معالج الحدث keyPressEvent نختبر الأزار اللتى يتم الضغط عليها، اذا تم ضغط السهم الأيمن فإننا نحاول تحريك القطعة الى اليمين(نحاول لأن قد لانستطيع تحريكها!)
def tryMove(self, newPiece, newX, newY):
for i in range(4):
x = newX + newPiece.x(i)
y = newY - newPiece.y(i)
if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
return False
if self.shapeAt(x, y) != Tetrominoes.NoShape:
return False
self.curPiece = newPiece
self.curX = newX
self.curY = newY
self.update()
return True
فى الطريقة tryMove() نحاول تحريك القطع، فى حال وجود القطعة عند الحافة او المجاور فإننا نعيد قيمة False وإلا نقوم بوضع القطعة فى الموقع الجديد
def timerEvent(self, event):
if event.timerId() == self.timer.timerId():
if self.isWaitingAfterLine:
self.isWaitingAfterLine = False
self.newPiece()
else:
self.oneLineDown()
else:
QtGui.QFrame.timerEvent(self, event)
فى معالج الحدث timerEvent
اما ننشئ قطعة جديدة تلى اللتى تم اسقاطها، او تحريك تلك القطع للأسفل
def removeFullLines(self):
numFullLines = 0
rowsToRemove = []
for i in range(Board.BoardHeight):
n = 0
for j in range(Board.BoardWidth):
if not self.shapeAt(j, i) == Tetrominoes.NoShape:
n = n + 1
if n == 10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.setShapeAt(l, k, self.shapeAt(l, k + 1))
...
عندما تصطدم القطعة بالأسفل نستدعى الطريقة removeFullLines() لحذف الصفوف المكتملة، ويتم ذلك بنقل كل الصفوف فوق الصف المكتمل. لاحظ عكس ترتيب الأسطر المحذوفة وإلا لن تعمل بصورة سليمة. فى حالتنا هذه نستخدم naive gravity مما يعنى ان القطع ربما تطفو فوق فراغات.
def newPiece(self):
self.curPiece = self.nextPiece
self.nextPiece.setRandomShape()
self.curX = Board.BoardWidth / 2 + 1
self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
if not self.tryMove(self.curPiece, self.curX, self.curY):
self.curPiece.setShape(Tetrominoes.NoShape)
self.timer.stop()
self.isStarted = False
self.emit(QtCore.SIGNAL("messageToStatusbar(QString)"), "Game over")
الطريقة newPiece() تنشئ قطعة عشوائية. اذا لم تستطع هذه القطعة التحرك فإن اللعبة انتهت.
الصف Shape يقوم بتخزين المعلومات عن القطعة
self.coords = [[0,0] for i in range(4)]
عند الإنشاء قمنا بعمل قائمة من الأحداثيات لتخزين احداثيات شكل القطعة.
عندما نرسم القطعة الساقطة نرسمها عند ال self.curX, self.curY وبعد ذلك ننظر الى جدول الأحداثيات لنرسم ال 4 مربعات.
نقاش
السلام عليكم:
هل سيطول ترجمة المقالة؟
ترجمة هذا المقال لم تبدأ بعد، يجري استيراد فصوله الطويلة فصلا فصلا
وأظن أنه سيكتمل اليوم.
هل هناك طريقة لتصدير الصفحة على شكل PDF ؟
نعم انقر على أيقونة ODT أوبن أوفس الموجودة في رأس المقال ستحصل على ملف ODT استخدم OpenOffice.org في تحويله إلى PDF
عندما ننتهي من المقال قد نضع رابط PDF مباشر
نعم جربتها قبل ان اسأل لكنها تظهر على شكل طلاسم بالاوبن اوفس ، اصلا لما اصدرها بصيغة ODT ينتج ملف حجمه 56 كيلو فقط !
شوف اذا ممكن تعمل PDF للموضوع، اكون لك شاكراً،،
على فكرة يوجد خطأ في احد الروابط بفقرة “تعليم البرمجة الرسومية في PyQt4”
رابط مقالة بايثون ،، راجعه لترى الخطأ
ماشاء الله عمل رائع وجزاكم الله خيرا
ولكن لى إستفسار صغير كدة هل أستطيح تحويل هذا الملف إلى exe فايل ليعمل على الوندوز مع العلم أنا حاولت convert to exe by the py2exe but it is failed
when i try to convert it to exe file it gave me this error
Traceback (most recent call last):
ImportError: No module named sip
بالنسبة للتحويل إلى PDF فإن الخلل هو وجود صور خارجة وهذه الصور ستم التخلص منها عند ترجمتها.
أما بالنسبة للتحويل إلى exe فكما تقول الرسالة لم تقم بتثبيت SIP وهي متوفرة على نفس الموقع. انظر إلى برنامج https://fedorahosted.org/liveusb-creator فهو معمول بهذه الطريقة
تم الإعلان عن طلب الترجمة في مجتمع qt العربي http://www.qt-ar.org/community/viewtopic.php?f=12&t=48
شكرا بارك الله فيك
هناك من قال … اجل انا سوف اقوم بترجمة هذا الفصل (محجوز) Events and Signals لكن قد أتأخر فترة لن تزيد عن ثلاثة اسابيع بإذن الله او قبلها وإنشاء الله تكون الترجمة جاهزة والله الموفق
حيّاك الله
وبالمناسبة عندما تعمل على الترجمة من خلال الويكي وتنقر على تحرير جزء بعينه فإن الموقع يحجز هذا الجزء ويمنعه عن غيرك حتى تنتهي منه
شرح وافي وجميل جزاك الله خير على هذا الموضوع واتمنى يكون فيه شروحات نفس دي عن PHP و JAVA وغيره من لغات البرمجة المشهورة.
لا اعرف اذا كان هذا هو المكان المناسب لعرض مشكلتي
ولكني ساضعها (لان هذه اللغة مدوخاني السبع دوخات) :
لقد قمت بتحميل pyqt4
وعند القيام بتسطيبهافي الجهاز فان المخرجات تقول:
ربما هذا يعود للتوزيعة التي تستعملها حيث هناك حزمة ناقصة أو ما شابه يبدو ان المشكلة بسيطة لعل القناة التعليمية تساعدك
طريقة استعمال القناة هنا و هنا
استاذي مؤيد توزيعتي هي *اعجوبة*
اما بالنسبة للمشكلة فهي: عند التسطيب فان المخرجات تقول ن الخطاء في ( module):
ولقد قمت بحل هذه المشكلة بتسطيب الـــ sip , وهو باكج يحتوي على مكتبة sipconfig.py .بعد اتمام عملية الانشاءبنجاح . والعوده للتحميل PyQt تظهر نفس المشكله السابقه
العفو اخي مؤيد على الاطاله ولكني اريد منك طلب : عن كتابة الامر التالي في الطرفيه:
فان المخرجات تقول :
« ولكني لم افهم ما هو المطلوب بمعنى كيف استخدم امر التسطيب »
وعيد مبارك عليك اخي مؤيد والله يجزيك الخير الكثير.
أخي الحبيب في أعجوبة هناك مدير حزم كل ما عليك هو فتح سطر الأوامر وكتابة
أو البحث عن PyQt4 في مدير الحزم الرسومي كذلك يمكنك تنزيل المزيد من الامثلة عبر حزمة PyQt-examples والوثائق في حزمة qt-doc
اخي مؤيد ولكني غير متصل بالانترنت فانا اقوم بتنزيل الباكجات يديويا من مقاهي الانترنت
بصراحه انا تعبت في تسطيب الباكجات على اعجوبة لينكس والوقت معي ضيق فليس لدي وقت للبحث عن حلول لهذه المشاكلولكن بعد انهاء اختباراتي الجامعيه ساقوم بالبحث عن الحل المناسب ---------------------------- ومشكور على الرد وما قصرت الله يبارك فيك.نشكركم على اهتمامكم بالبرمجمة بواسطة البيتون. الذكتور احمد بيه
الله يجعل هذا العمل في ميزان حسناتك
ويغفر لك ولوالديك
الف شكر على هذه الترجمة يا احمد اليوسف
وشكر موصول لكل من ساهم بترجمة هذه الدروس المفيدة جعلها الله في ميزان حسناتهم جميعاً
وجدت صعوبه في تحميل qtpy على ويندوز فلم استطع ترجمة الاكواد المصدريه حقيقة لا ادري من اين أبدأ …
حبذا لو تشرح بإسلوب خطوه خطوه من بداية تحميل بايثون وحتى اجتياز خطوات تعريف وتحميل qt
انظر هنا http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html
خطوات في نظري معقده لمن يريد الانتقال من بيئات اخرى فأنا اكتب بفيجول بيسك دوت نت بكل سهوله هنا وجدت صعوبه حتى في بداية التعليم الاحظ ان من يشرح يترك خطوات لأسباب قد تكون معروفه عند الخبراء او عند من يمارس المصادر المفتوحه ولم يفكر انها صعبه على المنتقلين من بيئات اخرى
طلبي ورجائي الحار ان يتم الشرح من بدايته بتحميل وتفعيل الادوات ثم البدء مع الشرح - ارجوا الاستجابه عاجلا وشكرا
السلام عليك ورحمة الله
أنا جديد فى لغة البايثون بس لحد الأن تعلم مقدمة يعنى شىء متوسط
بس أنا أريد تصميم برنامج ثرى دى مثل بلندر أرجو أن أحصل عل طريقة عمل نافذة ثلاتية الأبعاد و مجسمات ثى دى
يإخوانى أنا أريد تحميل هاذا الطقم كيف ووين أنا حملت نسخة منها بس ما عرفت أخليها تشتغل مسسسسسسساااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااعدة
يا اخوان انا عاوز احمل هاد اللعبه على الموبايل الرجاء اخباري كيف وباسرع وقت ممكن وشكرا
ما شاء الله عليكم …. جزاكم الله خيرا … المقال حلو جدا و ربنا يكرم اللى ترجمه
للأمام دائما إن شاء الله
لك التحية اللعبة ما عم تنفذ معي
اتشرف بزيارتكم الى مدونتي
http://alaslam-ahmd.blogspot.com/