使用 Python 和 PyQt5 构建一个 带图标的任务管理器 附源码

在本博客中,我将分享如何使用 Python 和 PyQt5 构建一个简单的任务管理器应用程序。此应用程序可以显示所有运行中的进程,并提供一个按钮来终止每个进程。此外,每个进程还显示其关联的图标。这将是一个非常有趣的项目,可以帮助您更好地理解 Python 和 PyQt5 的实际应用。

环境准备

在开始之前,您需要确保安装了以下 Python 包:

  • psutil
  • Pillow
  • pywin32
  • PyQt5

您可以使用以下命令来安装这些包:

pip install psutil Pillow pywin32 PyQt5

代码片段解析与使用步骤

1. 导入必要的库

首先,我们需要导入一些必要的库:

import sys
import os
import ctypes
import psutil
import win32gui
import win32ui
import win32process
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QLabel, QPushButton, QWidget, QScrollArea, QHBoxLayout
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QImage
from PIL import Image

这些库包括系统操作、图形界面、进程管理等功能。

2. 提取图标函数

定义一个函数 extract_icon(exe_path) 来提取可执行文件的图标:

def extract_icon(exe_path):
    try:
        large, small = win32gui.ExtractIconEx(exe_path, 0)
        hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
        hbmp = win32ui.CreateBitmap()
        hbmp.CreateCompatibleBitmap(hdc, 32, 32)
        hdc = hdc.CreateCompatibleDC()
        hdc.SelectObject(hbmp)
        hdc.DrawIcon((0, 0), large[0])

        # 保存图标
        hbmpinfo = hbmp.GetInfo()
        hbmpstr = hbmp.GetBitmapBits(True)
        img = Image.frombuffer(
            'RGBA',
            (hbmpinfo['bmWidth'], hbmpinfo['bmHeight']),
            hbmpstr, 'raw', 'BGRA', 0, 1
        )

        # 释放资源
        win32gui.DestroyIcon(large[0])
        win32gui.DestroyIcon(small[0])

        return img
    except Exception as e:
        print(f"Error extracting icon from {exe_path}: {e}")
        return None

这个函数从可执行文件中提取图标,并将其转换为 PIL 图像对象。

3. 创建主窗口类

定义一个 ProcessManager 类来创建主窗口:

class ProcessManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle('任务管理器')

        self.central_widget = QWidget()
        self.layout = QVBoxLayout(self.central_widget)

        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_content = QWidget()
        self.scroll_layout = QVBoxLayout(self.scroll_content)

        self.scroll_area.setWidget(self.scroll_content)
        self.layout.addWidget(self.scroll_area)

        self.central_widget.setLayout(self.layout)
        self.setCentralWidget(self.central_widget)

        self.load_processes()

这个类初始化了主窗口,并设置了滚动区域来显示进程信息。

4. 加载进程信息

定义一个 load_processes 方法来加载当前运行的进程信息:

    def load_processes(self):
        # 清空现有布局
        for i in reversed(range(self.scroll_layout.count())):
            widget = self.scroll_layout.itemAt(i).widget()
            if widget is not None:
                widget.deleteLater()

        for proc in psutil.process_iter(['pid', 'name', 'exe']):
            try:
                exe_path = proc.info['exe']
                icon = extract_icon(exe_path)

                if icon:
                    qimage = self.pil2pixmap(icon)
                    icon_label = QLabel(self)
                    icon_label.setPixmap(qimage)

                    pid_label = QLabel(f"PID: {proc.info['pid']}, Name: {proc.info['name']}", self)

                    kill_button = QPushButton("关闭进程")
                    kill_button.clicked.connect(lambda _, p=proc.info['pid']: self.kill_process(p))

                    h_layout = QHBoxLayout()
                    h_layout.addWidget(icon_label)
                    h_layout.addWidget(pid_label)
                    h_layout.addWidget(kill_button)

                    container = QWidget()
                    container.setLayout(h_layout)
                    self.scroll_layout.addWidget(container)

            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass

这个方法遍历所有运行的进程,并显示它们的图标、PID和名称,同时提供关闭进程的按钮。

5. 运行应用程序

最后,运行应用程序:

if __name__ == '__main__':
    app = QApplication(sys.argv)
    manager = ProcessManager()
    manager.show()
    sys.exit(app.exec_())

这段代码创建并显示主窗口,并启动应用程序的事件循环。

通过这些步骤,你可以创建一个简单的任务管理器应用程序,显示当前运行的进程,并提供关闭进程的功能。

代码实现

下面是完整的代码:

import sys
import os
import ctypes
import psutil
import win32gui
import win32ui
import win32process
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QLabel, QPushButton, QWidget, QScrollArea, QHBoxLayout
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QImage
from PIL import Image

def extract_icon(exe_path):
    try:
        large, small = win32gui.ExtractIconEx(exe_path, 0)
        hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
        hbmp = win32ui.CreateBitmap()
        hbmp.CreateCompatibleBitmap(hdc, 32, 32)
        hdc = hdc.CreateCompatibleDC()
        hdc.SelectObject(hbmp)
        hdc.DrawIcon((0, 0), large[0])

        # 保存图标
        hbmpinfo = hbmp.GetInfo()
        hbmpstr = hbmp.GetBitmapBits(True)
        img = Image.frombuffer(
            'RGBA',
            (hbmpinfo['bmWidth'], hbmpinfo['bmHeight']),
            hbmpstr, 'raw', 'BGRA', 0, 1
        )

        # 释放资源
        win32gui.DestroyIcon(large[0])
        win32gui.DestroyIcon(small[0])

        return img
    except Exception as e:
        print(f"Error extracting icon from {exe_path}: {e}")
        return None

class ProcessManager(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 800, 600)
        self.setWindowTitle('任务管理器')

        self.central_widget = QWidget()
        self.layout = QVBoxLayout(self.central_widget)

        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_content = QWidget()
        self.scroll_layout = QVBoxLayout(self.scroll_content)

        self.scroll_area.setWidget(self.scroll_content)
        self.layout.addWidget(self.scroll_area)

        self.central_widget.setLayout(self.layout)
        self.setCentralWidget(self.central_widget)

        self.load_processes()

    def load_processes(self):
        # 清空现有布局
        for i in reversed(range(self.scroll_layout.count())):
            widget = self.scroll_layout.itemAt(i).widget()
            if widget is not None:
                widget.deleteLater()

        for proc in psutil.process_iter(['pid', 'name', 'exe']):
            try:
                exe_path = proc.info['exe']
                icon = extract_icon(exe_path)

                if icon:
                    qimage = self.pil2pixmap(icon)
                    icon_label = QLabel(self)
                    icon_label.setPixmap(qimage)

                    pid_label = QLabel(f"PID: {proc.info['pid']}, Name: {proc.info['name']}", self)

                    kill_button = QPushButton("关闭进程")
                    kill_button.clicked.connect(lambda _, p=proc.info['pid']: self.kill_process(p))

                    h_layout = QHBoxLayout()
                    h_layout.addWidget(icon_label)
                    h_layout.addWidget(pid_label)
                    h_layout.addWidget(kill_button)

                    container = QWidget()
                    container.setLayout(h_layout)
                    self.scroll_layout.addWidget(container)

            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                pass

    def pil2pixmap(self, im):
        im = im.convert("RGBA")
        data = im.tobytes("raw", "BGRA")
        qimage = QImage(data, im.size[0], im.size[1], QImage.Format_ARGB32)
        return QPixmap.fromImage(qimage)

    def kill_process(self, pid):
        try:
            proc = psutil.Process(pid)
            proc.terminate()
            proc.wait()  # 等待进程终止
            self.load_processes()  # 刷新进程列表
        except psutil.NoSuchProcess:
            print(f"No such process: {pid}")
        except psutil.AccessDenied:
            print(f"Access denied: {pid}")
        except Exception as e:
            print(f"Error terminating process {pid}: {e}")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    manager = ProcessManager()
    manager.show()
    sys.exit(app.exec_())

代码详解

  1. 提取图标: 我们使用 win32guiwin32ui 提取可执行文件的图标。extract_icon 函数会返回提取到的图标图像。

  2. 创建主窗口ProcessManager 类继承自 QMainWindow,并在其中设置了布局和滚动区域,以便能够显示大量进程。

  3. 加载进程信息load_processes 方法会遍历所有运行中的进程,并为每个进程提取图标和显示信息。同时,还会添加一个按钮,用于终止进程。

  4. 转换图像格式pil2pixmap 方法用于将 PIL 图像转换为 PyQt5 可以显示的 QPixmap

  5. 终止进程kill_process 方法用于终止指定的进程,并在终止后刷新进程列表。

运行效果

        

运行该程序后,您会看到一个任务管理器窗口,显示当前所有运行中的进程。每个进程项包含进程的图标、进程 ID 和一个用于终止进程的按钮。点击按钮即可终止相应的进程,并且进程列表会自动刷新,显示当前的进程状态。

说明

这段代码实现了一个简单的任务管理器应用程序,使用 PyQt5 作为 GUI 框架,并结合了 psutil 库来获取系统进程信息。主要功能包括:

  1. 提取图标:从可执行文件中提取图标并转换为 PIL 图像。
  2. 显示进程信息:列出当前系统中的所有进程,包括进程 ID、名称和图标。
  3. 终止进程:提供按钮来终止选定的进程。

主要功能

  1. 提取图标

    • extract_icon(exe_path) 函数从指定的可执行文件中提取图标,并将其转换为 PIL 图像。
  2. 显示进程信息

    • load_processes() 方法遍历当前系统中的所有进程,提取其图标和信息,并在 GUI 中显示。
  3. 终止进程

    • kill_process(pid) 方法终止指定的进程,并刷新进程列表。

扩展

  1. 进程过滤和搜索

    • 添加一个搜索框,允许用户根据进程名称或 PID 过滤进程列表。
  2. 进程详细信息

    • 显示更多进程详细信息,如 CPU 和内存使用情况、启动时间等。
  3. 多语言支持

    • 使用 Qt 的翻译功能,支持多语言界面。
  4. 图标缓存

    • 缓存提取的图标,以减少重复提取的开销。

相关类型扩展

  1. 系统监控工具

    • 扩展为一个更全面的系统监控工具,显示 CPU、内存、磁盘和网络使用情况。
  2. 远程任务管理器

    • 实现远程任务管理功能,允许用户管理远程计算机上的进程。
  3. 进程优先级调整

    • 添加功能,允许用户调整进程的优先级。
  4. 进程树视图

    • 显示进程树视图,展示进程之间的父子关系。

   爬虫项目推荐

    其他项目推荐

总结

这段代码展示了如何使用 PyQt5 创建一个图形用户界面,并结合 psutil 库来获取和管理系统进程。通过提取进程图标、显示进程信息以及提供终止进程的功能,这个任务管理器应用程序为用户提供了一个直观且实用的工具来管理系统进程。

结论

这段代码成功实现了一个基本的任务管理器应用程序,利用 PyQt5 提供图形用户界面,并结合 psutil 库获取和管理系统进程。通过提取进程图标、显示进程信息以及提供终止进程的功能,这个应用程序为用户提供了一个直观且实用的工具来管理系统进程。进一步的扩展和优化可以使其功能更加丰富和强大。

通过这个项目,您可以学到如何使用 Python 和 PyQt5 构建一个实用的桌面应用程序,并掌握一些处理系统进程和图标提取的技巧,欢迎在评论区留言。继续探索和学习,祝你在深度学习的旅程中取得更多的成果!🚀


希望这个博客对你有所帮助!如果你有任何问题需要进一步的指导,请随时提问。继续加油! 🚀

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LIY若依

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值