PyQt is a set of Python bindings to Qt — Nokia’s cross-platform GUI toolkit. Qt is mature, versatile and is released under an Open Source license. It is used in software like Google Earth, KDE, Opera, Skype, VLC media player and VirtualBox (Source: Wikipedia).
One of the advantages of using PyQt for graphical user interface (GUI)
development is the bundled
Qt Designer
makes GUI design very easy and efficient. Widgets can be dragged and
dropped onto dialogs, main windows and widgets. The resulting XML user
interface (.ui
) files can be converted to Python code using the scripts
included with PyQt.
Using PyQt from within Bionumerics scripts opens up a number of possibilities — use simple message boxes with error or informative texts, create dialogs and display windows for working with databases and so on. Bionumerics includes some modules including BioPython but PyQt will need to be included manually. It is fairly simple to use an existing PyQt installation.
Requirements
-
Python 2.6
The latest stable version as of this writing is 2.6.5. It can be downloaded from http://python.org/download/releases/ -
PyQt
Latest version can be downloaded and installed from http://www.riverbankcomputing.co.uk/software/pyqt/download [under Binary packages]. PyQt version as of this writing isPyQt-Py2.6-gpl-4.7.3-2.exe
. -
To be able to import PyQt modules from within Bionumerics, the module directory:
C:\Python26\Lib\site-packages
should be added toPYTHONPATH
or appended tosys.path
before other import statements. -
The Qt DLL’s must be available or added to the system
PATH
. This will be done automatically by the PyQt installer. If the DLL’s are not added to the system path, it will lead to error messages like “DLL load failed” and the script will fail.
Sample Application
To demonstrate, I am going to use a simple dialog application as an example. This application is used to set the value of a given field of entries selected in the BioNumerics main window. When run, this is how it will appear:
Step 1 — Creating the dialog using Qt Designer
The dialog displayed above was created in Qt Designer and the
file was saved as
setfielddlg.ui
. The text at the top, Field
and Value
labels are all of
type QLabel
. The combo box used to display existing field names from the
database is of type QComboBox
. The input field
corresponding to Value
is
of type QLineEdit
. The Apply
, Close
and
Deselect All
buttons are all of type QPushButton
.
A list of tutorials on PyQt can be found here. The book Rapid GUI Programming with Python and Qt is an excellent reference on the topic. Also useful is Riverbank’s documentation of PyQt’s classes.
Step 2 — Generating Python code for the Dialog
The setfielddlg.ui
file generated from Qt Designer in the previous step is
an XML file describing all the elements of the dialog. This can
be converted to Python code using the pyuic4
command
included with PyQt4:
pyuic4 -o ui_setfielddlg.py setfielddlg.ui
The -o
option specifies the name of the output file.
The dialog can now be called from other scripts.
Note
C:\Python26\Lib\site-packages\PyQt4\bin
should be in the system PATH
for the pyuic4
command to work.
Step 3 — The main script
set_field.py
"""A simple dialog application to set the field of selected entries in
Bionumerics 6
"""
import sys
import bns
#append paths to PyQt4 and the current directory to sys.path
moduledir = [r'C:\Python26\Lib\site-packages', r'G:\Python\Bionumerics_scripts\src']
for moddir in moduledir:
if moddir not in sys.path:
sys.path.append(moddir)
import dbf
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QApplication, QDialog, QMessageBox
import ui_setfielddlg
class Dlg(QDialog, ui_setfielddlg.Ui_Dialog):
def __init__(self, fieldnames, parent=None):
super(Dlg, self).__init__(parent)
self.setupUi(self)
self.connect(self.close_button, SIGNAL('clicked()'), self.accept)
self.connect(self.apply_button, SIGNAL('clicked()'), self.setfield)
self.connect(self.deselect_button, SIGNAL('clicked()'), self.deselect)
if fieldnames:
self.field_combo_box.addItems(fieldnames)
else:
QMessageBox.critical(None, 'Error', 'Could not get fieldnames')
return
def deselect(self):
'''Deselect all entries if selected'''
if len(bns.Database.Db.Selection):
bns.Database.Db.Selection.Clear()
else:
return
def setfield(self):
'''Sets the field value of selected entries'''
field = unicode(self.field_combo_box.currentText())
value = unicode(self.value_line_edit.text())
selected = dbf.getSelected()
if selected:
for item in selected:
key = item.Key
try:
bns.Database.EntryField(key, field).Content = value
except Exception, e:
QMessageBox.critical(None, 'Error', str(sys.exc_info()))
bns.Database.Db.Fields.Save()
else:
QMessageBox.information(None,'No entries selected',
'Please select entries before running script')
if __name__ == '__main__':
sys.__dict__['argv'] = ['argv']
app = QApplication(sys.argv)
fields = dbf.getFieldNames()
dialog = Dlg(fields)
dialog.show()
__bnscontext__.Stop(app.exec_())
Step 4 — Running the script
Source code of all the scripts used here can be downloaded from my GitHub repository.
The scripts set_field.py
and ui_setfielddlg.py
and the module
dbf.py
should be in the same directory. In my case, this was
G:\Python\Bionumerics_scripts\src
and also specified in the
moduledir list in the set_field.py
script.
The dialog can be called by using the Scripts -> Run script from file
option in Bionumerics and selecting set_field.py
.
For this to work, database entries must be selected in the
main window of BioNumerics or else the dialog displays a
message and does nothing. Select the field from the Field
combo box, type in the value that needs to be set for the field and hit
Apply
. The value field has a maximum length of 80 characters, which is the
maxiumum length of a field in BioNumerics.
The script in detail
Some additional notes
Imports
bns
is the BioNumerics Python module- PyQt4 module directories are appended to
sys.path
, as is the path to the directory of the running script. This is done as to import any additional modules used by the script. dbf
is a module I wrote for general database functions. In this script, it is used for getting a list of selected entries (get_selected
) and for getting the list of field names in the database -get_field_names
.
ui_setfielddlg
contains code for the dialog:
import sys
import bns
#append paths to PyQt4 and the current directory to sys.path
moduledir = [r'C:\Python26\Lib\site-packages', r'G:\Python\Bionumerics_scripts\src']
for moddir in moduledir:
if moddir not in sys.path:
sys.path.append(moddir)
import dbf
from PyQt4.QtCore import SIGNAL
from PyQt4.QtGui import QApplication, QDialog, QMessageBox
import ui_setfielddlg
The dialog class — Dlg
Takes the fieldnames as an argument and populates the Field
combo box if it is not empty. The clicked()
signal of the
close button is connected to the accept
slot, which closes
the dialog. This is the same signal that is emitted by when
the window is closed from the menubar. The other buttons are connected to their respective slots which are
defined within the class.
main
The: sys.__dict__['argv'] = ['argv']
does nothing.
For some reason, sys.argv
is empty or not defined when running
the script from BioNumerics, and the line of the code
QApplication(sys.argv)
refuses to work without it.
As no commandline arguments are used in the code above, it should cause no harm:
__bnscontext__.Stop(app.exec_())
stops the program once the event
loop — app.exec_()
returns. This is similar to
sys.exit(app.exec_())
used in standard PyQt4 programs.
Troubleshooting
Some issues I am aware of:
- Only one instance of the script should run. Calling it twice causes a crash.
- The dialog must be closed before exiting Bionumerics or else the program crashes.