简体   繁体   中英

PyQT QThread follow the thread execution order (wait)

I'm trying to run a few slow processes but, I need to keep updated the QDialog to show the progress (maybe I put a progress bar too).

So I decide to use QThread, but on the first try, it doesn't work as I expected.

In my example code: 1- I'm using a simple ping to my default gateway 2- I'm pinging to my dns resolver

As you can see on imagem below, the information is showed according the thread is finalizing, but it is a mess to me.

Is possible to respect the threads order to show the informations?

Thanks.

例子

Follow my example code:

# -*- coding: utf8 -*-

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from ping3 import ping, verbose_ping
import socket
import dns.resolver

class ExternalTests(QThread):
    data_collected = pyqtSignal(object)
    
    def __init__(self, title, arg=None):
        QThread.__init__(self)
        self.title = title
        self.arg = arg

    def run(self):
        resp = ping(self.arg)
        self.data_collected.emit('%s: %s' % (self.title, resp))

class MainMenu(QMenu):
    def __init__(self, parent=None):
        QMenu.__init__(self)
        self.setStyleSheet("background-color: #3a80cd; color: rgb(255,255,255); selection-color: white; selection-background-color: #489de4;")
        # Diagnostics
        self.check_internet = QAction("Diagnosys")
        self.check_internet.setIcon(QIcon(QPixmap("..\\img\\lupa.png")))
        self.check_internet.triggered.connect(self.diagnosticNetwork)
        self.addAction(self.check_internet)
        self.addSeparator()

        # To quit the app
        self.quit = QAction("Quit")
        self.quit.triggered.connect(app.quit)
        self.addAction(self.quit)

    def diagnosticNetwork(self):
        self.check_internet_dialog = QDialog()
        self.check_internet_dialog.setWindowTitle("Check external connections")
        self.check_internet_dialog.setWindowModality(Qt.ApplicationModal)
        self.check_internet_dialog.setGeometry(150, 100, 700, 500)

        # text box
        self.textbox = QTextBrowser(self.check_internet_dialog)
        self.textbox.move(20, 20)
        self.textbox.resize(660,400)
        self.textbox.setFont(QFont("Courier New", 12))
        self.textbox.setStyleSheet("background-color: black;")

        #button copy
        btn_copy = QPushButton("Copy", self.check_internet_dialog)
        btn_copy.setIcon(QIcon(QPixmap("..\\img\\copy.png")))
        btn_copy.move(520,450)
        btn_copy.clicked.connect(self.dialogClickCopy)

        #button close
        btn_copy = QPushButton("Close", self.check_internet_dialog)
        btn_copy.setIcon(QIcon(QPixmap("..\\img\\close.png")))
        btn_copy.move(605,450)
        btn_copy.clicked.connect(self.dialogClickClose)
    
        # tests
        self.textbox.setTextColor(QColor("white"))
        self.textbox.append("Diagnosys")
        self.textbox.append("--------------------------------------------------")
        self.textbox.setTextColor(QColor("cyan"))
        
        self.threads = []
        #QCoreApplication.processEvents()
        
        ''' ping default gateway '''
        ping_default_gw = ExternalTests("default gatewat is reacheble", "192.168.0.1")
        ping_default_gw.data_collected.connect(self.onDataReady)
        self.threads.append(ping_default_gw)
        ping_default_gw.start()
        
        ''' ping dns resolver '''
        ping_dns_resolvers = dns.resolver.Resolver().nameservers
        for dns_resolver in ping_dns_resolvers:
            ping_dns_resolver = ExternalTests("dns resolver is reacheble %s" % dns_resolver, dns_resolver)
            ping_dns_resolver.data_collected.connect(self.onDataReady)
            self.threads.append(ping_dns_resolver)
            ping_dns_resolver.start()

        self.check_internet_dialog.exec_()

    
    def onDataReady(self, data):
        print(data)
        if data:
            self.textbox.append(data)
        else:
            self.textbox.append("error")


    def dialogClickCopy(self):
        pass

    def dialogClickClose(self):
        self.check_internet_dialog.close()

class SystemTrayIcon(QSystemTrayIcon):
    def __init__(self, menu, parent=None):
        QSystemTrayIcon.__init__(self)
        self.setIcon(QIcon("..\\img\\icon.png"))
        self.setVisible(True)
        self.setContextMenu(menu)

if __name__ == "__main__":
    import sys
    app = QApplication([])
    app.setQuitOnLastWindowClosed(False)
    app.setApplicationName('pkimonitor')      
    app.setApplicationVersion('0.1')
    app.setWindowIcon(QIcon("..\\img\\icon.png"))
    menu = MainMenu()
    widget = QWidget()
    trayIcon = SystemTrayIcon(menu, widget)
    trayIcon.show()
    sys.exit(app.exec_())

I tried to create a scheme to organize by "run position" and it works. Follow my code.

In 'diagnosticNetwork':

self.data_list = []
self.data_hold = []

In 'onDataReady':

if len(self.data_list) > 0:
    if self.data_list[-1][0] + 1 == data[0]:
        self.data_list.append(data)
        if data[2]:
            self.textbox.append("%s: %s" % (data[1], 'OK'))
        else:
            self.textbox.append("%s: %s" % (data[1], 'NOK'))
    elif self.data_list[-1][0] < data[0]:
        self.data_hold.append(data)
else:
    self.data_list.append(data)
    if data[2]:
        self.textbox.append("%s: %s" % (data[1], 'OK'))
    else:
        self.textbox.append("%s: %s" % (data[1], 'NOK'))

if len(self.data_hold) > 0:
    hold_sorted = self.data_hold[:]
    hold_sorted.sort()
    for line_hold in hold_sorted:
        if self.data_list[-1][0] + 1 == line_hold[0]:
            if line_hold[2]:
                self.textbox.append("%s: %s" % (line_hold[1], 'OK'))
            else:
                self.textbox.append("%s: %s" % (line_hold[1], 'NOK'))
            self.data_list.append(line_hold)
            del self.data_hold[0]

I worked with two lists, data_list and data_hold. The results that I received from the threads, I filled out the main list according to the order that I pre-established, if the result that entered was not the next one in the sequence, it goes to data_hold, then scan this list to fill the rest of my textbox.

Thank you for all help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM