【问题标题】:Program stops responding after clicking a button单击按钮后程序停止响应
【发布时间】:2019-10-17 21:46:02
【问题描述】:

我正在尝试制作我的第一个程序,一个显示远程服务器上所有开放端口的端口扫描程序,我已经让它在 CLI 中工作(感谢互联网)但决定制作一个 GUI( Qt5) 为它。我希望 textbox2 在输入 IP 地址并单击“扫描!”后输出所有打开的端口,显然程序在单击后不会崩溃。这是复制问题的相关代码

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QPushButton, QAction, QLineEdit, QMessageBox, QPlainTextEdit, QVBoxLayout, QLabel
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot, Qt
import socket
import time
import sys

class App(QMainWindow):

    def __init__(self):
        super().__init__()
        self.title = 'PPort'
        self.left = 10
        self.top = 10
        self.width = 800
        self.height = 400
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.label = QLabel('Enter Target Address:', self)
        self.label.move(50, -110)
        self.label.resize(300, 300)

        self.label2 = QLabel('Output:', self)
        self.label2.move(50, 80)
        self.label2.resize(300, 300)

        self.textbox = QLineEdit(self)
        self.textbox.move(50, 60)
        self.textbox.resize(540, 30)

        self.textbox2 = QPlainTextEdit(self)
        self.textbox2.move(50, 250)
        self.textbox2.resize(700, 100)
        self.textbox2.setReadOnly(True)

        self.button = QPushButton('Scan!', self)
        self.button.move(620, 60)
        self.button.clicked.connect(self.on_click)
        self.show()

    @pyqtSlot()

    def on_click(self):
        textboxValue = self.textbox.text()
        socket.gethostbyname(textboxValue)
        try:
            for port in range(1, 1025):
                socketprofile = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                result = socketprofile.connect_ex((textboxValue, port))
                if result == 0:
                    self.textbox2.appendPlainText('Port {} Is open'.format(port))
                socketprofile.close()
        except socket.gaierror:
            self.textbox2.appendPlainText('Hostname could not be resolved')
            time.sleep(5)
            sys.exit()
        except socket.error:
            self.textbox2.appendPlainText("Couldn't connect to server")
            time.sleep(5)
            sys.exit()


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

textbox2 中没有显示错误,令我感到奇怪的是,即使我将self.textbox2.appendPlainText 替换为 print(),它仍然不会在 vscode 终端中输出任何错误消息。但是,输入无效的 IP 地址会显示 gaierror(无法解析主机),而不是在 textbox2 中,而是在终端中,与输入有效 IP 地址(8.8.8.8、192.168.0.1)时它总是崩溃的情况相比.我怀疑我使用 if/for/try 错误地使其循环,但我真的看不出我做错了什么,因为我几乎不知道我作为一个新手在做什么。

【问题讨论】:

    标签: python sockets pyqt pyqt5


    【解决方案1】:

    for-loop 或 sleep 等耗时的任务会阻塞 GUI 事件循环,从而导致窗口冻结。在这些情况下,解决方案是在另一个线程中执行该任务并通过信号在线程之间发送信息

    import sys
    import time
    import socket
    from functools import partial
    
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QThread, QTimer
    from PyQt5.QtWidgets import (
        QApplication,
        QGridLayout,
        QLabel,
        QLineEdit,
        QMainWindow,
        QPlainTextEdit,
        QPushButton,
        QWidget,
    )
    
    
    class SocketWorker(QObject):
        messageChanged = pyqtSignal(str)
    
        @pyqtSlot(str)
        def start_task(self, ip):
            socket.gethostbyname(ip)
            try:
                for port in range(0, 65536):
                    socketprofile = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    result = socketprofile.connect_ex((ip, port))
                    if result == 0:
                        self.messageChanged.emit("Port {} Is open".format(port))
                    socketprofile.close()
            except socket.gaierror:
                self.messageChanged.emit("Hostname could not be resolved")
                time.sleep(5)
                sys.exit()
            except socket.error:
                self.messageChanged.emit("Couldn't connect to server")
                time.sleep(5)
                sys.exit()
    
    
    class App(QMainWindow):
        def __init__(self):
            super().__init__()
            self.title = "PPort"
            self.left = 10
            self.top = 10
            self.width = 800
            self.height = 400
            self.initUI()
    
        def initUI(self):
            self.setWindowTitle(self.title)
            self.setGeometry(self.left, self.top, self.width, self.height)
    
            self.textbox = QLineEdit()
            self.button = QPushButton("Scan!")
            self.textbox2 = QPlainTextEdit(readOnly=True)
    
            central_widget = QWidget()
            self.setCentralWidget(central_widget)
            grid_layout = QGridLayout(central_widget)
            grid_layout.addWidget(QLabel("Enter Target Address:"), 0, 0)
            grid_layout.addWidget(self.textbox, 1, 0)
            grid_layout.addWidget(self.button, 1, 1)
            grid_layout.addWidget(QLabel("Output:"), 2, 0)
            grid_layout.addWidget(self.textbox2, 3, 0, 1, 2)
    
            self.button.clicked.connect(self.on_click)
    
            thread = QThread(self)
            thread.start()
            self.socker_worker = SocketWorker()
            self.socker_worker.moveToThread(thread)
            self.socker_worker.messageChanged.connect(self.textbox2.appendPlainText)
    
        @pyqtSlot()
        def on_click(self):
            textboxValue = self.textbox.text()
            wrapper = partial(self.socker_worker.start_task, textboxValue)
            QTimer.singleShot(0, wrapper)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        ex = App()
        ex.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • 太棒了!我将研究所有的语法,如果不是因为它的效率极低,我的原始代码/脚本是否应该工作?我想知道我是否走在正确的轨道上以供将来参考。
    猜你喜欢
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    相关资源
    最近更新 更多