Sunday, December 14, 2008

My PyQt Scribbles (Python and Qt) #3: A calendar in 30 or so lines

I often amuse myself exploring APIs and experiment classes and functions which seem attractive or curious.
I've always thought that experimenting is a much more interesting and effective way to learn something than merely theoretical study.

By messing around with PyQt classes I stumbled upon the QCalendarWidget class and I had a sparkle.

Being a project manager I happen to use calendars quite much for scheduling and I always find those I use rather uncomfortable. MS Outlook calendar is a mess and for some reason (at least that installed on my office pc) it doesn't shows week numbers. The "Date&Time" XP app (the one appearing by double clicking the time displayed in the right corner of the systray) doesn't show the week numbers and the navigation of months and years is rather uncomfortable. The big company calendar pinned on the wall is fine but during the last 2 or 3 months of each year we reach that "dark zone" where there's me that must schedule activities in the first months of the new year and the huge calendar shows only the month of January(NewYear).
So I thought that this PyQt calendar could be a nice season-so experiment for my journey into PyQt.

I coded a bit and realised that the result was far more better than all the calendars surrounding me. No special functionality for the moment but it's small, fast, simple, clean, easy to navigate and shows the week numbers. Yippee!!

I started from the first gui created in the previous installment so I won't comment the parts already seen last time.
here we go:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class WizAndChipsCal(QMainWindow):
        def __init__(self, parent = None):
            QMainWindow.__init__(self, parent)
            self.setWindowTitle("Wiz and Chips Calendar")
            self.setGeometry(300,300,300,220)
            self.setWindowIcon(QIcon("C:/Python26/PyQt/Icon/date.png"))
            #self.setWindowIcon(QIcon("/home/MY_USR/PyQt/Icon/date.png")) - on Linux
            self.setToolTip("Hello to this Wiz and Chips fancy calendar!")

            self.CloseButton = QPushButton("&Close", self)
            self.CloseButton.setGeometry(26, 190, 50, 25)
            self.CloseButton.setToolTip("" + "Press here to Quit" + "")

            self.title = ("" + "Wiz and Chips Pushable Calendar!" + "")
            self.Label = QLabel(self.title, self)
            self.Label.setGeometry(45, 5, 250, 15)

            self.calendar = QCalendarWidget(self)
            self.calendar.setGridVisible(1)
            self.calendar.setGeometry(26, 30, 250, 150)

            self.connect(self.CloseButton,
                         SIGNAL("pressed()"),
                         self.close)

app = QApplication(sys.argv)
main_window = WizAndChipsCal()
main_window.show()
app.exec_()
# the underscore is used to avoid confusion with pyhton built-in exec()
And now some notes about the new parts:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
This time I decided to import all the classes from the modules I need so to avoid using module prefix every time I need a module object (i.e. avoid -> QtGui.QIcon)
self.CloseButton = QPushButton("&Close", self)
self.CloseButton.setGeometry(26, 190, 50, 25)
self.CloseButton.setToolTip("" + "Press here to Quit" + "")
Here comes a new object: a push button. QPushButton is the class we need to create the button. The arguments are the button label and the parent where the button is placed.The ampersand before the "C" provides ALT+C to activate the button.One more time the setGeometry method sets the relative position(x,y) onto the screen and the size (w,h) of the Button.
We use setToolTip method we used before but we specify it for our button (self.CloseButton).
setToolTip accepts HTML tags so we can fancily specify size, colors and more if we want
self.title = ("" + "Wiz and Chips Pushable Calendar!" + "")
self.Label = QLabel(self.title, self)
self.Label.setGeometry(45, 5, 250, 15)
I want to create a title, within the widget, for our tiny app, so I create a label using the QLabel class.
Since I don't want to put the label raw text directly as the label argument, I create instead a "title" object just to specify the text and then assign it to the label object.
QLabel accepts HTML tags so we can fancily specify size, colors and more if we want.
self.calendar = QCalendarWidget(self)
self.calendar.setGridVisible(1)
self.calendar.setGeometry(26, 30, 250, 150)
Here comes the funny part. PyQt QCalendarWidget() class provides a nice calendar which perfectly suits my purpose. setGridVisible() method creates a grid for the calendar once it's set to 1
self.connect(self.CloseButton,
SIGNAL("pressed()"),
self.close)
Before we created a button but now we need to write some code to make that button actually perform an action.
This is somewhat tricky in PyQt (at least for me at the moment and compared to what I remembered of Tkinter). Anyway, we need to invoke the connect function* and tell it 3 things:
1- the emitter (the button in our case)
2- the signal to connect (SIGNAL("pressed()")
3- the function called to perform the action
* connect inherits from QObject class of QtCore module
Here below how the calendar looks in Kubuntu 8.10 (with KDE 4.1 and Oxygen theme) and MS Windows XP.



There are two things to be noticed about the final product. The first thing is that the calendar is quite functional and the second one is that, despite it's apparent smoothness and functionality, the code is rather primitive and not so elegant. For example we haven't used any layout manager to arrange our widget so we better remedy to this point later on.

Note for Windows users. If you save your calendar file with .pyw extension you will be able to open it smoothly avoiding python console to pop up.

No comments:

Post a Comment