Qt - zły wynik zwrócony przez metodę QTabWidget::count()

0

Piszę sobie projekt w Qt i przy okazji uczę się tego frameworka. Zadam pytanie tutaj na forum, bo nie znalazłem nigdzie indziej choćby wskazówki dlaczego mój kod działa niepoprawnie. Przejdę do rzeczy. Oto pliki:

MainWindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class TabWidget;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_actionNext_tab_triggered();

private:
    Ui::MainWindow *ui;
    TabWidget *mTabWidget;
};
#endif // MAINWINDOW_H

TabWidget.h

#ifndef TABWIDGET_H
#define TABWIDGET_H

#include <QTabWidget>
#include <QMainWindow>

class TabWidget : public QTabWidget
{
public:
    TabWidget();
    void showNextTab();
};

#endif // TABWIDGET_H

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QTabWidget>

#include "TabWidget.h"

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

    mTabWidget = new TabWidget;
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_actionNext_tab_triggered()
{
    mTabWidget->showNextTab();
}

TabWidget.cpp

#include <QTabWidget>
#include <QTextStream>

#include "TabWidget.h"

TabWidget::TabWidget()
{

}

void TabWidget::showNextTab()
{
    QTextStream(stdout) << QTabWidget::count() << '\n';
}

Całość działa tak, że gdy kliknę Next Tab w menu programu, zostaje wywołana metoda void TabWidget::showNextTab(), która powinna zwrócić ilość zakładek umieszczonych wewnątrz tab baru. Niestety ilość zakładek według metody count wynosi 0 i nie potrafię rozgryźć dlaczego. W screenshotach przesyłam strukturę pliku .ui oraz wygląd tab baru z poziomu aplikacji.

Niestety nie wiem jeszcze jak mógłbym debugować taką sytuację więc informacja o tym jak wyłapywać takie błędy w Qt byłaby dla mnie bardzo przydatna i byłbym wdzięczny za pomoc :)

5

A gdzie dodajesz do tego tab widgeta zakładki?

Ogółem widzę tutaj niespójne podejście - masz klasę wygenerowaną na podstawie pliku .ui utworzonego za pomocą Qt Deisgnera, a jednocześnie dodajesz do niej widgety bezpośrednio. Dlaczego nie ma go w ui?

W designerze dodałeś inny obiekt QTabWidget (lub po nim dziedziczący), więc sytuacja jest analogiczna do:

struct ui{
    std::string my_string;
};

int main()
{
    // auto-generated
    ui my_ui { "foo"; }
    std::string my_string;
    cout << my_string.size();
    // dlaczego wypisało 0?!
}

Utwórz swojego tab widgeta bezpośrednio w designerze i problemu nie będzie (możesz dodać nowe klasy za pomocą funkcjonalności "promote to", gdzie możesz dodać swoją klasę TabWidget)

Ponadto: klasy Qt należy poprawnie inicjalizować wskaźnikiem na rodzica, nierobienie tego może nieść w konsekwencji dziwne błędy.

2

Jak wyżej: albo piszesz z palca, albo rysujesz w Designerze. IMHO powinieneś wziąć TabWidgeta przez ui->tabWidget1 (czy jaki on ma name w Designerze).

Druga rzecz: skoro alokujesz na stercie to albo przekazujesz do obiektu parenta, albo zwalniasz sam, czy to przez delete czy zapakowanie w smart pointer.
Albo po prostu... nie alokuj na stercie.

0

Kombinowałem z obiektami, bo cały czas się zastanawiam nad strukturą programu. Chciałem wydzielić logikę obiektu TabWidget do osobnego pliku i osobnego obiektu oddzielonego od MainWindow. Gdy użyję ui wtedy tę metodę showNextTab zaimplementowałbym jako metodę obiektu MainWindow. Jak do tego podejść? Logikę obiektów dodanych przy pomocy Designera implementować wewnątrz obiektu MainWindow czy można w jakiś sposób te implementacje wydzielić?

1

Możesz dodać swoją klasę w Designerze, tak jak pisałem, za pomocą mechanizmu promocji1. Klikasz na swojego QTabWidgeta, promote to... i wpisujesz nazwę swojej klasy:

Jest to prawodpodobnie więcej roboty niż to warte i prawie na pewno wygodniej jest użyć do tego mechanizmu sygnałów i slotów (gdzie sloty obsłużyłbyś w MainWindow). Tworzenie własnej klasy nabiera tutaj sensu jeśli chciałbyś pakować tam dużo logiki, lub jeśli byłaby to logika często powtarzalna.

1 przepraszam za kalkę językową.

0

A gdybym zrobił coś takiego?

MainWindow.cpp:

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

    mTabWidget = new TabWidget;
}

MainWindow::~MainWindow()
{
    delete ui;
    delete mTabWidget;
}

void MainWindow::on_actionNext_tab_triggered()
{
    mTabWidget->showNextTab(MainWindow::ui);
}

TabWidget.cpp:

#include <QTabWidget>
#include <QTextStream>

#include "TabWidget.h"
#include "ui_MainWindow.h"

TabWidget::TabWidget()
{

}

void TabWidget::showNextTab(Ui::MainWindow* ui)
{
    if (ui->tabwidget->count() <= 1) {
        return;
    }
    ui->tabwidget->setCurrentIndex((ui->tabwidget->currentIndex() + 1) % (ui->tabwidget->count()));
}

Wtedy w ui siedziałby obiekt stworzony w Designerze, ale jego funkcjonalność byłaby wydzielona do osobnego modułu i zarazem klasy. Intuicyjnie wydaję mi się to ładnym rozwiązaniem, strasznie mnie boli, że muszę pakować logikę tab bara w MainWindow. Jak radzisz @kq ? Zaimplementować tę funkcję showNextTab jednak jako metodę klasy MainWindow?

1

Ja bym po prostu podziedziczył z QTabWidget i zrobił swojego łidżeta (Jeżu co za słowo ;))

EDIT: albo przypiął lambdę do sygnału triggered.

2

To co zrobiłeś w ogóle działa? Przecież jak chcesz implementować swój TabWidget jako osobny moduł to już 2 razy podałem jak to zrobić! Zostaw go jako osobny moduł i użyj go z poziomu designera.

0

Hmm to co zrobiłem działa, ale chyba powinno się wywalać? Przecież tu:

void MainWindow::on_actionNext_tab_triggered()
{
    mTabWidget->showNextTab(MainWindow::ui);
}

Przekazuję do metody obiektu mTabWidget prywatne pole klasy MainWindow to dlaczego nie leci exception w poniższym kodzie?

void TabWidget::showNextTab(Ui::MainWindow* ui)
{
   if (ui->tabwidget->count() <= 1) {
       return;
   }
   ui->tabwidget->setCurrentIndex((ui->tabwidget->currentIndex() + 1) % (ui->tabwidget->count()));
}
2

Przekazujesz je z MainWindow (MainWindow podaje dalej własny prywatny element), więc to działa. Czy to dobra praktyka to osobny temat...

Powtórzę się: dobrym wyjściem będzie zrobienie swojego widgeta albo po prostu dobranie się do UI z MainWindow w MainWindow. Przypięcie lambdy do sygnału też się może nadać.

Albo jeszcze inaczej: co Ty chcesz zrobić i w jakiej sytuacji chcesz te taby przewijać? Bo to trochę mi na problem XY wygląda.

0

Zrobię własny widget, który będzie dziedziczył po QTabWidget - to rzeczywiście raczej najlepsze wyjście.

Przewijanie zakładek to po prostu feature całego programu i feature, który implementuję jako pierwszy. One się przewijają po naciśnięciu shortcuta

Już dalej sobie poradzę (tak myślę) :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1