Найти - Пользователи
Полная версия: PyQt5 QThread периодическое соединение с БД в отдельном потоке
Начало » GUI » PyQt5 QThread периодическое соединение с БД в отдельном потоке
1 2
cyberaxe77
Здравствуйте форумчане.
Собственно решил побаловаться с потоками в PyQt5. Придумал небольшую учебную задачку для себя по данной теме.
Форма авторизации для MySql. Всё стандартно: user - password в QLineEdit, кнопки “connect”, “disconnect” и QLabel сообщающая о статусе соединения. Форма должна проверять возможность соединения с сервером 1 раз в 3 секунды. Для этого я в метод run() класса QThread поместил соединение с БД.
 #!/usr/bin/python3
# -*- coding:utf-8 -*-
 
import sys
import UI_MainForm
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtSql import *
 
 
class MyThread(QThread):
    mysignal = pyqtSignal(str)
 
    def __init__(self, _user=None, _passwd=None, parent=None):
        self.user = _user
        self.passwd = _passwd
        QThread.__init__(self, parent)
 
    def run(self):
        while True:
            self.sleep(3)
            db = QSqlDatabase.addDatabase('QMYSQL')
            db.setHostName('localhost')
            db.setDatabaseName('testbase')
            db.setUserName(self.user)
            db.setPassword(self.passwd)
            ok = db.open()
            if ok:
                self.mysignal.emit('ok')
                print('Debug: Connection: Ok')
                db.close()
            else:
                self.mysignal.emit('failed')
                print('Debug: Connection: Failed ' + db.lastError().text())
 
 
class MainForm(QMainWindow, UI_MainForm.Ui_MainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self)
 
        self.mythread = MyThread()
 
        self.pushButton_quit.clicked.connect(qApp.quit)
        self.pushButton_connect.clicked.connect(self.connect_db)
        self.pushButton_disconnect.clicked.connect(self.disconnect_db)
        self.pushButton_connect.clicked.connect(self.start_thread)
        self.mythread.mysignal.connect(self.change_label_conn_status, Qt.QueuedConnection)
 
    def connect_db(self):
        db = QSqlDatabase.addDatabase('QMYSQL')
        db.setHostName('localhost')
        db.setDatabaseName('testbase')
        db.setUserName(self.lineEdit_user.text())
        db.setPassword(self.lineEdit_passwd.text())
        ok = db.open()
        if ok:
            self.label_connection_status.setText('Connection status: Ok')
            QMessageBox.information(self, 'Information', 'Successful database connection')
        else:
            self.label_connection_status.setText('Connection status: Failed')
            QMessageBox.critical(self, 'ERROR!', 'No database connection!')
 
    def disconnect_db(self):
        self.pushButton_connect.setEnabled(True)
        self.lineEdit_user.setEnabled(True)
        self.lineEdit_passwd.setEnabled(True)
        self.lineEdit_user.clear()
        self.lineEdit_passwd.clear()
        self.lineEdit_user.setFocus()
 
    def change_label_conn_status(self, s):
        self.label_connection_status.setText('Connection status: ' + s)    #текст не меняется
 
    def start_thread(self):
        self.pushButton_connect.setEnabled(False)
        self.lineEdit_user.setEnabled(False)
        self.lineEdit_passwd.setEnabled(False)
        self.mythread = MyThread(self.lineEdit_user.text(), self.lineEdit_passwd.text())
        self.mythread.start()
 
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainForm()
    win.show()
    sys.exit(app.exec_())
Собственно проблемы:
1. При дауне MySql, label_connection_status не изменяется на “failed”, хотя в консоль метод run() гадит print('Debug: Connection: Failed ' + db.lastError().text()), как и было задумано. Почему?
2. Два потока (в конструкторе и в методе start_thread) - бред. Как избавиться от этого бреда? Как сделать правильно?
3. Может быть можно как-то иначе реализовать проверку доступности MySql для соединения?
Заранее благодарю.
py.user.next
Здесь приводил пример использования QThread с отсылкой на бабу.
PEHDOM
cyberaxe77
1. При дауне MySql, label_connection_status не изменяется на “failed”, хотя в консоль метод run() гадит print('Debug: Connection: Failed ' + db.lastError().text()), как и было задумано. Почему?
эм-м-м- а что это у вас
 self.mythread.mysignal.connect(self.change_label_conn_status, Qt.QueuedConnection)
что у вас передается вторым аргументом? откуда вы взяли вообще такой синтаксис?
cyberaxe77
2. Два потока (в конструкторе и в методе start_thread) - бред. Как избавиться от этого бреда? Как сделать правильно?
очевидно вынести передачу логина-пароля из конструктора като так:
 class MyThread(QThread):
    mysignal = pyqtSignal(str)
    def setAuth(self, user=None, passwd=None):
        self._user = user
        self._passwd = passwd
    def run(self):
        ....
        db.setUserName(self._user)
        db.setPassword(self._passwd)
        ...
в принципе , наверно это можно и в run() сделать
ЗЫ Есть мнение что переопрелять метод run некошерно и не соответвует парадигме ООП а нужно делать как показал py.user.next в посте выше.
Учитывая что “ решил побаловаться с потоками в PyQt5. Придумал небольшую учебную задачку” то вам должно быть небезинтересно.


cyberaxe77
3. Может быть можно как-то иначе реализовать проверку доступности MySql для соединения?
Ну самый простой способ это смотреть открыт ли соответсвующий порт, правда при этом вы не знаете какая именно программа слушает этот порт и работает ли она, а так , кроме как попытаться установить соединение пока никакого иного способа не придумали.
cyberaxe77
Спасибо всем помогающим мне.
Вот “пофиксил”…
Как тебе такое, Илон Маск вам такой “фикс”, уважаемые знатоки?
 #!/usr/bin/python3
# -*- coding:utf-8 -*-
import sys
import UI_MainForm
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtSql import *
class MyThread(QThread):
    mysignal = pyqtSignal(str)
    def __init__(self, _user=None, _passwd=None, parent=None):
        self.user = _user
        self.passwd = _passwd
        QThread.__init__(self, parent)
    def run(self):
        while True:
            self.sleep(3)
            db = QSqlDatabase.addDatabase('QMYSQL')
            db.setHostName('localhost')
            db.setDatabaseName('testbase')
            db.setUserName(self.user)
            db.setPassword(self.passwd)
            ok = db.open()
            if ok:
                self.mysignal.emit('Ok')
                print('Debug: Connection: Ok')
                db.close()
            else:
                self.mysignal.emit('Failed')
                print('Debug: Connection: Failed ' + db.lastError().text())
class MainForm(QMainWindow, UI_MainForm.Ui_MainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setupUi(self)
        self.pushButton_quit.clicked.connect(qApp.quit)
        self.pushButton_connect.clicked.connect(self.connect_db)
        self.pushButton_disconnect.clicked.connect(self.disconnect_db)
        self.pushButton_connect.clicked.connect(self.start_thread)
    def connect_db(self):
        db = QSqlDatabase.addDatabase('QMYSQL')
        db.setHostName('localhost')
        db.setDatabaseName('testbase')
        db.setUserName(self.lineEdit_user.text())
        db.setPassword(self.lineEdit_passwd.text())
        ok = db.open()
        if ok:
            self.label_connection_status.setText('Connection status: Ok')
            QMessageBox.information(self, 'Information', 'Successful database connection')
        else:
            self.label_connection_status.setText('Connection status: Failed')
            QMessageBox.critical(self, 'ERROR!', 'No database connection!')
    def disconnect_db(self):
        self.pushButton_connect.setEnabled(True)
        self.lineEdit_user.setEnabled(True)
        self.lineEdit_passwd.setEnabled(True)
        self.lineEdit_user.clear()
        self.lineEdit_passwd.clear()
        self.lineEdit_user.setFocus()
    def change_label_conn_status(self, s):
        self.label_connection_status.setText('Connection status: ' + s)
    def start_thread(self):
        self.pushButton_connect.setEnabled(False)
        self.lineEdit_user.setEnabled(False)
        self.lineEdit_passwd.setEnabled(False)
        self.mythread = MyThread(self.lineEdit_user.text(), self.lineEdit_passwd.text())
        self.mythread.mysignal.connect(self.change_label_conn_status, Qt.QueuedConnection)
        self.mythread.start()
if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainForm()
    win.show()
    sys.exit(app.exec_())
cyberaxe77
PEHDOM
что у вас передается вторым аргументом? откуда вы взяли вообще такой синтаксис?
Таки у небезизвестных граждан Прохорёнка и Дронова подсмотрен сей код был.
PEHDOM
cyberaxe77
Таки у небезизвестных граждан Прохорёнка и Дронова подсмотрен сей код был.
хорошо, давайте я поставлю вопрос по другому, зачем вам там Qt.QueuedConnection? чем оно отличается от дефолтного Qt.AutoConnection? Если вы чтото пишете вы должны отдавать себе отчет за что отвечает каждая комманда.
Rodegast
> Здесь приводил пример использования QThread с отсылкой на бабу.

Да хватит уже повторять бред этой бабы!

> что у вас передается вторым аргументом? откуда вы взяли вообще такой синтаксис?

Это норм. он флаги для соединения передаёт, обычно они не нужны но иногда бывают полезными.

> Таки у небезизвестных граждан Прохорёнка и Дронова подсмотрен сей код был.

Не читай его книгу, там на 90% тупой перевод документации.
py.user.next
Rodegast
Да хватит уже повторять бред этой бабы!
Хорошая девочка - провела расследование. Хоть какую-то пользу принесла мировому сообществу. Прекрасный подход, где всё работает и метод run() не нужен.
Rodegast
> всё работает и метод run() не нужен.

Чем же тебе метод run() не нравится?
py.user.next
Rodegast
Чем же тебе метод run() не нравится?
https://doc.qt.io/qt-5/qthread.html#details
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots and invoked methods will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.

Если почитаешь комменты у той бабы, там один парень обнаружил, что эти действия, описанные в производном классе, выполняются в том же потоке, где объект производного класса QThread был создан, а не в новом потоке. И из-за этого появляются зависания, так как действия другого потока происходят не в другом потоке, а в этом же потоке - в главном потоке.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB