I've got a QTreeView populated by a QStandardItemModel with the TreeView set up so it only allows internal Drag and Drop (InternalMove).
I am trying to detect whenever the user makes such an internal move and would like to extract the item being dragged as well as the start and end location.
QStandardItemModel provides the "rowsMoved" signal which is supposed to emit precisely what I am looking for: parent, start, end, destination, row.
The problem: This signal never gets called when moving around items. Why is this?
Other signals like rowsInserted() or rowsRemoved() work just fine, but rowsMoved() does not.
In the following minimal example, the print() in onMove() should be called when the user moves around items, but it doesn't.
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
view = QtWidgets.QTreeView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(view)
view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
view.expandAll()
view.setDragEnabled(True)
view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
view.setDefaultDropAction(QtCore.Qt.MoveAction)
view.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
model.rowsMoved.connect(self.onMove)
def onMove(self, parent, start, end, destination, row):
print("parent",parent,"start",start,"end",end,"destination",destination,"row",row)
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setCheckable(False)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
The documentation states:
Components connected to this signal use it to adapt to changes
in the model's dimensions. It can only be emitted by the QAbstractItemModel
implementation, and cannot be explicitly emitted in subclass code.
Considering that this signal is not defined by the QStandardItemModel class, any instance of this class won't emit such signal.
The only signals you can connect to, as you mentioned, are rowsInserted() are rowsRemoved() because they are redefined in this class. Note that they are marked as internal in the source file.
If you need information about the item being dragged, I suggest that you create a custom class which inherit from QtWidgets.QTreeView and override the dragEnterEvent and dropEvent methods.
Here is an example on how to retrieve the item with its whole hierarchy being dragged and dropped:
import random
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
def getHierarchy(index, hierarchy=None):
hierarchy = hierarchy or []
if not index.isValid():
return hierarchy
hierarchy.insert(0, (index.row(), index.column()))
return getHierarchy(index.parent(), hierarchy)
class MyTreeView(QtWidgets.QTreeView):
def dropEvent(self, e):
super().dropEvent(e)
currentIndex = e.source().currentIndex()
print('dropEvent.source current index:', getHierarchy(currentIndex))
def dragEnterEvent(self, e):
super().dragEnterEvent(e)
currentIndex = e.source().currentIndex()
print('dragEnterEvent.source current index:', getHierarchy(currentIndex))
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
model = QtGui.QStandardItemModel(self)
view = MyTreeView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(view)
view.setModel(model)
root_item = QtGui.QStandardItem("Root")
model.appendRow(root_item)
self.populate(root_item, 3)
view.expandAll()
view.setDragEnabled(True)
view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
view.setDefaultDropAction(QtCore.Qt.MoveAction)
view.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
view.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectItems)
def populate(self, root_item, level):
for i in range(random.randint(2, 4)):
it = QtGui.QStandardItem("item {}".format(i))
it.setCheckable(False)
root_item.appendRow(it)
next_level = level - 1
if next_level > 0:
self.populate(it, next_level)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I suggest to look at this repository which deals with a similar problem as yours: https://github.com/d1vanov/PyQt5-reorderable-list-model
Related
I have a minimum example here of a QTableView widget that displays a long string that I want word wrapped when I start the app.
from PyQt6.QtWidgets import (
QMainWindow,
QTableView,
QHeaderView,
QApplication,
)
from PyQt6.QtCore import (
Qt,
QEvent,
QAbstractTableModel,
QSize,
QEvent
)
import sys
text = """A long string which needs word wrapping to fully display. A long string which needs word wrapping to fully display. A long string which needs word wrapping to fully display."""
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = QTableView()
header = self.table.horizontalHeader()
header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
self.table.horizontalHeader().sectionResized.connect(self.table.resizeRowsToContents)
self.model = TableModel([[text] for i in range(50)])
self.table.setModel(self.model)
self.setCentralWidget(self.table)
self.table.resizeRowsToContents()
def changeEvent(self, event):
if event.type() == QEvent.Type.WindowStateChange:
self.table.resizeRowsToContents()
return super(MainWindow, self).changeEvent(event)
class TableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.ItemDataRole.DisplayRole:
return self._data[index.row()][index.column()]
def rowCount(self, index):
return len(self._data)
def columnCount(self, index):
return len(self._data[0])
app = QApplication(sys.argv)
app.lastWindowClosed.connect(app.quit)
w = MainWindow()
w.show()
app.exec()
When I run the above I get this
but when I resize the window manually just slightly, I get what I'm expecting
How would I get the second image as the state when the app is started? I thought calling self.table.resizeRowsToContents() in the __init__ method would do it. Another question is, why does self.table.horizontalHeader().sectionResized.connect(self.table.resizeRowsToContents) work upon resizing when resizeRowsToContents() does not work in the __init__ method?
why does self.table.horizontalHeader().sectionResized.connect(self.table.resizeRowsToContents) work upon resizing when resizeRowsToContents() does not work in the init method?
Because the window isn't rendered yet, that's why the QTableView doesn't know yet how big the text is in order to resize the rows.
How would I get the second image as the state when the app is started? I thought calling self.table.resizeRowsToContents() in the init method would do it.
You could separate the population of the table from the init method, or delay it, until Your widget is rendered, preferably inside the class itself, but you can do something like this:
# ...
app = QApplication(sys.argv)
app.lastWindowClosed.connect(app.quit)
w = MainWindow()
w.show()
w.table.resizeRowsToContents() # I just added this line
app.exec()
I have a QToolTip on a QLineEdit and the tooltip contains variables in the text. The tooltip code is contained in the init. The problem is that the variable values in the tooltip do not update automatically when they are changed in the operation of the program. For example, I hover over the line edit and values appear in the tooltip. I change the program, go back to the line edit, and variables in the tooltip have not changed.
I can fix the issue by moving the .setToolTip to a function and calling the function EACH time ANYTHING is changed in the program, but that seems like overkill, especially when 99% of the program changes have nothing to do with this particular tooltip).
Are variables supposed to update automatically? Here is the tooltip setup code contained in the init.
self.ui.YourSSAmount.setToolTip(
'<span>Click Reports/Social Security to see your<br>SS income at each start age'
'<br><br>Your inf adj FRA amt at age {}: ${:,.0f}'
'<br>Age adjustment: {:.0f}%'
'<br>SS Income at age {}: ${:,.0f}</span>'.format(
self.generator.YouSSStartAge, self.generator.your_new_FRA_amt,
self.generator.SS66.get(self.generator.YouSSStartAge, 1.32) * 100, self.generator.YouSSStartAge,
self.generator.YourSSAmount))
The setToolTip method takes the text and stores it, and is not notified if any of the variables used to form the text change.
Given this there are 2 possible solutions:
Update the tooltip every time a variable changes:
from PyQt5 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.le = QtWidgets.QLineEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.le)
self.foo = QtCore.QDateTime.currentDateTime().toString()
self.update_tooltip()
timer = QtCore.QTimer(self, timeout=self.on_timeout)
timer.start()
def on_timeout(self):
self.foo = QtCore.QDateTime.currentDateTime().toString()
# every time any variable used to build the tooltip changes
# then the text of the tooltip must be updated
self.update_tooltip()
def update_tooltip(self):
# update tooltip text
self.setToolTip("dt: {}".format(self.foo))
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = Widget()
w.show()
app.exec_()
Override the toolTip to take the text using the variables:
from PyQt5 import QtCore, QtWidgets
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, parent=None):
super().__init__(parent)
self._foo = ""
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, foo):
self._foo = foo
def event(self, e):
if e.type() == QtCore.QEvent.ToolTip:
text = "dt:{}".format(self.foo)
QtWidgets.QToolTip.showText(e.globalPos(), text, self, QtCore.QRect(), -1)
e.accept()
return True
return super().event(e)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.le = LineEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.le)
self.le.foo = QtCore.QDateTime.currentDateTime().toString()
timer = QtCore.QTimer(self, timeout=self.on_timeout)
timer.start()
def on_timeout(self):
self.le.foo = QtCore.QDateTime.currentDateTime().toString()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = Widget()
w.show()
app.exec_()
How does one use a QFileSystemModel to populate several QComboBox with subdirectories?
I have built a project management tool that allows me to create and manage my projects. I am currently using a combination of os.listdir and json to populate and validate my QComboboxes. But I am trying to learn a more modelview approach with QFileSystemModel.
So this is what I have:
class FileSystemModel(QW.QFileSystemModel):
def __init__(self, root, parent=None):
QW.QFileSystemModel.__init__(self, parent)
self.root = root
self.rootIndex = self.setRootPath(root)
class Window(QW.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__()
self.init()
def init(self):
layout = QW.QVBoxLayout()
self.cbox = QW.QComboBox()
self.cbox2 = QW.QComboBox()
self.model = FileSystemModel("C:\\projects\\")
self.cbox.setModel(self.model)
self.cbox2.setModel(self.model)
self.cbox.setRootModelIndex(self.model.rootIndex)
self.cbox.currentIndexChanged.connect(self._indexChanged)
layout.addWidget(self.cbox)
layout.addWidget(self.cbox2)
self.setLayout(layout)
def _indexChanged(self):
row = self.sender().currentIndex()
index = self.sender().rootModelIndex().child(row, 0)
self.cbox2.setRootModelIndex(index)
def main():
app = QW.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
I was attempting to repopulate the cbox2 using the index from cbox, but with my code it doesn't seem to work - it just stays empty.
Okay here is modified version of what you had:
from sys import exit as sysExit
from PyQt5.QtCore import QDir, pyqtSlot
from PyQt5.QtWidgets import QApplication, QWidget, QFileSystemModel, QHBoxLayout, QComboBox
class SysDirModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
class SysFileModel(QFileSystemModel):
def __init__(self, DirPath):
QFileSystemModel.__init__(self)
self.setFilter(QDir.NoDotAndDotDot | QDir.Files)
self.setReadOnly(True)
# Property
self.setRootPath(DirPath)
# Property
self.RootIndex = self.index(DirPath)
def ResetPath(self, DirPath):
self.setRootPath(DirPath)
self.RootIndex = self.index(DirPath)
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setGeometry(150, 150, 450, 100)
# If you use forward slash this works in Windows as well and it is cleaner
self.SysDirs = SysDirModel('C:/projects/')
self.SysFils = SysFileModel('C:/projects/')
# Setup first ComboBox
self.cbxDirs = QComboBox()
self.cbxDirs.setMinimumWidth(200)
self.cbxDirs.setModel(self.SysDirs)
self.cbxDirs.setRootModelIndex(self.SysDirs.RootIndex)
# This sends a Signal to a predefined Slot
self.cbxDirs.currentIndexChanged.connect(self.IndexChanged)
self.cbxFiles = QComboBox()
self.cbxFiles.setMinimumWidth(200)
self.cbxFiles.setModel(self.SysFils)
self.cbxFiles.setRootModelIndex(self.SysFils.RootIndex)
HBox = QHBoxLayout()
HBox.addWidget(self.cbxDirs)
HBox.addStretch(1)
HBox.addWidget(self.cbxFiles)
self.setLayout(HBox)
# This is the receiver of a Signal (aka Slot) so it ought to be used as such
#pyqtSlot(int)
def IndexChanged(self, RowIdx):
# Get your Current DirPath based on the Selected Value
index = self.cbxDirs.rootModelIndex().child(RowIdx, 0)
DirPath = self.cbxDirs.model().filePath(index)
# Reset what ComboBox 2's Model and what it is looking at
self.cbxFiles.clear()
self.SysFils.ResetPath(DirPath)
self.cbxFiles.setModel(self.SysFils)
if __name__ == '__main__':
MainThred = QApplication([])
MainGui = MainWindow()
MainGui.show()
sysExit(MainThred.exec_())
I'm putting together a UI - users provide some information, and code executes. I've got some checkboxes. I want to enable/disable some of the checkboxes based on the status of other checkboxes. As an example, my UI has a checkbox which lets a user specify if they wanted a file to print, and a checkbox that let a user specify if they want it to print in colour. If the 'print' checkbox isn't ticked, I want to clear and disable the 'colour' checkbox: it doesn't make any sense to let someone say they want to print in colour if they aren't printing.
I can see how to do this with signals/slots, but I'm pretty new to Qt, so I'm wondering if there's a cleaner way to do this. Looking at ButtonGroups was my first port of call, but I couldn't see any way to make it work.
What I have looks something like this. I want to emphasize - this does exactly what I want it to do - I'm just not sure that it's the best way to do it, and I'd like not to hate myself if I come back to the code in a few months with more knowledge. I'd be entirely unsurprised if there were built-in functionality that accomplished my goals.
self.first_checkbox = QtWidgets.QCheckBox()
self.second_checkbox = QtWidgets.QCheckBox()
self.first_checkbox.stateChanged.connect(self._handleCheckboxStateChanged)
#QtCore.Slot()
def _handleCheckboxStateChange(self):
if self.first_checkbox.isChecked():
self.second_checkbox.setEnabled(True)
else:
self.second_checkbox.setEnabled(False)
self.second_checkbox.setChecked(False)
Your method is correct, my answer just tries to show other equivalent methods:
1.
from PySide2 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.first_checkbox = QtWidgets.QCheckBox("Print")
self.second_checkbox = QtWidgets.QCheckBox("color")
self.first_checkbox.stateChanged.connect(
lambda state: self.second_checkbox.setDisabled(
state != QtCore.Qt.Checked
)
)
self.first_checkbox.stateChanged.connect(
lambda state: self.second_checkbox.setCheckState(
QtCore.Qt.Unchecked
)
if not state
else None
)
self.second_checkbox.setDisabled(True)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.first_checkbox)
lay.addWidget(self.second_checkbox)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
2.
from PySide2 import QtCore, QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.first_checkbox = QtWidgets.QCheckBox("Print")
self.second_checkbox = QtWidgets.QCheckBox("color")
self.first_checkbox.stateChanged.connect(
self._handleCheckboxStateChange
)
self.second_checkbox.setDisabled(True)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.first_checkbox)
lay.addWidget(self.second_checkbox)
#QtCore.Slot(QtCore.Qt.CheckState)
def _handleCheckboxStateChange(self, state):
self.second_checkbox.setEnabled(state == QtCore.Qt.Checked)
if state != QtCore.Qt.Checked:
self.second_checkbox.setCheckState(QtCore.Qt.Unchecked)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I would like to add various widgets to various cells in a Table Widget, and I would like to trigger commands when those widgets' values are changed. I can get the widgets into the table as desired, but I'm having problems connecting signals so that I know which widget has generated the signal.
Below is a simple example explaining the problem, using just checkboxes:
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
[self.table.insertRow(i) for i in [0,1,2]]
[self.table.insertColumn(i) for i in [0,1]]
# set values for first column:
self.table.setItem(0, 0, QtWidgets.QTableWidgetItem('A') )
self.table.setItem(1, 0, QtWidgets.QTableWidgetItem('B') )
self.table.setItem(2, 0, QtWidgets.QTableWidgetItem('C') )
# add checkboxes to second column:
cb0 = QtWidgets.QCheckBox( parent=self.table )
cb1 = QtWidgets.QCheckBox( parent=self.table )
cb2 = QtWidgets.QCheckBox( parent=self.table )
self.table.setCellWidget(0, 1, cb0)
self.table.setCellWidget(1, 1, cb1)
self.table.setCellWidget(2, 1, cb2)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
self.table.itemChanged.connect(self.item_changed)
# connect checkbox signals:
cb0.clicked.connect(self.checkbox_clicked)
cb1.clicked.connect(self.checkbox_clicked)
cb2.clicked.connect(self.checkbox_clicked)
# show:
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def cell_changed(self, row, col):
print(row, col)
def checkbox_clicked(self, checked):
print(checked)
def item_changed(self, item):
print(item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
app.exec_()
Based on table.cellChanged.connect I would naively expect a cellChanged signal when the checkboxes are changed. However this signal is not generated. Nor is the itemChanged signal. I can indeed see the clicked signals, but that is not very useful because it is unclear which checkbox has produced the signal.
One way to solve the problem is to create a different checkbox_clicked function for each checkbox, but that hardly seems elegant.
My questions are:
Why is neither a cellChanged nor an itemChanged signal generated when a checkbox is changed?
How should signals be connected in order to know which checkbox has generated the clicked signal?
Why is neither a cellChanged nor an itemChanged signal generated when
a checkbox is changed?
because when you use setCellWidget() a QTableWidgetItem is not created, and if we check the documentation of cellChanged and itemChanged:
void QTableWidget::cellChanged(int row, int column)
This signal is emitted whenever the data of the item in the cell specified by row and column has changed.
void QTableWidget::itemChanged(QTableWidgetItem *item)
This signal is emitted whenever the data of item has changed.
How should signals be connected in order to know which checkbox has generated the clicked signal?
The way to obtain is indirectly, the first thing to know is that when the widget is added through the setCellWidget() method, the viewport() of the QTableWidget is set as a parent.
Also another thing that should be known is that the position of a widget that is accessed through pos() is relative to the parent, that is, in our case relative to viewport().
There is a very useful method called sender() that returns the object that emits the signal, in this case it will return the QCheckBox.
As the position of the widget with respect to the viewport() is known, its QModelIndex is accessed through the indexAt() method, the QModelIndex has the information of the cell.
All of the above is implemented in the following example:
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
self.table.setRowCount(3)
self.table.setColumnCount(2)
for i, letter in enumerate("ABC"):
self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
for i in range(self.table.rowCount()):
ch = QtWidgets.QCheckBox(parent=self.table)
ch.clicked.connect(self.onStateChanged)
self.table.setCellWidget(i, 1, ch)
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def onStateChanged(self):
ch = self.sender()
print(ch.parent())
ix = self.table.indexAt(ch.pos())
print(ix.row(), ix.column(), ch.isChecked())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
sys.exit(app.exec_())
Another way to do it is through lambda methods or partial.functions where we pass directly new parameters.
from PyQt5 import QtWidgets, QtGui, QtCore
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QtWidgets.QTableWidget()
self.table.setRowCount(3)
self.table.setColumnCount(2)
for i, letter in enumerate("ABC"):
self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
for i in range(self.table.rowCount()):
ch = QtWidgets.QCheckBox(parent=self.table)
ch.clicked.connect(lambda checked, row=1, col=i: self.onStateChanged(checked, row, col))
self.table.setCellWidget(i, 1, ch)
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def onStateChanged(self, checked, row, column):
print(checked, row, column)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
sys.exit(app.exec_())
If you want to know more information how to pass extra parameters through connect() you can review this answer.
use the stateChanged signal for checkboxes.
and my take about that code:
in some cases it's helpful to have a reference to checkbox widgets, for some logic actions.
use loops if possible
use explicit imports in PyQt - the class names are unique and it's more readable
for example:
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QCheckBox, QApplication
from typing import Dict
class Main(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QTableWidget()
self.table.insertColumn(0)
self.table.insertColumn(1)
self._items: Dict[QTableWidgetItem, QCheckBox] = {}
for i, tag in enumerate(['A', 'B', 'C']):
self.table.insertRow(i)
item = QTableWidgetItem(tag)
cb = QCheckBox(parent=self.table)
self._items[item] = cb
# set values for first column:
self.table.setItem(i, 0, item)
# add checkboxes to second column:
self.table.setCellWidget(i, 1, cb)
# connect cb signals:
self._items[item].stateChanged.connect(self.checkbox_clicked)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
# show:
self.setCentralWidget(self.table)
self.setWindowTitle('TableWidget, CheckBoxes')
self.show()
def cell_changed(self, row, col):
print(row, col)
def checkbox_clicked(self, checked):
print(checked)
def item_changed(self, item):
print(item)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
main = Main()
app.exec_()