一些PyQt5复选框在移动后不可点击

如何解决一些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_())

解决方法

您的代码存在各种问题,我将尝试解决所有问题。

  1. 排序机制不好:仅当项目更改优先级时才会发生排序(当用户单击复选框时,它也应适用)
  2. 使用类似的低值会导致意外的行为:如果我检查了priority 1的优先级会增加10,那么为什么它应该低于priority 2甚至是3?
  3. 排序机制应该是集中的,而不是被分解为不执行其他任何操作的更多功能:“一个功能可以全部统治” ;-)不要检查组合框内容,然后根据另一个外部函数调用进行排序:只需调用一个用于检查组合框复选框的函数,然后对内容进行排序;记住:虽然OOP是关于模块化的,但是您应该避免不必要的分散(记住KISS principle);
  4. 您可能不应该连接到activated信号,并且当然不应该使用[str]重载(对于基于索引的小部件,检查字符串很少是个好主意);改用currentIndex()及其信号currentIndexChanged()
  5. 应尽量避免为子小部件使用固定的几何形状;存在布局管理器的原因有很多,其中很多是出于非常好的理由(例如,如果鼠标光标离复选框太近,我就无法单击QLineEdit,即使上方进行修改);请注意,您也应避免在顶层窗口中使用setGeometry
  6. 您永远不应设置将被不同对象覆盖的实例属性:您正在不断覆盖它们(self.labelself.textbox等),结果是使它们成为实例属性是完全没有用的。因为您已经使用父窗口小部件创建了它们,所以垃圾收集器不会在函数末尾删除它们,因此您应该只使用局部变量(label = QLabel()等);
  7. 使用像您一样的类并不是很有用:无论如何都要对所有这些小部件进行分组,因此最好使用一个不仅可以管理其子级的 container 小部件(请参见关于布局),但将“集中”所有必要的编程逻辑;
  8. 如果类和变量引用不同的对象类型,请避免使用相似的名称:在您的情况下,app引用了QApplication实例,但是AppQMainWidget子类;

这里可能是您的代码的重新实现:

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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-