首页 > 解决方案 > 如何在界面中同时画圆和线?

问题描述

我设法通过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 );
    }
*/

标签: c++qt

解决方案


这在某种程度上是我对 SO 的回答的延续:更新不调用paintEvent?.

因此,我采用了之前的示例代码(仍然放在我的硬盘上)并进行了相应的扩展:

  1. 一种区分绘图图元的模式:
enum PaintMode {
  PaintLine, PaintCirc, NPaintModes
};
  1. 一个新的成员变量来记住当前的绘图原语(in class MainWindow):
    PaintMode _paintMode;
  1. 的扩展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;
  }
  1. 用于选择当前绘图图元的工具栏:
  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);
  1. ...具有足够的信号处理程序:
  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另一个(右图):

testQMainWindowPaint 的快照 testQMainWindowPaint 的截图(点击【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;
  }
}

单击两次后出现一条线(左图),更改绘制模式没有立即生效(中图),但再次单击两次后生效(右图):

testQMainWindowPaint 的快照 testQMainWindowPaint 的截图(点击【Circle】后) testQMainWindowPaint 的快照(在下一个输入周期之后)


样本的另一种修改:

鼠标交互和绘图现在是class CanvasQWidget. 虽然主窗口现在是 的一个实例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();
}

样本快照:

testQMainWindowPaint 的快照(在画布上单击两次后)


推荐阅读