Tworzenie widżetów PyQt4 za pomocą SIP
SIP to narzędzie do automatycznego generowania Pythonowego API z bibliotek C/C++. SIP powstało jako narzędzie do tworzenia API dla biblioteki PyQt4, lecz można stosować je do bibliotek niezwiązanych z Qt (choć w tym przypadku lepszym rozwiązaniem może być BOOST Python). SIP, jak i PyQt znajdziemy w praktycznie każdej dystrybucji Linuksa, a także pod Mac OS X czy MS Windows.
Dokumentacja do SIP istnieje - w antycznej wersji dla Qt3 z niepełnymi przykładami (brak części kodu, pokazano tylko samo obudowanie funkcji), lub też w postaci "popatrz na python-qscintilla". Pomęczyłem trochę Phila oraz Google i udało mi się nakłonić SIPa do współpracy przy tworzeniu API widżeta Qt4. Ogólnie jest to w miarę proste zadanie - dla prostych widżetów.
By stworzyć API dla PyQt4 widżeta musimy mieć jego plik nagłówkowy (np. QLabel.h) oraz odpowiednie biblioteki zainstalowane w systemie (tak jakbyśmy chcieli kompilować program w C++/Qt wykorzystujący ten widżet). Czyli standardowo pobieramy źródła widżetu, kompilujemy i instalujemy. Mając plik nagłówkowy należy zabrać się za stworzenie plików "konfiguracyjnych":
- plik.sip - plik SIP o nazwie takiej, jak nazwa biblioteki nagłówkowej (zalecane) zawierający "opis" API biblioteki zrozumiały dla SIP.
- config.py - plik konfiguracyjny generujący Makefile i inne dane potrzebne do kompilacji API
- config.py.in - dodatkowy plik konfiguracyjny (zazwyczaj pusty?)
Tworzenie API dla QTermWidget
Dla przykładu (a zarazem z powodu że to fajny widżet) wybrałem QTermWidget. Po rozpakowaniu źródeł wystarczy:qmake make make install
class QTermWidget : public QWidget
{
Q_OBJECT
public:
enum ScrollBarPosition
{
/** Do not show the scroll bar. */
NoScrollBar=0,
/** Show the scroll bar on the left side of the display. */
ScrollBarLeft=1,
/** Show the scroll bar on the right side of the display. */
ScrollBarRight=2
};
//Creation of widget
QTermWidget(int startnow = 1, //start shell programm immediatelly
QWidget *parent = 0);
~QTermWidget();
//start shell program if it was not started in constructor
void startShellProgram();
//look-n-feel, if you don`t like defaults
// Terminal font
// Default is application font with family Monospace, size 10
void setTerminalFont(QFont &font);
// Shell program, default is /bin/bash
void setShellProgram(QString &progname);
// Shell program args, default is none
void setArgs(QStringList &args);
//Text codec, default is UTF-8
void setTextCodec(QTextCodec *codec);
//Color scheme, default is white on black
void setColorScheme(int scheme);
//set size
void setSize(int h, int v);
// History size for scrolling
void setHistorySize(int lines); //infinite if lines < 0
// Presence of scrollbar
void setScrollBarPosition(ScrollBarPosition);
// Send some text to terminal
void sendText(QString &text);
signals:
void finished();
protected:
virtual void resizeEvent(QResizeEvent *);
protected slots:
void sessionFinished();
private:
void init();
TermWidgetImpl *m_impl;
};
%Module QtermWidget 0
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
class QTermWidget : QWidget {
%TypeHeaderCode
#include <qtermwidget.h>
%End
public:
QTermWidget(int startnow = 1, QWidget *parent = 0);
~QTermWidget();
enum ScrollBarPosition
{
NoScrollBar=0,
ScrollBarLeft=1,
ScrollBarRight=2
};
void setTerminalFont(QFont &font);
void setShellProgram(QString &progname);
void setArgs(QStringList &args);
void setTextCodec(QTextCodec *codec);
void setColorScheme(int scheme);
void setSize(int h, int v);
void setHistorySize(int lines);
void setScrollBarPosition(ScrollBarPosition);
void sendText(QString &text);
private:
void *createTermWidget(int startnow, void *parent);
};
- %Module QtermWidget 0 - określa nazwę modułu
%Import QtCore/QtCoremod.sip %Import QtGui/QtGuimod.sip
dołączenie plików nagłówkowych PyQt4 potrzebnych do opisania widżetu Qt4%TypeHeaderCode #include
dołączenie pliku nagłówkowego, w którym znajduje się nasza klasa%End
Kolejny etap to pliki konfiguracyjne. config.py wygląda tak:
import os
import sipconfig
from PyQt4 import pyqtconfig
# The name of the SIP build file generated by SIP and used by the build
# system.
build_file = "qtermwidget.sbf"
# Get the PyQt configuration information.
config = pyqtconfig.Configuration()
# Get the extra SIP flags needed by the imported qt module. Note that
# this normally only includes those flags (-x and -t) that relate to SIP's
# versioning system.
qt_sip_flags = config.pyqt_sip_flags
# Run SIP to generate the code. Note that we tell SIP where to find the qt
# module's specification files using the -I flag.
os.system(" ".join([config.sip_bin, "-c", ".", "-b", build_file, "-I", config.pyqt_sip_dir, qt_sip_flags, "qtermwidget.sip"]))
# We are going to install the SIP specification file for this module and
# its configuration module.
installs = []
installs.append(["qtermwidget.sip", os.path.join(config.default_sip_dir, "qtermwidget")])
installs.append(["qtermwidgetconfig.py", config.default_mod_dir])
# Create the Makefile. The QtModuleMakefile class provided by the
# pyqtconfig module takes care of all the extra preprocessor, compiler and
# linker flags needed by the Qt library.
makefile = pyqtconfig.QtGuiModuleMakefile(
configuration=config,
build_file=build_file,
installs=installs
)
# Add the library we are wrapping. The name doesn't include any platform
# specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the
# ".dll" extension on Windows).
makefile.extra_libs = ["qtermwidget"]
# Generate the Makefile itself.
makefile.generate()
# Now we create the configuration module. This is done by merging a Python
# dictionary (whose values are normally determined dynamically) with a
# (static) template.
content = {
# Publish where the SIP specifications for this module will be
# installed.
"qtermwidget_sip_dir": config.default_sip_dir,
# Publish the set of SIP flags needed by this module. As these are the
# same flags needed by the qt module we could leave it out, but this
# allows us to change the flags at a later date without breaking
# scripts that import the configuration module.
"qtermwidget_sip_flags": qt_sip_flags
}
# This creates the qtermwidgetconfig.py module from the qtermwidgetconfig.py.in
# template and the dictionary.
sipconfig.create_config_module("qtermwidgetconfig.py", "config.py.in", content)
Mając gotową konfigurację można sfinalizować naszą pracę kompilując API. W tym celu należy sparsować plik SIP poleceniem:
- Qt_4_4_1: identyfikator instalacji PyQt4 (znajdziemy w /usr/lib/python*/site-packages/PyQt4/pyqtconfig.py)
- /usr/share/sip/: ścieżka do katalogu SIP, w którym przetrzymywane są pliki sip (w podfolderach, m.in. te z PyQt4)
python config.py make make install
import sys
from PyQt4 import Qt
import QtermWidget
a = Qt.QApplication(sys.argv)
w = QtermWidget.QTermWidget()
#public methods can be used, for example setting font
#w.setTerminalFont(Qt.QFont('Terminus'))
w.show()
a.exec_()

Comment article