Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » PyQt5 QThread периодическое соединение с БД в отдельном потоке [RSS Feed]

#1 Ноя. 18, 2019 11:33:52

cyberaxe77
Зарегистрирован: 2015-10-08
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

Здравствуйте форумчане.
Собственно решил побаловаться с потоками в 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 для соединения?
Заранее благодарю.

Офлайн

#2 Ноя. 18, 2019 14:11:19

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9796
Репутация: +  849  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

Здесь приводил пример использования QThread с отсылкой на бабу.



Отредактировано py.user.next (Ноя. 18, 2019 14:12:04)

Офлайн

#3 Ноя. 18, 2019 14:33:53

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

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 для соединения?
Ну самый простой способ это смотреть открыт ли соответсвующий порт, правда при этом вы не знаете какая именно программа слушает этот порт и работает ли она, а так , кроме как попытаться установить соединение пока никакого иного способа не придумали.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Офлайн

#4 Ноя. 18, 2019 17:07:04

cyberaxe77
Зарегистрирован: 2015-10-08
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt5 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.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_())

Офлайн

#5 Ноя. 18, 2019 17:09:51

cyberaxe77
Зарегистрирован: 2015-10-08
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

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

Офлайн

#6 Ноя. 19, 2019 09:53:48

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

cyberaxe77
Таки у небезизвестных граждан Прохорёнка и Дронова подсмотрен сей код был.
хорошо, давайте я поставлю вопрос по другому, зачем вам там Qt.QueuedConnection? чем оно отличается от дефолтного Qt.AutoConnection? Если вы чтото пишете вы должны отдавать себе отчет за что отвечает каждая комманда.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Офлайн

#7 Ноя. 19, 2019 20:18:33

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2730
Репутация: +  183  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

> Здесь приводил пример использования QThread с отсылкой на бабу.

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

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

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

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

Не читай его книгу, там на 90% тупой перевод документации.



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#8 Ноя. 20, 2019 07:49:55

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9796
Репутация: +  849  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

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



Отредактировано py.user.next (Ноя. 20, 2019 07:51:06)

Офлайн

#9 Ноя. 20, 2019 11:12:53

Rodegast
От: Пятигорск
Зарегистрирован: 2007-12-28
Сообщения: 2730
Репутация: +  183  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

> всё работает и метод run() не нужен.

Чем же тебе метод run() не нравится?



С дураками и сектантами не спорю, истину не ищу.
Ели кому-то правда не нравится, то заранее извиняюсь.

Офлайн

#10 Ноя. 20, 2019 13:07:23

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9796
Репутация: +  849  -
Профиль   Отправить e-mail  

PyQt5 QThread периодическое соединение с БД в отдельном потоке

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 был создан, а не в новом потоке. И из-за этого появляются зависания, так как действия другого потока происходят не в другом потоке, а в этом же потоке - в главном потоке.



Отредактировано py.user.next (Ноя. 20, 2019 13:10:17)

Офлайн

  • Начало
  • » GUI
  • » PyQt5 QThread периодическое соединение с БД в отдельном потоке[RSS Feed]

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version