Reputation: 3082
I am using PyQt6 6.1.0 on Python 3.9.6 and I would like to get the parent QMainWindow of the parent QTreeWidget of parents QTreeWidgetItems of a QTreeWidgetItem, because I need to modify the window from within the QTreeWidgetItem.
The full code is very long, over 1800 lines and over 60 KiB in size, when I put it in one file it works without problem, but when in one file its readability is very low, and when split into files the global variables can be hard to access from within classes.
Basically my idea is to call .parent()
repetitively until the object's class name is 'MainWindow'.
def getWindow(obj):
window = obj.parent()
while True:
if type(window).__name__ == 'MainWindow':
break
window = window.parent()
return window
I didn't use the class directly because the class is defined in the main file.
Here are two classes defined in a file named groupA.py
:
class TreeNode(QTreeWidgetItem):
def __init__(self, texts):
super().__init__()
self.isSongNode = False
self.setFlags(
FLAG.ItemIsAutoTristate
| FLAG.ItemIsEnabled
| FLAG.ItemIsSelectable
| FLAG.ItemIsUserCheckable
)
self.setCheckState(0, Qt.CheckState.Unchecked)
self.data = dict(zip(fields, texts))
self.song = None
for i in ("title", "album", "artist"):
if i in self.data:
if i == "title":
self.setTextAlignment(0, Qt.AlignmentFlag.AlignLeft)
self.setText(0, self.data["title"])
self.setToolTip(0, self.data["title"])
self.setText(1, self.data["duration"])
self.setText(2, self.data["instrumental"])
self.setText(3, self.data["downloaded"])
self.setText(4, ",".join(self.data["language"]))
self.setText(5, self.data["artistgender"])
for j in range(1, 6):
self.setTextAlignment(j, Qt.AlignmentFlag.AlignCenter)
self.song = song(*[self.data[i] for i in cells])
self.isSongNode = True
else:
self.setText(0, self.data[i])
self.setToolTip(0, self.data[i])
break
def detail(self):
print(self)
print(self.parent())
print(self.parent().parent())
print(self.parent().parent().parent())
window = getWindow(self)
if self.childCount() != 0:
for i in range(self.childCount()):
self.child(i).detail()
else:
if self.song not in detailed_items and self.isSongNode:
widget = SongPage(self.data)
window.detailArea.scrollArea.Layout.addWidget(widget)
widget.autoResize()
detailed_items.append(self.song)
class Tree(QTreeWidget):
def __init__(self):
super().__init__()
self.init()
def init(self):
self.setColumnCount(len(HEADERS))
self.setHeaderLabels(HEADERS)
font = Font(9)
self.setFont(font)
self.header().setFont(font)
self.setColumnWidth(0, 900)
fontRuler = QFontMetrics(font)
for i in range(1, len(HEADERS)):
Width = fontRuler.size(0, HEADERS[i]).width() + 16
self.setColumnWidth(i, Width)
self.setAutoScroll(True)
self.setIndentation(32)
self.setAlternatingRowColors(True)
self.setUniformRowHeights(True)
self.itemClicked.connect(self.onItemClicked)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
@pyqtSlot(QTreeWidgetItem)
def onItemClicked(self, item):
window = getWindow(self)
nodes = self.findItems(
"", Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchRecursive
)
for node in nodes:
if (
node.checkState(0) == Qt.CheckState.Unchecked
and node.song in detailed_items
):
i = detailed_items.index(node.song)
detailed_items.remove(node.song)
layoutitem = window.detailArea.scrollArea.Layout.itemAt(i)
widget = layoutitem.widget()
window.detailArea.scrollArea.Layout.removeItem(layoutitem)
window.detailArea.scrollArea.Layout.removeWidget(widget)
elif (
node.childCount() == 0
and node.checkState(0) == Qt.CheckState.Checked
and node.isSongNode
):
node.detail()
if item.childCount() == 0 and item.isSongNode:
if item.song not in detailed_items:
item.detail()
index = detailed_items.index(item.song)
dim_others(window, index)
widget = window.detailArea.scrollArea.Layout.itemAt(index).widget()
QTimer.singleShot(
25, lambda: window.detailArea.scrollArea.ensureWidgetVisible(
widget)
)
widget.highlight()
setButtonsStatus(window)
The tree is filled by this:
def add_nodes(tree, cls, data):
indexes.clear()
for artist, albums in data.items():
artist_node = cls([artist])
indexes.add([artist])
for album, songs in albums.items():
album_node = cls([artist, album])
indexes.add([artist, album])
for song in songs:
song_node = cls([artist, album, *song])
indexes.add([artist, album, song[0]])
album_node.addChild(song_node)
artist_node.addChild(album_node)
tree.addTopLevelItem(artist_node)
And this is the start of code execution:
if __name__ == '__main__':
mysql80 = psutil.win_service_get("MySQL80").as_dict()
if mysql80["status"] != "running":
os.system("net start MySQL80")
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(u'Asceteria')
songs = fetchdata()
entries = treefy(songs)
app = QApplication(sys.argv)
app.setStyle("Fusion")
tree = Tree()
add_nodes(tree, TreeNode, entries)
filterArea = FilterArea()
Window = MainWindow()
print(tree)
print(tree.parent())
print(tree.parent().parent())
Window.showMaximized()
app.setWindowIcon(QIcon(ICON))
app.exec()
When I check any QTreeWidgetItem, the program crashes, because the top level QTreeWidgetItems don't have parents:
<groupA.Tree object at 0x0000022ABCFE9430>
<PyQt6.QtWidgets.QWidget object at 0x0000022AC10DBAF0>
<__main__.MainWindow object at 0x0000022AC10DBA60>
<groupA.TreeNode object at 0x0000022ABE087EE0>
<groupA.TreeNode object at 0x0000022ABE087E50>
<groupA.TreeNode object at 0x0000022ABE087DC0>
None
Traceback (most recent call last):
File "D:\Asceteria\groupA.py", line 148, in onItemClicked
node.detail()
File "D:\Asceteria\groupA.py", line 90, in detail
window = getWindow(self)
File "D:\Asceteria\functions.py", line 112, in getWindow
window = window.parent()
AttributeError: 'NoneType' object has no attribute 'parent'
How can I fix this?
Upvotes: 0
Views: 1235
Reputation: 244282
It is not necessary to implement any method to obtain the window associated with a widget since Qt provides the window()
method for it:
QWidget *QWidget::window()
const Returns the window for this widget, i.e. the next ancestor widget that has (or could have) a window-system frame.If the widget is a window, the widget itself is returned.
Typical usage is changing the window title:
aWidget->window()->setWindowTitle("New Window Title");
In your case use:
window = self.treeWidget().window()
Note: If you want to verify that an object is an instance of a class or its ancestor then you must use isinstace:
isinstance(window, MainWindow)
Note: The relationship between QTreeWidgetItem is different from that of QWidgets.
Upvotes: 1