QTableWidget: größe und multithreading

Alles rund um die Programmierung mit Qt
Antworten
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

QTableWidget: größe und multithreading

Beitrag von olebole »

Hallo,

ich habe eine Frage und ein Problem mit dem QTableWidget:
die Frage ist, wie ich die Größe der Tabelle dynamisch an die Größe des Widgets anpassen kann. Die Tabelle enthält nur zwei Spalten, von denen sich die erste anpassen sollte.

Das zweite Problem ist (mal wieder) ein Bug, von dem ich nicht weiß, ob er wirklich ein Qt-Problem ist oder ob es in der Python-Anbindung liegt. Der folgende Beispielcode:

Code: Alles auswählen

import threading, sys, time
from PyQt4 import QtGui

app = QtGui.QApplication(sys.argv)

main = QtGui.QMainWindow()
table = QtGui.QTableWidget(1, 1, main)
main.setCentralWidget(table)

class MyThread(threading.Thread):
    def run(self):
        time.sleep(3)
        table.setCellWidget(0, 0, QtGui.QLabel('Hallo Welt', main))

MyThread().start()
main.show()
app.exec_()
liefert beim Start jedenfalls diverse Qt-Fehlermeldungen:
Qt hat geschrieben:QObject::setParent: Cannot set parent, new parent is in a different thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QObject::installEventFilter(): Cannot filter events for objects in a different thread.
QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread
manchmal stürzt das Programm auch einfach ab. Wenn es nicht abstürzt, wird das QLabel auch nicht innerhalb der Tabelle angezeigt (was es sollte), sondern in einem Extra-Fenster. Wenn ich auf den Extra-Thread verzichte, funktioniert es wie gewünscht -- allerdings brauche ich den Extra-Thread (die Tabellenergänzung ist Resultat einer längeren Rechnung, die hier mit sleep() simuliert wird).

Ist es ein Fehler, die Tabelle aus einem eigenen Thread heraus zu ändern? Oder ist das ein Bug in Qt? In PyQt? Oder ein Denkfehler von mir?

Qt-Versionen sind 4.4.3 und 4.5 unter Linux (kubuntu und openSUSE).

Viele Grüße

Ole
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

class MyThread(threading.Thread):
def run(self):
time.sleep(3)
table.setCellWidget(0, 0, QtGui.QLabel('Hallo Welt', main))
QT Grundlagen !
Auf der (Qt)GUI darf nur einer schreiben, und zwar der Main(GUI) thread der qt !!!

obiger codeabschnitt macht aber genau das gegenteil oder taeusch ich mich ???

Ciao ...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Anwendungsfehler. Du darfst keine grafischen Elemente aus einem anderen als dem Hauptthread heraus verändern. Punkt. Steht so in der Doku.
Also leite deinen Thread von QThread ab, und spendier im ein Signal "valueChanged" oder "dataReady" oder was auch immer, und connecte das Signal in deinem MainWindow oder sonstwo auf nen Slot, der dann die Cell setzt.
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag von olebole »

Hallo Franzf,
franzf hat geschrieben:Also leite deinen Thread von QThread ab
Wofür soll das gut sein? Soweit ich QThread verstanden habe, ist das doch "nur" eine Convienience-Funktion, die mir plattformunabhängiges Threading gestattet. Da ich Python verwende, wird das ja schon mitgeliefert.
spendier im ein Signal "valueChanged" oder "dataReady" oder was auch immer, und connecte das Signal in deinem MainWindow oder sonstwo auf nen Slot, der dann die Cell setzt.
Es ist dann nicht so, dass der connectete Slot dann im Thread selber ausgeführt wird?

Viele Grüße

Olebole
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Ich hoffe doch stark, dass QThread auch unter PyQt ein QObject ist. Und auch PyQt wird ohne QObject-inheritance keine Signals+Slots können...
Und ein Signal wird nicht ausgeführt, das wird emittiert. Und wenn du als ConnectionType eine QueuedConnection wählst, wird der verknüpfte SLOT auch brav in dem Thread des SLOT-Objekts ausgeführt.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

zum threadwechsel hasst du technisch gesehen 1 möglichkeit, welche aber bei der qt in eine weitere mündet.

du musst events senden, um die mainloop (windows message queue, oder qt event loop, welche sich in die windows message queue reinhaengt ) zu unterbrechen, und dann in einen vorgesehen pfad (eventbehandlung ) springt. Dort liesst die daten (geschuetzt) aus, und beschiesst damit die GUI.

Alternativ kannst du die mainloop immer unterbrechen(timer) dort stati abfragen, und bei bedarf in nen entprechenden pfad springen (das nennt man pollen, nutzt man wenn man im thread selber ned auf die mainloop kommt )

Die Qt bietet dir alternativ den SIGNAL / SLOT mechanismus an, welcher automatisch threadwechsel macht wenn das SIGNAL in nem anderen thread geworfen wurde, als wie die eventloop des Objects (meist die globale, aber QThreads koennen auch eigene haben) laeuft.
Die SIGNAL/Slots nutzen intern natuerlich die qt Eventloop und machen das oben beschriebene nur eben komfortabler fuer den nutzer ...

Ciao ...
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag von olebole »

Ja, mit Signal/Slot funktioniert es (war nur etwas fipselig unter Python zu machen). Danke allen für die Nachhilfe.

Hat jemand zufällig noch eine Idee, wie ich das Problem mit der Größe der Zellen hinbekomme?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

olebole hat geschrieben:Hat jemand zufällig noch eine Idee, wie ich das Problem mit der Größe der Zellen hinbekomme?
Klar. Das machst du am geschicktesten über die QHeaderViews.
QTableWidget ist ja auch nur ein spezielles QTableView.
Der Inhalt meiner Python-Konsole:

Code: Alles auswählen

>>> table = QTableWidget(2,2)
>>> table.show()
>>> hh = table.horizontalHeader()
>>> hh.setResizeMode(1, QHeaderView.ResizeToContents)
>>> hh.setResizeMode(0, QHeaderView.Stretch)
Die Spalte 0 wird jetzt immer vergrößert, Spalte 1 passt sich dem Inhalt an.
Schau dir aber auch die anderen ResizeModes an, vor allem QHeaderView::Fixed.

Grüße
Franz
Antworten