PyQtタイマーサンプル(他人のもの)

キュートなタイマー - None is None is Noneを勝手にPython3に直しただけ

#encoding:utf-8
from __future__ import division
from itertools import *
import sys
import time
import math

from PyQt4.QtGui import *
from PyQt4.QtCore import *

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

class TimerThread(QThread):
    def __init__(self, parent, length, interval=0.1):
        QThread.__init__(self, parent)
        self.length = length
        self._stop = False
        self.interval = interval
    def stop(self):
        self._stop = True
    def run(self):
        starttime = time.time()
        while not self._stop:
            remain = int(math.ceil(self.length - (time.time() - starttime)))
            self.emit(SIGNAL('tick(int)'), remain)
            if remain == 0:
                self.emit(SIGNAL('finish()'))
                break
            time.sleep(self.interval)

def setTabOrders(*widgets):
    for w1, w2 in pairwise(widgets):
        QWidget.setTabOrder(w1, w2)
class TimerWindow(QWidget):
    def __init__(self, parent=None):
        super(TimerWindow, self).__init__(parent)
        font = self.font()
        font.setPointSize(18)
        self.setFont(font)
        self.min = QLineEdit(self)
        self.min.setValidator(QIntValidator(self))
        self.sec = QLineEdit(self)
        self.sec.setValidator(QIntValidator(self))
        self.btn = QPushButton("Start", self)
        self.btn.setDefault(True)
        toplayout = QHBoxLayout()
        toplayout.addWidget(self.min)
        toplayout.addWidget(QLabel("分"))
        toplayout.addWidget(self.sec)
        toplayout.addWidget(QLabel("秒"))
        bottomlayout = QHBoxLayout()
        bottomlayout.addWidget(self.btn)
        layout = QVBoxLayout()
        layout.addLayout(toplayout)
        layout.addLayout(bottomlayout)
        self.setLayout(layout)
        self.reset_display()
        setTabOrders(self.min, self.sec, self.btn, self.min)
        self.min.setFocus()
        self.min.selectAll()
        self.resize(300, 100)
        QObject.connect(self.btn, SIGNAL("clicked()"), self.start)
        def select_sec():
            self.sec.setFocus()
            self.sec.selectAll()
        QObject.connect(self.min, SIGNAL("returnPressed()"), select_sec)
        QObject.connect(self.sec, SIGNAL("returnPressed()"), self.start)
    def start(self):
        def err(msg): 
            QMessageBox.warning(self, "Timer", msg)
        secstr = self.sec.text().strip()
        minstr = self.min.text().strip()
        if not minstr:
            err("分を入力してください")
            return
        if not secstr:
            err("秒を入力してください")
            return
        try:
            m = int(minstr)
        except ValueError as e:
            err("分を半角数字で入力してください")
            return
        try:
            s = int(secstr)
        except ValueError as e:
            err("秒を半角数字で入力してください")
            return
        self.min.setEnabled(False)
        self.sec.setEnabled(False)
        self.btn.setText("Stop")
        QObject.disconnect(self.btn, SIGNAL("clicked()"), self.start)
        QObject.connect(self.btn, SIGNAL("clicked()"), self.stop)
        self.timer = TimerThread(self, m * 60 + s)
        QObject.connect(self.timer, SIGNAL('tick(int)'), self.tick)
        QObject.connect(self.timer, SIGNAL('finish()'), self.finish)
        self.timer.start()
    def stop(self):
        self.timer.stop()
        self.sec.setEnabled(True)
        self.min.setEnabled(True)
        self.setWindowTitle("Timer")
        self.min.setFocus()
        self.min.selectAll()
        self.btn.setText("Start")
        QObject.disconnect(self.btn, SIGNAL("clicked()"), self.stop)
        QObject.connect(self.btn, SIGNAL("clicked()"), self.start)
    def tick(self, remain):
        m, s = remain // 60, remain % 60
        self.min.setText(str(m))
        self.sec.setText(str(s))
        self.setWindowTitle("残{0}分{1}秒".format(m, s))
    def finish(self):
        self.stop()
        self.reset_display()
        QMessageBox.information(self, "Timer", "時間です")
    def reset_display(self):
        self.min.setText("0")
        self.sec.setText("0")
        self.setWindowTitle("Timer")
def main():
    global app
    app = QApplication(sys.argv)
    w = TimerWindow()
    w.show()
    app.exec_()
if __name__ == "__main__":
    import doctest
    doctest.testmod()
    main()