如何解决一些PyQt5复选框在移动后不可点击
我正在创建一个待办事项列表,每次单击“添加任务”按钮时,都会添加一个复选框,文本框(QLineEdit)和QComboBox(以设置优先级1、2等)。单击每个相应任务的复选框后,该任务将移至任务列表的底部。相反,如果设置了优先级,则任务将移至任务列表的顶部。
问题是,在某些情况下,当我单击复选框时,什么也没有发生。永远不会发送用于标识复选框状态已更改的信号。即使它(或者至少是它的图像)在旁边,也几乎就像没有该复选框一样。如果通过取消选中设置了 none 的所有优先级,则会发生此问题-检查任何给定的任务是否有效,但是某些任务无法取消选中。其他任务也可以取消选中,有时如果我在取消其他选择之后又回到无法取消选中的任务,它将再次变为可选中状态。如果我将某些任务设置为具有优先级,然后再尝试检查复选框,则将根本无法选中某些复选框。同样,如果我要检查其他一些任务,然后再回到无法检查的任务,那么这次可以检查它。
我认为该错误可能是由于移动复选框而引起的。由于某些原因,某些复选框的功能在代码中丢失了。值得注意的一件事是在GUI中浏览各项。一旦移动了项目,单击“选项卡”将按最初设置时的顺序浏览项目(因此,光标实际上将在文本框周围跳转,而不是从上到下的顺序)。不知道这是否与错误有关;这只是我注意到的东西。我不知道这是PyQt5本身内部的错误,还是代码内可修复的错误。
import sys
from PyQt5.QtWidgets import (QWidget,QComboBox,QPushButton,QApplication,QLabel,QLineEdit,QTextEdit,QMainWindow,QAction,QShortcut,QCheckBox)
from PyQt5.QtGui import (QKeySequence)
from PyQt5 import QtWidgets,QtCore
from PyQt5.QtCore import pyqtSlot,QObject
class App(QMainWindow):
def __init__(self):
super().__init__()
self.i = 40
self.j = 80
self.counter = 1
self.list_eTasks = []
self.initUI()
def initUI(self):
#sets up window
self.setWindowTitle('To-Do List')
self.setGeometry(300,300,800,855)
#button
self.btn1 = QPushButton("Add Task",self)
self.btn1.clicked.connect(self.add_task_button_clicked)
#close the window via keyboard shortcuts
self.exit = QShortcut(QKeySequence("Ctrl+W" or "Ctrl+Q"),self)
self.exit.activated.connect(self.close)
self.show()
@pyqtSlot()
def add_task_button_clicked(self):
self.label = QLabel(self)
self.label.setText(str(self.counter))
self.label.move(5,self.i)
self.btn1.move(50,self.j)
self.textbox = QLineEdit(self)
self.textbox.resize(280,40)
self.checkbox = QCheckBox(self)
self.checkbox.stateChanged.connect(self.click_box)
self.combobox = QComboBox(self)
self.combobox.addItem("No Priority")
self.combobox.addItem("Priority 1")
self.combobox.addItem("Priority 2")
self.combobox.addItem("Priority 3")
self.combobox.activated[str].connect(self.combobox_changed)
self.obj = eTask(self.checkbox,self.textbox,self.combobox)
self.list_eTasks.append(self.obj)
self.textbox.show()
self.label.show()
self.checkbox.show()
self.combobox.show()
self.i += 40
self.j += 40
self.counter += 1
self.sort_eTasks()
def click_box(self,state):
self.sort_eTasks()
def move_everything(self,new_list):
count = 0
for item in new_list:
y_value = ((40)*(count + 1))
item.check.move(20,y_value)
item.text.move(50,y_value)
item.combo.move(350,y_value)
count += 1
return
def combobox_changed(self,state):
#make new list to not include any already checked off items
for task in self.list_eTasks:
if task.combo.currentText() == "Priority 1":
task.priority = 1
elif task.combo.currentText() == "Priority 2":
task.priority = 2
elif task.combo.currentText() == "Priority 3":
task.priority = 3
else:
task.priority = 10
self.sort_eTasks()
def sort_eTasks(self):
self.list_eTasks.sort(key=lambda eTask: eTask.getRank())
self.move_everything(self.list_eTasks)
for task in self.list_eTasks:
print(str(task.priority) + " pos: " + str(task.check.pos()))
print("-----------------------------------")
class eTask:
check = ""
text = ""
combo = ""
priority = 10
def __init__(self,c,t,co):
self.check = c
self.text = t
self.combo = co
def setP(self,p):
self.priority = p
def getRank(self):
if self.check.isChecked():
return self.priority + 100
else:
return self.priority
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
解决方法
您的代码存在各种问题,我将尝试解决所有问题。
- 排序机制不好:仅当项目更改优先级时才会发生排序(当用户单击复选框时,它也应适用)
- 使用类似的低值会导致意外的行为:如果我检查了
priority 1
的优先级会增加10,那么为什么它应该低于priority 2
甚至是3? - 排序机制应该是集中的,而不是被分解为不执行其他任何操作的更多功能:“一个功能可以全部统治” ;-)不要检查组合框内容,然后根据另一个外部函数调用进行排序:只需调用一个用于检查组合框和复选框的函数,然后对内容进行排序;记住:虽然OOP是关于模块化的,但是您应该避免不必要的分散(记住KISS principle);
- 您可能不应该连接到
activated
信号,并且当然不应该使用[str]
重载(对于基于索引的小部件,检查字符串很少是个好主意);改用currentIndex()
及其信号currentIndexChanged()
; - 应尽量避免为子小部件使用固定的几何形状;存在布局管理器的原因有很多,其中很多是出于非常好的理由(例如,如果鼠标光标离复选框太近,我就无法单击QLineEdit,即使在上方进行修改);请注意,您也应避免在顶层窗口中使用
setGeometry
; - 您永远不应设置将被不同对象覆盖的实例属性:您正在不断覆盖它们(
self.label
,self.textbox
等),结果是使它们成为实例属性是完全没有用的。因为您已经使用父窗口小部件创建了它们,所以垃圾收集器不会在函数末尾删除它们,因此您应该只使用局部变量(label = QLabel()
等); - 使用像您一样的类并不是很有用:无论如何都要对所有这些小部件进行分组,因此最好使用一个不仅可以管理其子级的 container 小部件(请参见关于布局),但将“集中”所有必要的编程逻辑;
- 如果类和变量引用不同的对象类型,请避免使用相似的名称:在您的情况下,
app
引用了QApplication
实例,但是App
是QMainWidget
子类;
这里可能是您的代码的重新实现:
from PyQt5 import QtCore,QtWidgets
class TaskWidget(QtWidgets.QWidget):
priorityChanged = QtCore.pyqtSignal()
lastChanged = QtCore.QDateTime.currentDateTime()
def __init__(self,title='',parent=None):
super().__init__(parent)
layout = QtWidgets.QHBoxLayout(self)
self.checkBox = QtWidgets.QCheckBox()
layout.addWidget(self.checkBox)
self.textBox = QtWidgets.QLineEdit(title)
layout.addWidget(self.textBox)
self.textBox.setMinimumWidth(240)
self.priorityCombo = QtWidgets.QComboBox()
layout.addWidget(self.priorityCombo)
self.priorityCombo.addItems([
'No priority','Priority 1','Priority 2','Priority 3',])
self.priorityCombo.currentIndexChanged.connect(self.updatePriority)
self.checkBox.toggled.connect(self.updatePriority)
def updatePriority(self):
# update the "lastChanged" variable,ensuring that the sorting puts on
# top the last changed task
self.lastChanged = QtCore.QDateTime.currentDateTime()
self.priorityChanged.emit()
def priority(self):
priority = self.priorityCombo.currentIndex()
# set an arbitrary (but still reasonable) priority that would be fine for
# more items in the same priority
if priority > 0:
priority *= 100
else:
priority = self.priorityCombo.count() * 100
if self.checkBox.isChecked():
priority -= 1
return priority,self.lastChanged.msecsTo(QtCore.QDateTime.currentDateTime())
class TaskApp(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
central = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(central)
self.setCentralWidget(central)
self.taskLayout = QtWidgets.QGridLayout()
layout.addLayout(self.taskLayout)
self.addTaskButton = QtWidgets.QPushButton('Add task')
layout.addWidget(self.addTaskButton)
self.addTaskButton.clicked.connect(self.addTask)
self.tasks = []
self.addTask()
def addTask(self):
task = TaskWidget('Task no. {}'.format(len(self.tasks) + 1))
if not self.taskLayout.count():
# this is for the first item only; we cannot use rowCount,since it
# always returns at least 1,even if it's empty
row = 0
else:
row = self.taskLayout.rowCount()
self.taskLayout.addWidget(QtWidgets.QLabel(str(row + 1)))
self.taskLayout.addWidget(task,row,1)
self.tasks.append(task)
task.priorityChanged.connect(self.sortTasks)
def sortTasks(self):
tasks = self.tasks[:]
self.tasks.clear()
# since we're using a QGridLayout,we don't need to relate to
# insertWidget(),which is index based and might result in some
# inconsistencies while "regenerating" the UI; in this case,the
# grid will be the same,we're only going to rearrange the child widgets
for row,taskWidget in enumerate(sorted(tasks,key=lambda w: w.priority())):
self.taskLayout.addWidget(taskWidget,1)
self.tasks.append(taskWidget)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = TaskApp()
w.show()
sys.exit(app.exec_())
我只是猜测优先级的实现,但这不是重点。
我还添加了日期时间检查,因此具有相同优先级的最后更改的项目将始终位于最前面。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。