0

我在 Python 3.9.6 上使用 PyQt6 6.1.0,我想获取 QTreeWidgetItem 的父 QTreeWidgetItems 的父 QTreeWidget 的父 QMainWindow,因为我需要从 QTreeWidgetItem 中修改窗口。

完整的代码很长,超过 1800 行,大小超过 60 KiB,当我将它放在一个文件中时,它可以正常工作,但是当放在一个文件中时,它的可读性非常低,并且当拆分为文件时,全局变量可以是很难从类中访问。

基本上我的想法是重复调用.parent(),直到对象的类名是'MainWindow'。

def getWindow(obj):
    window = obj.parent()
    while True:
        if type(window).__name__ == 'MainWindow':
            break
        window = window.parent()
    return window

我没有直接使用该类,因为该类是在主文件中定义的。

以下是在名为 的文件中定义的两个类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)

树是这样填充的:

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)

这是代码执行的开始:

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()

当我检查任何 QTreeWidgetItem 时,程序崩溃,因为顶级 QTreeWidgetItems 没有父母:

<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'

我怎样才能解决这个问题?

4

1 回答 1

1

没有必要实现任何方法来获取与窗口小部件关联的窗口,因为 Qtwindow()为它提供了方法:

QWidget *QWidget::window()
const 返回此小部件的窗口,即具有(或可能具有)窗口系统框架的下一个祖先小部件。

如果小部件是窗口,则返回小部件本身。

典型用法是更改窗口标题:

aWidget->window()->setWindowTitle("New Window Title");

在你的情况下使用:

window = self.treeWidget().window()

注意:如果你想验证一个对象是一个类的实例还是它的祖先,那么你必须使用 isinstace:

isinstance(window, MainWindow)

注意: QTreeWidgetItem 之间的关系与 QWidgets 的关系不同。

于 2021-07-20T14:52:39.060 回答