Evgenii Legotckoi
1 сентября 2019 г. 20:15

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

Содержание

Учитывая количество вопросов на форуме, связанных с тем, как добавить кнопки внутри QGraphicsView, я решил написать небольшой туториал на эту тему. Более того, существуют различные варианты вопроса. Это может быть обычная кнопка или даже мини-карта, если в качестве большой карты выступает основной QGraphicsView. В общем, что именно представляет собой подобный виджет, не важно. Суть в том, что есть основной QWidget, внутри которого расположены остальные объекты QWidget, имеющие абсолютное позиционирование внутри этого виджета.

Это будет выглядеть так.

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


Вступление

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

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

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

widget.h

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5.  
  6. namespace Ui {
  7. class Widget;
  8. }
  9.  
  10. class QPushButton;
  11.  
  12. class Widget : public QWidget
  13. {
  14. Q_OBJECT
  15.  
  16. public:
  17. explicit Widget(QWidget *parent = nullptr);
  18. ~Widget();
  19.  
  20. virtual void resizeEvent(QResizeEvent *event) override;
  21.  
  22. private:
  23. void updateButtonsPosition();
  24.  
  25. Ui::Widget *ui;
  26.  
  27. // Buttons with absolute positioning
  28. QPushButton* m_topLeftButton;
  29. QPushButton* m_topRightButton;
  30. QPushButton* m_bottomLeftButton;
  31. QPushButton* m_bottomRightButton;
  32. };
  33.  
  34. #endif // WIDGET_H
  35.  

Widget.cpp

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include <QPushButton>
  4.  
  5. Widget::Widget(QWidget *parent) :
  6. QWidget(parent),
  7. ui(new Ui::Widget)
  8. {
  9. ui->setupUi(this);
  10. // Create all buttons with absolute positioning
  11. m_topLeftButton = new QPushButton("Top Left", ui->graphicsView);
  12. m_topRightButton = new QPushButton("Top Right", ui->graphicsView);
  13. m_bottomLeftButton = new QPushButton("Bottom Left", ui->graphicsView);
  14. m_bottomRightButton = new QPushButton("Bottom Right", ui->graphicsView);
  15. // Update button positions
  16. updateButtonsPosition();
  17. }
  18.  
  19. Widget::~Widget()
  20. {
  21. delete ui;
  22. }
  23.  
  24. void Widget::resizeEvent(QResizeEvent *event)
  25. {
  26. Q_UNUSED(event);
  27. // We update the button positions for each resize event of the main window
  28. updateButtonsPosition();
  29. }
  30.  
  31. void Widget::updateButtonsPosition()
  32. {
  33. // The logic of changing the absolute positions of buttons inside QGraphicsView
  34. QRect graphicsViewGeometry = ui->graphicsView->geometry();
  35. m_topLeftButton->setGeometry({25,
  36. 25,
  37. m_topLeftButton->geometry().width(),
  38. m_topLeftButton->geometry().height()});
  39. m_topRightButton->setGeometry({graphicsViewGeometry.width() - m_topRightButton->geometry().width() - 25,
  40. 25,
  41. m_topRightButton->geometry().width(),
  42. m_topRightButton->geometry().height()});
  43. m_bottomLeftButton->setGeometry({25,
  44. graphicsViewGeometry.height() - m_bottomLeftButton->geometry().height() - 25,
  45. m_bottomLeftButton->geometry().width(),
  46. m_bottomLeftButton->geometry().height()});
  47. m_bottomRightButton->setGeometry({graphicsViewGeometry.width() - m_bottomRightButton->geometry().width() - 25,
  48. graphicsViewGeometry.height() - m_bottomRightButton->geometry().height() - 25,
  49. m_bottomRightButton->geometry().width(),
  50. m_bottomRightButton->geometry().height()});
  51. }
  52.  

Вывод

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

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

Вам это нравится? Поделитесь в социальных сетях!

Александр Панюшкин
  • 2 сентября 2019 г. 0:39

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

Evgenii Legotckoi
  • 2 сентября 2019 г. 0:51

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

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

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

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь