Evgenii Legotckoi
01 вересня 2019 р. 20:15

Qt/C++ - Урок 089. Кнопки з абсолютним позиціонуванням всередині QGraphicsView

З огляду на кількість питань на форумі, що відносяться до того, як додати кнопки всередині QGraphicsView, вирішив написати невеликий туторіал на цю тему. Причому варіації питання бувають різні. Це може бути звичайна кнопка, або навіть міні-карта, якщо основний QGraphicsView виступає в якості великої карти. В цілому, що саме являє собою подібний віджет не важливо. Суть одна, є основною QWidget, всередині якого розташовуються інші QWidget об'єкти, які мають абсолютне позиціонування всередині даного віджету.

Виглядати це буде таким чином.

QGraphicsView, внутри которого располагаются кнопки QPushButton с абсолютным позиционированием.


Вступ

QGraphicsView буде розташовуватися в віджеті головного вікна і буде додано через Qt Designer.

Тоді як інші кнопки будуть додаватися тільки через програмний код. Цей якраз один з тих самих випадків, коли я вважаю використання графічного дизайнера не найзручнішим. Оскільки в будь-якому випадку доведеться часто змінювати положення кнопок. А саме при кожній зміні розмірів вікна програми.

В цілому програмний код досить простий, тому відразу розглянемо заголовки вікна програми, а також файл реалізації.

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class QPushButton;

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

    virtual void resizeEvent(QResizeEvent *event) override;

private:
    void updateButtonsPosition();

    Ui::Widget *ui;

    // Кнопки з абсолютним позиціонуванням
    QPushButton* m_topLeftButton;
    QPushButton* m_topRightButton;
    QPushButton* m_bottomLeftButton;
    QPushButton* m_bottomRightButton;
};

#endif // WIDGET_H

Widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    // Створимо всі кнопки з абсолютним позиціонуванням
    m_topLeftButton = new QPushButton("Top Left", ui->graphicsView);
    m_topRightButton = new QPushButton("Top Right", ui->graphicsView);
    m_bottomLeftButton = new QPushButton("Bottom Left", ui->graphicsView);
    m_bottomRightButton = new QPushButton("Bottom Right", ui->graphicsView);
    // Оновимо позиції кнопок
    updateButtonsPosition();
}

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

void Widget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    // Оновлюємо позиції кнопок при кожній події зміни розміру головного вікна
    updateButtonsPosition();
}

void Widget::updateButtonsPosition()
{
    // Логіка зміни абсолютних позицій кнопок всередині QGraphicsView
    QRect graphicsViewGeometry = ui->graphicsView->geometry();
    m_topLeftButton->setGeometry({25,
                                  25,
                                  m_topLeftButton->geometry().width(),
                                  m_topLeftButton->geometry().height()});
    m_topRightButton->setGeometry({graphicsViewGeometry.width() - m_topRightButton->geometry().width() - 25,
                                   25,
                                   m_topRightButton->geometry().width(),
                                   m_topRightButton->geometry().height()});
    m_bottomLeftButton->setGeometry({25,
                                     graphicsViewGeometry.height() - m_bottomLeftButton->geometry().height() - 25,
                                     m_bottomLeftButton->geometry().width(),
                                     m_bottomLeftButton->geometry().height()});
    m_bottomRightButton->setGeometry({graphicsViewGeometry.width() - m_bottomRightButton->geometry().width() - 25,
                                      graphicsViewGeometry.height() - m_bottomRightButton->geometry().height() - 25,
                                      m_bottomRightButton->geometry().width(),
                                      m_bottomRightButton->geometry().height()});
}

Висновок

Таким чином можна встановити абсолютне позиціонування для будь-яких інших віджетів у вашій програмі.

Важливим моментом є те, що в якості Паренте потрібно передати Тооте віджет, всередині якого повинен розташовуватися створюваний віджет. В даному випадку Парент об'єкт служить не тільки в якості контролера витоків пам'яті, тобто для автоматичного видалення вкладених об'єктів, але і для вказівки, вкладеності віджетів в GUI додатки.

Вам це подобається? Поділіться в соціальних мережах!

Александр Панюшкин
  • 02 вересня 2019 р. 00:39

Евгений, добрый день.
Вопрос не совсем по теме.
А почему вы объявили класс QPushButton в заголовочном файле таким образом, а не через include? Зачем include переносить в cpp?
В чём смысл?
Вопрос без "подковырки" - действительно интересно.

Evgenii Legotckoi
  • 02 вересня 2019 р. 00:51

Добрый день, Александр.
Это Forward Declaration - Предварительное объявление. Позволяет объявить класс без подключения заголовочного файла в заголовочном файле другого класса.
Такое объявление может использоваться как для шаблонных аргментов, так и для указателей. Если переменная объявляется на стеке в заголовочном файле, то тогда да, приходится с использованием include объявлять.

По большей части Forward Declaration преследует две основные цели:

  • Оптимизация подключения заголовочных файлов при компиляции (ну тут всё понятно)
  • Разрешение циклических зависимостей, когда один класс имеет объявленные указатели на другой класс, а второй класс в свою очередь имеет указатели на первый класс. Если в обоих классах подключать заголовочный файлы через include в заголовочных файлах, то ничего не скомпилируется. А вот подключение заголовочных файлов в cpp и с Forward Declaration позволяют разрулить такую ситуацию.

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up