QObject::startTimer Fehlermeldung beim beenden.

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Haragius
User
Beiträge: 10
Registriert: Sonntag 23. Juni 2013, 14:04

Hallo,

ich bekomme seit ich die views mit dem ModelView Konzept erstellt habe beim beenden des Programms diese Meldungen:

QObject::startTimer: QTimer can only be used with threads started with QThread

Ich kann mir nicht erklären, woher das kommt. Sobald ich die Modelzuweisung auskommentiere verschwinden diese Meldungen.
Sprich nach dem erstellen des Model's modelUser = listModel() funktioniert noch alles io. Aber sobald ich das Model einem
View zuweise, egal ob List oder Table erscheinen diese Meldungen...

Code: Alles auswählen

modelUser = listModel()
#self.listUser.setModel(modelUser)
Starten die QAbstract'XYZ'Model Threads?
QTimer habe ich nirgens verwendet...
Hab schon einiges über diese Meldung gelesen aber noch bin ich ohne Spur.

Was kann ich euch posten damit ihr einen Überblick habt?

Hier mal das listModel:

Code: Alles auswählen

class listModel(QtCore.QAbstractListModel):
	def __init__(self, parent = None):
		super(QtCore.QAbstractListModel, self).__init__(parent)
		self.__data = []

	def rowCount(self, parent):
		return len(self.__data)


	def data(self, index, role):
		if role == QtCore.Qt.DisplayRole:
			row = index.row()
			value = self.__data[row]
			return value
		
	def flags(self, index):
		return QtCore.Qt.ItemIsEnabled

	def insertRows(self, position, rows, parent, data):
		self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)

		for i in range(rows):
			self.__data.insert(position, data)

		self.endInsertRows()
		return True
lunar

@Haragius Startest Du denn Threads, oder verwendest Du eine Bibliothek, die das tut?
Benutzeravatar
Haragius
User
Beiträge: 10
Registriert: Sonntag 23. Juni 2013, 14:04

Ja, das tue ich.

Den hier:

Code: Alles auswählen

class receiverThread(QtCore.QThread):
	def __init__(self, sock, parent = None):
		super(QtCore.QThread, self).__init__(parent)
		self.sock = sock
		self.state = 1
	def run(self):
		while self.state == 1:
			response = str(self.sock.recv(1024), "UTF-8")
			if response:
				print(response)
				self.emit(QtCore.SIGNAL('addMsg(QString)'), response)
			else:
				return 

	def __del__(self):
		self.state = 0
		self.wait()
Hier sollte ich allerdings noch eine try Anweisung einbauen. Wo ich mir bei Threads nicht sicher bin, ist das beenden
eines Threads. Wo wir gerade dabei sind, ist es ok, wenn ich zB wie hier das Socketobjekt an den Thread weitergebe
oder ist das schlecht ala Zugriff auf Widgets direkt vom Thread aus?

Ev hilft das noch weiter. Der Slot zu Signal des Threads:

Code: Alles auswählen

def addMsg(self, msg):
		self.modelChat.insertRows(self.modelChat.rowCount(self), 1, QtCore.QModelIndex(), msg)
BlackJack

@Haragius: Da die Kommunikation über Signal/Slot abgewickelt wird, sollte das in Ordnung sein.

Was ein Problem ist, ist die `__del__()`-Methode. Die könnte am Programmende vom Hauptthread aufgerufen werden und wenn `wait()` intern einen `QTimer` verwendet, dann wird der aus einem nicht-`QThread`-Thread verwendet.

`__del__()` sollte man aber sowieso nicht, beziehungsweise nur in ganz bestimmten Fällen benutzen, denn das ist kein deterministischer Konstruktor und damit nahezu unbrauchbar. Die Sprache garantiert fast nichts, noch nicht einmal dass die Methode überhaupt aufgerufen wird. Alles was man darin verwenden will muss als Attribut auf dem Objekt existieren — sonst ist nicht garantiert, dass es zum Zeitpunkt des Aufrufs noch existiert. Und unter bestimmten Bedingungen macht das blosse Vorhandensein der Methode der Speicherbereinigung das Leben schwer, so dass Speicherlecks entstehen können.
Benutzeravatar
Haragius
User
Beiträge: 10
Registriert: Sonntag 23. Juni 2013, 14:04

Also verstehe ich es richtig, die __del__() Methode ist kein Destruktor und wird keinesfalls oder nicht zwingend beim terminieren des Subthreadobjekts aufgerufen?

Dan drängt sich aber die Frage auf wie Stoppe ich einen Subthread korrekt? <<-- werde mich da nochmals genauer einlesen...
BlackJack hat geschrieben:und wenn `wait()` intern einen `QTimer` verwendet
Ich habe die __del__() Methode einmal auskommentiert. Die Meldungen bleiben.
Der Thread wird erst beim klicken auf den 'Connect'-btn (client) bez. 'Start'-btn (server)
gestartet. Aber auch wen ich das Programm starte und direkt wieder beende kommt die Meldung.
Also dürfte der Thread nicht gestartet sein zu diesem Zeitpunkt.
BlackJack

@Haragius: `__del__()` wird aufgerufen wenn die Speicherbereinigung festgestellt hat, dass das Objekt nicht mehr referenziert wird und deshalb aus dem Speicher entfernt werden kann. Wobei das Vorhandensein von `__del__()` diese Feststellung unter bestimmten Umständen (Kreise in den Objektverweisen) schon verhindern kann. Und am Programmende muss diese Methode nicht zwingend aufgerufen werden.

Threads beenden geht im Grunde nur kooperativ. Du musst das im eigenen Code in irgendeiner Weise vorsehen, dass sich der Thread selbst beendet. Ein blockierender `recv()`-Aufruf ist da zum Beispiel ungünstig wenn man dem Thread asynchron sagen möchte, dass er sich beenden soll.

`__del__()` ist im Grunde nur interessant wenn man Ressourcen hat, die der Python-Laufzeitumgebung unbekannt sind, und die beim abräumen des Objekts ebenfalls freigegeben werden sollen. Mit der Einschränkung das man a) mit Kreisverweisen aufpassen muss und b) es nicht garantiert ist, dass die Methode am Programmende aufgerufen wird.

Beispiel für eine sinnvolle Anwendung sind Anbindungen an externe „shared libraries”/DLLs mittels `ctype`-Modul wo Python der „Besitzer” von Arbeitsspeicher wird, welcher wieder freigegeben werden soll wenn das Objekt zum Zeiger abgeräumt wird. Bei vielen anderen Ressourcen(arten) kann man eine explizite Methode zur Verfügung stellen und/oder das Objekt die Kontextmanager-API anbieten lassen, aber bei Arbeitspeicher will so etwas kein Anwender einer Bibliothek sehen. :-)
Antworten