使用PyQt編寫一個簡單的待辦程序
框架選擇
一個簡單的GUI程序,可以使用pyqt完成。pyqt是qt的python實現(xiàn)版本。
界面搭建
設(shè)計一個美觀

簡潔的界面
class ToDoApp(QWidget):
def __init__(self):
super().__init__()
# 設(shè)置窗口屬性
self.setWindowTitle("Daily To Do List")
self.setGeometry(100, 100, 400, 400)
# 初始化主布局
self.main_layout = QVBoxLayout()
# 創(chuàng)建輸入和添加按鈕
self.input_layout = QGridLayout()
# 標(biāo)題輸入
self.title_label = QLabel("標(biāo)題:")
self.title_input = QLineEdit()
self.input_layout.addWidget(self.title_label, 0, 0)
self.input_layout.addWidget(self.title_input, 0, 1)
# 描述輸入
self.description_label = QLabel("描述:")
self.description_input = QLineEdit()
self.input_layout.addWidget(self.description_label, 1, 0)
self.input_layout.addWidget(self.description_input, 1, 1)
# 水平按鈕布局
self.add_layout = QHBoxLayout()
self.add_layout.setSpacing(20)
# 導(dǎo)入
self.import_button = QPushButton('批量導(dǎo)入')
self.import_button.clicked.connect(self.import_item)
self.add_layout.addWidget(self.import_button)
self.add_button = QPushButton("添加")
self.add_button.clicked.connect(self.add_item)
self.add_layout.addWidget(self.add_button)
# 任務(wù)列表
self.to_do_list = QListWidget()
self.to_do_list.setStyleSheet("padding: 10px;")
self.to_do_list.setVerticalScrollMode(QListWidget.ScrollPerPixel)
# 創(chuàng)建操作按鈕
self.buttons_layout = QHBoxLayout()
self.mark_done_button = QPushButton("標(biāo)記完成")
self.mark_done_button.clicked.connect(self.mark_item_done)
self.delete_button = QPushButton("刪除")
self.delete_button.clicked.connect(self.delete_item)
self.buttons_layout.addWidget(self.mark_done_button)
self.buttons_layout.addWidget(self.delete_button)
# 將布局添加到主窗口
self.main_layout.addLayout(self.input_layout)
self.main_layout.addLayout(self.add_layout)
self.main_layout.addWidget(self.to_do_list)
self.main_layout.addLayout(self.buttons_layout)
# 設(shè)置窗口布局
self.setLayout(self.main_layout)
QGridLayout的使用
QGridLayout是網(wǎng)格布局,在添加子窗口時可以設(shè)定位置(行、列)和占據(jù)的大?。ㄕ紟仔袔琢校?/p>
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QLabel, QPushButton
class GridExample(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("PyQt QGridLayout Example")
self.setGeometry(100, 100, 400, 300)
# 創(chuàng)建 QGridLayout
layout = QGridLayout(self)
# 創(chuàng)建一個標(biāo)題標(biāo)簽
title_label = QLabel("Header Label", self)
title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: blue;")
# 將標(biāo)題設(shè)置為占據(jù)第一行的所有列
layout.addWidget(title_label, 0, 0, 1, 3) # 行, 列, 占據(jù)行數(shù), 占據(jù)列數(shù)
# 創(chuàng)建一個 3x3 的按鈕網(wǎng)格
for i in range(3):
for j in range(3):
button = QPushButton(f"Button {i*3 + j +1}", self)
layout.addWidget(button, i + 1, j) # 按鈕從行1開始
# 創(chuàng)建一個右側(cè)按鈕占據(jù)一列
right_button = QPushButton("Right Button", self)
# 設(shè)置右側(cè)按鈕占據(jù)第3列的所有行
layout.addWidget(right_button, 1, 3, 3, 1) # 行1到3, 列3
# 創(chuàng)建一個底部按鈕占據(jù)一行
bottom_button = QPushButton("Bottom Button", self)
# 設(shè)置底部按鈕占據(jù)第4行的所有列
layout.addWidget(bottom_button, 4, 0, 1, 4) # 行4, 列0-3
self.setLayout(layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = GridExample()
window.show()
sys.exit(app.exec_())
強制子窗口獨立
在 PyQt 中,當(dāng)給窗口(QWidget 或子類)設(shè)置 parent 后窗口不顯示,通常是因為 子窗口被嵌入到了父窗口的布局中,而非作為獨立窗口顯示。以下是常見原因和解決方案:
1. 根本原因:parent 的作用
parent 的作用:在 PyQt 中,parent 表示窗口的父控件。若設(shè)置 parent:
- 子窗口會嵌入到父窗口中,成為父窗口的一部分(類似按鈕、文本框等控件)。
- 子窗口的生命周期與父窗口綁定(父窗口銷毀時,子窗口自動銷毀)。
- 子窗口默認不會作為獨立窗口彈出,而是跟隨父窗口的布局顯示。
關(guān)鍵區(qū)別:
# 獨立窗口(無 parent) child_window = QWidget() child_window.show() # 嵌入父窗口(設(shè)置 parent) child_window = QWidget(parent=main_window) # 不會獨立顯示,而是嵌入到 main_window 中
2. 常見場景和解決方法
場景 1:希望子窗口作為獨立窗口彈出
錯誤寫法:
parent_window = QWidget() child_window = QWidget(parent=parent_window) # 設(shè)置 parent child_window.show() # ? 不會顯示獨立窗口!
原因:child_window 已成為 parent_window 的子控件,必須通過父窗口的布局顯示(例如將 child_window 添加到父窗口的 QVBoxLayout 中)。
解決方法:不要設(shè)置 parent,讓子窗口獨立:
parent_window = QWidget() child_window = QWidget() # 無 parent child_window.show() # ? 作為獨立窗口顯示
場景 2:希望子窗口作為模態(tài)對話框彈出
錯誤寫法:
parent_window = QWidget() child_window = QWidget(parent=parent_window) child_window.setWindowModality(Qt.ApplicationModal) # 設(shè)置為模態(tài) child_window.show() # ? 仍然不顯示!
原因:child_window 是 parent_window 的子控件,必須通過父窗口布局顯示,或明確設(shè)置為獨立窗口。
解決方法:使用 Qt.Window 標(biāo)志強制子窗口成為獨立窗口:
child_window = QWidget(parent=parent_window) child_window.setWindowFlags(Qt.Window) # 關(guān)鍵:強制為獨立窗口 child_window.setWindowModality(Qt.ApplicationModal) child_window.show() # ? 作為模態(tài)對話框彈出
場景 3:子窗口被正確添加到父窗口布局但仍不顯示
錯誤寫法:
parent_window = QWidget() child_window = QWidget(parent=parent_window) parent_window.show() # ? 只顯示 parent_window,但 child_window 未添加到布局中
原因:child_window 需要被添加到父窗口的布局管理器(如 QVBoxLayout),或手動設(shè)置其位置。
解決方法:將子窗口添加到父窗口布局:
parent_window = QWidget() layout = QVBoxLayout(parent_window) # 父窗口設(shè)置布局 child_window = QWidget() layout.addWidget(child_window) # 添加到布局 parent_window.show() # ? 父窗口和子控件均顯示
3. 通用檢查列表
如果子窗口不顯示,按以下步驟排查:
.是否設(shè)置 parent:
若設(shè)置 parent,子窗口需要添加到父窗口的布局中。
.父窗口是否已顯示:
父窗口調(diào)用 show() 后,子控件才會顯示。
.窗口標(biāo)志是否正確:
使用 setWindowFlags(Qt.Window) 強制子窗口獨立。
.布局是否正確:
確保子窗口被添加到父窗口的布局管理器(如 addWidget(child))。
.生命周期問題:
確保父窗口未被提前銷毀(例如在局部作用域中被垃圾回收)。
4. 完整示例對比
示例 1:子窗口嵌入父窗口(正確寫法)
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication
app = QApplication([])
# 父窗口
parent = QWidget()
layout = QVBoxLayout(parent)
# 子窗口(作為控件嵌入父窗口)
child = QPushButton("我是子控件", parent=parent)
layout.addWidget(child)
parent.show()
app.exec_()
示例 2:子窗口作為獨立窗口(正確寫法)
from PyQt5.QtWidgets import QWidget, QApplication
app = QApplication([])
# 父窗口
parent = QWidget()
parent.show()
# 子窗口(獨立窗口,無 parent)
child = QWidget()
child.setWindowTitle("我是獨立子窗口")
child.show()
app.exec_()
5. 特殊場景:動態(tài)創(chuàng)建子窗口
若通過按鈕點擊動態(tài)創(chuàng)建子窗口,需確保子窗口的引用不被銷毀:
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QApplication
app = QApplication([])
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.button = QPushButton("打開子窗口", self)
self.button.clicked.connect(self.open_child)
self.setLayout(QVBoxLayout())
self.layout().addWidget(self.button)
def open_child(self):
self.child = QWidget() # 必須保存為成員變量,否則會被垃圾回收!
self.child.setWindowTitle("子窗口")
self.child.show()
window = MainWindow()
window.show()
app.exec_()
通過理解 parent 的作用和布局機制,可以靈活控制窗口的顯示方式。
模型設(shè)計
每一個待辦事項有
- 標(biāo)題
- 描述信息
- 生成時間
- 是否完成標(biāo)識
- 截止時間
class ToDoItem:
def __init__(self, title, description,deadline_time=None,is_completed=False):
self.title = title
self.description = description
self.deadline_time = deadline_time
self.is_completed = False
自定義信號
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QMessageBox
from PyQt5.QtCore import pyqtSignal
import sys
class MyWidget(QWidget):
# 定義一個自定義信號
custom_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("PyQt Custom Signal Example")
self.setGeometry(100, 100, 300, 200)
# 創(chuàng)建一個按鈕
self.button = QPushButton("Click Me", self)
self.button.clicked.connect(self.emit_custom_signal)
# 設(shè)置布局
layout = QVBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
# 連接自定義信號到槽
self.custom_signal.connect(self.handle_custom_signal)
def emit_custom_signal(self):
"""發(fā)射自定義信號"""
self.custom_signal.emit("Hello from custom signal!")
def handle_custom_signal(self, message):
"""處理自定義信號"""
QMessageBox.information(self, "Custom Signal", message)
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
批量導(dǎo)入功能
使用json格式的字符串進行批量導(dǎo)入,大致格式如下
{
"items": [
{
"title": "測試標(biāo)題1",
"description": "測試1",
"deadline_time": "2024",
"is_completed": false
},
{
"title": "測試標(biāo)題2",
"description": "測試2",
"deadline_time": "2025",
"is_completed": true
}
]
}
大致就是新開一個窗口,創(chuàng)建一個QTextEdit輸入對應(yīng)的json數(shù)據(jù),然后通過json.loads()方法解析對應(yīng)數(shù)據(jù),
逐個使用add_item()接口添加,這要求add_item()能夠處理多種情況:按鈕點擊觸發(fā)無需參數(shù)和批量導(dǎo)入中需要傳遞參數(shù)。
而在python中實現(xiàn)類似重載的效果可以給參數(shù)一個默認值None,再在函數(shù)內(nèi)部分情況處理
class ImportWidget(QWidget):
# 自定義信號
import_finished = pyqtSignal(dict)
def __init__(self,parent):
super().__init__(parent)
self.setFixedSize = (500,500)
self.setWindowFlags(Qt.Window) # 關(guān)鍵:強制為獨立窗口
self.input_field = QTextEdit()
self.main_layout = QVBoxLayout()
self.btn_layout = QHBoxLayout()
self.confirm_button = QPushButton("導(dǎo)入")
self.cancel_button = QPushButton("取消")
self.btn_layout.addWidget(self.confirm_button)
self.btn_layout.addWidget(self.cancel_button)
self.confirm_button.clicked.connect(self.read_json_data)
# 連接自定義信號到槽
self.import_finished.connect(self.close)
self.main_layout.addLayout(self.btn_layout)
self.main_layout.addWidget(self.input_field)
self.setLayout(self.main_layout)
def read_json_data(self):
text = self.input_field.toPlainText() # 獲取輸入框的文本
# print(f"原始文本內(nèi)容: {text}") # 調(diào)試:打印原始文本內(nèi)容
try:
# 將輸入的文本解析為 JSON 數(shù)據(jù)
json_data = json.loads(text.strip()) # 使用 strip() 去除首尾空白字符
# print(f"解析后的 JSON 數(shù)據(jù): {json_data}")
self.json_data = json_data
self.import_finished.emit(json_data)
except json.JSONDecodeError as e:
# 如果 JSON 格式不正確,打印錯誤信息
print(f"JSON 解析失敗: {e}")
self.json_data = None
class ToDoApp(QWidget):
def batch_import(self,json_data):
print(json_data['items'])
items = json_data['items']
for item in items:
self.add_item(item['title'],item['description'])
def add_item(self, checked,title=None, description=None):
# 如果 title 和 description 是傳入的參數(shù)
if title is not None or description is not None:
# 使用傳入的參數(shù)
title = title.strip() if title else ""
description = description.strip() if description else ""
else:
# 獲取輸入框的文本
title = self.title_input.text().strip()
print(title, description)
description = self.description_input.text().strip()
# TODO 優(yōu)化時間顯示居右
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 創(chuàng)建 ToDoItem 實例
todo_item = ToDoItem(title, description, current_time)
# 創(chuàng)建新的列表項 創(chuàng)建自定義Widget
item_widget = QWidget()
layout = QHBoxLayout()
# 標(biāo)題部分
title_label = QLabel(todo_item.title)
title_label.setStyleSheet("QLabel{padding:0px}")
title_label.setAlignment(Qt.AlignmentFlag.AlignLeft| Qt.AlignmentFlag.AlignVCenter)
# 時間部分
time_label = QLabel(todo_item.created_time)
time_label.setStyleSheet("QLabel{padding:0px}") # 添加padding設(shè)置,Qlabel有默認padding,不設(shè)置話,會將文字截斷
time_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
# 添加控件到布局
layout.addWidget(title_label)
layout.addWidget(time_label)
item_widget.setLayout(layout)
# 創(chuàng)建ListWidgetItem
item = QListWidgetItem()
item.setSizeHint(item_widget.sizeHint()) # 設(shè)置每一項的寬高
item.setToolTip(todo_item.description) # 設(shè)置懸浮提示
item.setData(Qt.UserRole, todo_item) # 保存任務(wù)對象
item.setFlags(item.flags() | Qt.ItemIsSelectable | Qt.ItemIsEnabled)
item.setCheckState(Qt.CheckState.Unchecked)
self.to_do_list.addItem(item)
self.to_do_list.setItemWidget(item, item_widget)
# 清空輸入框
self.title_input.clear()
self.description_input.clear()
bug解析
使用按鈕連接點擊信號至槽函數(shù),發(fā)現(xiàn)槽函數(shù)add_item接收到的title參數(shù)不是預(yù)期的輸入值None(因為點擊事件的槽函數(shù)一般不帶參數(shù)),而是False。
1.PyQt 的 clicked 信號默認會傳遞一個布爾值:
QPushButton的clicked信號默認會發(fā)送一個checked參數(shù)(表示按鈕的選中狀態(tài))。- 如果你沒有顯式處理這個參數(shù),它會傳遞到槽函數(shù)中,導(dǎo)致
title參數(shù)被賦值為False(因為默認未選中)。
2.槽函數(shù)定義與信號參數(shù)不匹配:
你定義的 add_item 方法有兩個可選參數(shù):
def add_item(self, title=None, description=None):
當(dāng)通過 self.add_button.clicked.connect(self.add_item) 連接信號時,clicked 信號的 checked 參數(shù)(布爾值)會傳遞給 title 參數(shù)。
因此,點擊按鈕時 title 實際接收到的是 False,而不是預(yù)期的 None。
方法 1:顯式接收并忽略 checked 參數(shù)
修改槽函數(shù),增加一個參數(shù)接收 checked 值,但不在內(nèi)部使用它:
def add_item(self, checked, title=None, description=None): # 增加 checked 參數(shù)
# 如果 title 和 description 是傳入的參數(shù)
if title is not None or description is not None:
print("not null", title, description)
title = title.strip() if title else ""
description = description.strip() if description else ""
else:
# 獲取輸入框的文本
title = self.title_input.text().strip()
description = self.description_input.text().strip()
# 其他邏輯...
方法 2:使用 lambda 阻止參數(shù)傳遞
在連接信號時,通過 lambda 屏蔽 clicked 信號的參數(shù):
self.add_button.clicked.connect(lambda: self.add_item()) # 不傳遞任何參數(shù)
此時 title 和 description 將保持 None,代碼會從輸入框中讀取值。
關(guān)鍵點解釋
信號參數(shù)傳遞機制:
clicked信號默認發(fā)送checked(布爾值),而QPushButton默認不可選中,因此總是發(fā)送False。- 如果槽函數(shù)參數(shù)數(shù)量不匹配,第一個參數(shù)會接收這個
False。
參數(shù)優(yōu)先級問題:
- 如果調(diào)用
add_item時傳遞了參數(shù)(如add_item(title="測試")),title會被正確賦值。 - 若未傳遞參數(shù),
title會被錯誤地賦值為False(來自checked參數(shù))。
導(dǎo)出
既然有批量導(dǎo)入功能,就有導(dǎo)出功能
剪切板 QClipboard
在 PyQt 中,可以使用 QApplication.clipboard() 來訪問系統(tǒng)剪貼板,并通過 QClipboard 類的方法將數(shù)據(jù)復(fù)制到剪貼板
def export_to_clipboard(self):
# 獲取所有任務(wù)
items = []
for i in range(self.to_do_list.count()):
item = self.to_do_list.item(i)
if item:
todo_item = item.data(Qt.UserRole)
items.append({
"title": todo_item.title,
"description": todo_item.description,
"deadline_time": todo_item.deadline_time,
"is_completed": todo_item.is_completed
})
# 轉(zhuǎn)換為 JSON 格式
json_data = {
"items": items
}
json_str = json.dumps(json_data, indent=4, ensure_ascii=False) # 格式化 JSON 字符串
# 復(fù)制到剪切板
clipboard = QApplication.clipboard()
clipboard.setText(json_str)
# 彈出提示
QMessageBox.information(self, "提示", "已復(fù)制到剪切板")
常用的 QClipboard 方法
setText(text): 將文本復(fù)制到剪貼板。setPixmap(pixmap): 將圖片復(fù)制到剪貼板。setMimeData(mimeData): 將 MIME 數(shù)據(jù)(如 HTML)復(fù)制到剪貼板。clear(): 清除剪貼板內(nèi)容。
持久化存儲
- 數(shù)據(jù)庫sqlite
- 文件保存
直接寫入文件,不使用數(shù)據(jù)庫了,重寫關(guān)閉事件,保存代辦到文件,并在初始化的時候讀取文件
def init_from_file(self, file_path=None):
# 默認初始化文件為當(dāng)前目錄下的 to_do.json
if file_path is None:
file_path = "./to_do.json"
# 讀取文件內(nèi)容
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()
self.batch_import(json.loads(text))
def closeEvent(self, event):
# 關(guān)閉窗口時保存數(shù)據(jù)
with open("./to_do.json", "w", encoding="utf-8") as f:
self.export_to_clipboard(True)
f.write(QApplication.clipboard().text())
QApplication.clipboard().clear()
event.accept()
排序功能
截止時間ddl排序
def sort_by_ddl(self):
if self.sort_value == "asc":
self.sort_value = "desc"
else:
self.sort_value = "asc"
# 按 DDL 排序
items = []
for i in range(self.to_do_list.count()):
item = self.to_do_list.item(i)
if item:
todo_item = item.data(Qt.UserRole)
items.append(todo_item)
# 根據(jù)self.sort_value決定排序方向
if self.sort_value == "asc":
items.sort(key=self.sort_key)
else:
items.sort(key=self.sort_key, reverse=True)
# 清空列表
self.to_do_list.clear()
# 重新添加排序后的任務(wù)
for item in items:
self.add_item(item.title, item.description, item.deadline_time, item.is_completed)
def sort_key(self, item):
item.deadline_time.replace(":",":")
if item.deadline_time == "未知":
return datetime.datetime.max
else:
return datetime.datetime.strptime(item.deadline_time.replace(":",":"), "%Y-%m-%d %H:%M")
自定義排序規(guī)則
在Python中自定義排序規(guī)則,你可以使用內(nèi)置的sorted()函數(shù)或者列表對象的sort()方法,并通過key參數(shù)指定一個函數(shù)來定義排序規(guī)則。這個函數(shù)會對每個元素進行處理,并返回一個值,排序?qū)⒏鶕?jù)這個返回值進行。
按字符串長度排序:
strings = ["apple", "banana", "cherry", "date"] sorted_strings = sorted(strings, key=len) print(sorted_strings) # 輸出: ['date', 'apple', 'banana', 'cherry']
使用lambda函數(shù)按字符串的最后一個字符排序:
strings = ["apple", "banana", "cherry", "date"] sorted_strings = sorted(strings, key=lambda x: x[-1]) print(sorted_strings) # 輸出: ['banana', 'apple', 'date', 'cherry']
復(fù)雜排序規(guī)則,先按字符串長度排序,再按字母順序排序:
strings = ["apple", "banana", "cherry", "date"] sorted_strings = sorted(strings, key=lambda x: (len(x), x)) print(sorted_strings) # 輸出: ['date', 'apple', 'banana', 'cherry']
使用cmp_to_key將傳統(tǒng)比較函數(shù)轉(zhuǎn)換為key函數(shù):
from functools import cmp_to_key
def compare(x, y):
if x < y:
return -1
elif x > y:
return 1
else:
return 0
numbers = [3, 2, 5, 4, 1]
sorted_numbers = sorted(numbers, key=cmp_to_key(compare))
print(sorted_numbers) # 輸出: [1, 2, 3, 4, 5]
優(yōu)化條目顯示
添加一個標(biāo)題布局,顯示列表的標(biāo)題 => 放一個水平布局在QListWidget上對齊就可以

分離顯示與數(shù)據(jù)(QlistWidget):
- 不再直接使用 QListWidgetItem(text),而是通過
setItemWidget綁定自定義Widget - 數(shù)據(jù)仍存儲在 ToDoItem 對象中,界面僅負責(zé)展示
自定義Widget布局控制:
- 使用 QHBoxLayout 實現(xiàn)水平分列
- setAlignment 控制對齊方向
- setContentsMargins 調(diào)整內(nèi)容間距
Bug解析
文字出現(xiàn)了上下截斷的情況,嘗試過設(shè)置延伸策略,給item設(shè)置固定寬高都不能根治

發(fā)現(xiàn)隨著高度的變大,顯示的內(nèi)容越來越多,所以猜測是QLabel有默認的padding,所以截斷了文字
最后設(shè)置QStyleSheet成功解決
# TODO 優(yōu)化時間顯示居右
# 創(chuàng)建 ToDoItem 實例
todo_item = ToDoItem(title, description,deadline_time,is_completed)
# 創(chuàng)建新的列表項 創(chuàng)建自定義Widget
item_widget = QWidget()
layout = QHBoxLayout()
# 標(biāo)題部分
title_label = QLabel(todo_item.title)
title_label.setStyleSheet("QLabel{padding:0px}")
title_label.setAlignment(Qt.AlignmentFlag.AlignLeft| Qt.AlignmentFlag.AlignVCenter)
# 時間部分
time_label = QLabel(todo_item.deadline_time)
time_label.setStyleSheet("QLabel{padding:0px}") # 添加padding設(shè)置,Qlabel有默認padding,不設(shè)置話,會將文字截斷
time_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
# 添加控件到布局
layout.addWidget(title_label)
layout.addWidget(time_label)
item_widget.setLayout(layout)
# 創(chuàng)建ListWidgetItem
item = QListWidgetItem()
item.setSizeHint(item_widget.sizeHint()) # 設(shè)置每一項的寬高
item.setToolTip(todo_item.description) # 設(shè)置懸浮提示
item.setData(Qt.UserRole, todo_item) # 保存任務(wù)對象
item.setFlags(item.flags() | Qt.ItemIsSelectable | Qt.ItemIsEnabled |Qt.ItemIsUserCheckable)
if todo_item.is_completed:
item.setCheckState(Qt.CheckState.Checked)
else:
item.setCheckState(Qt.CheckState.Unchecked)
self.to_do_list.addItem(item)
self.to_do_list.setItemWidget(item, item_widget)
# 清空輸入框
self.title_input.clear()
self.description_input.clear()
self.deadline_input.clear()
綁定自定義widget后,點擊無法改變item的checkState
解決方法有很多種,這里采用連接父ListWidget的雙擊信號
self.to_do_list.doubleClicked.connect(self.on_double_clicked)
def on_double_clicked(self, index: QModelIndex):
print(index.row()) # 打印行號
print(index.column()) # 打印列號(通常為 0)
item = self.to_do_list.itemFromIndex(index) # 獲取 QListWidgetItem
if item.checkState() == Qt.CheckState.Unchecked:
item.setCheckState(Qt.CheckState.Checked)
elif item.checkState() == Qt.CheckState.Checked :
item.setCheckState(Qt.CheckState.Unchecked)
print(item.text()) # 打印項的文本
最終代碼
https://github.com/0zxm/ToDoApp/tree/master
以上就是使用PyQt編寫一個簡單的待辦程序的詳細內(nèi)容,更多關(guān)于PyQt待辦程序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決python3在anaconda下安裝caffe失敗的問題
下面小編就為大家?guī)硪黄鉀Qpython3在anaconda下安裝caffe失敗的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
numpy.std() 計算矩陣標(biāo)準(zhǔn)差的方法
今天小編就為大家分享一篇numpy.std() 計算矩陣標(biāo)準(zhǔn)差的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Python 獲取指定文件夾下的目錄和文件的實現(xiàn)
這篇文章主要介紹了Python 獲取指定文件夾下的目錄和文件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Python通過keyboard庫實現(xiàn)模擬和監(jiān)聽鍵盤
這篇文章主要為大家詳細介紹了Python如何通過keyboard庫實現(xiàn)模擬和監(jiān)聽鍵盤,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下2024-10-10
一波神奇的Python語句、函數(shù)與方法的使用技巧總結(jié)
這篇文章主要介紹了一波神奇的Python函數(shù)與方法的使用技巧總結(jié),包括裝飾器和with語句等的不常見用法,需要的朋友可以參考下2015-12-12

