Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to create rubber band line?
Qt 6.11 is out! See what's new in the release blog

How to create rubber band line?

Scheduled Pinned Locked Moved Unsolved General and Desktop
5 Posts 2 Posters 513 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    Jolene
    wrote on last edited by
    #1

    What I want to do:
    If you're gonne use the line tool in Photoshop, you will draw a straight line by one click-move-release mouse event. In the move term, You will see a temporary line connecting the current mouse position and the original mouse position that helps the user check what the result will be. And that temporary line is what I want to do.

    I tried:
    Create a class using QRubberBand that draws the line.
    I think the problem is mainly in mouseMoveEvent().

    The problem shows like: when click and move mouse, the line will only be correctly created if current mouse pos(endPosX) is at the bottomRight or topLeft corner of the orinal start point(startPoint.x()), if not, the line will be wrongly created. And maybe it's because the "setGeometry" only allows creating a rect from topLeft to bottomRight, and is coupled with "rect.normalize()"which limits the line will only be directed topLeft-bottomRight.

    But if we don't use (normalize.()), the line will only shows when current mouse pos(endPosX) is at the bottomRight or topLeft corner of the orinal start point(startPoint.x()). and This is because QRect limits that a rect will always be created from topLeft to BottomRight, when the vertices pos don't satisfy the rules, the rect will not be visible.

    Q:
    How to create that temporary line (rubber band line) as it's stated above?

    class mainFunc : public QMainWindow{
    Q_OBJECT
    //some code
    protected:
        void MainWindow::paintEvent(QPaintEvent *){
            QPainter painter(this);
            painter.drawImage(0,0,m_drawImage);
            painter.end();
    }
    }
    
    class LineRubberBand : public QRubberBand
    {
    public:
        LineRubberBand(QWidget* parent) : QRubberBand(QRubberBand::Line, parent) {}
    
    protected:
        void paintEvent(QPaintEvent* event) override
        {
            QPainter painter(this);
            painter.setRenderHint(QPainter::Antialiasing);
    
            QPen pen;
            pen.setColor(Qt::black);
            pen.setWidth(2);
            painter.setPen(pen);
    
            QLine line(rect().topLeft(), rect().bottomRight());
            painter.drawLine(rect().topLeft(), rect().bottomRight());
        }
    };
    #endif // MAINWINDOW_H
    

    Here is the .cpp file.

    void MainWindow::mousePressEvent(QMouseEvent *event){
        QPoint cursorPos = event->pos();
        //check status of OState
        qDebug()<<"Now OState = "<<this->GetOState();
        switch (this->GetOState()) {
        case 0: //OState = Idle
            break;
        case 1: //OState = FuncDrawLine
            if ((event->button()==Qt::LeftButton)&&m_drawState!=DrawState::DS_Idle){
                //in drawing mode
                if (m_drawState==DrawState::DS_StartPoint){
                    //this click determines the 1st point
                    startPoint = event->pos();
    
                    m_rubberBand = new LineRubberBand(this);
                    m_rubberBand->setGeometry(QRect(startPoint, QSize()));
                    m_rubberBand->show();
    
                    m_isDrawing = true;
                    FuncDrawPoint(startPoint);
                    m_drawState = DrawState::DS_EndPoint;
    
                    //qDebug()<<"1st point drawn" << "m_isDrawing = "<<m_isDrawing;
                }
                /*else if (m_drawState==DrawState::DS_EndPoint){
                    endPoint = event->pos();
                    FuncDrawLine(startPoint, endPoint);
                        m_drawState = DrawState::DS_Idle;
                        m_isDrawing = false;
                        m_OState = OccupiedState::OS_Idle;*/
                    //qDebug()<<"2nd point draw";
    
            }break;
    
    //        else{// not in drawing mode
    
    //            if (ui->BtnDrawLine->geometry().contains(cursorPos)){
    //                //BtnDrawLine Clicked
    //                m_drawState = DrawState::DS_StartPoint;
    //            };
    //        }
        case 2://OState = FuncDrawRect
            if (event->button()==Qt::LeftButton&& m_drawState!=DrawState::DS_Idle){
                if (m_drawState==DrawState::DS_StartPoint){
                    startPoint = event->pos();
                    FuncDrawPoint(startPoint);
                        m_drawState = DrawState::DS_EndPoint;
                }
                /*else if (m_drawState ==DrawState::DS_EndPoint){
                    endPoint = event->pos();
                    FuncDrawRect(startPoint, endPoint);
                        m_drawState = DrawState::DS_Idle;
                        m_isDrawing = false;
                        m_OState = OccupiedState::OS_Idle;
                }*/
            }break;
    }
    }
    
    void MainWindow::mouseMoveEvent(QMouseEvent *event){
        if ((event->buttons() & Qt::LeftButton) && m_isDrawing == true)
        {
    
                int tempStrPointX = startPoint.x();
                int tempStrPointY = startPoint.y();
                int endPosX = event->pos().x()-startPoint.x();
                int endPosY = event->pos().y()-startPoint.y();
    //the coordinates are adjusted and correct.
                QLine line(startPoint,endPoint);
                    m_rubberBand->setGeometry
                        (QRect(line.p1(),line.p2()).normalized());
    
        QRect t = QRect(startPoint.x(),startPoint.y(),endPosX,endPosY).normalized();
    
                qDebug()<<"start x = "<<startPoint.x()
                        <<"start y = "<<startPoint.y()
                        <<"end x = "<<event->pos().x()
                        <<"end x = "<<event->pos().y();
                qDebug()<<t.topLeft()
                        <<t.topRight()
                        <<t.bottomLeft()
                        <<t.bottomRight();}
    
    //        };
            endPoint = event->pos();
            FuncDrawLine(startPoint, endPoint, true);
            //update();
    
        }
    
    
    void MainWindow::mouseReleaseEvent(QMouseEvent *event){
        switch (this->GetOState()) {
        case 0: //OState = Idle
    
            break;
        case 1:
            if (m_drawState == DrawState::DS_Idle){
                endPoint = event->pos();
                m_isDrawing = false;
            }else if (m_drawState == DrawState::DS_EndPoint){
                if (m_rubberBand){
                    m_rubberBand->hide();
                    delete m_rubberBand;
                    m_rubberBand = nullptr;
                }
                endPoint = event->pos();
                FuncDrawLine(startPoint, endPoint, false);
                    m_drawState = DrawState::DS_Idle;
                    m_isDrawing = false;
                    m_OState = OccupiedState::OS_Idle;
    
            }break;
        case 2:
            break;
        update();
    
    }}
    
    jsulmJ 1 Reply Last reply
    0
    • J Jolene

      What I want to do:
      If you're gonne use the line tool in Photoshop, you will draw a straight line by one click-move-release mouse event. In the move term, You will see a temporary line connecting the current mouse position and the original mouse position that helps the user check what the result will be. And that temporary line is what I want to do.

      I tried:
      Create a class using QRubberBand that draws the line.
      I think the problem is mainly in mouseMoveEvent().

      The problem shows like: when click and move mouse, the line will only be correctly created if current mouse pos(endPosX) is at the bottomRight or topLeft corner of the orinal start point(startPoint.x()), if not, the line will be wrongly created. And maybe it's because the "setGeometry" only allows creating a rect from topLeft to bottomRight, and is coupled with "rect.normalize()"which limits the line will only be directed topLeft-bottomRight.

      But if we don't use (normalize.()), the line will only shows when current mouse pos(endPosX) is at the bottomRight or topLeft corner of the orinal start point(startPoint.x()). and This is because QRect limits that a rect will always be created from topLeft to BottomRight, when the vertices pos don't satisfy the rules, the rect will not be visible.

      Q:
      How to create that temporary line (rubber band line) as it's stated above?

      class mainFunc : public QMainWindow{
      Q_OBJECT
      //some code
      protected:
          void MainWindow::paintEvent(QPaintEvent *){
              QPainter painter(this);
              painter.drawImage(0,0,m_drawImage);
              painter.end();
      }
      }
      
      class LineRubberBand : public QRubberBand
      {
      public:
          LineRubberBand(QWidget* parent) : QRubberBand(QRubberBand::Line, parent) {}
      
      protected:
          void paintEvent(QPaintEvent* event) override
          {
              QPainter painter(this);
              painter.setRenderHint(QPainter::Antialiasing);
      
              QPen pen;
              pen.setColor(Qt::black);
              pen.setWidth(2);
              painter.setPen(pen);
      
              QLine line(rect().topLeft(), rect().bottomRight());
              painter.drawLine(rect().topLeft(), rect().bottomRight());
          }
      };
      #endif // MAINWINDOW_H
      

      Here is the .cpp file.

      void MainWindow::mousePressEvent(QMouseEvent *event){
          QPoint cursorPos = event->pos();
          //check status of OState
          qDebug()<<"Now OState = "<<this->GetOState();
          switch (this->GetOState()) {
          case 0: //OState = Idle
              break;
          case 1: //OState = FuncDrawLine
              if ((event->button()==Qt::LeftButton)&&m_drawState!=DrawState::DS_Idle){
                  //in drawing mode
                  if (m_drawState==DrawState::DS_StartPoint){
                      //this click determines the 1st point
                      startPoint = event->pos();
      
                      m_rubberBand = new LineRubberBand(this);
                      m_rubberBand->setGeometry(QRect(startPoint, QSize()));
                      m_rubberBand->show();
      
                      m_isDrawing = true;
                      FuncDrawPoint(startPoint);
                      m_drawState = DrawState::DS_EndPoint;
      
                      //qDebug()<<"1st point drawn" << "m_isDrawing = "<<m_isDrawing;
                  }
                  /*else if (m_drawState==DrawState::DS_EndPoint){
                      endPoint = event->pos();
                      FuncDrawLine(startPoint, endPoint);
                          m_drawState = DrawState::DS_Idle;
                          m_isDrawing = false;
                          m_OState = OccupiedState::OS_Idle;*/
                      //qDebug()<<"2nd point draw";
      
              }break;
      
      //        else{// not in drawing mode
      
      //            if (ui->BtnDrawLine->geometry().contains(cursorPos)){
      //                //BtnDrawLine Clicked
      //                m_drawState = DrawState::DS_StartPoint;
      //            };
      //        }
          case 2://OState = FuncDrawRect
              if (event->button()==Qt::LeftButton&& m_drawState!=DrawState::DS_Idle){
                  if (m_drawState==DrawState::DS_StartPoint){
                      startPoint = event->pos();
                      FuncDrawPoint(startPoint);
                          m_drawState = DrawState::DS_EndPoint;
                  }
                  /*else if (m_drawState ==DrawState::DS_EndPoint){
                      endPoint = event->pos();
                      FuncDrawRect(startPoint, endPoint);
                          m_drawState = DrawState::DS_Idle;
                          m_isDrawing = false;
                          m_OState = OccupiedState::OS_Idle;
                  }*/
              }break;
      }
      }
      
      void MainWindow::mouseMoveEvent(QMouseEvent *event){
          if ((event->buttons() & Qt::LeftButton) && m_isDrawing == true)
          {
      
                  int tempStrPointX = startPoint.x();
                  int tempStrPointY = startPoint.y();
                  int endPosX = event->pos().x()-startPoint.x();
                  int endPosY = event->pos().y()-startPoint.y();
      //the coordinates are adjusted and correct.
                  QLine line(startPoint,endPoint);
                      m_rubberBand->setGeometry
                          (QRect(line.p1(),line.p2()).normalized());
      
          QRect t = QRect(startPoint.x(),startPoint.y(),endPosX,endPosY).normalized();
      
                  qDebug()<<"start x = "<<startPoint.x()
                          <<"start y = "<<startPoint.y()
                          <<"end x = "<<event->pos().x()
                          <<"end x = "<<event->pos().y();
                  qDebug()<<t.topLeft()
                          <<t.topRight()
                          <<t.bottomLeft()
                          <<t.bottomRight();}
      
      //        };
              endPoint = event->pos();
              FuncDrawLine(startPoint, endPoint, true);
              //update();
      
          }
      
      
      void MainWindow::mouseReleaseEvent(QMouseEvent *event){
          switch (this->GetOState()) {
          case 0: //OState = Idle
      
              break;
          case 1:
              if (m_drawState == DrawState::DS_Idle){
                  endPoint = event->pos();
                  m_isDrawing = false;
              }else if (m_drawState == DrawState::DS_EndPoint){
                  if (m_rubberBand){
                      m_rubberBand->hide();
                      delete m_rubberBand;
                      m_rubberBand = nullptr;
                  }
                  endPoint = event->pos();
                  FuncDrawLine(startPoint, endPoint, false);
                      m_drawState = DrawState::DS_Idle;
                      m_isDrawing = false;
                      m_OState = OccupiedState::OS_Idle;
      
              }break;
          case 2:
              break;
          update();
      
      }}
      
      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Jolene Why do you use QRubberBand if you want to draw a line?
      You can draw a line easilly using one of the QPainter::drawLine overloads.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      J 1 Reply Last reply
      0
      • jsulmJ jsulm

        @Jolene Why do you use QRubberBand if you want to draw a line?
        You can draw a line easilly using one of the QPainter::drawLine overloads.

        J Offline
        J Offline
        Jolene
        wrote on last edited by
        #3

        @jsulm Because I'm trying to mimic an interaction in Photoshop. I think that is a great feedback it returns user a temporary line that shows the what result might be when using line tool.
        I apologize if I didn't express well. I want to create a temporary line, when the user is hesitating where to put the endPoint. The temporary line will offer user a preview of the result, thus help user decide.
        So the temporary line is dynamic, because it will change when the user mouse can move around anywhere.
        Then I tried to use "painter.drawLine" in mouseMoveEvent(), but it will always draw without clearing the last line, then the whole screen is filled the radial lines.
        That's when I came up with QRubberBand, and that's where I'm stuck at.

        jsulmJ 1 Reply Last reply
        0
        • J Jolene

          @jsulm Because I'm trying to mimic an interaction in Photoshop. I think that is a great feedback it returns user a temporary line that shows the what result might be when using line tool.
          I apologize if I didn't express well. I want to create a temporary line, when the user is hesitating where to put the endPoint. The temporary line will offer user a preview of the result, thus help user decide.
          So the temporary line is dynamic, because it will change when the user mouse can move around anywhere.
          Then I tried to use "painter.drawLine" in mouseMoveEvent(), but it will always draw without clearing the last line, then the whole screen is filled the radial lines.
          That's when I came up with QRubberBand, and that's where I'm stuck at.

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Jolene But QRubberBand is not a line

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          J 1 Reply Last reply
          0
          • jsulmJ jsulm

            @Jolene But QRubberBand is not a line

            J Offline
            J Offline
            Jolene
            wrote on last edited by
            #5

            @jsulm
            Yes, that's why I tried to create a LineRubberBand class inheriting QRubberBand, which allows me to draw a line instead of the default rect, and it allows dynamical update ( it can draw and erase within mouseMoveEvent, but painter.drawLine() cannot do). Now the line thing created by me and the dynamical update from QRubberBand are combined well, but there are still some problems mentioned above remaind unsolved.

            1 Reply Last reply
            0

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved