I'm trying to figure out how I can get the QWidget that I insert into a QListWidget as a QListWidgetItem to be able to access the list it is a part of so that it can do the following:
Increase/decrease it's position in the list
Remove itself from the list
Pass information from it's own class to a function in the main class
My script layout is a main.py which is where the MainWindow class is. The MainWindow uses the class generated from the main ui file. I also have the custom widget which is it's own class.
Example of GUI:
Relevant code snippets:
main.py
from PyQt4.QtGui import QMainWindow, QApplication
from dungeonjournal import Ui_MainWindow
from creature_initiative_object import InitCreatureObject
from os import walk
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.setupUi(self)
etc......
def AddToInitiative(self):
creature = self.comboBoxSelectCharacter.currentText()
if(creature):
creatureInfo = ''
with open("creatures/"+str(creature)+".creature", "r") as f:
creatureInfo = f.read()
creatureInfo = creatureInfo.split("|")
customWidget = InitCreatureObject()
customWidgetItem = QtGui.QListWidgetItem(self.initiativeList)
customWidgetItem.setSizeHint(QtCore.QSize(400,50))
self.initiativeList.addItem(customWidgetItem)
self.initiativeList.setItemWidget(customWidgetItem, customWidget)
customWidget.setName(creatureInfo[0])
return
creature_initiative_object.py
class Ui_InitCreatureObject(object):
def setupUi(self, InitCreatureObject):
etc...
class InitCreatureObject(QtGui.QWidget, Ui_InitCreatureObject):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QWidget.__init__(self, parent, f)
self.setupUi(self)
Edit 1:
To clarify again, I need to be able to use the buttons in the widget to modify the position of itself in the list. The list is part of the main ui. The buttons for up arrow, down arrow, Select, and Remove are the one's I'm trying to get to interact with things outside of their class.
The function they call needs to be able to determine which listItem is being called, be able to modify the list.
For example, if I click remove, it then needs to know which item in the list to remove. So it needs to first know what the list is, then it needs to know what item it is. I'm not sure how to access the instance of the widget that is occupying that listitem. I also am not sure how to get that listitem based on a button press from inside that listitem's class.
Edit 2:
Per the first answer I tried to work that into my code.
main.py had the following function added
def RemoveItem(self):
cwidget = self.sender().parent()
item = self.initiativeList.itemAt(cwidget.pos())
row = self.initiativeList.row(item)
self.initiativeList.takeItem(row)
print(row)
creature_initiative_object.py had the following added to the InitCreatureObject class
class InitCreatureObject(QtGui.QWidget, Ui_InitCreatureObject):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QWidget.__init__(self, parent, f)
self.setupUi(self)
self.mainwidget = main.MainWindow()
self.btnRemove.clicked.connect(self.mainwidget.RemoveItem)
Item is still not being passed. The parent object seems to be right but when I get the row it always says -1.
The strategy to get the QTableWidgetItem is to use the itemAt() method but for this you must know the position of some point within the QTableWidgetItem.
Since the main objective is to get the item when a signal is sent, then the connected slot is used, so I recommend connecting all the signals to that slot. Given the above the following steps are taken:
Get the object that emits the signal through sender().
Get the sender parent() since this will be the custom widget that was added to the QListWidget() along with the item.
Get the position of the custom widget through pos(), this is the position that should be used in the itemAt() method.
Then you get the text of the button or some parameter that tells me the task to know what action you want to do.
The above can be implemented as follows:
def someSlot(self):
p = self.sender().parent()
it = self.lw.itemAt(p.pos())
text = self.sender().text()
if text == "task1":
do task1
elif text == "task2":
do task2
From the above, the following example is proposed:
class CustomWidget(QWidget):
def __init__(self, text, parent=None):
QWidget.__init__(self, parent)
self.setLayout(QHBoxLayout())
self.buttons = []
vb = QVBoxLayout()
self.layout().addLayout(vb)
self.btnTask1 = QPushButton("task1")
self.btnTask2 = QPushButton("task2")
vb.addWidget(self.btnTask1)
vb.addWidget(self.btnTask2)
self.buttons.append(self.btnTask1)
self.buttons.append(self.btnTask2)
self.btnTask3 = QPushButton("task3")
self.btnTask4 = QPushButton("task4")
self.btnTask5 = QPushButton("task5")
self.btnTask6 = QPushButton("task6")
self.layout().addWidget(self.btnTask3)
self.layout().addWidget(self.btnTask4)
self.layout().addWidget(self.btnTask5)
self.layout().addWidget(self.btnTask6)
self.buttons.append(self.btnTask3)
self.buttons.append(self.btnTask4)
self.buttons.append(self.btnTask5)
self.buttons.append(self.btnTask6)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.lw = QListWidget(self)
self.setCentralWidget(self.lw)
for i in range(5):
cw = CustomWidget("{}".format(i))
for btn in cw.buttons:
btn.clicked.connect(self.onClicked)
item = QListWidgetItem(self.lw)
item.setSizeHint(QSize(400, 80))
self.lw.addItem(item)
self.lw.setItemWidget(item, cw)
def onClicked(self):
p = self.sender().parent()
it = self.lw.itemAt(p.pos())
row = self.lw.row(it)
text = self.sender().text()
print("item {}, row {}, btn: {}".format(it, row, text))
#if text == "task1":
# do task1
#elif text == "task2":
# do task2
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
In your Case:
class MainWindow(QMainWindow, Ui_MainWindow):
[...]
def AddToInitiative(self):
[...]
customWidget = InitCreatureObject()
customWidget.btnRemove.clicked.connect(self.RemoveItem)
# ^^^^^
[...]
def RemoveItem(self):
cwidget = self.sender().parent()
item = self.initiativeList.itemAt(cwidget.pos())
row = self.initiativeList.row(item)
self.initiativeList.takeItem(row)
print(row)
Related
I am making a program for translating text (see screenshot)
I have three classes
class for displaying a window that edits item :
class StyleDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(StyleDelegate, self).__init__()
def createEditor(self, widget, style, index):
self.mainWidget = QWidget(widget)
self.line = QLineEdit() # line for input text
self.delButton= QPushButton('❌') # button for delete current item
self.trnButton = QPushButton('➕') # button for make translation text in another QListView
self.qhbLayout = QHBoxLayout()
self.qhbLayout.addWidget(self.line)
self.qhbLayout.addWidget(self.delButton)
self.qhbLayout.addWidget(self.trnButton)
self.mainWidget.setLayout(self.qhbLayout)
return self.mainWidget
# there is still a lot of code in this place
class for storing, adding, deleting and editing data:
class TranslateListModel(QAbstractListModel):
def __init__(self, parent=None):
super(TranslateListModel, self).__init__()
self.words = ['1', '2', '3', '4']
def removeItem(self, index):
self.beginRemoveRows(index, index.row(), index.row())
del self.words[index.row()]
self.endRemoveRows()
return True
# there is still a lot of code in this place
main class of the program:
class QTranslate(QtWidgets.QDialog, log.Ui_Dialog):
def __init__(self):
super().__init__()
self.originalModel = TranslateListModel()
self.translateModel = TranslateListModel()
self.styleDelegate = StyleDelegate()
self.originalLV.setModel(self.originalModel)
#QListView from Ui_Dialog
self.translateLV.setModel(self.translateModel)
#QListView from Ui_Dialog
self.originalLV.setItemDelegate(self.styleDelegate)
self.translateLV.setItemDelegate(self.styleDelegate)
# there is still a lot of code in this place
How to implement buttons to delete the current item and change the translation in another QListView using QStyledItemDelegate? I cannot access these buttons outside the StyleDelegate class to associate them with the methods of the TranslateListModel class.
A possible solution is to create signals for the delegate and connect them to the functions that will delete or add items, then emit those signals when the buttons are clicked:
class StyleDelegate(QStyledItemDelegate):
deleteRequested = QtCore.pyqtSignal(int)
translateRequested = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(StyleDelegate, self).__init__()
def createEditor(self, widget, style, index):
# note: I removed the "self" references as they're unnecessary
mainWidget = QWidget(widget)
line = QLineEdit()
delButton= QPushButton('❌')
trnButton = QPushButton('➕')
qhbLayout = QHBoxLayout()
qhbLayout.addWidget(line)
qhbLayout.addWidget(delButton)
qhbLayout.addWidget(trnButton)
mainWidget.setLayout(qhbLayout)
delButton.clicked.connect(
lambda _, row=index.row(): self.deleteRequested.emit(row))
trnButton.clicked.connect(
lambda _, row=index.row(): self.translateRequested.emit(row))
return mainWidget
class QTranslate(QtWidgets.QDialog, log.Ui_Dialog):
def __init__(self):
# ...
self.originalLV.setItemDelegate(self.styleDelegate)
self.styleDelegate.deleteRequested.connect(self.deleteRow)
self.styleDelegate.translateRequested.connect(self.translateRow)
def deleteRow(self, row):
# ...
def translateRow(self, row):
# ...
Note that you should always use an unique delegate instance for each view, as explained in the documentation:
Warning: You should not share the same instance of a delegate between views. Doing so can cause incorrect or unintuitive editing behavior since each view connected to a given delegate may receive the closeEditor() signal, and attempt to access, modify or close an editor that has already been closed.
I have an application where upon start up the user is presented with a dialog to chose number of 'objects' required. This then generates necessary objects in the main window using a for loop (i.e. object1, object2, etc.). I want to move this selection into the main window so that this can be changed without the need to restart the application. I have no idea how to approach this as I'm not sure how to dynamically create/destroy once the application is running. Here's an example code that generates tabs in a tab widget with some elements in each tab.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class SelectionWindow(QDialog):
def __init__(self):
QDialog.__init__(self)
self.settings = QSettings('Example', 'Example')
self.numberOfTabs = QSpinBox(value = self.settings.value('numberOfTabs', type=int, defaultValue = 3), minimum = 1)
self.layout = QFormLayout(self)
self.button = QPushButton(text = 'OK', clicked = self.buttonClicked)
self.layout.addRow('Select number of tabs', self.numberOfTabs)
self.layout.addRow(self.button)
def buttonClicked(self):
self.settings.setValue('numberOfTabs', self.numberOfTabs.value())
self.accept()
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
self.tabs = self.settings.value('numberOfTabs', type = int)
self.tabWidget = QTabWidget()
for i in range(1, self.tabs + 1):
exec(('self.tab{0} = QWidget()').format(i))
exec(("self.tabWidget.addTab(self.tab{0}, 'Tab{0}')").format(i))
exec(('self.lineEdit{0} = QLineEdit()').format(i))
exec(('self.spinBox{0} = QSpinBox()').format(i))
exec(('self.checkBox{0} = QCheckBox()').format(i))
exec(('self.layout{0} = QFormLayout(self.tab{0})').format(i))
exec(("self.layout{0}.addRow('Name', self.lineEdit{0})").format(i))
exec(("self.layout{0}.addRow('Value', self.spinBox{0})").format(i))
exec(("self.layout{0}.addRow('On/Off', self.checkBox{0})").format(i))
self.setCentralWidget(self.tabWidget)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
dialog = SelectionWindow()
dialog.show()
if dialog.exec_() == SelectionWindow.Accepted:
mainwindow = MainWindow()
mainwindow.show()
sys.exit(app.exec_())
First of all, you should never use exec for things like these. Besides the security issues of using exec, it also makes your code less readable (and then much harder to debug) and hard to interact with.
A better (and more "elegant") solution is to use a common function to create tabs and, most importantly, setattr.
Also, you shouldn't use QSettings in this way, as it is mostly intended for cross-session persistent data, not to initialize an interface. For that case, you should just override the exec() method of the dialog and initialize the main window with that value as an argument.
And, even if it was the case (but I suggest you to avoid the above approach anyway), remember that to make settings persistent, at least organizationName and applicationName must be set.
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.settings = QSettings('Example', 'Example')
# this value does not need to be a persistent instance attribute
tabCount = self.settings.value('numberOfTabs', type = int)
# create a main widget for the whole interface
central = QWidget()
mainLayout = QVBoxLayout(central)
tabCountSpin = QSpinBox(minimum=1)
mainLayout.addWidget(tabCountSpin)
tabCountSpin.setValue(tabCount)
tabCountSpin.valueChanged.connect(self.tabCountChanged)
self.tabWidget = QTabWidget()
mainLayout.addWidget(self.tabWidget)
for t in range(tabCount):
self.createTab(t)
self.setCentralWidget(central)
def createTab(self, t):
t += 1
tab = QWidget()
self.tabWidget.addTab(tab, 'Tab{}'.format(t))
layout = QFormLayout(tab)
# create all the widgets
lineEdit = QLineEdit()
spinBox = QSpinBox()
checkBox = QCheckBox()
# add them to the layout
layout.addRow('Name', lineEdit)
layout.addRow('Value', spinBox)
layout.addRow('On/Off', checkBox)
# keeping a "text" reference to the widget is useful, but not for
# everything, as tab can be accessed like this:
# tab = self.tabWidget.widget(index)
# and so its layout:
# tab.layout()
setattr(tab, 'lineEdit{}'.format(t), lineEdit)
setattr(tab, 'spinBox{}'.format(t), spinBox)
setattr(tab, 'checkBox{}'.format(t), checkBox)
def tabCountChanged(self, count):
if count == self.tabWidget.count():
return
elif count < self.tabWidget.count():
while self.tabWidget.count() > count:
# note that I'm not deleting the python reference to each object;
# you should use "del" for both the tab and its children
self.tabWidget.removeTab(count)
else:
for t in range(self.tabWidget.count(), count):
self.createTab(t)
I created a tool using Qt Designer, where it has 3 QLineEdits that is catered for translateX, translateY and translateZ.
For each QLineEdit, I have created a context menu that allows me to set a keyframe for one of the above attribute depending on User's choice.
So instead of writing 3 separate functions that catered to each attribute, I thought of 'recycling' them by using 1 method, but I am having issues with it as I am not very sure if it will be possible since I am using a single QAction.
class MyTool(QtGui.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent = parent)
# Read off from convert uic file.
self.ui = Ui_MyWidget()
self.ui.setupUi(self)
# translateX
self.ui.xLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.xLineEdit.customContextMenuRequested.connect(self.custom_menu)
# translateY
self.ui.yLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.yLineEdit.customContextMenuRequested.connect(self.custom_menu)
# translateZ
self.ui.zLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.zLineEdit.customContextMenuRequested.connect(self.custom_menu)
self.popMenu = QtGui.QMenu(self)
set_key_action = QtGui.QAction("Set Key at Current Frame", self)
# I am having issues here..
set_key_action.triggered.connect(self.set_key)
self.popMenu.addAction(set_key_action)
...
...
def set_key(self, attr):
# assuming I am trying to effect this locator1 that already exists in the scene
current_item = "|locator1"
cmds.setKeyframe("{0}.{1}".format(current_item, attr))
def custom_menu(self, point):
self.popMenu.exec_(QtGui.QCursor.pos())
Again, because it is only a single QAction and hence I was stumped... Or will it be better for me to stick in using 3 separate functions instead?
The main problem is that when you connect the triggered signal you do not know that QLineEdit is going to be pressed. Where can we know that QLineEdit was pressed? Well, in the method custom_menu since there the method sender() returns the widget that opens its contextual menu, and to transfer it, a property or data is used, so the fine is to compare the property and the QLineEdit:
class MyTool(QtGui.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent=parent)
# Read off from convert uic file.
self.ui = Ui_MyWidget()
self.ui.setupUi(self)
for le in (self.ui.xLineEdit, self.ui.yLineEdit, self.ui.zLineEdit):
le.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
le.customContextMenuRequested.connect(self.custom_menu)
self.popMenu = QtGui.QMenu(self)
self.set_key_action = QtGui.QAction("Set Key at Current Frame", self)
self.set_key_action.triggered.connect(self.set_key)
self.popMenu.addAction(self.set_key_action)
def set_key(self):
le = self.set_key_action.property("lineedit")
# or
# le = self.set_key_action.data()
if le is self.ui.xLineEdit:
print("xLineEdit")
elif le is self.ui.yLineEdit:
print("yLineEdit")
elif le is self.ui.zLineEdit:
print("zLineEdit")
def custom_menu(self, p):
if self.sender() is not None:
self.set_key_action.setProperty("lineedit", self.sender())
# or
# self.set_key_action.setData(self.sender())
self.popMenu.exec_(QtGui.QCursor.pos())
Without debug or source code , i can't figure out what is happen here , because in theory all works , so or i can't understand correctly or have some error in other part of code.
class MyTool(QtGui.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent = parent)
# Read off from convert uic file.
self.ui = Ui_MyWidget()
self.ui.setupUi(self)
# translateX
self.ui.xLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.xLineEdit.customContextMenuRequested.connect(self.custom_menu)
# translateY
self.ui.yLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.yLineEdit.customContextMenuRequested.connect(self.custom_menu)
# translateZ
self.ui.zLineEdit.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.zLineEdit.customContextMenuRequested.connect(self.custom_menu)
self.popMenu = QtGui.QMenu(self)
set_key_action = QtGui.QAction("Set Key at Current Frame", self)
**# Assuming that this phase pass !**
set_key_action.triggered.connect(self.set_key)
self.popMenu.addAction(set_key_action)
...
...
def set_key(self, attr):
**# What happen when you debug this block ?**
current_item = "|locator1"
cmds.setKeyframe("{0}.{1}".format(current_item, attr))
def custom_menu(self, point):
self.popMenu.exec_(QtGui.QCursor.pos())
This question is similar to the one in this this topic Preserve QStandardItem subclasses in drag and drop but with issue that I cant find a good solution for. That topic partially helps but fail on more complex task.
When I create an item in QTreeView I put that item in my array but when I use drag&Drop the item gets deleted and I no longer have access to it. I know that its because drag and drop copies the item and not moves it so I should use setData. I cant setData to be an object because even then the object gets copied and I lose reference to it.
Here is an example
itemsArray = self.addNewRow
def addNewRow(self)
'''some code with more items'''
itemHolder = QStandardItem("ProgressBarItem")
widget = QProgressBar()
itemHolder.setData(widget)
inx = self.model.rowCount()
self.model.setItem(inx, 0, itemIcon)
self.model.setItem(inx, 1, itemName)
self.model.setItem(inx, 2, itemHolder)
ix = self.model.index(inx,2,QModelIndex())
self.treeView.setIndexWidget(ix, widget)
return [itemHolder, itemA, itemB, itemC]
#Simplified functionality
data = [xxx,xxx,xxx]
for items in itemsArray:
items[0].data().setPercentage(data[0])
items[1].data().setText(data[1])
items[2].data().setChecked(data[2])
The code above works if I won't move the widget. The second I drag/drop I lose reference I lose updates on all my items and I get crash.
RuntimeError: wrapped C/C++ object of type QProgressBar has been deleted
The way I can think of of fixing this problem is to loop over entire treeview recursively over each row/child and on name match update item.... Problem is that I will be refreshing treeview every 0.5 second and have 500+ rows with 5-15 items each. Meaning... I don't think that will be very fast/efficient... if I want to loop over 5 000 items every 0.5 second...
Can some one suggest how I could solve this problem? Perhaps I can edit dropEvent so it does not copy/paste item but rather move item.... This way I would not lose my object in array
Qt can only serialize objects that can be stored in a QVariant, so it's no surprise that this won't work with a QWidget. But even if it could serialize widgets, I still don't think it would work, because index-widgets belong to the view, not the model.
Anyway, I think you will have to keep references to the widgets separately, and only store a simple key in the model items. Then once the items are dropped, you can retrieve the widgets and reset them in the view.
Here's a working demo script:
from PyQt4 import QtCore, QtGui
class TreeView(QtGui.QTreeView):
def __init__(self, *args, **kwargs):
super(TreeView, self).__init__(*args, **kwargs)
self.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setAllColumnsShowFocus(True)
self.setModel(QtGui.QStandardItemModel(self))
self._widgets = {}
self._dropping = False
self._droprange = range(0)
def dropEvent(self, event):
self._dropping = True
super(TreeView, self).dropEvent(event)
for row in self._droprange:
item = self.model().item(row, 2)
self.setIndexWidget(item.index(), self._widgets[item.data()])
self._droprange = range(0)
self._dropping = False
def rowsInserted(self, parent, start, end):
super(TreeView, self).rowsInserted(parent, start, end)
if self._dropping:
self._droprange = range(start, end + 1)
def addNewRow(self, name):
model = self.model()
itemIcon = QtGui.QStandardItem()
pixmap = QtGui.QPixmap(16, 16)
pixmap.fill(QtGui.QColor(name))
itemIcon.setIcon(QtGui.QIcon(pixmap))
itemName = QtGui.QStandardItem(name.title())
itemHolder = QtGui.QStandardItem('ProgressBarItem')
widget = QtGui.QProgressBar()
widget.setValue(5 * (model.rowCount() + 1))
key = id(widget)
self._widgets[key] = widget
itemHolder.setData(key)
model.appendRow([itemIcon, itemName, itemHolder])
self.setIndexWidget(model.indexFromItem(itemHolder), widget)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.treeView = TreeView()
for name in 'red yellow green purple blue orange'.split():
self.treeView.addNewRow(name)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.treeView)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 150, 600, 400)
window.show()
sys.exit(app.exec_())
I'm working with a CustomTreeCtrl with checkboxes and I can't figure out how to determine which checkboxes are selected. I looked at http://xoomer.virgilio.it/infinity77/wxPython/Widgets/wx.TreeCtrl.html#GetSelection and put this together:
import string
import os
import sys
import wx
import wx.lib.agw.customtreectrl as CT
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "CustomTreeCtrl Demo")
custom_tree = CT.CustomTreeCtrl(self, agwStyle=wx.TR_DEFAULT_STYLE)
root = custom_tree.AddRoot("The Root Item")
for y in range(5):
last = custom_tree.AppendItem(root, "item %d" % y)
for z in range(5):
item = custom_tree.AppendItem(last, "item %d" % z, ct_type=1)
self.Bind(CT.EVT_TREE_ITEM_CHECKED, self.ItemChecked)
def ItemChecked(self, event):
print("Somebody checked something")
print(event.GetSelections())
app = wx.PySimpleApp()
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
When I check a box, I get the Traceback: "AttributeError: 'TreeEvent' object has no attribute 'GetSelections'" Any suggestions on how to read which boxes are selected would be great!
The event object in question doesn't have a GetSelections method. It does have a GetSelection, which will tell you which item was selected at that event. If you want to get all of the selected items inside ItemChecked, rename custom_tree to self.custom_tree and then you're allowed to call self.custom_tree.GetSelections() inside ItemChecked.
If in future you want to know what kind of methods are available for some event object, you can put print(dir(event)) in your handler.
The custom tree control doesn't have a method to get the checked items. One thing that you could do is create a self.checked_items list in your frame, and maintain it in your ItemChecked method. This list could hold either the string values for the items or the items themselves. For instance,
class MyFrame(wx.Frame):
def __init__(self, parent):
# ....
self.checked_items = []
# ....
def ItemChecked(self, event):
if event.IsChecked():
self.checked_items.append(event.GetItem())
# or to store the item's text instead, you could do ...
# self.checked_items.append(self.custom_tree.GetItemText(event.GetItem()))
else:
self.checked_items.remove(event.GetItem())
# or ...
# self.checked_items.remove(self.custom_tree.GetItemText(event.GetItem()))