Программа перестает отвечать на запросы после нажатия кнопки - PullRequest
3 голосов
/ 18 октября 2019

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

1 Ответ

1 голос
/ 18 октября 2019

Трудоемкие задачи, такие как цикл for или 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_())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...