Sunday, January 22, 2012

Qt Tutorial #1-9 With Cannon You Can

This is from Qt Tutorial #1-9 With Cannon You Can


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

'''
    PyQt4 conversion of Qt Tutorial 9
    
    In this example we become graphic by drawing a cute little blue cannon. 
    The only differences from 108_battle.pyw are:
        1. Added a short-cut key to the Quit button
           (In Windows, you have to press ALT to make the short-cut visible)
        2. re-written paintEvent() method in the CannonField class to draw the cannon
    
    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
    
    BEHAVIOUR:
    =========
    When the slider is operated the angle of the drawn cannon changes accordingly.
    The Q on the Quit button is now underlined, and Alt+Q does what you think it does.     
        
last modified: 2012-01-20 jg
ref: 
    http://doc.trolltech.com/3.3/tutorial1-09.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, QRect)


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):
        p = QPainter(self)
        p.setBrush(Qt.blue)     # brush colour for filling object
        p.setPen(Qt.NoPen)      # no special edges

        # set QPainter's origin (0,0) coords to the bottom-left
        p.translate(self.rect().bottomLeft())

        # draw a quarter circle in the bottom left corner
        p.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16)

        # rotate counter-clockwise 'ang' degrees around the origin
        # and draw the cannon's barrel
        p.rotate(-self.ang)
        p.drawRect(QRect(33, -4, 15, 8))

    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()