Search code examples
pythonpyqtpyqt5qstandarditemmodel

How to iterate through a dynamically created list of checkboxes created with QStandardItemModel?


I'm creating an application where I need to make a checklist with a list given to me from another class. The user will check the checkboxes next to the items they want and then click a button. For those rows that were checked, I'd like to print "You checked number ___." in the neighboring QBoxGroup's QLabel.

(In my real application, I'm iterating through classes, where I will call a function within the class). I'm having a hard time iterating through my list since I have to check if the box is checked and whether or not the name is the name I'd like to print (the printed statement is different than the actual name because in my application I'm going to call a function within the class whose row I'm in).

I'm also not sure how to work this where I can print more than one statement if the user checks multiple boxes with QLabel. I might have to make it where the user can only select one row at a time, but this is a smaller issue than the one above.

from PyQt5.QtWidgets import (QApplication, QTabWidget, QVBoxLayout, QWidget, QGridLayout, QLabel, QGroupBox,
                             QListView, QPushButton)
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt
import sys, os

class MyPage(QWidget):
    def __init__(self):

        super().__init__()
        self.app = QApplication(sys.argv)
        self.screen = self.app.primaryScreen()
        self.size = self.screen.size()
        self.mydata = None

        # These numbers are arbitrary and seemed
        # to have the best balance
        self.textWidth = self.size.width() * 0.64
        self.chooseWidth = self.size.width() * 0.29

        self.createTextGroup()
        self.createStatsGroup()

        self.layout = QGridLayout()
        self.layout.addWidget(self.TextGroup, 0, 0, 0, 1)
        self.layout.addWidget(self.StatsGroup, 0, 1)
        self.setLayout(self.layout)
        self.show()

    # The left side of AnalysisTab containing the textbox
    # where the analysis will be shown
    def createTextGroup(self):
        self.TextGroup = QGroupBox("Analysis")
        self.TextGroup.setFixedWidth(self.textWidth)
        self.setStyleSheet("font: 15pt Tw Cen MT")

        # Here is where the analysis will go
        self.analysis = QLabel()

        self.layout = QGridLayout()
        self.layout.addWidget(self.analysis)
        self.TextGroup.setLayout(self.layout)

    # The right side of AnalysisTab containing a checklist of tests that
    # may be run on the data and a button to press to run the tests
    def createStatsGroup(self):
        self.StatsGroup = QGroupBox("Statistics Tests")
        self.StatsGroup.setFixedWidth(self.chooseWidth)
        self.setStyleSheet("font: 15pt Tw Cen MT")

        # List widgets where users will choose what analysis
        # they would like ran on their data
        self.statsAnalysis = QListView()
        self.model = QStandardItemModel(self.statsAnalysis)

        for member in myList(self):
            item = QStandardItem(member)
            item.setCheckable(True)
            check = Qt.Unchecked
            item.setCheckState(check)
            self.model.appendRow(item)

        self.statsButton = QPushButton("Analyze")
        self.statsButton.clicked.connect(self.statsButtonClicked)

        self.statsAnalysis.setModel(self.model)
        self.layout = QGridLayout()
        self.layout.addWidget(self.statsAnalysis, 0, 0, 0, 1)
        self.layout.addWidget(self.statsButton, 1, 1)
        self.StatsGroup.setLayout(self.layout)

    def statsButtonClicked(self):
        model2 = self.model
        for index in range(model2.rowCount()):
            item = model2.item(index)
            if item.checkState() == Qt.Unchecked and item == "One":
                self.analysis.setText("You checked number 1")
            elif item.checkState() == Qt.Unchecked and item == "One":
                self.analysis.setText("You checked number 2")
            elif item.checkState() == Qt.Unchecked and item == "One":
                self.analysis.setText("You checked number 3")
            elif item.checkState() == Qt.Unchecked and item == "One":
                self.analysis.setText("You checked number 4")
            elif item.checkState() == Qt.Unchecked and item == "One":
                self.analysis.setText("You checked number 5")
            else:
                self.analysis.setText("You didn't check anything")

def myList(self):
    thisList = ["One", "Two", "Three", "Four", "Five"]
    return thisList

def runStatsWiz():
    app = QApplication(sys.argv)
    myPage = MyPage()
    myPage.show()
    app.exec_()


runStatsWiz()

Right now, the statsButtonClicked() function returns TypeError: 'QStandardItemModel' object is not callable

I've tried the information on these StackOverflow pages and I can't seem to figure out how to see the row's name and if the row is checked:


Solution

  • As I indicated in the comments you have a type since you have an attribute called self.model but you want to access using self.model().

    On the other hand your logic is incorrect since for example it is not defined that it is row, on the other hand assuming that this error did not exist and you had 2 options checked with your logic you are writing "You checked number X" and then replace it with "You checked number Y".

    The logic is to save the texts of the checked items in a list, and if that list is not empty then concatenate the information, otherwise indicate no items checked.

    def statsButtonClicked(self):
        checked_options = []
        for index in range(self.model.rowCount()):
            item = self.model.item(index)
            if item is not None and item.checkState() == Qt.Checked:
                checked_options.append(item.text())
        if checked_options:
            self.analysis.setText(
                "\n".join(["You checked number {}".format(option) for option in checked_options])
            )
        else:
            self.analysis.setText("You didn't check anything")