How to create rubber band line?
-
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_HHere 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(); }} -
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_HHere 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(); }} -
@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.@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. -
@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. -
@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.