Как можно дать информацию только один раз с QMessageBox, когда inte rnet соединение выключено? - PullRequest
0 голосов
/ 11 марта 2020

этот код открывает веб-сайт с браузером Qt. Если соединение inte rnet потеряно, я хочу дать информационное сообщение с QMessageBox:

import sys
from PyQt5.QtCore import *
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QLabel
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import QGuiApplication as App
from PyQt5.QtGui import QPixmap, QWindow
from PyQt5 import QtNetwork, QtCore, QtWidgets
import urllib
from urllib.request import urlopen
import threading
import time

class WebApp(QMainWindow):
    def __init__(self):
        self.is_connected = None
        self.is_msgshow = True
        self.msg = QtWidgets.QMessageBox()
        super().__init__()
        self.title = "OZI"
        self.t_internet = threading.Thread(target=self.is_internet)
        self.t_internet.start()
        self.t_refreshpage = threading.Thread(target=self.refresh_page)
        self.t_refreshpage.start()
        self.web = QWebEngineView()
        self.web.window().setWindowTitle(self.title)
        self.web.load(QUrl("http://www.google.com"))
        self.web.showFullScreen()

    def is_internet(self):
        """
        Query internet using python
        :return:
        """
        while True:
            time.sleep(5)
            try:
                urlopen("http://www.google.com", timeout=1)
                self.is_connected = True
            except urllib.error.URLError as Error:
                print(Error)
                self.is_connected = False
            print(self.is_connected)

    def refresh_page(self):
        while True:
            time.sleep(.1)
            if self.is_connected == False:
                time.sleep(5)
                if self.is_connected == True:
                    self.web.page().action(QWebEnginePage.Reload).trigger()
                else:
                    if self.is_msgshow == True:
                        print('testtt')
                        self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
                        self.is_msgshow = False
                        self.msg.close()
            else:
                pass

if __name__ == '__main__':
   app = QApplication(sys.argv)
   ex = WebApp()
   sys.exit(app.exec_())

Однако, я не могу преуспеть в

                 if self.is_msgshow == True:
                    print('testtt')

этой части, пока inte rnet соединение потеряно, мой код открывает много MessageBox. Я полагаю, что моя вина заключается в контроле флага self.is_msgshow.

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Вне проблемы, на которую вы указываете, у вас есть другие ошибки, и они более опасны, чем указанная: вы не должны обращаться к GUI из другого потока, и вы также не должны обращаться к той же переменной из 2 потоков без защиты через мьютекс, семафор или подобный. Например, вы вызываете self.web.page().action(QWebEnginePage.Reload).trigger() и self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!') из вторичного потока, а переменная is_connected доступна нескольким потокам.

Другая ошибка заключается в том, что метод QMessage::information() is stati c и генерирует новый объект, который не доступен напрямую, вместо этого вы должны использовать объект «self.msg» через его собственные методы для отображения необходимой информации.

Учитывая вышеизложенное, я создал класс, который отвечает только за анализирует состояние соединения и, если оно меняется, испускает сигнал. Для передачи информации между потоками в Qt вы можете использовать сигналы или QMetaObject::invokedMethod() в дополнение к мьютексам и семафорам * * * * * * * * * * * * * * * * * * * для того, чтобы анализировать значение статуса, отправленного на * 1019. * для реализации логики c:

import sys
import threading
import time

import urllib
from urllib.request import urlopen

from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets


class ConnectivityManager(QtCore.QObject):
    statusChanged = QtCore.pyqtSignal(bool)

    def __init__(self, *, timeout=4000, parent=None):
        super().__init__(parent)
        self._status = False

        self._timeout = timeout

    def start(self):
        threading.Thread(target=self._check, daemon=True).start()

    @QtCore.pyqtProperty(bool, notify=statusChanged)
    def status(self):
        return self._status

    @QtCore.pyqtSlot(bool)
    def _update_status(self, status):
        if self._status != status:
            self._status = status
            self.statusChanged.emit(self.status)

    def _check(self):
        while True:
            try:
                urlopen("http://www.google.com", timeout=1)
                status = True
            except urllib.error.URLError as Error:
                status = False
            QtCore.QMetaObject.invokeMethod(
                self,
                "_update_status",
                QtCore.Qt.QueuedConnection,
                QtCore.Q_ARG(bool, status),
            )
            time.sleep(5)


class WebApp(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("OZI")
        self.web = QtWebEngineWidgets.QWebEngineView()
        self.setCentralWidget(self.web)
        self.web.load(QtCore.QUrl("http://www.google.com"))

        self.connectivity_manager = ConnectivityManager()
        self.connectivity_manager.statusChanged.connect(self.on_status_changed)
        self.connectivity_manager.start()

        self.msg = QtWidgets.QMessageBox()
        self.msg.setWindowTitle("INFO")
        self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")

    @QtCore.pyqtSlot(bool)
    def on_status_changed(self, status):
        if status:
            self.msg.hide()
            self.statusBar().showMessage("Connected")
            self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
        else:
            self.statusBar().showMessage("Disconnected")
            self.msg.show()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ex = WebApp()
    ex.showFullScreen()
    sys.exit(app.exec_())

С другой стороны, те же логики c могут быть реализованы с использованием QtNetwork без необходимости в потоках:

import sys

from PyQt5 import QtCore, QtWidgets, QtNetwork, QtWebEngineWidgets


class ConnectivityManager(QtCore.QObject):
    statusChanged = QtCore.pyqtSignal(bool)

    def __init__(self, *, timeout=4000, parent=None):
        super().__init__(parent)
        self._status = False

        self._timeout = timeout
        self.manager = QtNetwork.QNetworkAccessManager()
        self._timer = QtCore.QTimer(
            singleShot=True, interval=self._timeout, timeout=self.verify_status
        )

    def start(self):
        QtCore.QTimer.singleShot(0, self._check)

    @QtCore.pyqtProperty(bool, notify=statusChanged)
    def status(self):
        return self._status

    @QtCore.pyqtSlot(bool)
    def _update_status(self, status):
        if self._status != status:
            self._status = status
            self.statusChanged.emit(self.status)

    def _check(self):
        url = QtCore.QUrl("https://www.google.com/")
        req = QtNetwork.QNetworkRequest(url)
        self._reply = self.manager.get(req)
        self._reply.finished.connect(self.verify_status)
        self._timer.start()

    @QtCore.pyqtSlot()
    def verify_status(self):
        if self._timer.isActive():
            self._timer.stop()
            if self._reply.error() == QtNetwork.QNetworkReply.NoError:
                v = self._reply.attribute(
                    QtNetwork.QNetworkRequest.HttpStatusCodeAttribute
                )
                if 200 <= v < 300:
                    self._update_status(True)
                else:
                    print("error", "code error: {}".format(v))
                    self._update_status(False)
            else:
                print("error", self._reply.errorString())
                self._update_status(False)
        else:
            self._reply.finished.disconnect(self.verify_status)
            self._reply.abort()
            print("Timeout")
            self._update_status(False)
        QtCore.QTimer.singleShot(5000, self._check)
        self._reply.deleteLater()


class WebApp(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("OZI")
        self.web = QtWebEngineWidgets.QWebEngineView()
        self.setCentralWidget(self.web)
        self.web.load(QtCore.QUrl("http://www.google.com"))

        self.connectivity_manager = ConnectivityManager()
        self.connectivity_manager.statusChanged.connect(self.on_status_changed)
        self.connectivity_manager.start()

        self.msg = QtWidgets.QMessageBox()
        self.msg.setWindowTitle("INFO")
        self.msg.setText("PLEASE CHECK YOUR INTERNET CONNECTION!!!")

    @QtCore.pyqtSlot(bool)
    def on_status_changed(self, status):
        if status:
            self.msg.hide()
            self.statusBar().showMessage("Connected")
            self.web.page().action(QtWebEngineWidgets.QWebEnginePage.Reload).trigger()
        else:
            self.statusBar().showMessage("Disconnected")
            self.msg.show()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ex = WebApp()
    ex.showFullScreen()
    sys.exit(app.exec_())
0 голосов
/ 11 марта 2020

Я вообще не знаю pyqt5, но догадываюсь по потоку вашего кода, здесь:

if self.is_msgshow == True:
    print('testtt')
    self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
    self.is_msgshow = False
    self.msg.close()

Кажется, что вызов self.msg.information() будет синхронным . Если это так, то пока он остается открытым, is_msgshow по-прежнему True, потому что вы меняете его после закрытия диалога. И это ваша ошибка, как будто новое происходит даже тогда, ничто не блокирует показ нового диалога. Исправление довольно простое - просто переместите self.is_msgshow = False, чтобы быть первым, что сделано в этом блоке кода, вам должно быть хорошо:

if self.is_msgshow:
    self.is_msgshow = False
    print('testtt')
    self.msg.information(None, 'INFO', 'PLEASE CHECK YOUR INTERNET CONNECTION!!!')
    self.msg.close()

Дополнительные примечания, вы можете сбросить его обратно на True как только подключение восстановится, иначе вы не увидите ничего в следующий раз, когда сеть не работает.

Класс QMessageBox предоставляет модальный диалог для информирования пользователя или для запроса у пользователя вопрос и получение ответа.

https://doc.qt.io/qtforpython/PySide2/QtWidgets/QMessageBox.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...