通过“待办事项列表项目”快速学习Pyqt5的一些特性

news2024/9/21 4:39:10

Pyqt5相关文章:
快速掌握Pyqt5的三种主窗口
快速掌握Pyqt5的2种弹簧
快速掌握Pyqt5的5种布局
快速弄懂Pyqt5的5种项目视图(Item View)
快速弄懂Pyqt5的4种项目部件(Item Widget)
快速掌握Pyqt5的6种按钮
快速掌握Pyqt5的10种容器(Containers)
快速掌握Pyqt5的20种输入控件(Input Widgets)
快速掌握Pyqt5的9种显示控件
详细学习Pyqt5中的5种布局方式
详细学习Pyqt5中的6种按钮
详细学习Pyqt5中的2种弹簧
详细学习Pyqt5的5种项目视图(Item View)
详细学习Pyqt5的4种项目部件(Item Widget)
详细学习Pyqt5的20种输入控件(Input Widgets)
详细学习Pyqt5的9种显示控件
详细学习Pyqt5的10种容器(Containers)
详细学习PyQt5与数据库交互
详细学习PyQt5中的多线程
快速学习PyQt5的动画和图形效果
快速学习PyQt5的高级自定义控件
快速学会绘制Pyqt5中的所有图(上)
快速学会绘制Pyqt5中的所有图(下)
待续。。。

项目软件最终效果图:

在这里插入图片描述

第一部分:项目概述

本项目的目标是创建一个简单而直观的待办事项列表应用程序。这个应用程序可以帮助用户有效地管理他们的日常任务,提供以下关键功能:

  1. 添加任务:允许用户输入并添加新任务到待办事项列表中。
  2. 删除任务:提供选项以从列表中删除不再需要的任务。
  3. 编辑任务:让用户能够修改现有任务的描述。
  4. 搜索和过滤:使用户能够通过关键词搜索任务,便于快速找到特定任务。
  5. 进度跟踪:通过进度条展示任务完成的总体进度,帮助用户了解他们完成任务的情况。

应用程序的用户界面(UI)设计注重简洁性和易用性,以确保用户能够轻松地进行日常任务管理。界面包括:

  1. 任务输入框:用于输入新任务的文本区域。
  2. 控制按钮:包括添加、删除和编辑任务的操作按钮。
  3. 任务显示区:展示任务列表,每个任务项包含一个复选框和任务描述。
  4. 搜索框:允许用户输入关键词以过滤任务列表。
  5. 进度指示器:动态显示完成的任务占总任务的百分比。

第二部分:环境搭建

准备PyQt5图形库

  • 打开命令提示符或终端,并运行以下命令:
    pip install PyQt5
    
  • 这个命令会从Python包索引(PyPI)下载并安装PyQt5及其依赖项。

第三部分:基础知识讲解

1. QApplication 对象

每个PyQt5应用程序的核心是QApplication对象。这个对象负责管理GUI应用程序的控制流和主要设置。在任何PyQt5程序中,QApplication对象是必须的。

app = QApplication([])

这段代码创建了一个QApplication实例,它是我们待办事项列表应用程序的起点。

2. QMainWindowQWidget

QMainWindow 是主窗口类,为应用程序提供了一个带有菜单栏、工具栏、状态栏和中心部件的框架。在我们的项目中,TodoList类继承自QMainWindow

class TodoList(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("待办事项列表")
        self.setGeometry(300, 300, 600, 500)
        self.setWindowIcon(QIcon('icon.png'))

QWidget 是所有用户界面对象的基类。在这里,我们创建了一个QWidget作为中心部件,并在其上布置其他控件。

central_widget = QWidget(self)
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
3. 布局管理

布局管理器,如QHBoxLayoutQVBoxLayout,是用来在窗口中组织控件的工具。在我们的项目中,我们使用了QVBoxLayout来垂直排列控件,并使用了QHBoxLayout来水平排列按钮。

layout = QVBoxLayout(central_widget)
buttons_layout = QHBoxLayout()
4. 控件和事件处理

在PyQt5中,控件是用户与应用程序交互的元素。我们的项目使用了多种控件,例如QLineEdit来输入任务,QPushButton来添加、删除和编辑任务。

self.task_input = QLineEdit(self)
add_button = QPushButton("添加任务", self)
delete_button = QPushButton("删除任务", self)

这些按钮通过点击事件触发相应的函数,如add_taskdelete_task

5. 自定义控件 - MovingTextProgressBar

在PyQt5中,我们还可以自定义控件。在此项目中,我们定义了MovingTextProgressBar,这是QProgressBar的一个子类,用来显示任务的完成进度。

class MovingTextProgressBar(QProgressBar):
    def __init__(self, parent=None):
        super().__init__(parent)
    
    def paintEvent(self, event):
        # Custom painting code here

这个自定义进度条在进度变化时显示百分比,并根据进度动态调整文本位置。

6. 数据存储和加载

最后,我们的项目还涉及到数据的保存和加载。使用Python的json模块,我们可以将任务列表保存到一个文件,并在应用程序启动时加载这些任务。

def save_tasks(self):
    tasks = []
    for i in range(self.task_list.count()):
        # 代码来保存任务
    with open('tasks.json', 'w') as file:
        json.dump(tasks, file)

def load_tasks(self):
    try:
        with open('tasks.json', 'r') as file:
            # 代码来加载任务
    except Exception as e:
        print(f"Error loading tasks: {e}")

第四部分:逐步构建项目

1. 构建静态窗口方法(初始化__init__)详解

类定义和初始化
class TodoList(QMainWindow):
    def __init__(self):
        super().__init__()
  • TodoList 类继承自 PyQt5 的 QMainWindow 类,提供了一个主窗口框架。
  • __init__ 方法是类的构造函数,用于初始化这个窗口。
  • super().__init__() 调用基类的构造函数,是创建窗口的基本步骤。
设置窗口属性
self.setWindowTitle("待办事项列表")
self.setGeometry(300, 300, 600, 500)
self.setWindowIcon(QIcon('icon.png'))
  • setWindowTitle 设置窗口的标题。
  • setGeometry 设置窗口的位置和大小。这里窗口被放置在屏幕的 (300, 300) 位置,大小为 600x500 像素。
  • setWindowIcon 设置窗口的图标。图标文件应该是名为 ‘icon.png’ 的图像文件。
创建中心小部件和布局
central_widget = QWidget(self)
self.setCentralWidget(central_widget)
layout = QVBoxLayout(central_widget)
  • 创建了一个QWidget作为窗口的中心小部件。
  • setCentralWidget 将这个小部件设置为主窗口的中心区域。
  • QVBoxLayout 创建了一个垂直布局,用于在中心小部件中垂直排列其他控件。
添加任务输入框
self.task_input = QLineEdit(self)
self.task_input.setMinimumHeight(30)
self.task_input.returnPressed.connect(self.add_task)
layout.addWidget(self.task_input)
  • QLineEdit 创建了一个文本输入框,用于输入任务。
  • setMinimumHeight 设置了输入框的最小高度。
  • returnPressed.connect 将输入框中的回车键事件连接到 add_task 方法;QLineEdit 控件有一个名为 returnPressed 的信号,当用户在该控件中按下回车键时,会触发去调用括号内的方法。
  • addWidget 将输入框添加到布局中。
添加控制按钮
buttons_layout = QHBoxLayout()
add_button = QPushButton("添加任务", self)
add_button.clicked.connect(self.add_task)
buttons_layout.addWidget(add_button)

delete_button = QPushButton("删除任务", self)
delete_button.clicked.connect(self.delete_task)
buttons_layout.addWidget(delete_button)

edit_button = QPushButton("编辑任务", self)
edit_button.clicked.connect(self.edit_task)
buttons_layout.addWidget(edit_button)

layout.addLayout(buttons_layout)
  • QHBoxLayout 创建了一个水平布局。
  • QPushButton 创建了添加、删除和编辑任务的按钮。
  • clicked.connect 将按钮点击事件连接到相应的方法。
  • addWidget 将按钮添加到水平布局中。
  • addLayout 将水平布局添加到主垂直布局中。
添加任务列表、搜索框和进度条
self.task_list = QListWidget(self)
layout.addWidget(self.task_list)

self.search_input = QLineEdit(self)
self.search_input.setPlaceholderText("搜索任务...")
self.search_input.textChanged.connect(self.search_tasks)
layout.addWidget(self.search_input)

self.process_label = QLabel(self)
self.process_label.setText("任务进度:")
layout.addWidget(self.process_label)

self.progress_bar = MovingTextProgressBar(self)
self.progress_bar.setMaximum(100)
layout.addWidget(self.progress_bar)
  • QListWidget 创建了一个任务列表。
  • QLineEdit 用作搜索框。
  • QLabel 显示了进度条的标签。
  • MovingTextProgressBar 是一个自定义的进度条类,用来展示任务完成进度。
加载任务和应用样式表
self.load_tasks()
self.update_progress()
self.apply_stylesheet()
  • load_tasks 加载保存的任务。
  • update_progress 更新进度条的状态。
  • apply_stylesheet 应用自定义样式来美化界面。

2. 自定义控件(自定义进度条MovingTextProgressBar)详解

这段代码定义了一个自定义的进度条控件 MovingTextProgressBar,它继承自PyQt5的 QProgressBar。这个自定义控件重写了进度条的绘制方法,使得进度条中可以显示带有百分比的文本。让我们逐步分析这段代码:

类定义和初始化
class MovingTextProgressBar(QProgressBar):
    def __init__(self, parent=None):
        super().__init__(parent)
  • MovingTextProgressBar 类继承自 QProgressBar,使用 QProgressBar 的所有功能并添加新的特性。
  • __init__ 方法是构造函数,用于初始化这个控件。parent 参数允许这个控件被嵌入到其他QWidget中。

绘制进度条

def paintEvent(self, event):
    painter = QPainter(self)
    # ... 绘制代码 ...
  • paintEvent 方法是Qt中用于绘制控件的事件处理器。
  • QPainter 对象用于所有绘图操作。
绘制背景
rect = self.rect()
painter.setBrush(self.palette().color(QPalette.Window))
painter.drawRect(rect)
  • self.rect() 获取控件的矩形区域。
  • setBrush 设置画笔的填充颜色,这里使用窗口的默认背景色。
  • drawRect 绘制进度条的背景矩形。
计算并绘制进度
progress_rect = QRect(rect)
progress_rect.setWidth(int(rect.width() * self.value() / self.maximum()))
painter.setBrush(self.palette().color(QPalette.Highlight))
painter.drawRect(progress_rect)
  • 计算进度条的宽度,基于进度条的当前值和最大值。
  • 设置画笔为高亮颜色(通常用于进度条的填充颜色)。
  • 绘制表示进度的矩形。
绘制文本
painter.setPen(self.palette().color(QPalette.Text))
text = f"{self.value():.2f}%"
text_rect = QRect(rect)
text_width = painter.fontMetrics().width(text)
# ... 文本位置计算和绘制 ...
  • 设置画笔颜色用于绘制文本。
  • 创建要显示的文本,显示当前进度的百分比。
  • fontMetrics().width(text) 用于计算文本的宽度,以便于定位。
计算文本位置并绘制
if self.value() > 0:
    text_position = max(0, progress_rect.width() - text_width - 5)
else:
    text_position = int((rect.width() - text_width) / 2)
text_rect.setLeft(text_position)
painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, text)
  • 根据进度条的填充情况计算文本的位置。
  • 如果有进度(值大于0),则确保文本位于进度条填充的右侧。
  • 如果没有进度,则将文本居中。
  • drawText 实际绘制文本。

总结

MovingTextProgressBar 类通过重写 paintEvent 方法自定义了进度条的绘制方式。这种自定义控件使得进度条不仅能够显示进度的可视化表示,还能在进度条内部显示具体的进度百分比,增强了用户界面的信息展示能力。这个自定义控件可以被重复使用在任何需要显示带文本的进度条的场景中。

3. 添加任务(add_task) 方法详解

获取任务文本

首先,我们从QLineEditself.task_input)获取用户输入的文本。这是用户希望添加到任务列表中的任务描述。

task_text = self.task_input.text()
if task_text:
    ...

这段代码检查是否有文本输入。如果task_text不为空,那么接下来的代码就会执行,将这个新任务添加到任务列表中。

创建列表项

接下来,我们创建一个QListWidgetItem,这将是任务列表中的一个新项。

item = QListWidgetItem()
item.setSizeHint(QSize(0, 40))

这里,setSizeHint(QSize(0, 40))是为了确保列表项有足够的空间来展示任务,其中40是列表项的高度。

设计任务小部件

现在,我们创建一个小部件来展示任务的文本和一个复选框。这个小部件将被放置在任务列表项中。

task_widget = QWidget()
task_layout = QHBoxLayout()
task_layout.setContentsMargins(5, 0, 5, 0)

我们使用QHBoxLayout(水平布局)来放置复选框和任务标签。setContentsMargins方法用于设置布局边距。

添加复选框和标签

我们接着添加一个QCheckBox和一个QLabel到布局中。复选框用于标记任务是否完成,而标签显示任务的文本。

chkBox = QCheckBox()
chkBox.stateChanged.connect(self.update_progress)

task_label = QLabel(task_text)
task_label.setMargin(5)

task_layout.addWidget(chkBox)
task_layout.addWidget(task_label)

stateChanged信号连接到update_progress方法,这样当复选框的状态改变时,进度条会更新。

设置任务小部件的布局并添加到列表

最后,我们将这个布局应用到task_widget,然后将这个小部件设置为item的小部件。

task_layout.addStretch(1)
task_widget.setLayout(task_layout)

self.task_list.addItem(item)
self.task_list.setItemWidget(item, task_widget)

addStretch(1)确保复选框和标签靠左排列,余下的空间被拉伸填充。

清除输入字段并更新进度

任务添加到列表后,输入框被清空,准备接受下一个任务的输入。同时,我们调用update_progress方法来更新进度条。

self.task_input.clear()
self.update_progress()
总结

add_task方法的核心是创建一个新的列表项,将用户输入的任务文本放入一个包含复选框和标签的小部件中,并将这个小部件添加到任务列表中。这个方法体现了PyQt5在处理用户界面和事件方面的灵活性和强大功能。通过这种方式,我们的待办事项列表应用程序能够动态地响应用户的输入,提供一个直观且互动的用户体验。

4. 删除任务(delete_task) 方法详解

循环遍历所选任务

delete_task方法首先遍历所有被用户选中的任务项。在PyQt5中,可以通过selectedItems()方法获取到QListWidget中所有被用户选中的项。

for item in self.task_list.selectedItems():
    ...

这段代码遍历任务列表中的每个被选中的项(item)。

删除选中的任务

接下来,使用takeItem方法从列表中移除这些选中的项。takeItem需要一个索引参数,我们通过row(item)方法获取这个索引。

self.task_list.takeItem(self.task_list.row(item))

这里,row(item)返回被选中项的索引,然后takeItem根据这个索引删除相应的项。

更新进度条

删除任务后,进度条需要更新以反映当前任务的完成状态。

self.update_progress()

调用update_progress方法来重新计算并更新进度条。

编辑任务(edit_task) 方法详解

检查是否有任务被选中

在编辑任务前,首先检查是否有任务被选中。如果至少有一个任务被选中,我们将取第一个选中的任务进行编辑。

selected_items = self.task_list.selectedItems()
if selected_items:
    item = selected_items[0]
    ...

这段代码确定是否有任务被选中,并将第一个选中的任务项赋值给item变量。

弹出对话框以编辑任务

使用QInputDialog.getText方法弹出一个对话框,允许用户编辑选中任务的文本。这个方法返回用户输入的新文本(new_text)和一个布尔值(ok),表示用户是否点击了对话框的确认按钮。

new_text, ok = QInputDialog.getText(self, "编辑任务", "任务描述:", QLineEdit.Normal, item.text())

这里的item.text()是当前任务的文本,用作对话框中的初始值。

更新任务文本

如果用户点击了确认并输入了新文本,则更新任务的文本。

if ok and new_text:
    item.setText(new_text)

setText方法用于更新列表项的文本为用户输入的新文本。

总结

delete_task方法通过遍历并移除选中的任务项来实现删除功能,而edit_task方法则通过弹出对话框并更新任务文本来实现编辑功能。这两个方法展示了如何在PyQt5应用程序中处理用户交互,如选择、删除和编辑列表项。通过这些方法,我们的待办事项列表应用变得更加灵活和用户友好,使用户能够轻松地管理他们的任务。

5. load_tasks 方法详解

尝试读取任务数据

load_tasks方法首先尝试打开一个名为tasks.json的文件,该文件包含以前保存的任务数据。

try:
    with open('tasks.json', 'r') as file:
        tasks = json.load(file)
    ...
except Exception as e:
    print(f"Error loading tasks: {e}")

这里使用json.load(file)将文件中的JSON数据转换成Python对象。如果文件不存在或者文件格式不正确,会抛出异常,并打印错误信息。

遍历任务并添加到列表

接下来,遍历从文件中加载的每个任务,并将它们添加到任务列表中。

for task in tasks:
    item = QListWidgetItem()
    self.task_list.addItem(item)

    task_widget = QWidget()
    task_layout = QHBoxLayout()
    task_layout.setContentsMargins(5, 5, 5, 5)

    chkBox = QCheckBox()
    chkBox.setChecked(task['completed'])
    chkBox.stateChanged.connect(self.update_progress)

    task_label = QLabel(task['text'])
    task_layout.addWidget(chkBox)
    task_layout.addWidget(task_label)
    task_layout.addStretch(1)

    task_widget.setLayout(task_layout)

    item.setSizeHint(task_widget.sizeHint())
    self.task_list.setItemWidget(item, task_widget)

在这个循环中,每个任务被创建为一个QListWidgetItem,其中包含一个复选框(表示任务是否完成)和一个标签(显示任务文本)。这样做可以确保应用程序在启动时加载用户之前的任务。

6. save_tasks 方法详解

准备保存的任务数据

save_tasks方法中,首先创建一个空列表tasks,用于存放将要保存到文件的任务数据。

tasks = []
遍历任务列表并收集数据

接着,遍历任务列表中的每一项,收集任务的文本和完成状态。

for i in range(self.task_list.count()):
    item = self.task_list.item(i)
    widget = self.task_list.itemWidget(item)
    if widget:
        label = widget.findChild(QLabel)
        checkbox = widget.findChild(QCheckBox)
        tasks.append({'text': label.text(), 'completed': checkbox.isChecked()})

这里使用findChild方法来找到每个任务项中的QLabelQCheckBox,并获取它们的文本和勾选状态。

将任务数据保存到文件

最后,使用json.dump将任务数据以JSON格式保存到tasks.json文件中。

with open('tasks.json', 'w') as file:
    json.dump(tasks, file)

这样,当用户关闭应用程序时,他们的任务数据被保存,并且可以在下次打开应用时重新加载。

总结

load_taskssave_tasks方法是待办事项列表应用程序的关键部分,它们确保了任务数据的持久化。通过这些方法,用户的任务列表在应用程序关闭和重新打开之间保持一致,从而提供了更好的用户体验和数据的持久性。

总结

本文详细介绍了如何使用Python和PyQt5创建一个功能完备的待办事项列表应用程序。从项目概述到环境搭建,再到基础概念的讲解和实际构建过程,文章全面覆盖了应用程序开发的各个方面。通过复现这个项目,可以学习到以下知识:PyQt5的基本使用、事件处理和信号槽机制、自定义控件的创建、数据保存。

附录(全部代码)

import json
from PyQt5.QtWidgets import QLabel,QHBoxLayout,QApplication, QMainWindow, QListWidget, QListWidgetItem, QLineEdit, QPushButton, QVBoxLayout, QWidget, QCheckBox, QProgressBar, QInputDialog, QComboBox
from PyQt5.QtCore import QSize,QRect, Qt
from PyQt5.QtGui import QIcon,QPainter, QColor, QPalette
 

class MovingTextProgressBar(QProgressBar):
    def __init__(self, parent=None):
        super().__init__(parent)

    def paintEvent(self, event):
        painter = QPainter(self)
        # Draw the progress bar's background
        rect = self.rect()
        painter.setBrush(self.palette().color(QPalette.Window))  # Use 'Window' role for background
        painter.drawRect(rect)

        # Calculate the width of the progress bar
        progress_rect = QRect(rect)
        progress_rect.setWidth(int(rect.width() * self.value() / self.maximum()))
        painter.setBrush(self.palette().color(QPalette.Highlight))  # Use 'Highlight' role for progress
        painter.drawRect(progress_rect)

        # Set the text color
        painter.setPen(self.palette().color(QPalette.Text))  # Use 'Text' role for text color

        # Calculate the text position
        text = f"{self.value():.2f}%"
        text_rect = QRect(rect)
        text_width = painter.fontMetrics().width(text)
        # Draw the text
        if self.value() > 0:
            # If there is progress, ensure the text is on the right side of the filled area
            text_position = max(0, progress_rect.width() - text_width - 5)  # Use max to avoid negative position
        else:
            # If there is no progress, center the text
            text_position = int((rect.width() - text_width) / 2)  # Cast to int to ensure correct argument type

        text_rect.setLeft(text_position)
        painter.drawText(text_rect, Qt.AlignLeft | Qt.AlignVCenter, text)







class TodoList(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("待办事项列表")
        self.setGeometry(300, 300, 600, 500)
        self.setWindowIcon(QIcon('icon.png'))  # 设置窗口图标,确保 'icon.png' 在您的文件夹中  

        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        layout.setSpacing(10)
        layout.setAlignment(Qt.AlignTop)

        self.task_input = QLineEdit(self)
        self.task_input.setMinimumHeight(30)
        self.task_input.returnPressed.connect(self.add_task)  # 连接信号

        layout.addWidget(self.task_input)

        # Create a horizontal layout for the buttons
        buttons_layout = QHBoxLayout()

        # Add Task Button
        add_button = QPushButton("添加任务", self)
        add_button.clicked.connect(self.add_task)
        buttons_layout.addWidget(add_button)

        # Delete Task Button
        delete_button = QPushButton("删除任务", self)
        delete_button.clicked.connect(self.delete_task)
        buttons_layout.addWidget(delete_button)

        # Edit Task Button
        edit_button = QPushButton("编辑任务", self)
        edit_button.clicked.connect(self.edit_task)
        buttons_layout.addWidget(edit_button)

        # Add the horizontal layout to the main vertical layout
        layout.addLayout(buttons_layout)

        self.task_list = QListWidget(self)
        layout.addWidget(self.task_list)



        self.search_input = QLineEdit(self)
        self.search_input.setPlaceholderText("搜索任务...")
        self.search_input.textChanged.connect(self.search_tasks)
        layout.addWidget(self.search_input)



        self.process_label = QLabel(self)
        self.process_label.setText("任务进度:")
        layout.addWidget(self.process_label)

        self.progress_bar = MovingTextProgressBar(self)
        self.progress_bar.setMaximum(100)
        layout.addWidget(self.progress_bar)

        self.load_tasks()
        self.update_progress()  # Update the progress bar


        self.apply_stylesheet()







    def apply_stylesheet(self):
        self.setStyleSheet("""  
            QListWidget, QLineEdit, QProgressBar, QComboBox {                font-size: 14px;                border: 1px solid #c0c0c0;                border-radius: 5px;                padding: 5px;            }            QPushButton {                background-color: #5cacee;                border-radius: 5px;                color: white;                padding: 6px;                margin: 5px 0;            }            QPushButton:hover {                background-color: #1e90ff;            }        """)

    def add_task(self):
        task_text = self.task_input.text()
        if task_text:
            # 创建一个新的QListWidgetItem并设置其大小提示
            item = QListWidgetItem()
            item.setSizeHint(QSize(0, 40))  # 根据需要调整高度

            # 创建包含复选框和标签的小部件
            task_widget = QWidget()
            task_layout = QHBoxLayout()
            task_layout.setContentsMargins(5, 0, 5, 0)  # 适当设置边距

            # 创建复选框并连接状态变化信号
            chkBox = QCheckBox()
            chkBox.stateChanged.connect(self.update_progress)

            # 创建标签显示文本
            task_label = QLabel(task_text)
            task_label.setMargin(5)  # 如果文本靠得太近,可以添加一些边距

            # 将复选框和标签添加到布局
            task_layout.addWidget(chkBox)
            task_layout.addWidget(task_label)
            task_layout.addStretch(1)  # 添加拉伸因子使得控件靠左排列

            # 设置任务小部件的布局
            task_widget.setLayout(task_layout)

            # 将任务小部件设置为列表项的小部件
            self.task_list.addItem(item)
            self.task_list.setItemWidget(item, task_widget)

            # 清除输入字段并更新进度
            self.task_input.clear()
            self.update_progress()

    def delete_task(self):
        for item in self.task_list.selectedItems():
            self.task_list.takeItem(self.task_list.row(item))
        self.update_progress()

    def update_progress(self):
        total_tasks = self.task_list.count()
        completed_tasks = sum(1 for i in range(total_tasks) if
                              self.task_list.itemWidget(self.task_list.item(i)).findChild(QCheckBox).isChecked())
        # Protect against division by zero if there are no tasks
        progress = int((completed_tasks / total_tasks) * 100) if total_tasks > 0 else 0
        self.progress_bar.setValue(progress)
        self.progress_bar.setFormat(f"{progress:.2f}%" if progress >= 0 else "0.00%")

        self.save_tasks()

    def edit_task(self):
        selected_items = self.task_list.selectedItems()
        if selected_items:
            item = selected_items[0]
            new_text, ok = QInputDialog.getText(self, "编辑任务", "任务描述:", QLineEdit.Normal, item.text())
            if ok and new_text:
                item.setText(new_text)

    def closeEvent(self, event):
        tasks = []
        for i in range(self.task_list.count()):
            item = self.task_list.item(i)
            widget = self.task_list.itemWidget(item)
            if widget:
                checkbox = widget.findChild(QCheckBox)
                tasks.append({'text': item.text(), 'completed': checkbox.isChecked()})
        with open('tasks.json', 'w') as file:
            json.dump(tasks, file)
        event.accept()

    def load_tasks(self):
        try:
            with open('tasks.json', 'r') as file:
                tasks = json.load(file)
            for task in tasks:

                item = QListWidgetItem()
                self.task_list.addItem(item)

                task_widget = QWidget()
                task_layout = QHBoxLayout()
                task_layout.setContentsMargins(5, 5, 5, 5)

                chkBox = QCheckBox()
                # First, disconnect any existing connections to avoid duplicate signals
                try:
                    chkBox.stateChanged.disconnect()
                except TypeError:
                    # If there is no connection, a TypeError is thrown, which is fine
                    pass
                # Then, set the checked state
                chkBox.setChecked(task['completed'])
                # Now, connect the signal to the slot
                chkBox.stateChanged.connect(self.update_progress)

                task_label = QLabel(task['text'])
                task_layout.addWidget(chkBox)
                task_layout.addWidget(task_label)
                task_layout.addStretch(1)

                task_widget.setLayout(task_layout)

                item.setSizeHint(task_widget.sizeHint())
                self.task_list.setItemWidget(item, task_widget)

            # After all tasks have been loaded, update the progress bar
                self.update_progress()



        except Exception as e:
            print(f"Error loading tasks: {e}")

            # 新增 sort_tasks 方法




    def search_tasks(self, keyword):
        for i in range(self.task_list.count()):
            item = self.task_list.item(i)
            widget = self.task_list.itemWidget(item)
            if widget:
                label = widget.findChild(QLabel)
                # 确保我们找到了标签并且比较其文本
                if label and keyword.lower() in label.text().lower():
                    item.setHidden(False)
                else:
                    item.setHidden(True)

    # 程序结束时调用
    def closeEvent(self, event):
        self.save_tasks()
        super().closeEvent(event)

    # 新增保存和加载任务的方法
    def save_tasks(self):
        tasks = []
        for i in range(self.task_list.count()):
            item = self.task_list.item(i)
            widget = self.task_list.itemWidget(item)
            if widget:
                label = widget.findChild(QLabel)
                checkbox = widget.findChild(QCheckBox)
                task_text = label.text() if label else ""
                task_completed = checkbox.isChecked() if checkbox else False
                tasks.append({'text': task_text, 'completed': task_completed})
        with open('tasks.json', 'w') as file:
            json.dump(tasks, file)


app = QApplication([])
window = TodoList()
window.show()
app.exec_()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1316696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

持续集成交付CICD:Jenkins流水线操作Harbor仓库

目录 一、实验 1.Jenkins主节点安装Docker 2.Jenkins主节点安装Harbor 3.Jenkins从节点安装Docker 4.Jenkins流水线操作Harbor仓库 二、问题 1.Jenkins主节点登录Harbor仓库报错 2.Jenkins流水线里从节点操作docker报错 3.Jenkins流水线里从节点远程登录Harbor仓库报错…

西南科技大学数据库实验二(表数据插入、修改和删除)

一、实验目的 (1)学会用SQL语句对数据库进行插入、修改和删除数据操作 (2)掌握insert、update、delete命令实现对表数据插入、修改和删除等更新操作。 二、实验任务 创建数据库,并创建Employees表、Departments表和…

佛山IBM System x3550 M4服务器维修检查

案例背景: 一家位于东莞的制造公司,在其佛山分厂中安装了一台IBM X3550 M4服务器作为其关键业务设备。该服务器负责管理和存储公司的生产数据、ERP系统和供应链数据。在生产过程中,该服务器突然发生了故障,导致佛山分厂的生产中断…

maven+spock

pom配置 话说JunitMockito的组合用起来是真难用&#xff0c;还是Spock的简单&#xff0c;尤其是参数化的测试。junit的Parameter是鸡肋&#xff0c;杂恶心&#xff1b;Theories用来也不爽。 <?xml version"1.0" encoding"UTF-8"?><project xm…

如何预防最新的.locked、.locked1勒索病毒感染您的计算机?

尊敬的读者&#xff1a; 近期&#xff0c;网络安全领域迎来一股新潮——.locked、.locked1勒索病毒的威胁&#xff0c;其先进的加密技术令人生畏。本文将深入剖析.locked、.locked1勒索病毒的阴谋&#xff0c;提供特色数据恢复策略&#xff0c;并揭示锁定恶劣行径的先锋预防手…

如何实现订单自动取消

由于Redis具有过期监听的功能&#xff0c;于是就有人拿它来实现订单超时自动关闭的功能&#xff0c;但是这个方案并不完美。今天来聊聊11种实现订单超时自动关闭的方案&#xff0c;总有一种适合你&#xff01;这些方案并没有绝对的好坏之分&#xff0c;只是适用场景的不大相同。…

linux系统启动时运行web程序

1.修改rc.local文件 执行命令如果找不到会报错command not found &#xff0c;使用全路径即可 找不到的话 可以使用which 命令 找到路径 后台查看执行日志 2.修改rc.local文件的权限 chmod x rc.local 然后reboot 可以查到进程和启动日志

设计模式 简单工厂 工厂方法模式 抽象工厂模式

工厂模式介绍 工厂模式是我们最常用的实例化对象模式了&#xff0c;是用工厂方法代替new操作的一种模式。它是创建型模式。 简单工厂 简单工厂模式是指由一个工厂对象决定创建出哪一种产品类的实例, 但它不属于GOF 23种设计模式 简单工厂适用于工厂类负责创建的对象较少的场景,…

Docker部署wordpress和Jenkins

准备机器&#xff1a; 192.168.58.151 &#xff08;关闭防火墙和selinux&#xff09; 安装好docker服务 &#xff08;详细参照&#xff1a;http://t.csdnimg.cn/usG0s 中的国内源安装docker&#xff09; 部署wordpress: 创建目录&#xff1a; [rootdocker ~]# mkdi…

2043杨辉三角(C语言)

目录 一&#xff1a;题目 二&#xff1a;思路分析 三&#xff1a;代码 一&#xff1a;题目 二&#xff1a;思路分析 1.通过杨辉三角&#xff0c;不难发现中间的数等于肩头两个数之和 2.但是当我们的输出结果&#xff0c;与杨辉三角的形式有所不同&#xff0c;但是我们可以找…

智能优化算法应用:基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.秃鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

【MySQL】SQL通用语法 、介绍SQL分类

SQL通用语法 1.SQL语句可以单行或多行书写&#xff0c;以分号结尾 2.MySQL数据库的SQL语句不区分大小写&#xff0c;关键字建议使用大写。 3.注释&#xff1a; 单行注释&#xff1a; -- 或 # 多行注释: /* */ SQL分类 SQL分类主要分为4类 分别是 DDL DML DQL DCL

Stable Diffusion 微调及推理优化实践指南

随着 Stable Diffsuion 的迅速走红&#xff0c;引发了 AI 绘图的时代变革。然而对于大部分人来说&#xff0c;训练扩散模型的门槛太高&#xff0c;对 Stable Diffusion 进行全量微调也很难入手。由此&#xff0c;社区催生了一系列针对 Stable Diffusion 的高效微调方案&#xf…

go学习redis的学习与使用

文章目录 一、redis的学习与使用1.Redis的基本介绍2.Redis的安装下载安装包即可3.Redis的基本使用1&#xff09;Redis的启动&#xff1a;2&#xff09;Redis的操作的三种方式3&#xff09;说明&#xff1a;Redis安装好后&#xff0c;默认有16个数据库&#xff0c;初始默认使用0…

深入解析 Spring 和 Spring Boot 的区别

目录 引言 1. 设计理念 1.1 Spring 框架的设计理念 1.2 Spring Boot 的设计理念 2. 项目配置 2.1 Spring 框架的项目配置 2.2 Spring Boot 的项目配置 3. 自动配置 3.1 Spring 框架的自动配置 3.2 Spring Boot 的自动配置 4. 微服务支持 4.1 Spring 框架的微服务支持…

C# WPF上位机开发(加密和解密)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在报文传输的过程中&#xff0c;根据报文传输的形态&#xff0c;有两种形式&#xff0c;一种是明文传输&#xff0c;一种是加密传输。当然明文传输…

基于单片机的太阳能数据采集系统(论文+源码)

1. 系统设计 在本次太阳能数据采集系统的设计中&#xff0c;以AT89C52单片机为主要核心&#xff0c;主要是由LCD液晶显示模块、存储模块、温度检测模块、串口通信模块&#xff0c;光照检测模块等组成&#xff0c;其实现了对太阳能板的温度&#xff0c;光照强度的检测和记录&…

【Qt开发流程】之UDP

概述 UDP (User Datagram Protocol)是一种简单的传输层协议。与TCP不同&#xff0c;UDP不提供可靠的数据传输和错误检测机制。UDP主要用于那些对实时性要求较高、对数据传输可靠性要求较低的应用&#xff0c;如音频、视频、实时游戏等。 UDP使用无连接的数据报传输模式。在传…

2机5节点系统潮流MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 电力系统潮流计算是电力系统最基本的计算&#xff0c;也是最重要的计算。所谓潮流计算&#xff0c;就是已知电网的接线方式与参数及运行条件&#xff0c;计算电力系统稳态运行各母线电压、各支路电流、功率及…

AntDesignBlazor示例——分页查询

本示例是AntDesign Blazor的入门示例&#xff0c;在学习的同时分享出来&#xff0c;以供新手参考。 示例代码仓库&#xff1a;https://gitee.com/known/BlazorDemo 1. 学习目标 分页查询框架天气数据分页功能表格自定义分页 2. 创建分页查询框架 Table组件分页默认为前端分…