c++ - 如何在界面中同时画圆和线?
问题描述
我设法通过mousepress event和paintevent这两个函数画了一条线和一个圆。我想要一些想法,比如当我激活工具栏中的按钮时,我将激活绘制线条的功能,如果我单击另一个按钮,我将激活绘制圆圈的功能。
void Label::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
(firstClick ? start : end) = pQEvent->pos();
if(firstClick = !firstClick)
{
double distance=sqrt(pow(start.x()-end.x(), 2) + pow(start.y()-end.y(), 2));
emit sendCalculDistance(distance);
}
update();
pQEvent->accept();
}
}
void Label::paintEvent(QPaintEvent *pQEvent)
{
QLabel::paintEvent(pQEvent);
if (!firstClick) return;
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(4);
painter.setPen(pen);
painter.drawLine(start, end);
}
****************
/* void Label::mousePressEvent(QMouseEvent *pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
(firstClick ? center : mouse_pos) = pQEvent->pos();
firstClick = !firstClick;
update();
pQEvent->accept();
}
}
void Label::paintEvent(QPaintEvent *pQEvent)
{
QLabel::paintEvent(pQEvent);
if (!firstClick) return;
int radius = QLineF( center, mouse_pos ).length();
QPainter painter( this );
QPen pen(Qt::red);
pen.setWidth(4);
painter.setPen(pen);
painter.drawEllipse( center, radius, radius );
}
*/
解决方案
这在某种程度上是我对 SO 的回答的延续:更新不调用paintEvent?.
因此,我采用了之前的示例代码(仍然放在我的硬盘上)并进行了相应的扩展:
- 一种区分绘图图元的模式:
enum PaintMode {
PaintLine, PaintCirc, NPaintModes
};
- 一个新的成员变量来记住当前的绘图原语(in
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
在 Windows 10 上用cygwin64编译:
$ 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
在主窗口单击两次后,我做了一个快照(左图),然后单击Circle另一个(右图):
恕我直言,该应用程序现在执行 OP 字面要求的操作:
我想要一些想法,比如当我激活工具栏中的按钮时,我将激活绘制线条的功能,如果我单击另一个按钮,我将激活绘制圆圈的功能。
但是,不知何故,我有一种感觉,这可能还不是预期的。绘制模式更改时(通过单击工具栏),绘制的绘图图元会立即更改。相反,工具栏选择可能会在下一个输入时生效。
如果是这种情况,则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;
}
}
单击两次后出现一条线(左图),更改绘制模式没有立即生效(中图),但再次单击两次后生效(右图):
样本的另一种修改:
鼠标交互和绘图现在是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();
}
样本快照:
推荐阅读
- python - 如何在 pytest 中处理 sys.argv?
- database - Oracle 数据库外部表问题。ORA-29913、ORA-29400 和 KUP-04004
- javascript - 如何在几小时内设置 vue-cookie 到期时间
- scala - 如何从 scala 中的泛型类型生成 TypeInformation
- cryptography - 如何使用 XMC4500 正确生成 1024 位 rsa 密钥对?
- java - 如何向 slf4j 提供我自己的 MDCAdapter?
- excel - 如何将数据从启用宏的 Excel 工作簿复制到另一个 Excel 文件
- javascript - 向左平滑滚动
- javascript - 如何在选择表单中添加搜索
- javascript - 根据 Javascript 中的两个选项条件显示/隐藏 div