如何在界面中同时绘制圆和线?

时间:2019-06-09 18:33:04

标签: c++ qt

我通过两个函数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 );
    }
*/

1 个答案:

答案 0 :(得分:0)

这是我对SO: update does not call paintEvent?的回答的延续。

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

  1. 区分绘图图元的模式:
enum PaintMode {
  PaintLine, PaintCirc, NPaintModes
};
  1. 一个新的成员变量,用于记住当前的绘图基元(在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

在主窗口中单击两次后,我做了一个快照(左图),然后在另一个[kbd] Circle 上单击了(右图):

Snapshot of testQMainWindowPaint Snapshot of testQMainWindowPaint (after clicking [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;
  }
}

单击两次(左图)后会出现一条线,更改绘制模式不会立即生效(中间图),但是再次单击两次(右图)时才生效:

Snapshot of testQMainWindowPaint Snapshot of testQMainWindowPaint (after clicking [Circle]) Snapshot of testQMainWindowPaint (after next input cycle)


样本的另一种修改:

鼠标交互和绘图现在位于从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)