Seite 1 von 1

[PyQt] Events nachladen

Verfasst: Donnerstag 26. März 2009, 20:21
von INFACT
Sehr geehrtes Python Forum.

Ich versuche ein paintEvent nachzuladen, weil das Bild, zu einem Zeitpunkt verändert wurde.
Ich habe jetzt folgenden code der nicht funktioniert :wink::

Code: Alles auswählen

from PyQt4 import QtCore, QtGui
import time
import thread
import sys

class Win(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)
        self.Grafik = QtGui.QImage("python-logo.png")
        self.Ziel = QtCore.QRect(10, 10, 130, 130)
        self.Quelle = QtCore.QRect(0, 0,
                                   self.Grafik.width(),
                                   self.Grafik.height())

    def paintEvent(self, event):
        def ev():
            while 1:
                Painter = QtGui.QPainter(self)
                Painter.drawImage(self.Ziel, self.Grafik, self.Quelle)
                time.sleep(1)
        thread.start_new_thread(ev,())
    

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    picwin = Win()
    picwin.show()
    app.exec_()
Bin für Vorschläge offen.

Danke für Antworten!

Hier noch das Bild
http://img.xrmb2.net/images/506162.png


MfG Robin

Verfasst: Freitag 27. März 2009, 13:02
von jerch
Ich fasse mal kurz zusammen, was Du da machst:
Für jedes Paintevent startest Du einen neuen Thread. Jeder dieser Threads versucht, im Sekundentakt den Gui-MainThread zu manipulieren.
Fällt Dir da selbst was auf?

Paintevents löst die Gui z.B. für jede Größenänderung des Fensters aus (und zwar pixelweise), hier stößt Du schnell an das max. Threadlimit. Und Gui-Elemente ausserhalb des MainThreads anzufassen ist ohne Locks ein no go. Wobei ich nicht glaube, das Du im Sinn hattest, von abertausenden Threads auf die Gui zuzugreifen.

Wenn Du ein Bild ändern willst, falls sich die Ressource geändert hat, geht das ohne Threads, z.B. mit QTimer() Ressource überprüfen und bei Änderung Laden und ein update() des Widgets auslösen, wobei in der Methode paintEvent() die geänderte Ressource gezeichnet wird.

Verfasst: Freitag 27. März 2009, 17:44
von INFACT
ahaa! :idea: :wink:
Ich habe das jetzt mit einem thread und update gemacht:

Code: Alles auswählen

from PyQt4 import QtCore, QtGui
import time
import thread
import sys

class Win(QtGui.QWidget):
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)
        self.Grafik = QtGui.QImage("python-logo.png")
        self.Ziel = QtCore.QRect(10, 10, 130, 130)
        self.Quelle = QtCore.QRect(0, 0,
                                   self.Grafik.width(),
                                   self.Grafik.height())
        thread.start_new_thread(self.__event__,())

    def __event__(self):
        while 1:
            self.update()

    def paintEvent(self, event):
        self.Grafik = QtGui.QImage("python-logo.png")
        Painter = QtGui.QPainter(self)
        Painter.drawImage(self.Ziel, self.Grafik, self.Quelle)
   

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    picwin = Win()
    picwin.show()
    app.exec_()

Verfasst: Freitag 27. März 2009, 18:26
von jerch
Das Aufrufen von Widget-Funktionen von einem anderen Thread als dem Gui-MainThread ist nicht thread safe (self.update() in __event__()). Mit update() manipulierst Du den Eventloop der Gui, im worst case macht der Mainthread aber gerade dasselbe und dann passiert von nix bis Programmabsturz alles mögliche.

Wenn Du unbedingt Threads nehmen willst, brauchst Du hier entweder einen Locking-Mechanismus (z.B. QMutex), einen Callback zur Gui oder einen Signalmechanismus (QThread bringt den für Qt gleich mit). Siehe auch http://www.qtforum.org/article/26801/qt ... dgets.html
Ich glaube halt, das Du mit Threads hier mit Kanonen auf Spatzen schießt und mit QTimer gut bedient wärst.

Zwei Dinge noch zur Methode __event__(). Welchen Zweck verfolgst Du mit den Doppelunterstrichen? Die sind eigentlich nicht zum Selbstimplementieren gedacht sondern für Python-interne Dinge, die im Usernamensraum existieren (siehe PEP 8). Desweiteren dürfte Dein Loop dem Prozessor ordentlich einheizen ;)

Verfasst: Freitag 27. März 2009, 18:28
von name
wozu das thread? und ich glaub so ohne locking wird das nicht gehen. was spricht gegen QtCore.QTimer?

Verfasst: Freitag 27. März 2009, 20:11
von jerch
Suchst Du nicht eher sowas?

Code: Alles auswählen

import sys, os
from PyQt4 import QtGui, QtCore

class MyWidget(QtGui.QWidget):
    def __init__(self,  parent=None):
        QtGui.QWidget.__init__(self,  parent)
        self.pixfile = 'bild.png'
        self.pix = QtGui.QPixmap(self.pixfile)
        self.pix.mtime = os.path.getmtime(self.pixfile)
        self.timer = QtCore.QTimer(self)
        self.connect(self.timer, QtCore.SIGNAL('timeout()'),
                     self.checkPixmap)
        self.timer.start(2000)
    
    def paintEvent(self, event):
        p = QtGui.QPainter(self)
        p.drawPixmap(QtCore.QRect(0, 0, self.pix.width(), self.pix.height()),
                     self.pix)
    
    def checkPixmap(self):
        last_change = os.path.getmtime(self.pixfile)
        if self.pix.mtime < last_change:
            self.pix.mtime = last_change
            self.pix.load(self.pixfile)
            self.update()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = MyWidget()
    w.show()
    sys.exit(app.exec_())
Der Dateizugriff für die mtime und das Laden müßte allerdings noch ein Exceptionhandling bekommen (falls die Datei zwischendurch nicht vorhanden sein sollte etc.)

Verfasst: Samstag 28. März 2009, 15:58
von lunar
Mit QFileSystemWatcher kann man das auch gänzlich ohne Polling lösen.

Verfasst: Samstag 28. März 2009, 17:33
von jerch
Danke für den Tipp, lunar. Wobei ich die file-descriptor-Beschränkung bei stärkerem Gebrauch ziemlich nervig finden würde.