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

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_()

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

W25N01GV 芯片应用

项目中处于成本考虑,要把Nor Flash换成低成本的Nand Flash。 这里总结下芯片应用。 总体概述: 1)W25N01(NandFlash)和W25Q(Nor Flash)的操作大不一样。 NandFlash擦除以块(128KB&…

LeetCode-反转链表问题

1.反转链表 题目描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 思路: 反转链表也就是链表倒置,我一直以来的办法就是先建立一个头节点,之后再遍历链表来进行头插。 代码&#xff1…

HBuilderX 配置 夜神模拟器 详细图文教程

在电脑端查看App的效果,不用真机调试,下载一个模拟器就可以了 --- Nox Player,夜神模拟器,是一款 Android 模拟器。他的使用非常安全,最重要的是完全免费。 一. 安装模拟器 官网地址: (yeshen.com) 二.配…

也许你不需要人工智能

已经不记得我是什么时候开始使用谷歌搜索引擎的, 在刚开始的时候,我看到了一本书,里面有各种各样的搜索技巧。在考虑到如果我不会搜索引擎这种关键技能,那么我将在这个信息时代落后,我读了那本书。 从那本书中我学到了…

运行软件时提示msvcp140.dll丢失的5个解决方法

电脑打开软件提示找不到msvcp140.dll丢失,这是许多用户在使用电脑过程中会遇到的问题。本文将为您介绍五个详细的解决方法,以及msvcp140.dll丢失的原因、作用和是什么。 一、msvcp140.dll丢失原因 系统损坏:操作系统在使用过程中&#xff0…

使用SPSS的McNemar检验两种深度学习模型的差异性

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 使用SPSS的McNemar检验两种深度学习模型的差异性 前言简述:一、McNemar检验1.1来源1.2 两配对样本的McNemar(麦克尼马尔)变化显著性检验1.3 适用范围:…

机器学习之无监督学习

聚类:发掘纵向结构的某种模式信息,某些x属于相同的分布或者类别 特征学习:发掘横向结构的某种模式信息,每一行都可以看成是一种属性或特征 密度估计:发掘底层数据分布,x都是从某个未知分布p(x)采出来的&a…

Mysql数据库 19.Mysql 锁

MySQL锁 锁:锁是计算机用以协调多个进程间并发访问同一共享资源的一种机制,在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源,如何保证数据并发访问的一…

C#中的封装、继承和多态

1.引言 在面向对象的编程中,封装、继承和多态是三个重要的概念。它们是C#语言中的基本特性,用于设计和实现具有高内聚和低耦合的代码。本文将详细介绍C#中的封装、继承和多态的相关知识。 目录 1.引言2. 封装2.1 类2.2 访问修饰符 3. 继承4. 多态4.1 虚方…

高云GW1NSR-4C开发板M3核串口通信

1.PLLVR频率计算 高云的M3核要用到PLLVR核,其输出频率FCLKIN*(FBDIV_SEL1)/(IDIV_SEL1),但同时要满足FCLKIN*(FBDIV_SEL1)*ODIV_SEL)/(IDIV_SEL1)的值在600MHz和1200MHz之间。例如官方示例,其输入频率FCLKIN50MHz,要输出80MHz&am…

鸿蒙原生应用/元服务开发-Stage模型能力接口(三)

一、说明 AbilityLifecycleCallback模块提供应用上下文ApplicationContext的生命周期发生变化时触发相应回调的能力,包括onAbilityCreate、onWindowStageCreate、onWindowStageActive、onWindowStageInactive、onWindowStageDestroy、onAbilityDestroy、onAbilityFo…

一文弄懂自编码器 -- Autoencoders

1. 引言 近年来,自编码器(Autoencoder)一词在许多人工智能相关的研究论文、期刊和学位论文中被频繁提及。自动编码器于1980年推出,是一种用于神经网络的无监督学习技术,可以从未被标注的训练集中学习。 本文重点介绍…

指针进阶篇

指针的基本概念: 指针是一个变量,对应内存中唯一的一个地址指针在32位平台下的大小是4字节,在64位平台下是8字节指针是有类型的,指针类型决定该指针的步长,即走一步是多长指针运算:指针-指针表示的是两个指…

协方差和相关系数,还有信号与系统里的 互相关函数

协方差和相关系数参考:https://www.bilibili.com/video/BV1vK411N7Yp/ 协方差和相关系数的思想就是:同增同减,找相关的变量 协方差公式是如下图老师的板书 可以发现,当 X Y 同增同减趋势明显时,协方差的值就越大 所…

STM32_启动流程详解

目录标题 前言 启动流程概述复位中断函数详解SystemInit函数详解 __main函数详解 附录 stm32单片机的存储器映像中断向量表的映射 前言 最近在学习IAP远程OTA升级单片机固件程序,发现自己对单片机的启动流程还不是那么了解,就总结整理一下吧。 启动流程…

记录一次API报文替换点滴

1. 需求 各位盆友在日常开发中,有没有遇到上游接口突然不合作了,临时需要切换其他接口的情况?这不巧了,博主团队近期遇到了,又尴尬又忐忑。 尴尬的是临时通知不合作了,事前没有任何提醒; 忐忑…

位图、布隆过滤器、海量数据处理

文章目录 位图布隆过滤器海量数据处理 正文开始前给大家推荐个网站,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 位图 概念:所谓位图,就是用每一…

【Spring】08 BeanNameAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点,其中之一就是 Bean 生命周期中的回调接口。本文将聚焦于其中的一个接口 BeanNameAware,介绍它的作…

深度学习中的预测图片中的矩形框、标签、置信度分别是什么意思。

问题描述:深度学习中的预测图片中的矩形框、标签、置信度分别是什么意思。 问题解答: 目标框(Bounding Box): 描述目标位置的矩形边界框。 类别标签: 表示模型认为目标属于哪个类别(例如&#…

opencv 十六 python下各种连通域处理方法(按面积阈值筛选连通域、按面积排序筛选连通域、连通域分割等方法)

本博文基于python-opencv实现了按照面积阈值筛选连通域、按照面积排序筛选topK连通域、 连通域细化(连通域骨架提取)、连通域分割(基于分水岭算法使连通域在细小处断开)、按照面积排序赛选topK轮廓等常见的连通域处理代码。并将代码封装为shapeUtils类,在自己的python代码…