Harsh J

Memoirs of a QWERTY Keyboard

Archive for the ‘PyQt’ tag

PyQt FAQ – Custom Signals, JPEG, Mouse Hovers and More

leave a comment

Note: If you’re new to using PyQt but are interested in great cross-platform GUI application development please read the PyQt Introduction article.

I actually think of this more as an FSQ than an FAQ. Frequently Searched Questions on my blog.

These are the contents of this article:


Lets start with the most searched item.

Writing Custom Signals in PyQt

Writing custom signals that you want to ‘emit’ or trigger during certain events is an easy task. You need to create your signal object first. This is done using the PyQt.QtCore provided pyqtSignal() function. Its signature is given as:

QtCore.pyqtSignal([optional argument types], [optional name])

We need to create a signal object using this signal as a property in our class, then call its connect and emit methods to actually use it. If I were to explain diagrammatically:

Creating Custom Signals using PyQt

Creating Custom Signals using PyQt

The following code sample explains three types of signals:

  • Signals with no arguments (simpleSig)
  • Signals with some arguments (2, argumentSig)
  • Signals with multiple-type (overloaded) arguments (int and QString, doubleSig)
# -*- coding: utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import sys

class MyWidget(QWidget):

    # Defining signals below:

    # No arguments, its a simple signal that can
    # be emitted.
    simpleSig = pyqtSignal()

    # A signal that requires two parameters.
    # (One is an integer, other is a Python list type)
    # You can have one or more than two as well.
    argumentSig = pyqtSignal(int, list)

    # A signal that allows two different sets of params.
    # One is if you send an int, the other is for QString.
    # This signal is also optionally named using 'name'
    # for use in more dynamic purposes.
    doubleSig = pyqtSignal((int,), (QString,), name='doubles')

    def __init__(self, parent=None):
        # Construct the parent, never forget this!
        super(MyWidget, self).__init__(parent)

        # Connect our signals to their slots
        self.simpleSig.connect(self.simpleSlot)
        self.argumentSig.connect(self.argumentSlot)
        # We have same slot for the double signals
        # (aka) overloaded signals.
        # Cause we can check the argument from within too!
        self.doubleSig['int'].connect(self.doubleSlot)
        self.doubleSig['QString'].connect(self.doubleSlot)

        # Connect our button to a slot which
        # will call the signals.
        self.button = QPushButton('Press me', self)
        self.button.clicked.connect(self.buttonClicked)

    def buttonClicked(self, checked=False):
        # Emit the various signals to use them.
        self.simpleSig.emit()
        self.argumentSig.emit(2, [1,2,3])
        # Overloaded signals have the following format
        # for emitting specifically.
        self.doubleSig['int'].emit(42)
        self.doubleSig['QString'].emit('Hitchhiking a ride.')

    # Defining custom-signal slots below:

    def simpleSlot(self):
        # Takes no arguments
        print "Simple custom signal:",
        print "No arguments in this type of signal."

    def argumentSlot(self, *args):
        # ( Can also be 'argumentSlot(self, intArg, listArg)' )
        # Has two arguments anyway, which is collected into one
        # using '*' (A Python feature)
        print "Multi-argument signal:",
        print "Two arguments were passed: value: '%d' of %s and value: '%s' of %s." % (args[0], type(args[0]), args[1], type(args[1]))

    def doubleSlot(self, someArgument):
        # This slot handles both doubleSig(int)
        # and doubleSig(QString)
        print "Overloaded signal:",
        print "An argument was passed and it was of '%s' and value: '%s'." % (type(someArgument), someArgument)

if __name__=='__main__':
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    app.exec_()

The output upon running this example code would be (printed in stdout):

Simple custom signal: No arguments in this type of signal.
Multi-argument signal: Two arguments were passed: value: '2' of <type 'int'> and value: '[1, 2, 3]' of <type 'list'>.
Overloaded signal: An argument was passed and it was of '<type 'int'>' and value: '42'
Overloaded signal: An argument was passed and it was of '<class 'PyQt4.QtCore.QString'>' and value: 'Hitchhiking a ride.'

I hope this makes it clear on how to create your own/custom signals and to piggyback data on it via arguments (using their class/type to create them).

References: PyQt Documentation

Saving an Image as JPEG using QImage

JPEG is a wonderful, lossy image format. PyQt supports JPEG saving if a plugin called libjpeg exists. Anyhow to save a QImage as a JPEG image, do the following:

# Create a 50 pixel square image
# (or load your own using a filename).
img = QImage(50, 50, QImage.Format_RGB32)
# Lets draw a red square on this.
painter = QPainter(img)
painter.setBrush(QBrush(QColor('#ff0000')))
painter.drawRect(0,0,50,50)
painter.end()
# Save it as JPEG. Extension mention does
# all internal magic. 'jpg' also allowed.
img.save("sample.jpeg")

The above code snippet creates an image ‘sample.jpeg’ that looks like below:

A QImage saved JPEG - With a shape drawn using QPainter

A QImage saved JPEG - With a shape drawn using QPainter

There it is, as simple as that! Can be done via QPixmap too.

References: QImage, QPainter (Lots of other shape/figure drawing help here)

Tracking Mouse Hover Events with PyQt

Mouse hover events (known as the mouseMoveEvent(event) in PyQt) are tracked by default if the mouse is pressed and dragged. However if you’d like to also track mouse move events upon simple hover also (without a press requirement) you will need to enable the mouseTracking property of the QWidget (also available in all its derivatives, naturally).

So, to enable mouse tracking / mouse hover tracking for your widget you will need to call widget.setMouseTracking(True). The following code snippet is again a simple program that prints co-ordinates of the mouse pointer upon hover (without requiring a press held).

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

import sys

class MyWidget(QWidget):

    def __init__(self, parent=None):
        super(MyWidget, self).__init__(parent)

        # Enable mouse hover event tracking
        self.setMouseTracking(True)

    def mouseMoveEvent(self, event):
        # This is a QWidget provided event
        # that fires for every mouse move event
        # (In our case the move is also the hover)
        print "Mouse Pointer is currently hovering at: ", event.pos()

if __name__=='__main__':
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    app.exec_()

It should spew out some output like:

Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(415, 66)
Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(420, 60)
Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(424, 56)
Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(427, 51)
Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(433, 41)
Mouse Pointer is currently hovering at:  PyQt4.QtCore.QPoint(438, 36)

An additional way, apart from QWidget.setMouseTracking(True) is to set a Qt.Hover attribute via QWidget.setAttribute(attribute) and then filtering this event out. If you’d like me to give an example for this method too, post a comment. I think the mouse tracking solution does it for detecting mouse hover events over your widgets as the other way might look ugly.

References: QMouseEvent

Bonus Tips

Screen Size

Getting the screen size of the desktop (the display resolution in other words) is simple. You need to access the desktop widget using QApplication.desktop() [Its static so you don't need your QApplication object] and then pulling out the size() from it. That is, do:

QApplication.desktop().size()

References: QApplication

Setting the Window Size

Um, am not sure what’s so ambiguous about setting the size/geometry of a widget in PyQt but to generally set any widget’s size, be it your QMainWindow object or a simple inner QWidget derivative, all you have to do is to set its geometry property like so:

# x and y are co-ordinates where the widget/window has to appear
# and width and height are the sizes you're looking to set
# (You can keep position same by using widgetObj.x()/y() to get current
# values of x/y and passing them back in).
widgetObj.setGeometry(x, y, width, height)

References: QWidget

End

That’s all for now folks, keep the searches or comment-queries coming if you still have some! (And oh, please do refer Qt Assistant too, it’s the right thing to do).

Written by Harsh

May 6th, 2010 at 10:46 pm

PyQt – Creating interfaces visually with Designer

2 comments

Note: If you’re new to using PyQt but are interested in great cross-platform GUI application development please read the PyQt Introduction article.

Designing a graphical interface for an application could be a tiresome task. There are guidelines to keep track of, layouts to maintain and more of such stuff. In the PyQt code samples you’ve seen so far we’ve written our interfaces in pure code. While this is fun and easy to do for little applications that consist of about 5 to 10 widgets, it’s not worth spending the time upon in creating fully blown up interfaces for complete applications.

The Qt Designer

Fortunately, Qt provides us a tool to design interfaces and to port it to usable code automatically. This tool is called the Qt Designer, and it comes with the Qt library bundle you installed. Additionally we would require a converter for processing the XML .ui files Designer produces into a python module .py file; also installed with the PyQt4 bundle.

Thus, to design in PyQt we need the two following tools:

  • Qt Designer from Nokia’s Qt Software
  • The pyuic4 script from Riverbank’s PyQt

Once you have Designer and PyQt4 tools installed, let’s fire it up and get started.

The initial interface might look familiar to those who have used Visual Studio, or Glade and such tools. For the rest, it’s intuitive enough to learn smoothly. Read the Qt Designer guide for more elaborate help if you still don’t find it usable.

The Qt Designer

The Qt Designer

In this article, we’ll be using Designer to design and create a simple Image Viewer application.

References: Qt Designer, The PyQt + Qt Designer Documentation

Image Viewer

Our Image Viewer application would be a very simple one, with options to Open an image file of major types (JPEG, PNG, GIF, etc.) and another to Quit. Upon opening of an image, we shall also set the status bar to show the dimensions of the image.

Designing the GUI

Open Qt Designer (‘designer’ command if on Linux) and choose File | New | “Main Window” under templates/forms expansion. This creates a new QMainWindow widget for us to work upon. The QMainWindow is basically a composition of a main-area widget, a menu-bar and a status-bar at the bottom, the very usual stuff an application consists of.

Design of the Image Viewer UI

Design of the Image Viewer UI

From the Widget Box, drag a Label onto the main window’s area. Right-Click at any of the free space available on the same area, choose the “Lay out” sub-menu and then hit the “Lay out Horizontally” option. Designer will automatically stretch your Label across the area available this way. We need this layout (any layout will do here, actually) since we want the image to show in the entire window area.

Now on to menus, Add a File menu and under it add an Open and a Quit option, as shown below. To add these just click where it says “Type here” and get typing; Hit Enter/Return key to get to the next element in the menu after you’ve typed.

Under the Property Editor, select and set the Label’s text property to blank, and its alignment property to AlignHCenter and AlignVCenter. This completes our UI design in Qt Designer. But don’t quit already, do explore the other properties of the widgets used and figure out their possible uses; get used to the interface and available tools like Form Previews, etc.

The imageLabel (QLabel) object Properties

The imageLabel (QLabel) object Properties

For instance, check out the properties like geometry (Height and Width), font, tooltip, etc. If you’re in a good reading mood, also checkout the amazing Style Sheet documentation in Qt Assistant, as it explains a lot about each Widget’s construct and how to go about customizing it till your heart’s content.

Finally, renaming the widget elements’ variables would be a good idea, since it will help us code our application in a much more readable manner. This is how I’ve named these widgets but feel free to follow whatever naming conventions you would like to use:

Image Viewer object names

Image Viewer object names

Just double-click on the Object Name items and edit them in-line after that. Note and remember your QMainWindow class’s object name. Once you’re done, save the file as ‘ImageViewerUI.ui‘.

References: QLabel, QAction, QMenuBar, Layouts Guide

Using PyQt’s pyuic4 script

Our next step requires the use of the aforementioned pyuic4 tool. It’s usage syntax is as follows:

pyuic4 input_file.ui -o output_file.py
# Optionally takes -x parameter to make the generated code executable.

Thus we should run, for our UI file:

pyuic4 ImageViewerUI.ui -o ImageViewerUI.py

This will create a file ImageViewerUI.py that we can now use. Looking into the file will show you that its just a long list of widget constructs and of applying property settings. There is no need to edit this file, and is advised not to since pyuic4 will over-write all changes in it if you run it once again after some changes you wanted to do.

Whenever you edit your .ui files, make sure to run the pyuic4 tool to convert it into its Python equivalent (or to update existing Python code such that it reflects the new changes).

Additional info: Try `pyuic4 –help` for more fine-tuning options.

Running the basic GUI

Create a new file ImageViewer.py to finally add the application logic. Before we get to the code part, let me explain how the Qt’s subclass approach works. A Qt Designer file is inherited by the QMainWindow-derived class, and then the interface is setup using the setupUi() call with an instance. This creates all the objects/widgets for the interface as attributes of the derived QMainWindow class so that it’s ready to show. Next, we need to add the application-level logic to this derived class as normal methods. Since we have access to all the widgets used in the interface via our class, we can do as we like with their available features. The following diagram explains the hierarchy we ought to follow each time we need to implement a UI class for use:

Image Viewer Inheritance Diagram

Image Viewer Inheritance Diagram

Thus, a basic class that can be run would look like:

#!/usr/bin/python

from PyQt4 import QtGui, QtCore
import sys

# Import the interface class
import ImageViewerUI

class ImageViewer(QtGui.QMainWindow, ImageViewerUI.Ui_mainWindow):
    """ The second parent must be 'Ui_<obj. name of main widget class>'.
        If confusing, simply open up ImageViewer.py and get the class
        name used. I'd named mine as mainWindow, hence Ui_mainWindow. """

    def __init__(self, parent=None):
        super(ImageViewer, self).__init__(parent)
        # This is because Python does not automatically
        # call the parent's constructor.
        self.setupUi(self)
        # Pass this "self" for building widgets and
        # keeping a reference.

    def main(self):
        self.show()

if __name__=='__main__':
    app = QtGui.QApplication(sys.argv)
    imageViewer = ImageViewer()
    imageViewer.main()
    app.exec_()
    # This shows the interface we just created. No logic has been added, yet.

Run this class and you can see the window you just created.

Basic GUI of Image Viewer during Runtime

Basic GUI of Image Viewer during Runtime

You MUST call self.setupUi(self) to make the UI file run the setup statements and build the GUI interface for use by our class.

References: QMainWindow

Adding Application Logic and other Code

Now to add the opening-an-image feature, let’s define a few methods in the class as:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from PyQt4 import QtGui, QtCore
import sys

# Import the interface class
import ImageViewerUI

class ImageViewer(QtGui.QMainWindow, ImageViewerUI.Ui_mainWindow):
    """ The second parent must be Ui_<obj. name of main widget class>. \
      If confusing, simply open up ImageViewer.py and get the class \
      name used. I'd named mine as mainWindow and hence the use. """

    def __init__(self, parent=None):
        super(ImageViewer, self).__init__(parent)
        # This is because Python does not automatically
        # call the parent's constructor.
        self.setupUi(self)
        # Pass this "self" for building widgets and
        # keeping a reference.
        self.connectActions()

    def connectActions(self):
        self.actionQuit.triggered.connect(QtGui.qApp.quit)
        # Connect the Quit action's triggered signal
        # to a proper Quit method
        # given by qApp (which points to your QApplication
        # object).
        self.actionOpen.triggered.connect(self.openImage)
        # Connect the Open action's triggered signal
        # to load an image onto the image label.

    def openImage(self):
        # Lets get a user-provided file to open
        # using PyQt's QFileDialog class.
        fileName = QtGui.QFileDialog.getOpenFileName(
                        self,
                        "Open Image File",
                        QtCore.QDir.homePath(),
                        "Image Files (*.jpg *.jpeg *.gif *.png)"
                    )
        # Don't attempt to open if open dialog
        # was cancelled away.
        if fileName:
            self.imageLabel.setPixmap(QtGui.QPixmap(fileName))
            # Load the image file as a pixmap onto the
            # labelImage QLabel GUI object.

    def main(self):
        self.show()

if __name__=='__main__':
    app = QtGui.QApplication(sys.argv)
    imageViewer = ImageViewer()
    imageViewer.main()
    app.exec_()

Note the two new method additions connectActions() and openImage(). They complete the application logic that’s involved for our image viewing application example. Try to understand them better by seeing the PyQt classes used in them and referring them up in Qt Assistant for much more detailed information.

Now the application can open images via File | Open and can also be quit via File | Quit.

This is the final result of our work:

Image Viewer in Action

Image Viewer in Action

References: QPixmap, QFileDialog, qApp

Exercise

Try to build a simple text-based evaluator application that contains of two things:

  • A text input box that allows for mathematical expressions input.
  • An output label or text area (non-editable) for showing results of these expressions.

Classes you could use are QLabel, QLineEdit and QTextEdit. Another hint is to use Python’s ‘eval‘ function. As a bonus try to make the evaluation via Python safe.

Written by Harsh

April 18th, 2010 at 9:28 pm

Fall of the Sulks

4 comments

My undergraduate project is complete, with a few glitches to cover up. It ain’t much for now, but I guess its enough to make a sizable impact. I’d like to say that PyMT is an excellent library, and I can’t thank the developers behind it enough for their work so far (They’ve written a tracker too now, you gotta check that out). Maybe I shall contribute to it in near future, provided I have the time and skill. Contributing by code is something I only recently learned and its not wise to be hasty. Plus, there’s loads of math involved ;)

Randomly placed warning for a random post.

On the Qt front, I have this draft of a PyQt4 + Designer tutorial lying around for 7 months and it still doesn’t feel complete (alright, am lazy to make it so). Maybe I should do another screenshot + diagramming session and see if that turns out satisfactory. Something brewing in my mind for a feature in KDE 4.5 has pushed me to handle XML with the same toolkit – and writing SAX parsers is ugly, even more so if its C++. I wish I didn’t have to do that, I hate writing readers of tags and the characters between them – it always conks out someday or sometime.

Fall of the Hulks Poster

Poster to 'Fall of the Hulks'

Am addicted to comics these days, more than I’ve been to TV shows or movies (You may count “Lost” and ‘The Matrix’ as exclusions to that). Let’s talk about ‘Fall of the Hulks‘. Its twisted in its story, not many have great guesses on the new Red-Hulk (or Rulk) character and the entire screenplay is Nick Fury-esque; Apparently the story following this arc will have all major heroes of the Marvel Universe hulked up, which would be totally awesome to read (the covers are making me ogle already). For now Siege and Realm of Kings satisfy the addiction hunger. I’m not much of a fan when it comes to DC, having read pretty out-dated stuff only (such as Death of Superman or Knightfall), and thus am not quite enjoying the over-stretched Blackest Night titles they have everywhere. Karthik would not be happy with this, but fear not my friend – the completionist in me won’t give up till it ends.

Re-read Mostly Harmless and am dying to purchase and read ‘And Another Thing…‘ by Eoin Colfer but it seems too expensive even now. Will get it in the sale season probably, and read it at the Eloor libraries until that happens. H2G2 and its associated books are a must read for anyone with brains and capacity for painful-cheeks humor.

Until the next update!

Written by Harsh

March 8th, 2010 at 8:04 pm

Posted in Personal

Tagged with , , , , ,

PyQt – Signals, Slots and Layouts Tutorial

8 comments

Note: If you’re new to using PyQt but are interested in great cross-platform GUI application development please read the PyQt Introduction article.

Having seen how a simple PyQt application code looks, let’s delve into user-interaction. We’ll learn about Qt’s signal-to-slot connection model for processing input and other events, and layouts for proper placement of widgets on a window.

The PyQt Class Hierarchy

PyQt is completely built upon the Object-Oriented concepts, so it is important to understand how all classes are related to each other in it.

Almost all GUI classes extend upon their Abstract class which defines common behaviour for similar widgets. These abstract classes, or any widget class, inherit QWidget, the base class of all drawable GUI components. QWidget inherits QObject, a class that has nothing to do with GUI but forms the base class of every PyQt class and helps provide the framework-related features.

The following hierarchy diagram depicts this clearly for the QPushButton class:

The QPushButton Class Hierarchy

The QPushButton Class Hierarchy

The QPaintDevice class helps draw (or paint) things on the screen, thus its also used with anything that’s drawable – We’ll learn more about Painting in a later article.

References: QWidget
Read the rest of this entry »

Written by Harsh

May 14th, 2009 at 11:37 am

Posted in Software

Tagged with , , , , ,

Basics of PyQt

one comment

Note: If you’re new to using PyQt but are interested in great cross-platform GUI application development please read the PyQt Introduction article.

This is a total beginners-only post. In this post, I will talk about what PyQt is, what it does and what are its major components you should know about before you start thinking about developing with it. I’ve made a simple Google Docs presentation about these and its viewable below or in full screen at this link.

Read the rest of this entry »

Written by Harsh

April 29th, 2009 at 9:34 am

Posted in Software

Tagged with , , , ,