单击按钮后程序停止响应 [英] Program stops responding after clicking a button

查看:50
本文介绍了单击按钮后程序停止响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to make my first ever program, a port scanner that shows all the open ports on a remote server, I've gotten it to work in CLI (thanks to the internet) but decided to make a GUI (Qt5) for it. I want textbox2 to output all the open ports after entering an IP address and clicking "Scan!", and obviously for the program to not crash after clicking it. Here is the relevant code to replicate the issue

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 使其循环,但我真的看不出我做错了什么,因为我几乎不知道我在做什么作为新手.

No error is shown in textbox2, what is strange to me is that even when I replace self.textbox2.appendPlainText with print(), it still doesn't output any error messages in the vscode terminal. However, entering an invalid IP address shows the gaierror(couldn't resolve host), and not in textbox2, but in the terminal, compared to how it always crashes if you enter a valid IP address(8.8.8.8, 192.168.0.1). I suspect I'm using if/for/try wrongly making it loop, but I can't really see what I'm doing wrong as I barely know what I'm doing as a newbie.

推荐答案

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

Time-consuming tasks such as for-loop or sleep block the GUI event-loop causing the window to freeze. In these cases the solution is to execute that task in another thread and send the information between the threads through signals

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

这篇关于单击按钮后程序停止响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆