Bedarfsangepasste Widgets in PyQt4
Haben Sie sich jemals einen Anwendung angesehen und sich gefragt, wie ein bestimmtes Item der grafischen Oberfläche erzeugt wurde? Wahrscheinlich ist das jedem Programmieranfänger schon mal so gegangen. Sie suchen danach in einer Auflistung aller Widgets Ihrer bevorzugten Bibliothek für Benutzeroberflächen, aber können dort nichts entsprechendes finden. Toolkits bieten gewöhnlich nur die meist verbreiteten Widgets wie Knöpfe, Textwidgets, Regler usw. an. Kein Toolkit kann Ihnen jedes denkbare Widget anbieten.
Es gibt im Grund zwei Arten von Toolkits: Superleicht-Toolkits und Schwergewichts-Toolkits. Ein Besipiel für die sparsamere Variante ist das FLTK-Toolkit. Es bringt nur die ganz grundlegenden Widgets mit und setzt voraus, dass der Programmierer sich die komplexeren selber zusammenstellt. PyQt4 hingegen gehört zu den Schwergewichten. Es hat einen ganzen Haufen Widgets. Dennoch kann es nicht alle Spezial-Widgets abdecken. Ein Geschwindigkeitsmesser-Widget zum Beispiel oder ein Widget, das die Speicherkapazität einer zu brennenden CD misst.
Programmierer müssen sich solche Widgets selber basteln. Sie erreichen dies, indem sie die Malwerkzeuge des Toolkits einsetzen. Dabei kann man zwei mögliche Wege einschlagen: Entweder der Programmierer modifiziert oder erweiter ein bereits existierendes Widget oder er erzeugt ein komplett eigenes.
Brenn-Widget
Hierbei handelt es sich um ein Widget, wie wir es in Nero, K3B oder anderen CD/DVD-Brennprogrammen vorfinden können.
#!/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_()
In unserem Beispiel verwenden wir einen QSlider und ein selbsterzeugtes Widget. Der Regler steuert das erstellte Widget. Dieses Widget zeigt grafisch die vollständige Kapazität eines Speichermediums und den freien, noch verfügbaren Platz auf ihm an. Der Minimalwert unseres erstellten Widgets ist 1, der maximale 750. Wenn wir einen Wert von 700 erreichen, beginnen wir damit, in roter Farbe zu zeichnen, was gewöhnlich das Überbrennen des Datenträgers anzeigt.
Das Brenn-Widget wir am Fuße des Fensters positioniert. Dies erreicht man durch Verwendung einer QHBoxLayout und ein QVBoxLayout
class Widget(QtGui.QLabel):
def __init__(self, parent):
QtGui.QLabel.__init__(self, parent)
Das Brenn-Widget basiert auf dem QLabel-Widget.
self.setMinimumSize(1, 30)
Wir ändern die Minimalhöhe des Widgets. Der Standarwert ist wenig zu klein für uns.
font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
paint.setFont(font)
Wir nutzen einen kleinen Schrifttyp als Standardeinstellung, das entspricht besser unseren Bedürfnissen.
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))
Wir zeichnen das Widget dynamisch. Je größer das Fenster, desto größer das Brenn-Widget und umgekehrt. Aus diesem Grund müssen wir die Größe des Widget, auf die wir das Widget zeichnen, berechnen. Der till-Parameter bestimmt die zu zeichnende Gesamtgröße. Dieser Wert stammt vom Regler-Widget. Es handelt sich um eine Teilmenge der Gesamtfläche. Der full-Parameter bestimmt den Punkt, an dem wir mit rot zu zeichnen beginnen. Beachten Sie, dass wir Gleitkommazahlen verwenden, um eine höhere Genauigkeit zur erlangen.
Das tatsächliche Nachzeichnen geschieht in drei Schritten: Wir zeichnen das gelbe oder rote und gelbe Rechteck, danach die vertikalen Linien, die das Widget in die verschiedenen Abschnitte einteilen. Schließlich zeichnen wir die Zahlen, die die Speicherkapazität anzeigen.
metrics = paint.fontMetrics() fw = metrics.width(str(self.num[j])) paint.drawText(i-fw/2, h/2, str(self.num[j]))
Wir verwenden Schriftmetrik zum Zeichnen des Textes. Um den Text um die vertikale Liniie herum anordnen zu können, müssen wir seine Breite kennen.
Abbildung: Das Brenn-Widget