C++. Qt. Forward declaration. One way to get access to the parent form from a child form

При работе с Qt встала задача: как правильно организовать доступ к данным родительской формы из дочерней формы. Был выбран вариант передачи указателя на экземпляр данной формы в конструктор дочерней формы. В этом случае у Вас может возникнуть следующая проблема. Для родительской формы вы подключаете заголовочный файл определения класса дочерней формы. Но и дочернему классу нужно сообщить о классе (типе) родительской формы. Если подключить заголовочный файл определения класса родительской формы, то возникнет ситуация циклического подключения этих заголовочных файлов. Чтобы этого избежать, необходимо воспользоваться техникой "опережающего определения" (forward declaration):

bar.h

#include "foo.h"

class bar {
public:
    bar();
};

bar.cpp

bar::bar() {
    foo f ();
    // work with object f
}

foo.h

class bar; // forward declaration

class foo {
public:
    foo(bar* obj); // pointer or a reference
};

foo.cpp

#include "bar.h"
#include "foo.h"

foo::foo(bar* obj) {
    // work with pointer obj
}

Ниже приведен пример данного подхода, в котором дочерняя форма при отрисовке таблицы со списком элементов обращается к соответствующему списку родительской формы.

item.h

#ifndef ITEM
#define ITEM

#include <QString>

class Item {
private:
    int id;
    QString description;

public:
    Item(int id, QString description) : id(id), description(description) {}

    void setId(int id) {
        this->id = id;
    }

    int getId() const {
        return id;
    }

    void setDescription(QString description) {
        this->description = description;
    }

    QString getDescription() const {
        return description;
    }
};

#endif

mainwindow.h

#ifndef MAINWINDOW
#define MAINWINDOW

#include <QMainWindow>
#include "itemswindow.h"
#include "item.h"

class MainWindow : public QMainWindow {
Q_OBJECT

public:
    MainWindow(QWidget* wgt = 0);
    QList<Item> itemList;

public slots:
    void slotItems();
};

#endif

mainwindow.cpp

#include <QtWidgets>
#include "mainwindow.h"

MainWindow::MainWindow(QWidget* wgt) : QMainWindow(wgt) {
    QMenu* menu = new QMenu("Menu");
    menu->addAction("Exit", qApp, SLOT(quit()));
    menuBar()->addMenu(menu);

    // central widget
    QWidget* centralWidget = new QWidget;

    QVBoxLayout* buttonsWidgetLayout = new QVBoxLayout(centralWidget);
    buttonsWidgetLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);

    QPushButton* itemsBttn = new QPushButton("Items");
    itemsBttn->setFixedWidth(150);
    buttonsWidgetLayout->addWidget(itemsBttn);

    connect(itemsBttn, SIGNAL(clicked()), SLOT(slotItems()));

    setCentralWidget(centralWidget);
    setWindowTitle("MainWindow");

    // for testing
    itemList.append(Item(1, "First item"));
    itemList.append(Item(2, "Second item"));
    itemList.append(Item(3, "Third item"));
    itemList.append(Item(4, "Fourth item"));
    itemList.append(Item(5, "Fifth item"));
}

void MainWindow::slotItems() {
    ItemsWindow* itemsWindow = new ItemsWindow(this, this);
    itemsWindow->exec();
    delete itemsWindow;
}

itemswindow.h

#ifndef ITEMSWINDOW
#define ITEMSWINDOW

#include <QtWidgets>
#include <item.h>

class MainWindow;

class ItemsWindow : public QDialog {
Q_OBJECT

private:
    QTableWidget* table;

public:
    ItemsWindow(MainWindow* mw = 0, QWidget* wgt = 0);
};

#endif

itemswindow.cpp

#include "mainwindow.h"
#include "itemswindow.h"

ItemsWindow::ItemsWindow(MainWindow* mw, QWidget* wgt) :
    QDialog(wgt, Qt::WindowTitleHint | Qt::WindowCloseButtonHint) {

    QVBoxLayout* verticalLayout = new QVBoxLayout;
    QHBoxLayout* horizontalBottomLayout = new QHBoxLayout;

    QPushButton* okBttn = new QPushButton("&OK");
    okBttn->setFixedWidth(60);

    connect(okBttn, SIGNAL(clicked()), SLOT(accept()));
    
horizontalBottomLayout->setAlignment(Qt::AlignRight);
horizontalBottomLayout->addWidget(okBttn);

    table = new QTableWidget();
    table->setColumnCount(2);

    QStringList list;
    list << "Id" << "Description";
    table->setHorizontalHeaderLabels(list);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);

    if (mw != NULL && !mw->itemList.empty()) {
        int itemsCnt = mw->itemList.count();
        table->setRowCount(itemsCnt);

        QTableWidgetItem* tableItem = 0;
        for (int i = 0; i < itemsCnt; i++) {
            Item item = mw->itemList.at(i);

            tableItem = new QTableWidgetItem(QString::number(item.getId()));
            tableItem->setTextAlignment(Qt::AlignCenter);
            table->setItem(i, 0, tableItem);

            tableItem = new QTableWidgetItem(item.getDescription());
            table->setItem(i, 1, tableItem);
        }
    }

    this->setWindowTitle("Items");
    verticalLayout->addWidget(table);
    verticalLayout->addLayout(horizontalBottomLayout);
    setLayout(verticalLayout);
}

main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char** argv) {
    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec();
}

Исходники доступны по данной ссылке - source.

C++. Qt5.6. Combobox with checkboxes

На днях работал с замечательной библиотекой Qt. Нужно было добавить возможность выбора элементов по флажкам в выпадающем списке. Вроде тривиальная gui задача, но у меня немного вызвало затруднение. Ниже приведено мое решение. Исходная платформа - Win64.


multilist.h

#ifndef MULTILIST
#define MULTILIST

#include <QtWidgets>

class MultiList : public QComboBox {
    Q_OBJECT

private:
    QString displayText;
    QStandardItemModel *model;

public:
    MultiList(QWidget *parent = 0) : QComboBox(parent) {
        setEditable(true);

        displayText = "";
        model = new QStandardItemModel;
        slotUpdateText();

        connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(slotUpdate()));
    }

    void addItem(const QString &text) {
        int row = model->rowCount();
        QStandardItem* item = new QStandardItem();
        item->setText(text);
        item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
        item->setData(Qt::Unchecked, Qt::CheckStateRole);
        model->setItem(row, 0, item);
        this->setModel(model);
    }

    void addItems(const QStringList &texts) {
        for (int i = 0; i < texts.count(); i++) {
            addItem(texts.at(i));
        }
    }

    QStringList getCheckedItems() const {
        QStringList checkedItems;

        for (int i = 0; i < model->rowCount(); i++) {
            if (model->item(i, 0)->checkState() == Qt::Checked) {
                checkedItems << model->item(i, 0)->text();
            }
        }

        return checkedItems;
    }

    void setCheckedItems(const QStringList &items) {
        for (int i = 0; i < items.count(); i++) {
            int index = findText(items.at(i));

            if (index != -1) {
                model->item(index)->setData(Qt::Checked, Qt::CheckStateRole);
            }
        }

        slotUpdate();
    }

public slots:
    void slotUpdateText() {
        lineEdit()->setText(displayText);
    }

    void slotUpdate() {
        displayText = "";

        for (int i = 0; i < model->rowCount(); i++) {
            if (model->item(i, 0)->checkState() == Qt::Checked) {
                displayText = displayText + model->item(i, 0)->text() + "; ";
            }
        }

        QTimer::singleShot(0, this, SLOT(slotUpdateText()));
    }
};


#endif

main.cpp

#include "multilist.h"

int main(int argc, char** argv) {
    QApplication app(argc, argv);

    MultiList *multiList = new MultiList();

    multiList->addItems(QStringList() << "One" << "Two" << "Three" << "Four");
    multiList->setCheckedItems(QStringList() << "One" << "Two");

    QHBoxLayout *layout = new QHBoxLayout();

    layout->addWidget(new QLabel("Select items:"));
    layout->addWidget(multiList, 1);

    QWidget widget;
    widget.setWindowTitle("MultiList example");
    widget.setLayout(layout);
    widget.show();

    return app.exec();

}