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

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

Форум на CrossPlatform.RU _ Qt GUI _ Как получить текущий QDockWidget, который отображается в QTabBar'e на MainWindow

Автор: SABROG 26.12.2008, 0:01

Сегодня озадачился таким вопросом. Как получить текущий QDockWidget, который отображается в QTabBar'e на MainWindow. Т.е. это QDockWidget'ы положенные друг на друга таким образом, что появляются ярлычки как в QTabWidget. Но проблема в том, что это QTabBar и у него нет метода типа QTabWidget::currentWidget(). Более того, по сути эти QTabBar'ы не принадлежат нам и получить на них указатели стандартными средствами QMainWindow мы не можем. Также напрашивается решение - пробежаться по всем QDockWidget'ам и найти те, которые не спрятаны (isVisible). Со слов другого человека этот вариант не работает, т.к. у всех виджетов метод isVisible возвращает true. Также я предложил вариант проверить x,y,width,height. Если у двух виджетов они одинаковы, значит они лежат друг на друге, а следовательно в "стеке" (QMainWindow не использует ни один из вариантов стека виджетов или лэйаутов). Но оказалось, что у видимых QDockWidget'ов координаты положительные, в то время как у скрытых за ними виджетами они отрицательные. Т.е. этот способ можно использовать, но выглядит он не очень надежным. В итоге этот вопрос привел меня к следующему решению.

Сразу хочу предупредить, что этот вариант может не работать на других версиях Qt, т.к. в них может быть уже по другому реализован класс QMainWindow/QDockWidget.

    QList <QTabBar *>lst = this->findChildren<QTabBar *>(); // рекурсивно ищем деток типа QTabBar* в QMainWindow
    quint8 i = 0; // 255 TabBar'ов должно хватить;)
    foreach(QTabBar *tab, lst)
    {
/*
QTabBar'ы могут создаваться, но не уничтожатся, поэтому они могут быть невидимы.
Еще есть вариант, когда в TabBar'e всего 2 DockWidget'а, при этом один плавает (floating),
пользователь видет, что TabBar исчез, однако реально исчезает только ярлычек.
QTabBar скрывается, когда плавающее окно пришвартовывается куда-нибудь и в QTabBare
остается всего 1 элемент. В общем проверка с count'ом опциональна, чтобы детектить
где QTabBar'ы имеют ярлычки.
*/
        if (tab->isVisible() /*&& tab->count() > 1*/)
        {
            i++; // ведем подсчет видимых QTabBar'ов, если надо
/*
Далее идет непереводимая игра слов :)
Разработчики Qt в каждый ярлычек помещают указатель на QDockWidget, которому ярлычек принадлежит.
Процедура перевода указателя QWidget'a в QVariant с пользовательским типом и обратно.
Страшный тип quintptr - кроссплатформенный вариант указателя, где надо его размер 32 бита, где не надо 64.
*/
            quintptr wId = qvariant_cast<quintptr>(tab->tabData(tab->currentIndex()));
            QDockWidget *widget = reinterpret_cast<QDockWidget *>(wId);
            QMessageBox::information(0,"Info", QString("class:%1, title:%2").arg(widget->metaObject()->className()).arg(widget->windowTitle()));
        }
    }


Логика кода следующая. Ищем все видимые QTabBar'ы и получаем указатель на QDockWidget, который в данный момент показывается пользователю (тоже самое что и QTabWidget::currentWidget()).



---
Я не помещаю решение в Вики из-за того, что оно по сути - хак (если администрация даст добро, то помещу) и хотелось бы услышать мнения людей, может быть от меня ускользнули очевидные вещи.

Автор: Litkevich Yuriy 26.12.2008, 0:18

Цитата(SABROG @ 26.12.2008, 3:01) *
хочу предупредить, что этот вариант может не работать на других версиях Qt
а на какой проверял и ОСька какая?

Автор: SABROG 26.12.2008, 0:22

Windows XP Professional SP3 (2002 build)
MinGW 3.4.5
Qt 4.4.0-beta1 (такой и нет уже наверное)

Автор: Litkevich Yuriy 26.12.2008, 0:40

может я что-то не понял, но узнать видимый или нет я могу так:

void MainWindow::createDockWindows()
{
...
QDockWidget *dock = new QDockWidget(tr("Customers"), this);
...
// Добавил соединение
connect(dock, SIGNAL(visibilityChanged(bool) ), this, SLOT(slot_visibilityChanged(bool)));
...
dock = new QDockWidget(tr("Paragraphs"), this);
...
// Добавил соединение
connect(dock, SIGNAL(visibilityChanged(bool) ), this, SLOT(slot_visibilityChanged(bool)));
...
}


// Добавил слот
void MainWindow::slot_visibilityChanged ( bool visible )
{
    QDockWidget *dock = qobject_cast<QDockWidget *>(sender());
    qDebug() << "Dock" << dock->windowTitle() << "isVisible?" << visible;
}

это доработка примера %QTDIR%\examples\mainwindows\dockwidgets


вывод в консоль при старте:
Dock "Customers" isVisible? true
Dock "Paragraphs" isVisible? true

и в том же духе при перетаскивании и всяком разном расположении

Автор: SABROG 26.12.2008, 9:14

Тоже вариант, правда неудобный. Придется создавать лист виджетов с их состояниями, чтобы можно было определить какие из-них в данный момент отображаются. Удалять и добавлять в лист закрытые и новые созданные виджеты. Да и переключением закладок програмным методом уже не воспользоваться.

Автор: kwisp 26.12.2008, 10:16

в Qt 4.4.3. есть
int QTabWidget::currentIndex()const;
void QTabWidget::currentChanged(int);[signal]

не поможет?

Автор: Litkevich Yuriy 26.12.2008, 10:54

SABROG, вобщем, тему стоит пожевать и найти решение. Я думаю надо делать либо класс "организатор", либо наследника более умного от QDockWidget.

Автор: SABROG 26.12.2008, 12:12

Цитата(kwisp @ 26.12.2008, 10:16) *
не поможет?

Совершенно. QMainWindow использует QTabBar'ы, не QTabWidget'ы. А в QTabBar'е есть только currentIndex.
Но опять же стандартными методами через QMainWindow невозможно получить список указателей на QTabBar'ы, если таковые существуют. А их может и не быть, если пользователь не манипулировал с доками.

Цитата(Litkevich Yuriy @ 26.12.2008, 10:54) *
SABROG, вобщем, тему стоит пожевать и найти решение. Я думаю надо делать либо класс "организатор", либо наследника более умного от QDockWidget.

Если делать умного наследника от QDockWidget'а, то что это должно дать ? Проверку типа isTopWidget() ?
Класс организатор тоже решение. Хоть и правильное решение с точки зрения программирования, но достаточно непростое и скорее смахивает на затычку.

По-хорошему надо написать и класс-менеджер и добавить в таск-лист троллям, что хотелось бы иметь подобную возможность. Т.е. добавить методы для QMainWindow и доп.класс типа QDockTabBar на базе QTabBar:
QList <QDockTabBar *> QMainWindow::dockTabBars() const

Для класса QDockTabBar метод:
QDockWidget * QDockTabBar::currentWidget() const
void QDockTabBar::setCurrentWidget(QDockWidget *dw)

Может еще какие-то.

Автор: Litkevich Yuriy 26.12.2008, 12:37

SABROG, давай еще раз формулировку задачи.
Положим есть такие ситуации:
1) доквиджеты расположены самостоятельно, т.е. не скрывая другдруга
2) только два доквиджета из нескольких сложены в виде вкладок
3) существует несколько групп доквиджетов сложенных в виде вкладок (т.е. как на рисунке в первом посте)

при этом только один из доквиджетов имеет фокус

что нужно определить/сделать?

Автор: SABROG 26.12.2008, 13:15

Предположим ситуацию. У пользователя есть доквиджеты в которых размещаются таблицы базы данных.
От сервера приходит сообщение, что данные в таблицах изменились и их надо обновить.
Операция обновления может быть достаточно долгим процессом, особенно если данных много, а канал маленький.
Более того, операция обновления может быть бесполезной, если пользователь работает только с несколькими
активными доквиджетами. Стало быть надо определить какие виджеты в данный момент может видеть пользователь,
чтобы обновлять только их.

Или можно аналогичный пример только уже с графикой. Есть несколько виджетов - окон,
в которых происходит отрисовка сцены, предположим каждое окно - вид камеры.
Цель - обновлять только видимые камеры.

Автор: Litkevich Yuriy 26.12.2008, 13:23

в итоге: список видимых (или тех, что сверху) в данный момент доквиджетов.

да, уместнее было бы чтобы это была функция QMainWindow, например: QList<QWidget*> QMainWindow::topDocWidgetsList()

Автор: SABROG 26.12.2008, 13:34

Я предлагаю не ограничиваться только этой задачей. Т.к. переключение табов, добавление, удаление виджета из/в таб/а програмным методом было бы тоже интересно. Правда я не пробывал, возможно ли сейчас сложить в табы выбранные доквиджеты програмным методом ? Это надо move() делать на заданные координаты или флаги какие устанавливать ?

Автор: kwisp 26.12.2008, 13:48

Цитата(SABROG @ 26.12.2008, 12:12) *
Цитата(kwisp @ 26.12.2008, 10:16) *
не поможет?

Совершенно. QMainWindow использует QTabBar'ы, не QTabWidget'ы. А в QTabBar'е есть только currentIndex.
Но опять же стандартными методами через QMainWindow невозможно получить список указателей на QTabBar'ы, если таковые существуют. А их может и не быть, если пользователь не манипулировал с доками.

Цитата(Litkevich Yuriy @ 26.12.2008, 10:54) *
SABROG, вобщем, тему стоит пожевать и найти решение. Я думаю надо делать либо класс "организатор", либо наследника более умного от QDockWidget.

Если делать умного наследника от QDockWidget'а, то что это должно дать ? Проверку типа isTopWidget() ?
Класс организатор тоже решение. Хоть и правильное решение с точки зрения программирования, но достаточно непростое и скорее смахивает на затычку.

По-хорошему надо написать и класс-менеджер и добавить в таск-лист троллям, что хотелось бы иметь подобную возможность. Т.е. добавить методы для QMainWindow и доп.класс типа QDockTabBar на базе QTabBar:
QList <QDockTabBar *> QMainWindow::dockTabBars() const

Для класса QDockTabBar метод:
QDockWidget * QDockTabBar::currentWidget() const
void QDockTabBar::setCurrentWidget(QDockWidget *dw)

Может еще какие-то.


прошу прощения
в Qt 4.4.3. есть
int QTabBar::currentIndex()const;
void QTabBar::currentChanged(int);[signal]

очепятка вышла.

Автор: SABROG 26.12.2008, 14:03

Цитата(kwisp @ 26.12.2008, 13:48) *
прошу прощения
в Qt 4.4.3. есть
int QTabBar::currentIndex()const;
void QTabBar::currentChanged(int);[signal]

очепятка вышла.


Опять же. Ты не можешь стандартными методами QMainWindow получить указатели на QTabBar'ы.
И полученные сигналы и индексы бесполезны, если нет возможности связать их с конкретными виджетами.

Автор: SABROG 26.12.2008, 23:12

В общем решил развить тему сигнала visibilityChanged. На этот раз метод должен работать везде, т.к. привязки к нестандартным вещам нет :)

В конструкторе соединями сигналы каждого QDockWidget'а к одному слоту. Можно соединять вручную поименно каждый виджет, как удобнее. Просто с учетом на будущее я решил пробежаться по всем детишкам, мало ли добавится еще пара окон.

DockWidgets::DockWidgets(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    QList <QDockWidget *> lst = findChildren<QDockWidget *>();
    foreach(QDockWidget *widget, lst)
    {
        connect(widget, SIGNAL(visibilityChanged(bool)), SLOT(slotVisibilityChanged(bool)));
    }
}


Наш слот. Хочу заметить, что он вызывается для каждого доквиджета при открытии главного окна с параметром true. Далее при перетаскивании любого доквиджета слот вызывается 4 раза. Сначала доквиджет скрывается (false), появляется плавающее окно (true), когда перетаскивание завершается, плавающий доквиджет скрывается (false), и вновь отображается уже в доке (true). При складывании доквиджетов в таббар приходят наши две пары сигналов плюс еще пара сигналов, всего 6 сигналов для одного виджета. Кроме этого приходит еще один сигнал с того виджета, который скрывается под перетащенным.

void DockWidgets::slotVisibilityChanged(bool visible)
{
    QObject *sndr = sender();
    if (sndr)
    {
        sndr->setProperty("dockIsVisible", visible);
        qDebug()
        << "CName:" << sndr->metaObject()->className()
        << "WTitle: " << sndr->property("windowTitle").toString()
        << "isVisible(): " << sndr->property("visible").toString()
        << "vChange: " << sndr->property("dockIsVisible").toString();
    }
}


Логика следующая. Мы добавляем динамический параметр к каждому QDockWidget'у, который отображает его текущее состояние. В любой момент можно будет узнать какой доквиджет в данный момент не скрыт. По сути это аналог параметру visible у QWidget, но по неизвестным мне причинам он не работает. Отладочный вывод это показывает на последних строчках.

CName: QDockWidget WTitle:  "2" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "1" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "3" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "4" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "5" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "6" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "7" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "8" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "9" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "10" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "3" isVisible():  "false" vChange:  "false"
CName: QDockWidget WTitle:  "3" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "3" isVisible():  "false" vChange:  "false"
CName: QDockWidget WTitle:  "3" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "3" isVisible():  "true" vChange:  "false"
CName: QDockWidget WTitle:  "3" isVisible():  "true" vChange:  "true"
CName: QDockWidget WTitle:  "2" isVisible():  "true" vChange:  "false"


Пока я не затрагиваю тему управления QTabBar'ом.

Как вам такой вариант решения ?

Автор: Litkevich Yuriy 26.12.2008, 23:19

SABROG, чёто с форматированием, как-то все плохо :(

Цитата(SABROG @ 27.12.2008, 2:12) *
isVisible()
этот метод имеет связь с методами show() и hide(), т.е. для определения виден ли пользователю определенно не подходит

для С++ кода надо использовать параметр в тэге код:
[code=cpp][/code]

Автор: SABROG 26.12.2008, 23:23

Цитата(Litkevich Yuriy @ 26.12.2008, 23:19) *
SABROG, чёто с форматированием, как-то все плохо :(

Цитата(SABROG @ 27.12.2008, 2:12) *
isVisible()
этот метод имеет связь с методами show() и hide(), т.е. для определения виден ли пользователю определенно не подходит

для С++ кода надо использовать параметр в тэге код:
[code=cpp][/code]

С форматированием беда какая-то. При выборе cpp подсветка не отображается вообще. Если же пользовать "Код с подсветкой" вместо "Код", то код почему-то идентифицируется как perl, при том, что я указал cpp. Но даже в perl подсвечиваются только 2 слова. Еще текст весь поломался из-за того, что я не заметил закрытый тег код, который не открывал.

Просто по логике, если окно исчезает, значит оно должно хайдиться чтоли :) Надо посмотреть как это организовано в QTabWidget.

Автор: Litkevich Yuriy 27.12.2008, 0:51

Цитата(SABROG @ 27.12.2008, 2:23) *
Просто по логике, если окно исчезает, значит оно должно хайдиться чтоли
что значит исчезает?
Если два доквиджета сложены вкладками и одному скомандывать hide(), то его вкладка исчезнет - виджет спрячится и isVisible() вернет "ложь"

Цитата(SABROG @ 27.12.2008, 2:23) *
При выборе cpp подсветка не отображается вообще
ну как видишь отображается

в первом блочке стандартных С++ных слов только два: this и bool

Автор: SABROG 27.12.2008, 13:35

Цитата(Litkevich Yuriy @ 27.12.2008, 0:51) *
что значит исчезает?
Если два доквиджета сложены вкладками и одному скомандывать hide(), то его вкладка исчезнет - виджет спрячится и isVisible() вернет "ложь"

Я еще вчера посмотрел, в QTabWidget используется QStackedWidget, а он ничего не хайдит. В общем все понятно.
Цитата(Litkevich Yuriy @ 27.12.2008, 0:51) *
ну как видишь отображается

в первом блочке стандартных С++ных слов только два: this и bool

Скудная подсветка просто, сразу не понял, другие хайлайтеры и скобочками со звездочками перекрашивают.

А по теме есть что-нибудь ? Думаю менеджер классов делать бессмысленно, если есть динамические свойства. Стоит в вики внести оба варианта, с reinterpret как дополнительный ?

Автор: Litkevich Yuriy 27.12.2008, 14:00

Цитата(SABROG @ 27.12.2008, 16:35) *
Стоит в вики внести оба варианта, с reinterpret как дополнительный ?
если есть желание напиши в ЧаВо

Автор: Litkevich Yuriy 4.3.2009, 5:50

из изменений в 4.5.0:

Цитата
- QMainWindow
* added API to know which dock widgets are tabified together with a dock widget
http://www.qtsoftware.com/developer/changes/changes-4.5.0

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