Это как-то продолжение моего ответа на ТАК: обновление не вызывает paintEvent? .
Следовательно, я взял предыдущий пример кода (все еще лежал на моем жестком диске) и сделал соответствующие расширения:
- режим дифференциации примитива рисования:
enum PaintMode {
PaintLine, PaintCirc, NPaintModes
};
- новая переменная-член для запоминания текущего примитива рисования (в
class MainWindow
):
PaintMode _paintMode;
- расширение
MainWindow::paintEvent()
:
switch (_paintMode) {
case PaintLine:
painter.drawLine(_start, _end);
break;
case PaintCirc: {
const int width = std::abs(_end.x() - _start.x());
const int height = std::abs(_end.y() - _start.y());
const int r = std::min(width, height) / 2;
painter.drawEllipse((_start + _end) / 2, r, r);
} break;
}
- панель инструментов для выбора текущего примитива рисования:
QToolBar qToolBar;
QActionGroup qTglGrp(&qToolBar);
QAction qTglLine("Line", &qTglGrp);
qTglLine.setCheckable(true);
if (winMain.paintMode() == PaintLine) qTglLine.setChecked(true);
qToolBar.addAction(&qTglLine);
QAction qTglCirc("Circle", &qTglGrp);
qTglCirc.setCheckable(true);
if (winMain.paintMode() == PaintCirc) qTglCirc.setChecked(true);
qToolBar.addAction(&qTglCirc);
winMain.addToolBar(&qToolBar);
- ... с достаточным количеством обработчиков сигналов:
QObject::connect(&qTglLine, &QAction::triggered,
[&](bool checked) { if (checked) winMain.setPaintMode(PaintLine); });
QObject::connect(&qTglCirc, &QAction::triggered,
[&](bool checked) { if (checked) winMain.setPaintMode(PaintCirc); });
Полный образец testQMainWindowPaint.cc
:
#include <QtWidgets>
enum PaintMode {
PaintLine, PaintCirc, NPaintModes
};
class MainWindow: public QMainWindow {
private:
QPoint _start, _end;
bool _firstClick;
PaintMode _paintMode;
public:
MainWindow();
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
PaintMode paintMode() const { return _paintMode; }
void setPaintMode(PaintMode mode);
protected:
virtual void mousePressEvent(QMouseEvent *pQEvent) override;
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
MainWindow::MainWindow():
QMainWindow(),
_start(0, 0), _end(0, 0), _firstClick(true),
_paintMode(PaintLine)
{ }
void MainWindow::setPaintMode(PaintMode mode)
{
_paintMode = mode;
update();
}
void MainWindow::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
(_firstClick ? _start : _end) = pQEvent->pos();
_firstClick = !_firstClick;
update();
pQEvent->accept();
}
}
void MainWindow::paintEvent(QPaintEvent *pQEvent)
{
QMainWindow::paintEvent(pQEvent);
if (!_firstClick) return;
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(4);
painter.setPen(pen);
switch (_paintMode) {
case PaintLine:
painter.drawLine(_start, _end);
break;
case PaintCirc: {
const int width = std::abs(_end.x() - _start.x());
const int height = std::abs(_end.y() - _start.y());
const int r = std::min(width, height) / 2;
painter.drawEllipse((_start + _end) / 2, r, r);
} break;
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
MainWindow winMain;
QToolBar qToolBar;
QActionGroup qTglGrp(&qToolBar);
QAction qTglLine("Line", &qTglGrp);
qTglLine.setCheckable(true);
if (winMain.paintMode() == PaintLine) qTglLine.setChecked(true);
qToolBar.addAction(&qTglLine);
QAction qTglCirc("Circle", &qTglGrp);
qTglCirc.setCheckable(true);
if (winMain.paintMode() == PaintCirc) qTglCirc.setChecked(true);
qToolBar.addAction(&qTglCirc);
winMain.addToolBar(&qToolBar);
winMain.show();
// install signal handlers
QObject::connect(&qTglLine, &QAction::triggered,
[&](bool checked) { if (checked) winMain.setPaintMode(PaintLine); });
QObject::connect(&qTglCirc, &QAction::triggered,
[&](bool checked) { if (checked) winMain.setPaintMode(PaintCirc); });
// runtime loop
return app.exec();
}
Файл проекта Qt (не изменился) testQMainWindowPaint.pro
:
SOURCES = testQMainWindowPaint.cc
QT += widgets
Скомпилировано в cygwin64 в Windows 10:
$ qmake-qt5 testQMainWindowPaint.pro
$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQMainWindowPaint.o testQMainWindowPaint.cc
g++ -o testQMainWindowPaint.exe testQMainWindowPaint.o -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread
$ ./testQMainWindowPaint
Qt Version: 5.9.4
Дважды щелкнув в главном окне, я сделал снимок (левое изображение), а после нажатия Круг еще один (правое изображение):
![Snapshot of testQMainWindowPaint (after clicking [Circle])](https://i.stack.imgur.com/fCkXC.png)
ИМХО, приложение теперь делает то, что ОП требовало буквально:
Мне нужны идеи, например, когда я активирую кнопку на панели инструментов, я активирую функцию, которая рисует линию, и, если я нажимаю на другую кнопку, я активирую функцию, которая рисует круг.
Однако у меня почему-то возникает ощущение, что это, возможно, еще не ожидалось. Примитив для рисования изменяется сразу же после изменения режима рисования (при нажатии на панели инструментов). Вместо этого можно предположить, что выбор панели инструментов вступает в силу для следующего ввода.
Если это так, class MainWindow
нужно немного изменить: для фактического рисования необходим второй режим рисования, который не изменяется до завершения следующего цикла взаимодействия:
class MainWindow: public QMainWindow {
private:
QPoint _start, _end;
bool _firstClick;
PaintMode _paintMode, _paintModeEff;
public:
MainWindow();
virtual ~MainWindow() = default;
MainWindow(const MainWindow&) = delete;
MainWindow& operator=(const MainWindow&) = delete;
PaintMode paintMode() const { return _paintMode; }
void setPaintMode(PaintMode mode);
PaintMode effectivePaintMode() const { return _paintModeEff; }
protected:
virtual void mousePressEvent(QMouseEvent *pQEvent) override;
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
MainWindow::MainWindow():
QMainWindow(),
_start(0, 0), _end(0, 0), _firstClick(true),
_paintMode(PaintLine), _paintModeEff(PaintLine)
{ }
void MainWindow::setPaintMode(PaintMode mode)
{
_paintMode = mode;
}
void MainWindow::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
(_firstClick ? _start : _end) = pQEvent->pos();
_firstClick = !_firstClick;
if (_firstClick) _paintModeEff = _paintMode;
update();
pQEvent->accept();
}
}
void MainWindow::paintEvent(QPaintEvent *pQEvent)
{
QMainWindow::paintEvent(pQEvent);
if (!_firstClick) return;
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(4);
painter.setPen(pen);
switch (_paintModeEff) {
case PaintLine:
painter.drawLine(_start, _end);
break;
case PaintCirc: {
const int width = std::abs(_end.x() - _start.x());
const int height = std::abs(_end.y() - _start.y());
const int r = std::min(width, height) / 2;
painter.drawEllipse((_start + _end) / 2, r, r);
} break;
}
}
Линия появляется после двойного щелчка (левое изображение), изменение режима рисования не оказывает немедленного эффекта (среднее изображение), но становится эффективным при повторном щелчке дважды (правое изображение):
![Snapshot of testQMainWindowPaint (after next input cycle)](https://i.stack.imgur.com/yG3KW.png)
Еще одна модификация образца:
Взаимодействие с мышью и рисование теперь в class Canvas
, полученном из QWidget
. Хотя главное окно теперь является экземпляром QMainWindow
, существует новый экземпляр Canvas canvas
, который используется в качестве центрального виджета QMainWindow winMain
.
#include <QtWidgets>
enum PaintMode {
PaintLine, PaintCirc, NPaintModes
};
class Canvas: public QWidget {
private:
QPoint _start, _end;
bool _firstClick;
PaintMode _paintMode, _paintModeEff;
public:
Canvas();
virtual ~Canvas() = default;
Canvas(const Canvas&) = delete;
Canvas& operator=(const Canvas&) = delete;
PaintMode paintMode() const { return _paintMode; }
void setPaintMode(PaintMode mode);
PaintMode effectivePaintMode() const { return _paintModeEff; }
protected:
virtual void mousePressEvent(QMouseEvent *pQEvent) override;
virtual void paintEvent(QPaintEvent *pQEvent) override;
};
Canvas::Canvas():
QWidget(),
_start(0, 0), _end(0, 0), _firstClick(true),
_paintMode(PaintLine), _paintModeEff(PaintLine)
{ }
void Canvas::setPaintMode(PaintMode mode)
{
_paintMode = mode;
}
void Canvas::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
(_firstClick ? _start : _end) = pQEvent->pos();
_firstClick = !_firstClick;
if (_firstClick) _paintModeEff = _paintMode;
update();
pQEvent->accept();
}
}
void Canvas::paintEvent(QPaintEvent *pQEvent)
{
QWidget::paintEvent(pQEvent);
if (!_firstClick) return;
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(4);
painter.setPen(pen);
switch (_paintModeEff) {
case PaintLine:
painter.drawLine(_start, _end);
break;
case PaintCirc: {
const int width = std::abs(_end.x() - _start.x());
const int height = std::abs(_end.y() - _start.y());
const int r = std::min(width, height) / 2;
painter.drawEllipse((_start + _end) / 2, r, r);
} break;
}
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// init GUI
QMainWindow winMain;
Canvas canvas;
winMain.setCentralWidget(&canvas);
QToolBar qToolBar;
QActionGroup qTglGrp(&qToolBar);
QAction qTglLine("Line", &qTglGrp);
qTglLine.setCheckable(true);
if (canvas.paintMode() == PaintLine) qTglLine.setChecked(true);
qToolBar.addAction(&qTglLine);
QAction qTglCirc("Circle", &qTglGrp);
qTglCirc.setCheckable(true);
if (canvas.paintMode() == PaintCirc) qTglCirc.setChecked(true);
qToolBar.addAction(&qTglCirc);
winMain.addToolBar(&qToolBar);
winMain.show();
// install signal handlers
QObject::connect(&qTglLine, &QAction::triggered,
[&](bool checked) { if (checked) canvas.setPaintMode(PaintLine); });
QObject::connect(&qTglCirc, &QAction::triggered,
[&](bool checked) { if (checked) canvas.setPaintMode(PaintCirc); });
// runtime loop
return app.exec();
}
Снимок образца:
![Snapshot of testQMainWindowPaint (after clicking twice into canvas)](https://i.stack.imgur.com/u61Q1.png)