Наследовать пользовательский TabBar и реализацию resizeEvent

Я динамически вставляю и удаляю вкладки через QSpinBox, который отлично работает. Чтобы заполнить всю ширину экрана (800 пикселей), мне нужно расширить вкладки, используя мой собственный фильтр событий:

mainwindow.h

namespace Ui {
class MainWindow;
}

class CustomTabBar : public QTabBar
{
public:
    CustomTabBar(QWidget *parent = Q_NULLPTR)
        : QTabBar(parent)
    {
    }

    void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE
    {
        /* Resize handler */
                if (e->type() == QEvent::Resize) {
                    // The width of each tab is the width of the tab widget / # of tabs.
                    resize(size().width()/count(), size().height());
                }
    }

    void tabInserted(int index) Q_DECL_OVERRIDE
    {
        /* New tab handler */
        insertTab(count(), QIcon(QString("")), QString::number(index));
    }

    void tabRemoved(int index) Q_DECL_OVERRIDE
    {
        /* Tab removed handler */
        removeTab(count() - index);
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    CustomTabBar *tabs;
};

Соответствующий код моего главного окна выглядит следующим образом:

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    cells = new CustomTabBar(this);
    cells->tabInserted(1);
//    cells->installEventFilter(resizeEvent());
}

void MainWindow::changeCells(int value)   // Called when QSpinBox is changed
{
    if (cells->count() < value) {
        cells->tabInserted(1);
    }
    else if (cells->count() > value) {
        cells->tabRemoved(1);
    }
}

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

  • Одна вкладка: ширина 800 пикселей.
  • Две вкладки: ширина 400 пикселей каждая
  • ...

Но везде, где я использую одно из этих пользовательских событий, возникает ошибка сегментации.

Что я здесь делаю неправильно?


person MichaW.    schedule 09.06.2016    source источник
comment
Вы пробовали отладить segfault?   -  person Violet Giraffe    schedule 09.06.2016
comment
Ну да... в моем примере кода выше ошибка в первой строке cells->tabInserted(1);. Если я комментирую это, это ошибка в классе customTabBar mainwindow.h. Видимо, я не могу найти, что или где я что-то упускаю.. :)   -  person MichaW.    schedule 09.06.2016
comment
Похоже, вы вызываете изменение размера из переопределения resizeEvent, которое а) изменит размер самой панели вкладок, а не отдельных вкладок, и б) приведет к бесконечной рекурсии и, возможно, к segfault.   -  person G.M.    schedule 09.06.2016
comment
Спасибо за ваши ответы! Я вижу, но как я могу достичь своей цели?   -  person MichaW.    schedule 09.06.2016
comment
Является ли setExpanding(true) тем, что вы ищете? Из документации: «Это свойство сохраняется, когда расширение равно true. QTabBar будет расширять вкладки, чтобы использовать пустое пространство».   -  person G.M.    schedule 09.06.2016
comment
setExpanding мне совсем не помог, так как он только расширяет вкладки (вроде) на половину экрана.   -  person MichaW.    schedule 09.06.2016


Ответы (3)


Следуя комментариям, я попытался переопределить элемент tabSizeHint QTabBar, и "это работает для меня"...

class tab_bar: public QTabBar {
protected:
  virtual QSize tabSizeHint (int index) const override
  {
    QSize s(QTabBar::tabSizeHint(index));
    s.setWidth(width() / count());
    return(s);
  }
};

Похоже, он работает так, как вам нужно - размеры вкладок занимают всю ширину окна.

Обратите внимание, что могут быть и другие факторы стиля, которые влияют на это, но у меня не было времени проверить.

person G.M.    schedule 09.06.2016
comment
Большое спасибо ГМ! Это помогло с некоторыми изменениями. К сожалению, я пока не могу проголосовать. - person MichaW.; 09.06.2016

Вы захотите disconnect(this, SIGNAL(resize()), this, SLOT(resizeEvent()), затем повторить connect([...]) после вызова resize(), чтобы избежать переполнения стека, вызванного бесконечной рекурсией resize -> resizeEvent. Редактировать: так же, как @G.M. говорит.

void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE
{
    disconnect(this, SIGNAL(resize()), this, SLOT(resizeEvent());

    /* Resize handler */
    if (e->type() == QEvent::Resize) {
        // The width of each tab is the width of the tab widget / # of tabs.
        resize(size().width()/count(), size().height());
    }

    connect(this, SIGNAL(resize()), this, SLOT(resizeEvent());
}

При правильном проектировании обходных путей, подобных этому, можно было бы избежать, но чтобы дать более полезное предложение, нам нужно лучше понять ваш вариант использования.

person zyndor    schedule 09.06.2016
comment
Вот для чего нужен QObject::blockSignals(bool). - person Violet Giraffe; 09.06.2016

Поскольку этот пост связан с этим, я вижу, что вы не поняли, как работает наследование.

resizeEvent, tabInserted и tabRemoved вызываются из среды Qt и не должны вызываться напрямую, поэтому protected. Вот для чего нужны insertTab и removeTab.

Что вы делаете, так это звоните tabInserted, который звонит insertTab, который звонит tabInserted, который звонит insertTab, который...

Что вы хотите сделать, так это реализовать изменение размера вкладок в ваших tabInserted и tabRemoved, точно так же, как вы (несколько ошибочно) сделали в resizeEvent.

if (e->type() == QEvent::Resize)

Всегда истинно в resizeEvent, так как это условие для Qt, чтобы вызвать его.

person JefGli    schedule 09.06.2016
comment
Мы с Наследством еще не друзья, это правда. Я последовал вашему совету и попытался сохранить tabInserted, tabRemoved, resizeEvent защищенным, но что бы я ни делал, я получил сообщение об ошибке «virtual void CustomTabBar:: tabInserted/whatever» защищен. Честно говоря, я полностью потерял это. - person MichaW.; 09.06.2016
comment
Защищенный означает, что вы не можете вызывать его вне класса по уважительной причине. Обычно вы делаете это для целей реализации, которые могут отличаться в зависимости от производного класса, как это происходит здесь. Почти всегда есть способ вызвать защищенный метод через общедоступный метод, например insertTab или removeTab в этом сценарии. Я бы действительно посоветовал прочитать о наследовании C++ и Qt, прежде чем продолжить. - person JefGli; 09.06.2016
comment
Ну, я знаю разницу между public, private or protected. Также читал о наследовании и думал, что у меня есть идея, но, видимо, нет, и поэтому я ищу помощи в этом случае. В любом случае, я использовал cells->insertTab(...) в своем mainwindow.cpp раньше, но как только tabInserted был защищен, я получил указанное выше сообщение об ошибке. - person MichaW.; 09.06.2016
comment
В какой строке вы пытались вызвать защищенную функцию? Возможно, вы забыли изменить один, поскольку я вижу, что вы называете это в двух местах. - person JefGli; 09.06.2016
comment
Виноват. Забыл исправить во второй строке. Но я все еще что-то упускаю, так как это все равно не работает ... нужно искать это, я думаю. Спасибо за ваши усилия в любом случае!! - person MichaW.; 09.06.2016