QComboBox with QTreeView popup: how can I have different display in QComboBox label and QComboBox item?



  • Hello!
    I'm new to Qt. I use python and PyQt4 (Qt 4.8).

    I'm writing an application where user can choose data he wants to display on a graph. These data are contained in stuctures, so their names are of the form:
    "root.struct1.struct2.data0"

    To let the user choose a data, I would like to use a QComboBox with a QTreeView as a popup using model / view approach. QAbstractItemModel subclassing, QTreeView work fine. My problem is that when the user selects a data name in the QTreeView, the QComboBox label displays the last part of the name (e.g. "data0"), which conforms to model / view, but I woud like it to display the whole name (e.g. "root.struct1.struct2.data0")...

    I'm stuck on this problem, so if someone can give me a solution, or explains how QComboBox gets the user selection, I would be very happy! Thanks for any help...

    Here is what I code:
    @

    -- coding: utf-8 --

    from PyQt4 import QtCore, QtGui

    class MyTreeNode(object):

    def init(self,name,parent,data = None):
    self.name = name
    self.parent = parent
    self.data = data
    self.childs = {}

    def addChild(self,name,data):
    try:
    node = self.childs[name]
    except KeyError:
    node = MyTreeNode(name,self,data)
    self.childs[name] = node
    return node

    def getParent(self):
    return self.parent

    def subnode(self,name):
    return self.childs[name]

    def subnodeIndex(self,name):
    return sorted(self.childs.keys()).index(name)

    def subnodeAt(self,index):
    key = sorted(self.childs.keys())[index]
    return self.childs[key]

    def subnodeCount(self):
    return len(self.childs)

    def isRootOf(self,tree):
    return tree.nodeIsRoot(self)

    class MyTree(object):
    def init(self):
    self.root = MyTreeNode(None,None)

    def setitem(self,key,data):
    if type(key) in [list, tuple]:
    node = self.root
    for k in key:
    node = node.addChild(k,data)
    node.data = data
    else:
    self.root.addChild(key,data)

    def getitem(self,key):
    return self.node(key).data

    def nodeIsRoot(self,node):
    return node == self.root

    def node(self,key):
    if type(key) in [list, tuple]:
    node = self.root
    for k in key:
    node = node.subnode(k)
    return node
    else:
    return self.root[key]

    class MyTreeModel(QtCore.QAbstractItemModel):
    def init(self,tree,parent = None):
    super(MyTreeModel,self).init(parent)
    self.tree = tree

    def rowCount(self,parent):
    node = parent.internalPointer()
    if node == None: return self.tree.root.subnodeCount()
    return node.subnodeCount()

    def columnCount(self,parent):
    return 1

    def data(self,index,role):
    if not index.isValid():
    return None
    if role != QtCore.Qt.DisplayRole:
    return None
    node = index.internalPointer()
    return node.name

    def index(self,row,column,parent):
    if not parent.isValid():
    node = self.tree.root
    else:
    node = parent.internalPointer()
    if row >= node.subnodeCount():
    return QtCore.QModelIndex()
    return self.createIndex(row,column,node.subnodeAt(row))

    def parent(self,index):
    """ returns the grand parent of index """
    if not index.isValid():
    return QtCore.QModelIndex()
    node = index.internalPointer()
    nodeParent = node.getParent()
    if nodeParent == None or nodeParent.isRootOf(self.tree):
    return QtCore.QModelIndex()
    row = nodeParent.getParent().subnodeIndex(nodeParent.name)
    return self.createIndex(row,0,nodeParent)

    def headerData(self,section,orientation,role):
    if orientation == QtCore.Qt.Horizontal:
    if role == QtCore.Qt.DisplayRole:
    return 'Data Name'
    return None

    class TreeBox(QtGui.QComboBox):
    """
    All event / skipNextHide stuff was grabbed from the web
    """
    def init(self,parent = None):
    super(TreeBox,self).init(parent)
    self.skipNextHide = False
    self.setView(QtGui.QTreeView(self))
    self.view().viewport().installEventFilter(self)
    self.view().setAlternatingRowColors(True)
    self.setMinimumSize(QtCore.QSize(200,30))

    def eventFilter(self,obj,event):
    if event.type() == QtCore.QEvent.MouseButtonPress:
    if obj == self.view().viewport():
    mouseEvent = QtGui.QMouseEvent(event)
    pos = mouseEvent.pos()
    index = self.view().indexAt(pos)
    if not self.view().visualRect(index).contains(pos):
    self.skipNextHide = True
    return False

    def hidePopup(self):
    if self.skipNextHide:
    self.skipNextHide = False
    else:
    super(TreeBox,self).hidePopup()

    testDataNames = [
    'box0.box00.box000.sensor0.pressure',
    'box0.box00.box000.sensor0.density',
    'box0.box00.box001.sensor0.salinity',
    'box1.box10.sensor0.temperature',
    'box1.box11.sensor0.sound_velocity',
    'box1.box11.box110.sensor0.water',
    'box1.box12.box120.sensor0.pressure'
    ]

    if name == 'main':
    import sys
    import random

    tree = MyTree()
    for dataName in testDataNames:
    tree[dataName.split('.')] = random.random()

    app = QtGui.QApplication(sys.argv)
    app.setStyle('windows')

    treeModel = MyTreeModel(tree)

    comboBox = TreeBox()
    comboBox.setModel(treeModel)

    comboBox.show()

    sys.exit(app.exec_())

    @


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You could set a custom QStyledItemDelegate where you reimplement the displayText method to return the value you want.

    Hope it helps



  • Hi, thanks for your answer...

    But how may I know if displayText is called for display in QTreeView or for display of the QComboBox label?



  • Back again...
    I spent a bit of time reading some Qt tutorials...

    For this problem, as suggest by SGaist, I used a QStyledItemDelegate for my TreeView. According a flag, the displayText method give the long name or short name of the data. This flag value is set in QComboBox showPopup (the flag is set to short name for QTreeView) and QComboBox hidePopup (long name for QComboBox label)...
    This does work...


  • Lifetime Qt Champion

    Nice !

    You can also set a custom QStyleItemDelegate on your combo box depending on what you would like to get shown in it



  • i try to do it and arrive to do... not so difficult to see it...
    but i experiment that the problem after that is QtreeView will be... inside the QComboBox object, and then, when you click for popup the tree, there is no redimension of the little window of the combobox, and also, you have an event click generated inside the QCombobox object...
    so you need after to:
    re-implement the event mouse clicks...
    and re-design the comboBox for be able to change size for reallyu show the opened tree childs at time.... and maybe more...for add slide bar.

    big work, lot of code, eat time... for little render for me, i forget it, but it is possible.
    I don't know why Qt not provide a tool for do it easy... but when you talk around this on the IRC... some peoples (qorks for Qt) don't like this vision of evolution and block... i think too much work.



  • @jerome_isAvailable,
    I think the real work is reading tutorials, testing examples, practising a bit... Qt is a big thing, and like big things, you can't handle every part of it with a two days practise time... It's easy when you know the right way to do it, but you have to work to know.
    I'm quite happy with what I did in two days, it's far to be perfect, but it's really better than what I had before!


  • Lifetime Qt Champion

    Do you mean you want to show a QTreeView in a QComboBox popup ?



  • @albertho: thank you for your help...
    i would be curious and happy to see your result around this kind of work/experience/practice.
    Maybe you will be able to pastebin the little code you do for show how you do it well ?
    For me... after 3 months, 25 cpp files in my application(who is not finish), 14000 lines of codes and 26 postgresql tables used... i consider to not loose my time to do something (who is sure possible to do) who is not my priority and need lot of experience/work/practice/TIME/CODES LINES for see it working.
    But... maybe i'm wrong and it is possible to do it without loose to much time... and without write to muche lines of codes... i'm sure you will show me. Im' happy you have time for do this, i think this could help the other one who would like to do same (i was one... and i'm interested about your result to).

    Thank you Albertho.



  • @SGaist
    I solved my problem, thanks to your help!

    @jerome_isAvailable
    Well, surely my english writting is not very good. I tried to tell you that I'm a Qt newbie, that I need to work to understand it well... But, within a short time, I was able to put a QTreeView in a QComboBox, and that's fine for me. My test code is in my first post, since I just add less than 10 lines of code. I can't post the whole application...



  • i'm a newbie too... and my english is poor too.
    no problem, i just would like to know if you could use it well: without any problem from open/close trees and see the little window of the combobox adjust the dimension or not ?
    Because for me, it was not so simple. But also, not by python, but by C++ coding (but i think it is maybe not so different... except python coding is shorter numbers of lines).

    I will try your code translated in C++ to, maybe this could help me also in some futur thinks like this style. So i copy your code for translate and use it.

    thanks albertho.



  • Hi @albertho, I am just like you were 2 years back.. a new bee to pyqt. I wanted to have an UI where a QTreeView will get populated inside a QComboBox. Can you help me with an example just to get an idea on how to approach this. Many thanks :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.