Sunday, January 22, 2012

Qt Tutorial #1-8 Preparing for Battle

This is from Qt Tutorial #1-8 Preparing for Battle


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
    PyQt4 conversion of Qt Tutorial 8
    
    In this example, we introduce the first custom widget that can paint itself. 
    We also add a useful keyboard interface (with two lines of code).
    
    NOTES:
    =====
    The original example in C++ was split across five files,
    here they have been combined into one module containing the
    following classes:
        LCDRange
        CannonField
        MyWidget
    
    CannonField has a custom pyqtSignal named 'angleChanged'
    
    BEHAVIOUR:
    =========
    The keyboard arrow keys, Home, End, PageUp and PageDown 
    all move the 'angle' widget slider.

    When the slider is operated, the CannonField displays the new angle value. 
    Upon resizing, CannonField is given as much space as possible.
        
        
last modified: 2012-01-20 jg
ref: 
    http://doc.trolltech.com/3.3/tutorial1-08.html
'''
import sys
from PyQt4.QtGui import (QApplication, QWidget, QPushButton, QFont,
                         QVBoxLayout, QGridLayout, QLCDNumber, QSlider,
                         QColor, QPainter, QSizePolicy)
from PyQt4.QtCore import (Qt, pyqtSlot, pyqtSignal, qWarning)


class LCDRange(QWidget):
    '''
        A two digit QLCDNumber and QSlider widget.
    '''
    def __init__(self, parent=None):
        super(LCDRange, self).__init__(parent)

        lcd = QLCDNumber(2, self);

        self.slider = QSlider(Qt.Horizontal, self);
        self.slider.setRange(0, 99);
        self.slider.setValue(0);

        self.slider.valueChanged.connect(lcd.display)

        layout = QVBoxLayout()
        layout.addWidget(lcd)
        layout.addWidget(self.slider)
        self.setLayout(layout)

        # set the widget focus to the 'slider' object
        self.setFocusProxy(self.slider)

    def value(self):
        return self.slider.value()

    @pyqtSlot(int)
    def setValue(self, value):
        self.slider.setValue(value)

    # set the 'slider' range, if min and max values are not
    # between 0 and 99, print a warning message and leave
    # the slider values as they were
    def setRange(self, minVal, maxVal):
        if (minVal < 0 or maxVal > 99 or minVal > maxVal) :
            qWarning("LCDRange.setRange({0},{1})\n"
                     "\tRange must be 0..99\n"
                     "\tand minVal must not be greater than maxVal".format(minVal, maxVal))
            return

        self.slider.setRange(minVal, maxVal)

class CannonField(QWidget):

    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.setObjectName('cannonField')
        self.ang = 45

        # set background colour
        pal = self.palette()
        pal.setColor(self.backgroundRole(), QColor(250, 250, 200))
        self.setPalette(pal)
        self.setAutoFillBackground(True)

    # a custom signal
    angleChanged = pyqtSignal(int, name="angleChanged")

    def setAngle(self, degrees):
        if degrees < 5: degrees = 5
        elif degrees > 70: degrees = 70
        elif self.ang == degrees: return

        self.ang = degrees
        self.repaint()
        self.angleChanged.emit(self.ang)

    def paintEvent(self, event):
        s = "Angle = " + str(self.ang)
        p = QPainter(self)
        p.drawText(200, 200, s)

    def sizePolicy(self):
        return QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

class MyWidget(QWidget):
    def __init__(self, parent=None, name=''):
        super(MyWidget, self).__init__(parent)
        if name:
            self.setObjectName(name)

        quitBtn = QPushButton('Quit', self)
        quitBtn.setFont(QFont("Times", 18, QFont.Bold))
        quitBtn.clicked.connect(QApplication.instance().quit)

        angle = LCDRange(self)
        angle.setRange(5, 70)
        cannonField = CannonField(self)
        angle.slider.valueChanged.connect(cannonField.setAngle)
        cannonField.angleChanged.connect(angle.setValue)

        grid = QGridLayout()
        grid.addWidget(quitBtn, 0, 0)
        grid.addWidget(angle, 1, 0, Qt.AlignTop)
        grid.addWidget(cannonField, 1, 1)
        grid.setColumnStretch(1, 10)
        self.setLayout(grid)

        angle.setValue(60)
        angle.setFocus()    # give the LCDRange object keyboard focus

def main():
    app = QApplication(sys.argv)    # required

    w = MyWidget()
    w.setGeometry(100, 100, 500, 355)
    w.show()

    sys.exit(app.exec_())   # start main event loop, exit when app closed

if __name__ == '__main__':
    main()