基于PyQt5的自動化任務管理軟件:高效、智能的任務調度與執行管理
相關資源文件已經打包成EXE文件,可雙擊直接運行程序,且文章末尾已附上相關源碼,以供大家學習交流,博主主頁還有更多Python相關程序案例,秉著開源精神的想法,望大家喜歡,點個關注不迷路!!!
一、項目概述
本項目通過PyQt5構建圖形界面,使用Python實現了一個自動化任務管理系統。該系統支持添加各種類型的任務,能夠根據用戶設置的時間自動執行任務,如打開程序、關閉程序、關機、重啟、鎖屏等,同時記錄任務執行過程中的日志,幫助用戶實時跟蹤任務的執行狀態。
項目目標
- 任務類型:支持打開程序、關閉程序、關機、重啟和鎖屏等任務類型。
- 任務調度:能夠按計劃時間定時執行任務,支持計劃時間選擇和倒計時執行。
- 日志記錄:每個任務的執行狀態和結果都會被記錄到日志中,方便用戶查看歷史記錄。
- UI設計:通過PyQt5實現用戶友好的圖形界面,支持添加、刪除、取消任務,界面美觀,操作簡單。
二、功能使用
1. 添加任務
在任務管理界面中,用戶可以通過選擇任務類型(如打開程序、關閉程序、關機等)并設置目標路徑(程序的路徑),來創建一個新的任務。例如,選擇“打開程序”后,用戶可以通過“瀏覽”按鈕選擇需要啟動的程序路徑,設置好計劃執行時間后,點擊“添加任務”按鈕,系統會根據設定的時間自動執行該任務。
2. 定時任務執行
任務被成功添加后,系統會計算當前時間與任務設定時間之間的差值,然后通過定時器(QTimer)在任務設定的時間點觸發任務執行。例如,如果設置任務在“2023年12月31日 20:00:00”執行,程序會在該時刻自動運行相應的操作,如啟動程序、關機等。
3. 任務管理
在任務列表中,用戶可以查看所有已添加的任務,并根據任務的執行狀態進行管理。系統支持以下操作:
取消任務:用戶可以取消一個待執行的任務,任務一旦被取消,將不會再執行。
刪除任務:用戶可以刪除已經執行過的任務,這樣可以清理歷史記錄。
查看日志:每個任務的執行日志會實時記錄,包括任務的啟動、執行結果、是否成功等信息,用戶可以查看并導出日志,進行長期保存。
4. 日志功能
在任務管理軟件中,日志功能是一個非常重要的部分。所有任務的執行過程都會被記錄到日志中,用戶可以實時查看任務的執行狀態。如果任務執行失敗,錯誤信息會被詳細記錄,并可以導出成文本文件,方便后期審查和分析。
def log_task(self, task, message):timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")log_entry = f"{timestamp}: {message}"self.logs.append(log_entry)self.update_log_view()
通過log_task函數,任務執行過程中產生的各種信息(如任務開始執行、執行成功或失敗、錯誤信息等)都會記錄在日志中。用戶還可以導出這些日志,以便進一步分析。
5. 自定義界面設計
整個軟件的界面使用了PyQt5框架進行設計。界面元素簡潔、直觀,用戶可以方便地進行任務添加、刪除、查看日志等操作。同時,通過QSS(Qt樣式表)實現了美觀的界面風格,如任務表格的樣式、按鈕的動態變化等,極大提升了用戶體驗。
def get_button_style(self, color):return f"""QPushButton {{background-color: {color};color: white;border: none;border-radius: 4px;padding: 8px;}}QPushButton:hover {{background-color: {self.darken_color(color)};}}QPushButton:pressed {{background-color: {self.darken_color(color, 30)};}}"""
通過get_button_style函數,按鈕在不同狀態下(如懸停、點擊)會顯示不同的顏色,增加了界面的交互性。
三、代碼實現與技術解析
1. PyQt5界面設計
在本項目中,界面設計主要通過PyQt5中的各種布局組件(如QVBoxLayout, QHBoxLayout, QTableWidget等)來實現。QTableWidget用于顯示任務列表,QComboBox用于選擇任務類型,QLineEdit用于輸入目標路徑,QPushButton用于操作按鈕。
2. 定時任務執行
定時任務的實現依賴于QTimer類,系統通過計算當前時間與任務設定時間之間的差值來啟動定時器,一旦定時器到期,就會調用任務執行函數。通過這種方式,可以非常準確地按照用戶設定的時間執行任務。
3. 任務執行與錯誤處理
在執行任務時,系統會根據任務類型調用相應的操作。例如,調用subprocess.Popen來啟動程序,調用os.system來關機或重啟。如果任務執行失敗,系統會捕獲異常并記錄錯誤信息到日志中。
def execute_task(self, task):try:if task_type == '打開程序':subprocess.Popen(target_path)elif task_type == '關閉程序':os.system(f'taskkill /IM {os.path.basename(target_path)} /F /T')elif task_type == '關機':os.system("shutdown /s /t 0")elif task_type == '重啟':os.system("shutdown /r /t 0")elif task_type == '鎖屏':os.system("rundll32.exe user32.dll,LockWorkStation")except Exception as e:status = f"失敗: {str(e)}"log_entry += f" - 錯誤: {status}"else:log_entry += " - 成功"
4. 日志導出
用戶可以將日志導出為文本文件,方便長期保存。通過QFileDialog對話框,用戶可以選擇保存日志的路徑和文件名,系統會將日志內容寫入到指定的文件中。
def export_logs(self):file_path, _ = QFileDialog.getSaveFileName(self, '導出日志', f'任務日志_{timestamp}.txt', 'Text Files (*.txt);;All Files (*)')if file_path:try:with open(file_path, 'w', encoding='utf-8') as f:f.write("=== 任務執行日志 ===\n")f.write(f"導出時間: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")for log in self.logs:f.write(log + '\n')QMessageBox.information(self, "成功", f"日志已成功導出到:\n{file_path}")except Exception as e:QMessageBox.critical(self, "錯誤", f"導出日志失敗:\n{str(e)}")
四、效果展示
五、相關源碼
import sys
import os
import subprocess
import datetime
from PyQt5.QtCore import Qt, QDateTime, QTimer
from PyQt5.QtGui import QIcon, QColor, QBrush
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QComboBox,QLineEdit, QLabel, QTableWidget, QTableWidgetItem, QFileDialog,QDateTimeEdit, QMessageBox, QTextEdit, QHeaderView, QSizePolicy
)class TaskManager(QWidget):def __init__(self):super().__init__()self.setWindowTitle('自動化任務管理軟件')self.setWindowIcon(QIcon('icon.png'))self.setGeometry(100, 100, 1000, 700)self.setup_ui_style()self.tasks = []self.task_timers = {}self.task_id_counter = 0self.logs = []self.init_ui()def setup_ui_style(self):self.setStyleSheet("""QWidget {font-family: 'Microsoft YaHei';font-size: 12px;}QLabel {font-weight: bold;}QTableWidget {alternate-background-color: #f5f5f5;selection-background-color: #e0f7fa;border: 1px solid #e0e0e0;}QHeaderView::section {background-color: #607d8b;color: white;padding: 8px;font-weight: bold;}QLineEdit, QDateTimeEdit, QComboBox {padding: 5px;border: 1px solid #bdbdbd;border-radius: 3px;min-height: 25px;}QTextEdit {background-color: #fafafa;border: 1px solid #e0e0e0;padding: 5px;}""")def init_ui(self):main_layout = QVBoxLayout()main_layout.setContentsMargins(15, 15, 15, 15)main_layout.setSpacing(15)# 任務類型選擇task_type_layout = QHBoxLayout()task_type_layout.setSpacing(10)self.task_type_combo = QComboBox()self.task_type_combo.setMinimumWidth(200)self.task_type_combo.addItems(['打開程序', '關閉程序', '關機', '重啟', '鎖屏'])self.task_type_combo.currentIndexChanged.connect(self.update_target_path_status)task_type_layout.addWidget(QLabel('任務類型:'))task_type_layout.addWidget(self.task_type_combo)main_layout.addLayout(task_type_layout)# 目標路徑輸入self.target_path_input = QLineEdit()self.target_path_input.setPlaceholderText("請輸入程序路徑(如:C:\\Program Files\\App\\app.exe)")self.browse_button = QPushButton("瀏覽")self.browse_button.setFixedWidth(80)self.browse_button.setStyleSheet(self.get_button_style("#2196F3"))self.browse_button.clicked.connect(self.browse_file)target_layout = QHBoxLayout()target_layout.setSpacing(10)target_layout.addWidget(QLabel('目標路徑:'))target_layout.addWidget(self.target_path_input)target_layout.addWidget(self.browse_button)main_layout.addLayout(target_layout)# 執行時間選擇self.execution_time_picker = QDateTimeEdit()self.execution_time_picker.setCalendarPopup(True)self.execution_time_picker.setDisplayFormat("yyyy-MM-dd HH:mm:ss")current_time = QDateTime.currentDateTime()current_time.setSecsSinceEpoch(current_time.toSecsSinceEpoch() - current_time.time().second())self.execution_time_picker.setDateTime(current_time)time_layout = QHBoxLayout()time_layout.addWidget(QLabel('執行時間:'))time_layout.addWidget(self.execution_time_picker)main_layout.addLayout(time_layout)# 操作按鈕button_layout = QHBoxLayout()button_layout.setSpacing(20)self.add_task_button = QPushButton('添加任務')self.add_task_button.setFixedHeight(35)self.add_task_button.setStyleSheet(self.get_button_style("#4CAF50"))self.add_task_button.clicked.connect(self.add_task)self.export_log_button = QPushButton('導出日志')self.export_log_button.setFixedHeight(35)self.export_log_button.setStyleSheet(self.get_button_style("#2196F3"))self.export_log_button.clicked.connect(self.export_logs)button_layout.addWidget(self.add_task_button)button_layout.addWidget(self.export_log_button)main_layout.addLayout(button_layout)# 任務列表self.task_table = QTableWidget(0, 5)self.task_table.setHorizontalHeaderLabels(['任務類型', '目標路徑', '執行時間', '執行狀態', '操作'])self.task_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)self.task_table.setColumnWidth(3, 100)self.task_table.setColumnWidth(4, 120)self.task_table.verticalHeader().setDefaultSectionSize(40)self.task_table.setAlternatingRowColors(True)main_layout.addWidget(QLabel('任務列表:'))main_layout.addWidget(self.task_table)# 日志顯示self.log_text_edit = QTextEdit()self.log_text_edit.setReadOnly(True)main_layout.addWidget(QLabel('日志:'))main_layout.addWidget(self.log_text_edit)self.setLayout(main_layout)def get_button_style(self, color):return f"""QPushButton {{background-color: {color};color: white;border: none;border-radius: 4px;padding: 8px;}}QPushButton:hover {{background-color: {self.darken_color(color)};}}QPushButton:pressed {{background-color: {self.darken_color(color, 30)};}}"""def darken_color(self, hex_color, percent=20):"""顏色變暗效果"""hex_color = hex_color.lstrip('#')rgb = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))darkened = tuple(max(0, int(c * (100 - percent) / 100)) for c in rgb)return f"#{darkened[0]:02x}{darkened[1]:02x}{darkened[2]:02x}"def update_target_path_status(self):task_type = self.task_type_combo.currentText()if task_type in ['打開程序', '關閉程序']:self.target_path_input.setEnabled(True)self.browse_button.setEnabled(True)else:self.target_path_input.setEnabled(False)self.browse_button.setEnabled(False)def browse_file(self):file_path, _ = QFileDialog.getOpenFileName(self, '選擇程序', '', 'Executable Files (*.exe);;All Files (*)')if file_path:self.target_path_input.setText(file_path)def add_task(self):task_type = self.task_type_combo.currentText()target_path = self.target_path_input.text()execution_time = self.execution_time_picker.dateTime()execution_time_str = execution_time.toString("yyyy-MM-dd HH:mm:ss")# 檢查執行時間是否已過期if execution_time < QDateTime.currentDateTime():error_msg = f"錯誤:不能添加過期任務(計劃時間:{execution_time_str})"self.show_error(error_msg)self.log_task(None, error_msg)returnif task_type in ['打開程序', '關閉程序'] and not target_path:error_msg = '錯誤:目標路徑不能為空!'self.show_error(error_msg)self.log_task(None, error_msg)returntask_id = self.task_id_counterself.task_id_counter += 1task = {'task_id': task_id,'任務類型': task_type,'目標路徑': target_path,'執行時間': execution_time_str,'執行狀態': '待執行'}delay = QDateTime.currentDateTime().msecsTo(execution_time)if delay <= 0:self.execute_task(task)else:timer = QTimer(self)timer.setSingleShot(True)timer.timeout.connect(lambda: self.execute_task(task))timer.start(delay)self.task_timers[task_id] = timerself.tasks.append(task)self.update_task_table()log_entry = f"已添加任務:{task_type} {target_path if target_path else ''},計劃執行時間:{execution_time_str}"self.log_task(task, log_entry)def execute_task(self, task):task_type = task['任務類型']target_path = task['目標路徑']status = '執行成功'log_entry = f"開始執行任務:{task_type} {target_path if target_path else ''}"try:if task_type == '打開程序':subprocess.Popen(target_path)elif task_type == '關閉程序':os.system(f'taskkill /IM {os.path.basename(target_path)} /F /T')elif task_type == '關機':os.system("shutdown /s /t 0")elif task_type == '重啟':os.system("shutdown /r /t 0")elif task_type == '鎖屏':os.system("rundll32.exe user32.dll,LockWorkStation")except Exception as e:status = f"失敗: {str(e)}"log_entry += f" - 錯誤: {status}"else:log_entry += " - 成功"task['執行狀態'] = '已執行'if task['task_id'] in self.task_timers:del self.task_timers[task['task_id']]self.log_task(task, log_entry)self.update_task_table()def update_task_table(self):self.task_table.setRowCount(0)for row, task in enumerate(self.tasks):self.task_table.insertRow(row)self.task_table.setRowHeight(row, 40)# 任務類型列type_item = QTableWidgetItem(task['任務類型'])self.task_table.setItem(row, 0, type_item)# 目標路徑列path_item = QTableWidgetItem(task['目標路徑'])path_item.setToolTip(task['目標路徑'])self.task_table.setItem(row, 1, path_item)# 執行時間列time_item = QTableWidgetItem(task['執行時間'])self.task_table.setItem(row, 2, time_item)# 執行狀態列status_item = QTableWidgetItem(task['執行狀態'])status_item.setTextAlignment(Qt.AlignCenter)if task['執行狀態'] == '已執行':status_item.setBackground(QBrush(QColor(200, 200, 200))) # 灰色status_item.setForeground(QBrush(QColor(50, 50, 50)))else:status_item.setBackground(QBrush(QColor(144, 238, 144))) # 淺綠色status_item.setForeground(QBrush(QColor(0, 100, 0)))self.task_table.setItem(row, 3, status_item)# 操作列button_container = QWidget()button_layout = QHBoxLayout(button_container)button_layout.setContentsMargins(5, 2, 5, 2)button_layout.setSpacing(5)if task['執行狀態'] == '待執行':cancel_button = QPushButton('取消任務')cancel_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)cancel_button.setStyleSheet(self.get_button_style("#f44336"))cancel_button.clicked.connect(lambda _, r=row: self.cancel_task(r))button_layout.addWidget(cancel_button)else:delete_button = QPushButton('刪除任務')delete_button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)delete_button.setStyleSheet(self.get_button_style("#f44336"))delete_button.clicked.connect(lambda _, r=row: self.delete_task(r))button_layout.addWidget(delete_button)self.task_table.setCellWidget(row, 4, button_container)def cancel_task(self, row):if row < 0 or row >= len(self.tasks):returntask_to_cancel = self.tasks[row]# 停止定時器if task_to_cancel['task_id'] in self.task_timers:timer = self.task_timers[task_to_cancel['task_id']]if timer.isActive():timer.stop()del self.task_timers[task_to_cancel['task_id']]# 從任務列表中移除self.tasks.pop(row)# 從表格中移除self.task_table.removeRow(row)# 記錄日志log_entry = f"已取消任務:{task_to_cancel['任務類型']} {task_to_cancel['目標路徑'] if task_to_cancel['目標路徑'] else ''},原計劃執行時間:{task_to_cancel['執行時間']}"self.log_task(task_to_cancel, log_entry)QMessageBox.information(self, "成功", "任務已取消并刪除!")def delete_task(self, row):if row < 0 or row >= len(self.tasks):returntask_to_delete = self.tasks[row]self.tasks.pop(row)self.task_table.removeRow(row)log_entry = f"已刪除任務:{task_to_delete['任務類型']} {task_to_delete['目標路徑'] if task_to_delete['目標路徑'] else ''},原執行時間:{task_to_delete['執行時間']}"self.log_task(task_to_delete, log_entry)QMessageBox.information(self, "成功", "任務已刪除!")def log_task(self, task, message):timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")log_entry = f"{timestamp}: {message}"self.logs.append(log_entry)self.update_log_view()def update_log_view(self):self.log_text_edit.clear()for log in self.logs:self.log_text_edit.append(log)def export_logs(self):timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")file_path, _ = QFileDialog.getSaveFileName(self, '導出日志', f'任務日志_{timestamp}.txt', 'Text Files (*.txt);;All Files (*)')if file_path:try:with open(file_path, 'w', encoding='utf-8') as f:f.write("=== 任務執行日志 ===\n")f.write(f"導出時間: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")for log in self.logs:f.write(log + '\n')QMessageBox.information(self, "成功", f"日志已成功導出到:\n{file_path}")except Exception as e:QMessageBox.critical(self, "錯誤", f"導出日志失敗:\n{str(e)}")def show_error(self, message):msg = QMessageBox(self)msg.setIcon(QMessageBox.Critical)msg.setWindowTitle("錯誤")msg.setText(message)msg.setStyleSheet("""QMessageBox {font-family: 'Microsoft YaHei';}QMessageBox QLabel {font-size: 12px;}""")msg.exec_()if __name__ == '__main__':app = QApplication(sys.argv)app.setStyle('Fusion')task_manager = TaskManager()task_manager.show()sys.exit(app.exec_())
六、總結
通過使用PyQt5和Python,成功實現了一個功能完善的自動化任務管理軟件。該軟件能夠滿足日常使用中對定時任務執行的需求,具備豐富的功能,包括任務添加、定時執行、任務取消、任務刪除和日志導出等。通過對PyQt5的靈活使用和Python的強大支持,使得這一軟件在功能實現、界面交互等方面都做得非常優秀。
未來可以進一步擴展和優化該軟件的功能,例如:
- 支持更多的任務類型,如自動備份文件、自動清理系統等。
- 增加任務優先級管理,讓用戶可以根據任務的緊急程度設定執行順序。
- 加強錯誤處理和系統監控功能,提供更多的反饋信息。
總的來說,這是一個非常適合個人和小型團隊使用的自動化任務管理工具,能夠顯著提高工作效率和系統管理的便捷性。