iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
Software Development

[Python QT] 玩玩 Pyside 的各種功能系列 第 28

【Day28】QTreeView 完全新手 - 續續續之假按鈕

  • 分享至 

  • xImage
  •  

今天花了很多時間在想該怎麼取的 tree 裡面的單一資料, 原本希望在星星後直接加按鈕前往網站, 經過一翻研究後, 目前只知道能放圖片, 就這樣, 所以我們先來找個圖片來當作假按鈕吧

圖標一樣是從 Flaticon 取得

import sys
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *

data = {"Saber": {"Buster":{}, "Arts":{}, "Quick":{}},
        "Archer": {"Buster": {"EMIYA": {"star": "4", "website": "https://gamepress.gg/grandorder/servant/emiya"},
            "Gilgamesh": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/gilgamesh"},
            "Arjuna": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/arjuna"},
            "Kid Gilgamesh": {"star": "3", "website": "https://gamepress.gg/grandorder/servant/kid-gilgamesh"}}, "Arts":{}, "Quick":{}},
        "Lancer": {"Buster": {"Karna": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/karna"}}, "Arts": {},
            "Quick": {"Cu Chulainn": {"star": "3", "website": "https://gamepress.gg/grandorder/servant/cu-chulainn"}}},
        "Rider": {"Buster": {}, "Arts": {}, "Quick": {"Alexander": {"star": "3", "website": "https://gamepress.gg/grandorder/servant/alexander"}}},
        "Caster": {"Buster": {},
            "Arts": {"Zhuge Liang (El-Melloi II)": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/zhuge-liang-el-melloi-ii"},
            "Solomon (Grand Caster)": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/solomon-grand-caster"}},"Quick": {}},
        "Assassin": {"Buster":{}, "Arts":{}, "Quick":{}},
        "Berserker": {"Buster": {"Cu Chulainn (Alter)": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/cu-chulainn-alter"}},
            "Arts": {}, "Quick": {}},
        "Extra" : {"Buster":{}, "Arts":{},
            "Quick":{"Edmond Dantes": {"star": "5", "website": "https://gamepress.gg/grandorder/servant/edmond-dantes"}}}}

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.treeV = QTreeView(self)
        self.treeV.setIconSize(QSize(80, 30))
        self.treeV.clicked.connect(self.treeMousePress)
        model = QStandardItemModel(self.treeV)
        model.setHorizontalHeaderItem(0, QStandardItem("Servant"))
        model.setHorizontalHeaderItem(1, QStandardItem("Star"))
        model.setHorizontalHeaderItem(2, QStandardItem("Website"))
        for key, values in data.items():
            itemClass = QStandardItem(QIcon("icon\\"+key+".png"), "")
            itemClass.setEditable(False)
            for np, sv in values.items():
                itemNP = QStandardItem(QIcon("icon\\"+np+".png"), "")
                itemNP.setEditable(False)
                for name, info in sv.items():
                    itemName = QStandardItem(QIcon("icon\\"+name+".png"), name)
                    itemName.setEditable(False)
                    stars = ""
                    for i in range(int(info["star"])):
                        stars = stars + "★"
                    itemStar = QStandardItem(stars)
                    itemStar.setEditable(False)
                    icon = QIcon("icon\\http.png")
                    itemBtn = QStandardItem(QIcon("icon\\http.png"), "Click to see more...")
                    itemNP.appendRow([itemName, itemStar, itemBtn])
                itemClass.appendRow(itemNP)
            model.appendRow(itemClass)
        self.treeV.setModel(model)
        self.treeV.resize(500, 300)

if __name__ == "__main__":
    app = QApplication([])

    widget = MyWidget()
    widget.adjustSize()
    widget.show()

    sys.exit(app.exec())

目前的長相
https://ithelp.ithome.com.tw/upload/images/20220930/20151144yBDkWA6zut.png

因為現在沒有按鈕讓我們直接連接到函示, 所以在這裡我們要使用到的是 QtWidgets 大感是本身就有的基本函式(因為我還沒全部的 Widget 都看過一遍, 所以我不敢講得很肯定), 之前在按鈕那邊就看過的 clicked.connect()

沒錯! 不只按鈕, 樹(Tree) 也可以, 前面講過的元件都可以使用!

在這裡我們就用 self.treeV.clicked.connect(self.treeMousePress)

然後因為 tree 的性質不像 table 可以直接取得行列的序號, 原本我還想說要用 iteration 的方式, 好險馬上就看到 QTreeView 的成員函示 currentIndex()

話不多說, 先上程式碼, 一樣為了不讓文章太長, data 的部份去掉

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.treeV = QTreeView(self)
        self.treeV.setIconSize(QSize(80, 30))
        self.treeV.clicked.connect(self.treeMousePress)
        model = QStandardItemModel(self.treeV)
        model.setHorizontalHeaderItem(0, QStandardItem("Servant"))
        model.setHorizontalHeaderItem(1, QStandardItem("Star"))
        model.setHorizontalHeaderItem(2, QStandardItem("Website"))
        for key, values in data.items():
            itemClass = QStandardItem(QIcon("icon\\"+key+".png"), "")
            itemClass.setEditable(False)
            for np, sv in values.items():
                itemNP = QStandardItem(QIcon("icon\\"+np+".png"), "")
                itemNP.setEditable(False)
                for name, info in sv.items():
                    itemName = QStandardItem(QIcon("icon\\"+name+".png"), name)
                    itemName.setEditable(False)
                    stars = ""
                    for i in range(int(info["star"])):
                        stars = stars + "★"
                    itemStar = QStandardItem(stars)
                    itemStar.setEditable(False)
                    icon = QIcon("icon\\http.png")
                    itemBtn = QStandardItem(QIcon("icon\\link.png"), "Click to see more...")
                    itemNP.appendRow([itemName, itemStar, itemBtn])
                itemClass.appendRow(itemNP)
            model.appendRow(itemClass)
        self.treeV.setModel(model)
        self.treeV.resize(500, 300)

    def treeMousePress(self):
        index = self.treeV.currentIndex()
        if (index.data(0) == "Click to see more..."):
            row = index.row()
            np = index.sibling(0,0).parent().row()
            _class = index.sibling(0,0).parent().parent().row()
            getClass = data[list(data)[_class]]
            getNp = getClass[list(getClass)[np]]
            url = getNp[list(getNp)[row]]["website"]
            if not QDesktopServices.openUrl(url):
                QMessageBox.warning('Open Url', 'Could not open url')

if __name__ == "__main__":
    app = QApplication([])

    widget = MyWidget()
    widget.adjustSize()
    widget.show()

    sys.exit(app.exec())

在這裡為了從樹的底部回流找到頂端, 因為完全是第一次使用而且完全只靠看內建的成員函示來做, 所以找是哪個資料被點的方式有點蠢蠢

我們以點擊迦爾納為例, 來看看 def treeMousePress(self) 裡每一行會給甚麼結果

index = self.treeV.currentIndex()

會輸出

<PySide6.QtCore.QModelIndex(2,1,0x1fa798c4940,QStandardItemModel(0x1fa7988cf50)) at 0x000001FA7AA21EC0>

完全看不懂!

沒關係, 下一句!

index.data(0)

案左至右的順序按名字, 星星, 跟連結的輸出為

Arjuna
★★★★★
Click to see more...

終於是看懂了, 這樣至少可以知道目前點到的是哪一個資料

然後這邊發現 QTreeView 還有成員函示叫做 sibling(0,0) 跟 parent()
從字面上來看的話, 應該就是在說目前點擊到的資料的同階層的其他資料跟上一層資料
經過測試後果然沒錯!
如果直接使用 sibling(0,0) 跟 parent() 的話, 會得到跟剛剛上方 currentIndex() 那樣看不懂的輸出結果, 在這兩個函式後方加上 .data(0) 後就可以得到內容資料
然而因為我在前面初始化樹的時候, 有些地方用圖片而不是文字, 所以只能用 row() 來看目前所在位置
繼續往下看

row = index.row()

輸出為 2, 而加爾納正好位於 Archer->Buster 從 0 開始, index 為 2 的位置

有了這些知識後, 就可以找到目前點的東西是如何從 node 到達的了
接下來就是從 data 中取的我們要的資料
以下展示
tree


上一篇
【Day27】QTreeView 完全新手 - 續續之圖片
下一篇
【Day29】QHelpContentWidget
系列文
[Python QT] 玩玩 Pyside 的各種功能31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言