taste-progress-dialog.py 5.44 KB
Newer Older
1 2
#!/usr/bin/env python

3
from __future__ import print_function
4 5 6
import sys
import time
import signal
7
import os
8
from collections import deque
9

10
import PySide
11
from PySide import QtGui
12
from PySide.QtCore import QThread, Signal, QObject, Qt, Slot, QTimer
13 14 15

from PySide.QtGui import (QApplication,
                          QMessageBox,
16 17
                          QDialog,
                          QPushButton,
18 19
                          QProgressDialog)

20 21 22
log = deque()


23
class MyThread(QThread, QObject):
24 25
    ''' Thread waiting for data on stdin and sending signals to the prgress
    bar in case something came in.
26 27 28 29 30 31 32
    To update the bar, the line shall start with either a range or a numer.
    e.g.
        0-50 Doing something     # The bar will progress from 0 to 49%
        70   Something else      # The bar will remain at 70%

    Without a number/range, the text is only appended to the log
    If the line starts with @ERROR@ the thread will stop
33 34
    '''
    text       = Signal(str)
35
    progress   = Signal(int, int)
36 37 38 39
    end        = Signal()
    error      = Signal()
    force_quit = False

40 41 42 43 44 45 46 47 48 49
    def update_bar(self, from_value, to_value, text):
        ''' Request an update of the progress bar (value/range and text) '''
        if 100 < from_value < 0 or 100 < to_value < 0:
            return
        self.progress.emit(from_value, to_value)
        self.text.emit(text)
        if from_value == 100:
            self.end.emit()
            self.force_quit = True

50 51 52
    def run(self):
        while True:
            # read from stdin without any buffering
53 54
            if self.force_quit:
                return
55 56
            line = sys.stdin.readline()
            if len(line) == 0:
57
                self.end.emit()
58 59
                return
            else:
60
                split = line.split(' ', 1)
61
                try:
62 63 64 65
                    # check for a range format (e.g. 10-20)
                    left, right = split[0].split('-')
                    from_v, to_v = int(left), int(right)
                    if from_v > to_v:
66
                        raise ValueError
67 68 69 70
                    if 100 < from_v < 0 or 100 < to_v < 0:
                        raise ValueError
                    text = split[1]
                    self.update_bar(from_v, to_v, text)
71
                except (ValueError, IndexError):
72 73 74 75 76 77 78 79
                    try:
                        # check for format "NUMBER text"
                        possible_val = split[0]
                        value = int(possible_val)
                        text = split[1]
                        self.update_bar(value, value, text)
                    except (ValueError, IndexError):
                        text = line
80
                log.append(text)
81 82 83
                try:
                    if split[0] == '@ERROR@':
                        self.error.emit()
84
                        return
85 86
                except IndexError:
                    pass
87

88

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
class MyDialog(QDialog):
    def __init__(self):
        super(MyDialog, self).__init__()
        self.bar         = QtGui.QProgressBar()
        self.more_button = QtGui.QPushButton("Details")
        self.extension   = QtGui.QWidget()
        self.log_window  = QtGui.QListWidget()
        self.label       = QtGui.QLabel()

        # Layouts
        self.top_layout  = QtGui.QVBoxLayout()
        self.ext_layout  = QtGui.QVBoxLayout()
        self.main_layout = QtGui.QVBoxLayout()

        self.more_button.setCheckable(True)
        self.more_button.hide()
        self.more_button.toggled.connect(self.log_window.setVisible)

        self.top_layout.addWidget(self.label)
        self.top_layout.addWidget(self.bar)
        #self.top_layout.addWidget(self.more_button)
        self.top_layout.setStretch(0, 0)
        self.top_layout.setStretch(1, 0)
        self.top_layout.setStretch(2, 1)
        self.main_layout.addLayout(self.top_layout)
        self.main_layout.addWidget(self.log_window)
        self.setLayout(self.main_layout)
        self.main_layout.setStretch(2, 1)
        self.setWindowTitle("TASTE")
        self.extension.hide()
        self.log_window.hide()

        self.done = False

    @Slot()
    def complete_or_cancel(self):
        self.done = True

127 128 129
    def periodic_update(self):
        self.current_value += 5
        if self.current_value >= self.target_value:
130
            self.current_value = self.target_value
131 132 133 134 135 136 137 138 139 140 141
        else:
            QTimer.singleShot(200, self.periodic_update)
        self.bar.setValue(self.current_value)

    @Slot(int, int)
    def reach_target(self, from_value, to_value):
        self.current_value = from_value
        self.target_value  = to_value
        self.bar.setValue(self.current_value)
        QTimer.singleShot(100, self.periodic_update)

142 143 144 145 146 147 148 149
    def closeEvent(self, e):
        if not self.done:
            e.ignore()


def handle_error():
    print("== An error occured, here is the log ==")
    print("\n".join(log))
150 151 152


def run_gui():
153 154 155 156 157
    app       = QApplication(sys.argv)
    thread    = MyThread()
    dialog    = MyDialog()
    progress  = dialog.bar

158 159
    progress.setValue(0)

160 161 162 163 164
    thread.text.connect      (dialog.label.setText)
    thread.end.connect       (dialog.complete_or_cancel)
    thread.end.connect       (dialog.close)
    thread.progress.connect  (dialog.reach_target)
    thread.error.connect     (handle_error)
165 166

    thread.start()
167 168
    dialog.exec_()

169
    thread.wait()
170 171

def main():
172 173
    if os.getenv("DISABLE_PROGRESS_BAR") is not None:
        sys.exit(0)
174 175 176 177 178 179
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    run_gui()

if __name__ == '__main__':
    main()