Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt GUI _ Получение порядкового номера QTreeView

Автор: poluna 15.12.2015, 17:11

Привет всем.
Есть у меня класс TreeComboBox, как можно понять из названия в QComboBox у меня находится QTreeView.

Проблема в том, что я никак не могу получить порядковый номер элемента в QTreeView, для автоматического выбора при выделении в моей модели.
С помощью idx.row() я могу получить, но если у элемента уровень вложенности например 2, то получается ерунда.

Автор: lanz 15.12.2015, 17:42

А как вы определяете порядковый номер элемента в дереве? Всмысле что это значит? Т.е. какой порядковый номер должен быть у первого элемента вложенности 2 например?

Автор: poluna 16.12.2015, 9:40

Забыла сказать, что QTreeView всегда находится в состоянии expandAll.
Порядковый номер хочу определять визуально так же как и в QComboBox.
Например

1 Item 1
2   |-item 2
3   |-item 3
4        |-item 4
5        |-item 5
6   |-item 6
7        |-item 7
8 Item 8
9   |-item 9
Пока только приходит в голову, обход всех элементов рекурсивно с их дочерними элементами и присвоением номера.
Или ввести свою сущность (строка) в модель, и с помощью нее строить модель и нумеровать строки.

Хотя честно говоря внятной и полной картины как это сделать нет. :(

Автор: Алексей1153 16.12.2015, 10:04

poluna, расскажи поподробнее, какие действия хочешь совершить ? Для идентификации элемента вроде достаточно методов parent и data индекса

ну, и да, если хочешь такую сквозную нумерацию сделать - необходимо реализовать у элемента метод навроде GetMyNumber() , где произойдёт рекурсивный вызов метода родителя в том числе

Автор: poluna 16.12.2015, 10:57

Так как мой класс производен TreeComboBox от QComboBox, то мне нужно хотя бы переопределить функции: currentIndex, setCurrentIndex и currentText.
Это нужно например для ситуации, когда мне нужно добавить пользователя в группу, выбрав группу в дереве, нажав кнопку добавить и в появившемся диалоге добавления пользователя будет выбрана группа (выделенная ранее) и ...

Автор: Алексей1153 16.12.2015, 11:09

poluna, не совсем понимаю участия комбы в этом процессе :search: . В классе дерева обрабатываешь contextMenuEvent , там определяешь, какой элемент модели дерева щёлкнули (viewport() , indexAt() ) . По индексу модели отыскивается элемент

Автор: poluna 16.12.2015, 11:27

Мне просто нужен комбобокс с деревом! :)
Всё есть, но есть нюансы, из-за которых пока грустно

Автор: Алексей1153 16.12.2015, 11:31

Цитата(poluna @ 16.12.2015, 13:27) *
Мне просто нужен комбобокс с деревом

так он же уже есть, как я понял )) Осталось обработку в дерево добавить

Автор: poluna 16.12.2015, 12:10

И я о том же!
Видимо я не так выражаюсь.


На картинке, показано, что у меня есть.
В диалоге "Создание группы" указывается, родитель он должен быть выбран таким же, как и выделенный в левом дереве основного окна.
А я пока могу выбрать только ветки первого уровня.
Так надеюсь понятней, что мне нужно. :)

Или я тебя не понимаю! :D

Автор: Алексей1153 16.12.2015, 12:28

poluna, у меня обычно есть "внемодельное" дерево - источник, по которому модель пересобирается. Контейнер обычно на основе QMap<>

struct s_item
{
s_item* m_parent;
QVector<s_item*> m_children;

...

};


s_item* m_root;
QMap<s_item*,s_item*> m_tree;


У тебя этой очень удобной детальки , похоже, нету, поэтому мы не и понимаем друг друга :D В контейнере всё ищется и перебирается запросто

Автор: poluna 16.12.2015, 12:36

Теперь я поняла тебя. :)
Вариант неплох, и вроде как даже проще в реализации.

Всё, вопрос пока снимаю. Знаю как реализовывать! :yahoo:

Автор: lanz 16.12.2015, 12:44

Цитата
"внемодельное" дерево

Ойойой, не слушайте его, он вас плохому научит! :lol:
По идее модель в комбобоксе и модель в дереве слева - это должна быть одна и та же модель.
Поэтому индекс от одного должен подходить к индексу от другого.

Т.е. вы сначала получаете выделенный индекс от дерева
(назовем его idx)

потом в комбобоксе делаете
combo->setRootModelIndex(idx.parent())
combo->setCurrentIndex(idx.row())

http://doc.qt.io/qt-4.8/qcombobox.html#setRootModelIndex
http://doc.qt.io/qt-4.8/qcombobox.html#currentIndex-prop

Автор: Алексей1153 16.12.2015, 13:00

lanz, да можно и в модели хранить, но я так не люблю делать, это же неудобно ))

lanz, оно, вообще говоря, так и происходит - противоречий нету, но некоторые операции по своему контейнеру удобнее производить

А в данном случае твой вариант лучше будет, конечно )

Автор: ViGOur 16.12.2015, 13:27

А по моему Алексей1153 предложил хороший способ, я сам подобным же пользуюсь.
Есть список (QList) или дерево(QMap), которое откуда-то загружается и которое отображается в модели. Очень удобно добавлять, редактировать, удалять. А модель это же абстракция и она не должна по идее хранить данные, как и вид. :)

lanz, выше сказано

Цитата(poluna @ 15.12.2015, 17:11) *
Есть у меня класс TreeComboBox, как можно понять из названия в QComboBox у меня находится QTreeView.
и как я понимаю твой метод идеально подходит для QComboBox, но не для переопределенного класса. Дождемся автора, что она скажет!

Автор: poluna 16.12.2015, 13:34

lanz, если в комбобоксе стандартными средствами можно показать дерево, то твой метод подойдет, но я не смогла.
Как я поняла для показа дерева в комбобокс нужно переопределять класс, я сделала так:

#! /usr/bin/python
# -*- coding: UTF-8 -*-

from PyQt4 import QtCore, QtGui


class TreeComboBox(QtGui.QComboBox):
    def __init__(self, parent=None):
        super(QtGui.QComboBox, self).__init__(parent)
        self._skipNextHide = False
        self._treeView = QtGui.QTreeView(self)
        self.setView(self._treeView)
        self._treeView.header().hide()
        self._treeView.viewport().installEventFilter(self)

    def eventFilter( self, object, event):
        if event.type() == QtCore.QEvent.MouseButtonPress and object == self.view().viewport():
            index = self.view().indexAt(event.pos())
            if not self.view().visualRect(index).contains(event.pos()):
                self._skipNextHide = True
        return False

    def showPopup(self):
        self.setRootModelIndex(QtCore.QModelIndex())
        self._treeView.expandAll()
        QtGui.QComboBox.showPopup(self)

    def hidePopup(self):
        self.setRootModelIndex(self.view().currentIndex().parent())
        self.setCurrentIndex(self.view().currentIndex().row())
        if self._skipNextHide:
            self._skipNextHide = False
        else:
            QtGui.QComboBox.hidePopup(self)
если я не права, то буду только рада, сразу куча проблем исчезнет! :)
Но пока не знаю как!

Автор: lanz 16.12.2015, 14:53

poluna, ну у меня ваш код вроде работает как надо, ЧЯДНТ? :lol:
Немножко поменял hidePopup, чтобы он сразу все не корячил:

    def hidePopup(self):
        if self._skipNextHide:
            self._skipNextHide = False
        else:
            self.setRootModelIndex(self.view().currentIndex().parent())
            self.setCurrentIndex(self.view().currentIndex().row())
            QtGui.QComboBox.hidePopup(self)

Автор: poluna 16.12.2015, 16:06

Все, поняла, все работает! :)

Выкладываю работающий пример, так же на python:

пример
#! /usr/bin/python
# -*- coding: UTF-8 -*-

import sys
from PyQt4 import QtCore, QtGui

class TreeComboBox(QtGui.QComboBox):
    def __init__(self, parent=None):
        super(QtGui.QComboBox, self).__init__(parent)
        self._skipNextHide = False
        self._treeView = QtGui.QTreeView(self)
        self.setView(self._treeView)
        self._treeView.header().hide()
        self._treeView.viewport().installEventFilter(self)

    def eventFilter( self, object, event):
        if event.type() == QtCore.QEvent.MouseButtonPress and object == self.view().viewport():
            index = self.view().indexAt(event.pos())
            if not self.view().visualRect(index).contains(event.pos()):
                self._skipNextHide = True
        return False

    def showPopup(self):
        self.setRootModelIndex(QtCore.QModelIndex())
        self._treeView.expandAll()
        QtGui.QComboBox.showPopup(self)

    def hidePopup(self):
        if self._skipNextHide:
            self._skipNextHide = False
        else:
            self.setRootModelIndex(self.view().currentIndex().parent())
            self.setCurrentIndex(self.view().currentIndex().row())
            QtGui.QComboBox.hidePopup(self)

class Main(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self._comboBox = TreeComboBox(self)
        self._treeView = QtGui.QTreeView(self)

        layout = QtGui.QVBoxLayout()
        layout.addWidget(self._comboBox)
        layout.addWidget(self._treeView)
        self.setLayout(layout)

        model = QtGui.QStandardItemModel()
        for a in range(3):
            i = QtGui.QStandardItem('Item ' + str(a))
            for b in range(3):
                ii = QtGui.QStandardItem('sub 1 Item ' + str(b))
                i.setChild(b, ii)
                for c in range(3):
                    iii = QtGui.QStandardItem('sub 2 Item ' + str(c))
                    ii.setChild(c, iii)
            model.appendRow(i)

        self._comboBox.setModel(model)
        self._treeView.setModel(model)

        self.connect(self._treeView, QtCore.SIGNAL("clicked(const QModelIndex&)"), self.comboSelect)

    def comboSelect(self, idx):
        self._comboBox.setRootModelIndex(idx.parent())
        self._comboBox.setCurrentIndex(idx.row())

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)

    main = Main()
    main.show()

    sys.exit(app.exec_())


lanz, спасибо огромное! :)

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)