#!/usr/bin/env python

#/****************************************************************************
#** $Id:  progress.py   3.3.7   edited Aug 31 2005 $
#**
#** Copyright (C) 1992-2005 Trolltech AS.  All rights reserved.
#**
#** This file is part of an example program for Qt.  This example
#** program may be used, distributed and modified without limitation.
#**
#** Converted to PyQt3 by Hans-Peter Jansen <hpj@urpla.net>
#** 
#*****************************************************************************/

import sys
from qt import *
from random import randint

class AnimatedThingy(QLabel):
    def __init__(self, parent, s):
        QLabel.__init__(self, parent)
        self.label = QString(s)
        self.setEraseColor(Qt.white)
        self.label.append("\n... and wasting CPU\nwith this animation!\n")
        self.step = 0
        self.nqix = 10
        self.stepoff = 2
        self.stepmax = 7
        self.lwidth = 2
        self.tdelay = 100
        self.ox, self.oy = [], []
        for i in range(self.nqix):
            self.ox.append((0, 0))
            self.oy.append((0, 0))
        self.x0, self.y0, self.x1, self.y1 = 0, 0, 0, 0
        self.dx0 = randint(0, self.stepmax) + self.stepoff
        self.dy0 = randint(0, self.stepmax) + self.stepoff
        self.dx1 = randint(0, self.stepmax) + self.stepoff
        self.dy1 = randint(0, self.stepmax) + self.stepoff

    def show(self):
        if not self.isVisible():
            self.startTimer(self.tdelay)
        QWidget.show(self)

    def hide(self):
        QWidget.hide(self)
        self.killTimers()

    def sizeHint(self):
        return QSize(120, 100)

    def timerEvent(self, e):
        p, pn = self._painter()
        pn.setColor(self.eraseColor())
        p.setPen(pn)
        self.step = step = (self.step + 1) % self.nqix
        # erase line
        p.drawLine(self.ox[step][0], self.oy[step][0],
                   self.ox[step][1], self.oy[step][1])
        # calc new line coordinates
        ww, wh = self.width(), self.height()
        self.x0, self.dx0 = self._inc(self.x0, self.dx0, ww)
        self.y0, self.dy0 = self._inc(self.y0, self.dy0, wh)
        self.x1, self.dx1 = self._inc(self.x1, self.dx1, ww)
        self.y1, self.dy1 = self._inc(self.y1, self.dy1, wh)
        self.ox[step] = (self.x0, self.x1)
        self.oy[step] = (self.y0, self.y1)
        self._drawqix(p, pn, step)
        self._drawtxt(p)

    def paintEvent(self, event):
        p, pn = self._painter()
        p.setClipRect(event.rect())
        for i in range(self.nqix):
            self._drawqix(p, pn, i)
        self._drawtxt(p)

    def _painter(self):
        p = QPainter(self)
        # we need to create a new pen from p.pen() with QPen() in order
        # to make it fully functional (changes are discarded otherwise)
        pn = QPen(p.pen())
        pn.setWidth(self.lwidth)
        p.setPen(pn)
        return p, pn

    def _drawqix(self, p, pn, step):
        # rainbow effect
        pn.setColor(QColor((step * 255)/self.nqix, 255, 255, QColor.Hsv))
        p.setPen(pn)
        p.drawLine(self.ox[step][0], self.oy[step][0],
                   self.ox[step][1], self.oy[step][1])

    def _drawtxt(self, p):
        p.setPen(self.colorGroup().text())
        p.drawText(self.rect(), Qt.AlignCenter, self.label)

    def _inc(self, x, dx, b):
        x += dx
        if x < 0:
            x = 0
            dx = randint(0, self.stepmax) + self.stepoff;
        elif x >= b:
            x = b - 1
            dx = -(randint(0, self.stepmax) + self.stepoff)
        return x, dx


class CPUWaster(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.first_draw_item = 1000
        self.last_draw_item = 1006
        self.rects = 0
        self.timer_driven = 0
        self.default_label = 0
        self.got_stop = False
        self.pb = None      # non modal progress dialog
        self.menubar = menubar = QMenuBar(self, "menu")
        self.file_ = file_ = QPopupMenu()
        menubar.insertItem("&File", file_)
        for i in range(self.first_draw_item, self.last_draw_item + 1):
            file_.insertItem(self.drawItemText(i), i)
            file_.setAccel(QKeySequence(
                           "Ctrl+%s" % (i - self.first_draw_item)), i)
        self.connect(menubar, SIGNAL("activated(int)"), self.doMenuItem)
        file_.insertSeparator()
        file_.insertItem("Quit", qApp, SLOT("quit()"), QKeySequence("Ctrl+Q"))
        self.options = options = QPopupMenu()
        menubar.insertItem("&Options", options)
        self.ld_id = options.insertItem("Loop driven", self.loopDriven,
                                        QKeySequence("Alt+L"))
        self.td_id = options.insertItem("Timer driven", self.timerDriven,
                                        QKeySequence("Alt+T"))
        options.insertSeparator()
        self.dl_id = options.insertItem("Default label", self.defaultLabel,
                                        QKeySequence("Alt+D"))
        self.cl_id = options.insertItem("Custom label", self.customLabel,
                                        QKeySequence("Alt+C"))
        options.insertSeparator()
        self.md_id = options.insertItem("No minimum duration",
                                        self.toggleMinimumDuration,
                                        QKeySequence("Alt+M"))
        options.setCheckable(True)
        # default option settings
        self.timerDriven()
        self.customLabel()
        self.toggleMinimumDuration()
        self.resize(400, 300)
        self.setEraseColor(Qt.black)

    def drawItemRects(self, id_):
        r = 100
        for n in range(id_ - self.first_draw_item):
            if not n % 3:
                r *= 4
            else:
                r *= 5
        return r

    def drawItemText(self, id_):
        return QString("%d Rectangles" % self.drawItemRects(id_))

    def enableDrawingItems(self, yes):
        for i in range(self.first_draw_item, self.last_draw_item + 1):
            self.menubar.setItemEnabled(i, yes)

    # slots
    def doMenuItem(self, id_):
        if id_ >= self.first_draw_item and id_ <= self.last_draw_item:
            self.draw(self.drawItemRects(id_))

    def timerDriven(self):
        self.timer_driven = True
        self.options.setItemChecked(self.td_id, True)
        self.options.setItemChecked(self.ld_id, False)

    def loopDriven(self):
        self.timer_driven = False
        self.options.setItemChecked(self.ld_id, True)
        self.options.setItemChecked(self.td_id, False)

    def defaultLabel(self):
        self.default_label = True
        self.options.setItemChecked(self.dl_id, True)
        self.options.setItemChecked(self.cl_id, False)

    def customLabel(self):
        self.default_label = False
        self.options.setItemChecked(self.dl_id, False)
        self.options.setItemChecked(self.cl_id, True)

    def toggleMinimumDuration(self):
        self.options.setItemChecked(self.md_id,
                                    not self.options.isItemChecked(self.md_id))

    def stopDrawing(self):
        self.got_stop = True

    def newProgressDialog(self, label, steps, modal):
        d = QProgressDialog(label, "Cancel", steps, self, "progress", modal)
        if self.options.isItemChecked(self.md_id):
            d.setMinimumDuration(0)
        if not self.default_label:
            d.setLabel(AnimatedThingy(d, label))
        d.setCaption("Please Wait")
        return d

    def draw(self, n):
        if self.timer_driven:
            if self.pb:
                qWarning("This cannot happen!")
                return

            self.rects = n
            self.pb = self.newProgressDialog("Drawing rectangles.\n"
                                             "Using timer event.", n, False)
            self.connect(self.pb, SIGNAL("cancelled()"), self.stopDrawing)
            self.enableDrawingItems(False)
            self.startTimer(0)
            self.got_stop = False
        else:   # loop driven with modal progress dialog
            lpb = self.newProgressDialog("Drawing rectangles.\n"
                                         "Using loop.", n, True)
            p = QPainter(self)
            for i in range(n):
                lpb.setProgress(i)
                if lpb.wasCancelled():
                    break
                self._draw(p)
            self._clear(p)
            lpb.cancel()
            del lpb

    def timerEvent(self, e):
        if not self.got_stop:
            self.pb.setProgress(self.pb.totalSteps() - self.rects)
        self.rects -= 1
        p = QPainter(self)
        self._draw(p)

        if not self.rects or self.got_stop:
            if not self.got_stop:
                self.pb.setProgress(self.pb.totalSteps())
            self._clear(p)
            self.enableDrawingItems(True)
            self.killTimers()
            self.pb.cancel()
            del self.pb
            self.pb = None

    def _draw(self, p):
        ww = self.width()
        wh = self.height()
        if ww > 8 and wh > 8:
            c = QColor(randint(0, 255), randint(0, 255), randint(0, 255))
            x = randint(0, ww - 8)
            y = randint(0, wh - 8)
            w = randint(0, ww - x)
            h = randint(0, wh - y)
            p.fillRect(x, y, w, h, QBrush(c))

    def _clear(self, p):
        p.fillRect(0, 0, self.width(), self.height(), QBrush(self.eraseColor()))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    try:
        n = int(sys.argv[1])
    except:
        n = 1
    wlst = []   # keep a ref around
    for i in range(n):
        cpuw = CPUWaster()
        if i == 0:
            app.setMainWidget(cpuw)
        cpuw.show()
        wlst.append(cpuw)
    app.exec_loop()
