You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt Designer无法显示网络扫描结果的问题求助

网络检测应用扫描结果不显示问题排查与修复

问题描述

我正在用Python3在PyCharm中开发一款网络检测应用,基于nmap实现扫描功能。目前遇到的问题是:点击扫描按钮后扫描能正常完成,但Qt Designer设计的界面上没有任何扫描结果展示。

代码文件

主页面 main.py

class Home(QtWidgets.QMainWindow):
    def __init__(self):
        super(Home, self).__init__()

        self.stacked_widget = QtWidgets.QStackedWidget()
        self.setCentralWidget(self.stacked_widget)

        self.pages = {}

        self.home_page(Dashboard(self), 'dashboard')
        self.home_page(Stats(), 'stats')
        self.home_page(Details(), 'details')
        self.home_page(Data(), 'data')
        self.home_page(Threats(), 'threats')
        self.goto('dashboard')

  
    def home_page(self, page, name):
        self.pages[name] = page
        self.stacked_widget.addWidget(page)

        if isinstance(page, PageWindow):
            page.gotoSignal.connect(self.goto)

    @QtCore.pyqtSlot(str)
    def goto(self, name):
        if name in self.pages:
            self.stacked_widget.setCurrentWidget(self.pages[name])


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    home = Home()
    dashboard = Dashboard(home)
    dashboard.show()
    dashboard.showMaximized()
    sys.exit(app.exec_())

扫描线程页面 scan_thread.py

class ScanThread(QtCore.QThread):
    scan_complete = QtCore.pyqtSignal(list)

    def __init__(self, parent=None):
        super(ScanThread, self).__init__(parent)
        self.parent = parent

    def __def__(self):
        self.wait()

    def run(self):
        nm = nmap.PortScanner()
        nm.scan(hosts='192.168.1.0/24', arguments='-p- --open -sV')
        self.results = []

        for host in nm.all_hosts():
            for proto in nm[host].all_protocols():
                lport = nm[host][proto].keys()
                sorted(lport)
                for port in lport:
                    result = dict()
                    result['Host'] = host
                    result['Port'] = port
                    result['Protocol'] = proto
                    result['State'] = nm[host][proto][port]['state']
                    result['Address'] = ', '.join(nm[host][proto][port].get('addresses', {}).values())
                    result['OP System'] = nm[host][proto][port].get('osmatch', [{'name': ''}])[0]['name']
                    result['Product'] = nm[host][proto][port].get('product', '')
                    self.results.append(result)

        self.scan_complete.emit(self.results)

仪表盘页面 dashboard_page.py

class Dashboard(PageWindow):
    def __init__(self, home):
        super(Dashboard, self).__init__()
        self.resultsTable = None
        self.scanThread = ScanThread(self)
        uic.loadUi('ui/dashboard.ui', self)
        self.home = home
        self.display_results([])

        self.statsMenu.mousePressEvent = lambda event: self.goto_page('stats')
        self.detailsMenu.mousePressEvent = lambda event: self.goto_page('details')
        self.dataMenu.mousePressEvent = lambda event: self.goto_page('data')
        self.threatsMenu.mousePressEvent = lambda event: self.goto_page('threats')
        self.scanBtn.clicked.connect(self.scan_host)

    def goto_page(self, event):
        self.goto(event)

    def scan_host(self):
        self.scanBtn.setEnabled(False)
        self.scanBtn.setText('Scanning...')
        self.scanThread = ScanThread(self)
        self.scanThread.scan_complete.connect(self.display_results)
        self.scanThread.scan_complete.connect(self.update_scan_results)
        self.scanThread.scan_complete.connect(self.scan_finished)
        self.scanThread.start()

    def display_results(self, results):
        if not self.resultsTable:
            self.resultsTable = QTableWidget(self)
            self.layout().addWidget(self.resultsTable)
            self.resultsTable.setColumnCount(8)

        self.resultsTable.setRowCount(len(results))

        for i, result in enumerate(results):
            self.resultsTable.setItem(i, 0, QtWidgets.QTableWidgetItem(result['Host']))
            self.resultsTable.setItem(i, 1, QtWidgets.QTableWidgetItem(result['State']))
            self.resultsTable.setItem(i, 5, QtWidgets.QTableWidgetItem(' '))
            self.resultsTable.setItem(i, 6, QtWidgets.QTableWidgetItem(result['OP System']))

            open_ports = 0
            closed_ports = 0
            scanned_ports = 0

            for port in result.get('ports', []):
                if port.get('state', '') == 'open':
                    open_ports += 1
                elif port.get('state', '') == 'closed':
                    closed_ports += 1

            scanned_ports = len(result.get('ports', []))

            self.resultsTable.setItem(i, 2, QtWidgets.QTableWidgetItem(str(open_ports)))
            self.resultsTable.setItem(i, 3, QtWidgets.QTableWidgetItem(str(closed_ports)))
            self.resultsTable.setItem(i, 4, QtWidgets.QTableWidgetItem(str(scanned_ports)))

        self.scanBtn.setEnabled(True)
        self.scanBtn.setText('Scan')

    def update_scan_results(self, results):
        self.display_results(results)

        # Display the new scan results
        for i, result in enumerate(results):
            self.resultsTable.setItem(i, 0, QtWidgets.QTableWidgetItem(result['Host']))
            self.resultsTable.setItem(i, 1, QtWidgets.QTableWidgetItem(result['State']))
            self.resultsTable.setItem(i, 5, QtWidgets.QTableWidgetItem(' '))
            self.resultsTable.setItem(i, 6, QtWidgets.QTableWidgetItem(result['OP System']))

            open_ports = 0
            closed_ports = 0
            scanned_ports = 0

            for port in result.get('ports', []):
                if port.get('state', '') == 'open':
                    open_ports += 1
                elif port.get('state', '') == 'closed':
                    closed_ports += 1

            scanned_ports = len(result.get('ports', []))

            self.resultsTable.setItem(i, 2, QtWidgets.QTableWidgetItem(str(open_ports)))
            self.resultsTable.setItem(i, 3, QtWidgets.QTableWidgetItem(str(closed_ports)))
            self.resultsTable.setItem(i, 4, QtWidgets.QTableWidgetItem(str(scanned_ports)))

    def scan_finished(self):
        self.scanThread.scan_complete.disconnect(self.display_results)
        self.scanThread.scan_complete.disconnect(self.update_scan_results)
        self.scanThread.scan_complete.disconnect(self.scan_finished)
        self.scanThread.deleteLater()

    def clear_scan_results(self):
        self.resultsTable.clearContents()
        self.resultsTable.setRowCount(0)

问题排查与修复方案

核心问题点

  1. 数据结构不匹配:扫描线程返回的是每个端口一条记录,但仪表盘代码试图从每个结果中读取不存在的ports字段,导致端口统计全为0,表格数据异常。
  2. 主入口窗口错误:单独创建并显示Dashboard实例,而非Home中已关联栈窗口的实例,导致界面上下文混乱。
  3. 重复UI更新逻辑:display_results和update_scan_results代码完全重复,同时触发会导致冗余更新。
  4. 表格初始化问题:未设置列标题,布局可能存在问题导致表格不显示;另外osmatch字段获取层级错误,取不到操作系统信息。

修复后的代码

1. 主页面 main.py(修正入口)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    home = Home()
    home.show()
    home.showMaximized()
    sys.exit(app.exec_())

2. 扫描线程页面 scan_thread.py(修正数据结构与字段获取)

class ScanThread(QtCore.QThread):
    scan_complete = QtCore.pyqtSignal(list)

    def __init__(self, parent=None):
        super(ScanThread, self).__init__(parent)
        self.parent = parent

    def __del__(self):
        self.wait()

    def run(self):
        nm = nmap.PortScanner()
        nm.scan(hosts='192.168.1.0/24', arguments='-p- --open -sV')
        self.results = []

        # 按主机分组整理结果,避免同一主机多条重复条目
        for host in nm.all_hosts():
            host_info = {
                'Host': host,
                'State': nm[host].state(),
                'OP System': nm[host].get('osmatch', [{'name': ''}])[0]['name'],
                'ports': []
            }
            for proto in nm[host].all_protocols():
                lport = sorted(nm[host][proto].keys())  # 正确排序端口
                for port in lport:
                    port_info = {
                        'Port': port,
                        'Protocol': proto,
                        'State': nm[host][proto][port]['state'],
                        'Product': nm[host][proto][port].get('product', '')
                    }
                    host_info['ports'].append(port_info)
            self.results.append(host_info)

        self.scan_complete.emit(self.results)

3. 仪表盘页面 dashboard_page.py(简化逻辑,优化表格显示)

class Dashboard(PageWindow):
    def __init__(self, home):
        super(Dashboard, self).__init__()
        self.resultsTable = None
        uic.loadUi('ui/dashboard.ui', self)
        self.home = home
        
        # 初始化表格(优先使用UI文件中的控件,没有则创建)
        self.resultsTable = self.findChild(QtWidgets.QTableWidget, "resultsTable")
        if not self.resultsTable:
            self.resultsTable = QtWidgets.QTableWidget(self)
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.resultsTable)
            self.resultsTable.setColumnCount(7)
            # 设置列标题
            self.resultsTable.setHorizontalHeaderLabels([
                '主机', '状态', '开放端口', '关闭端口', '扫描总数', '预留', '操作系统'
            ])
            # 自动调整列宽
            self.resultsTable.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
        
        self.display_results([])

        # 菜单跳转逻辑简化
        self.statsMenu.mousePressEvent = lambda event: self.goto('stats')
        self.detailsMenu.mousePressEvent = lambda event: self.goto('details')
        self.dataMenu.mousePressEvent = lambda event: self.goto('data')
        self.threatsMenu.mousePressEvent = lambda event: self.goto('threats')
        self.scanBtn.clicked.connect(self.scan_host)

    def scan_host(self):
        self.scanBtn.setEnabled(False)
        self.scanBtn.setText('扫描中...')
        self.scanThread = ScanThread(self)
        self.scanThread.scan_complete.connect(self.display_results)
        self.scanThread.scan_complete.connect(self.scan_finished)
        self.scanThread.start()

    def display_results(self, results):
        self.resultsTable.setRowCount(len(results))

        for i, result in enumerate(results):
            # 填充基础主机信息
            self.resultsTable.setItem(i, 0, QtWidgets.QTableWidgetItem(result['Host']))
            self.resultsTable.setItem(i, 1, QtWidgets.QTableWidgetItem(result['State']))
            self.resultsTable.setItem(i, 5, QtWidgets.QTableWidgetItem(' '))
            self.resultsTable.setItem(i, 6, QtWidgets.QTableWidgetItem(result['OP System']))

            # 统计端口状态
            open_ports = 0
            closed_ports = 0
            scanned_ports = len(result.get('ports', []))
            
            for port in result['ports']:
                if port['State'] == 'open':
                    open_ports += 1
                elif port['State'] == 'closed':
                    closed_ports += 1

            # 填充统计数据
            self.resultsTable.setItem(i, 2, QtWidgets.QTableWidgetItem(str(open_ports)))
            self.resultsTable.setItem(i, 3, QtWidgets.QTableWidgetItem(str(closed_ports)))
            self.resultsTable.setItem(i, 4, QtWidgets.QTableWidgetItem(str(scanned_ports)))

        self.scanBtn.setEnabled(True)
        self.scanBtn.setText('开始扫描')

    def scan_finished(self):
        self.scanThread.deleteLater()

    def clear_scan_results(self):
        self.resultsTable.clearContents()
        self.resultsTable.setRowCount(0)

修复说明

  • 修正主入口逻辑,确保显示的是Home中管理的Dashboard实例,避免窗口上下文混乱。
  • 重新组织扫描结果的数据结构,按主机分组,添加ports字段,让仪表盘代码能正确统计端口。
  • 移除重复的update_scan_results方法,简化UI更新逻辑。
  • 为表格添加列标题并优化布局,确保控件可见;修正osmatch字段的获取层级,正确读取操作系统信息。
  • 修复扫描线程中排序端口的代码错误,以及析构函数的拼写错误。

内容的提问来源于stack exchange,提问作者Corey G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:57:00