I'm wondering if there's any way to get all widgets (QLabel specifically) inside a QRubberBand area.
void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
//...do something with the widgets inside
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
}
Related
I have a problem with qRubberBand not painting continuous rectangles in some cases. I used exact example from Qt documentation:
void Widget::mousePressEvent(QMouseEvent *event)
{
origin = event->pos();
if (!rubberBand)
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(QRect(origin, QSize()));
rubberBand->show();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
rubberBand->hide();
}
my problem is shown in this video:
https://imgur.com/a/TMpUz0l
as you can see, if I am painting the rectangle from left top to right bottom, the rectangle is painted smoothly. But otherwise the painting is not continuous.
Here are some events of my code:
void Graph::onMousePress(QMouseEvent* event){
if ((event->buttons() & Qt::RightButton) == Qt::RightButton) {
m_RMBPressed = true;
m_globalOrigin = QPoint(event->globalPos().x(), event->globalPos().y());
if (!m_rubberBand) {
m_rubberBand = new QRubberBand(QRubberBand::Rectangle, qobject_cast<QWidget*>(this));
}
m_rubberBand->setGeometry(QRect(m_globalOrigin, QSize()));
m_rubberBand->show();
}
}
void Graph::onMouseRelease(QMouseEvent* event){
m_RMBPressed = false;
if (m_rubberBand) {
m_rubberBand->hide();
}
}
void Graph::onMouseMove(QMouseEvent* event){
if (m_RMBPressed) {
auto rectangle = QRect(QPoint(event->globalPos().x(), event->globalPos().y()), m_globalOrigin).normalized();
m_rubberBand->setGeometry(rectangle);
m_rubberBand->update();
}
}
I tried to swap the coordinates of the QRect, remove the .normalized() function and then correct the rectangle coordinates but none of it worked. Any help is appreciated.
I have some items on a qgraphicsscene and I want to be able to drag and drop an item onto another or onto a blank part of the scene. Depending on whether the drop occurs over another item or over a blank part of the scene two different things happen. How to approach this? i have implemented scene and graphics item drag and drop. but this approach not working, by this both scene and item handles drop events, for me only one should handle based on dropping position.
Below is working example.
when i drag rect1 and droppped on rect2, i should get rect2 drop event.
when i drag rect1 and droppped on blank scene, i should get scene drop event.
graphicsview.h
class GraphicsView : public QGraphicsView
{
public:
GraphicsView();
};
graphicsview.cpp
GraphicsView::GraphicsView()
{
//setAcceptDrops(true);
}
graphicsScene.h
class GraphicsScene : public QGraphicsScene
{
public:
GraphicsScene();
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
graphicsScene.cpp
GraphicsScene::GraphicsScene()
{
}
void GraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "Scene::dragEnterEvent";
event->acceptProposedAction();
}
void GraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "Scene::dragMoveEvent";
event->acceptProposedAction();
}
void GraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "Scene::dropEvent";
event->acceptProposedAction();
}
graphicsRectItem.h
class GraphicsRectItem : public QGraphicsRectItem
{
public:
GraphicsRectItem();
static QString MimeType() { return QStringLiteral("job/x-job"); }
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
graphicsRectItem.cpp
GraphicsRectItem::GraphicsRectItem()
{
setAcceptDrops(true);
}
void GraphicsRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "RectItem::mousePressEvent";
setCursor(Qt::ClosedHandCursor);
}
void GraphicsRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "RectItem::mouseMoveEvent";
if (QLineF(event->screenPos(), event-
>buttonDownScreenPos(Qt::LeftButton))
.length() < QApplication::startDragDistance()) {
QGraphicsRectItem::mouseMoveEvent(event);
return;
}
qDebug() << "drag before executed";
QByteArray itemData;
QString m_jobName = "rect";
DataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << m_jobName ;
QMimeData *mimeData = new QMimeData;
mimeData->setData(GraphicsRectItem::MimeType(), itemData);
QDrag *drag = new QDrag(event->widget());
drag->setMimeData(mimeData);
drag->exec();
qDebug() << "drag executed";
setCursor(Qt::OpenHandCursor);
}
void GraphicsRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "RectItem::mouseReleaseEvent";
setCursor(Qt::OpenHandCursor);
}
void GraphicsRectItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "RectItem::dragEnterEvent";
event->acceptProposedAction();
}
void GraphicsRectItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "RectItem::dragMoveEvent";
event->acceptProposedAction();
}
void GraphicsRectItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
qDebug() << "RectItem::dropEvent";
event->acceptProposedAction();
}
Mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pView = new GraphicsView;
m_pScene = new GraphicsScene;
m_pView->setScene(m_pScene);
GraphicsRectItem *rect1 = new GraphicsRectItem;
rect1->setRect(10,10,55,55);
GraphicsRectItem *rect2 = new GraphicsRectItem;
rect2->setRect(75,10,55,55);
m_pScene->addItem(rect1);
m_pScene->addItem(rect2);
this->setCentralWidget(m_pView);
}
MainWindow::~MainWindow()
{
delete ui;
}
OK, this should be easy.
I tried to handle drop event onto a QGraphicsView widget. Incoming data dragged from a QTreeView widget. For that, I re-implemented these methods:
void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event.accept();
}
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
this.scene()->addPixmap(pixmap);
}
This works fine; but how can I change another graphicsview scene within this widget's drop event? That is:
void QGraphicsView::dropEvent(QDropEvent *event)
{
QPixmap pixmap(event->mimedata()->urls()[0].toString().remove(0,8));
// I cannot access ui; and cannot access my widgets...:
ui->anotherview->scene()->addPixmap(pixmap);
}
What about making a custom signal in your QGraphicsView like void showPixmap(QPixmap p) and connecting it to a slot in your main gui class where you can access ui elements. You can then call emit showPixamp(pixmap) in the dropEvent.
Subclassing QGraphicsView
//header file
class CustomView : public QGraphicsView
{
public:
CustomView(QGraphicsScene*, QWidget*=NULL);
~CustomView();
signals:
void showPixmap(QPixmap p);
protected:
virtual void dropEvent(QDropEvent *event);
};
//cpp file
CustomView::CustomView(QGraphicsScene *scene, QWidget* parent)
:QGraphicsView(scene, parent)
{
//if you need to initialize variables, etc.
}
void CustomView::dropEvent(QDropEvent *event)
{
//handle the drop event
QPixmap mPixmap;
emit showPixmap(mPixmap);
}
Using event filters in your main GUI class
void GUI::GUI()
{
ui->mGraphicsView->installEventFilter(this);
}
bool GUI::eventFilter(QObject *object, QEvent *event)
{
if (object == ui->mGraphicsView && event->type() == QEvent::DropEnter) {
QDropEvent *dropEvent = static_cast<QDropEvent*>(event);
//handle the drop event
return true;
}
else
return false;
}
I am stuck with this weird behavior where, if I enable rubberband drag, the wheel event doesn't zoom under mouse anymore. It does zooming but irrespective of mouse position. And if I disable other events, then wheelEvent works properly.
I have a custom class inheriting QGraphicsView as :
class MyGraphics : public QGraphicsView{
public :
MyGraphics(QWidget *parent = NULL);
public slots:
void zoomIn() { scale(1.2, 1.2); }
void zoomOut() { scale(1 / 1.2, 1 / 1.2); }
protected :
QRubberBand *rubberBand;
QPoint origin;
QPointF InitialCenterPoint;
QPointF CurrentCenterPoint;
QPoint rubberBandOrigin;
bool rubberBandActive;
QPoint LastPanPoint;
int _numScheduledScalings;
//if I uncomment these three event handlers, the wheelevent doesnt zoom properly!
// virtual void mousePressEvent(QMouseEvent *event);
// virtual void mouseMoveEvent(QMouseEvent *event);
// virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *);
virtual void resizeEvent(QResizeEvent *event);
};
The constructor :
MyGraphics::MyGraphics(QWidget *parent) : QGraphicsView(parent){
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
this->installEventFilter(this);
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
this->setResizeAnchor( QGraphicsView::AnchorUnderMouse );
QGraphicsScene *graphScene = new QGraphicsScene(this);
this->setScene(graphScene);
QGraphicsItem* pEllipse = graphScene->addEllipse(0,100,50,50);
QGraphicsItem* pRect = graphScene->addRect(200,100,50,50);
pEllipse->setFlag(QGraphicsItem::ItemIsSelectable, true);
pRect->setFlag(QGraphicsItem::ItemIsSelectable, true);
setSceneRect(0, 0, 1000, 1000);
rubberBandOrigin = QPoint(0,0);
}
Event handlers :
void MyGraphics::wheelEvent(QWheelEvent *event){
if(event->delta() > 0){
//Zoom in
this->zoomIn();
} else {
this->zoomOut();
}
}
/*
void MyGraphics::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton) {
rubberBandOrigin = event->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(event->x(),event->y(),0, 0);
rubberBand->show();
rubberBandActive = true;
}
if(event->button() == Qt::LeftButton){
LastPanPoint = event->pos();
}
}
void MyGraphics::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() == Qt::MiddleButton && rubberBandActive == true){
rubberBand->resize( event->x()-rubberBandOrigin.x(), event->y()-rubberBandOrigin.y() );
}
else{
if(!LastPanPoint.isNull()) {
//Get how much we panned
QGraphicsView * view = static_cast<QGraphicsView *>(this);
QPointF delta = view->mapToScene(LastPanPoint) - view->mapToScene(event->pos());
LastPanPoint = event->pos();
}
}
}
void MyGraphics::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton){
QGraphicsView * view = static_cast<QGraphicsView *>(this);
QPoint rubberBandEnd = event->pos();
QRectF zoomRectInScene = QRectF(view->mapToScene(rubberBandOrigin), view->mapToScene(rubberBandEnd));
QPointF center = zoomRectInScene.center();
view->fitInView(zoomRectInScene, Qt::KeepAspectRatio);
rubberBandActive = false;
delete rubberBand;
}
else{
LastPanPoint = QPoint();
}
}
*/
Any idea where I am doing wrong or how do I fix it ?
QGraphicsView::scale function's behaviour depends on the mouse position. It's performing automatically and internally by QGraphicsView. Since you don't pass mouse position to the scale function, I think QGraphicsView tracks the mouse and remembers the last position on its own.
By reimplementing mouse event handlers you have taken this ability from it. The view can't determine the mouse position anymore because its original handlers aren't called.
Luckily this issue can be easily fixed. You need to call base class implementation before your own:
void MyGraphics::mousePressEvent(QMouseEvent *event) {
QGraphicsView::mousePressEvent(event);
// your implementation goes here
}
It's an example for mousePressEvent but you should add similar statements to all your event handlers unless you need to disable some part of default behavior.
so I am trying to draw a line between two points. Left mouse click starts the line then I would like the line to by dynamically drawn as the mouse moves (almost like a preview of the line). Left mouse click again and the line will be permanently drawn. I know there are a lot of other posts about QPaintEvents and I have combined some of the techniques used, but for some reason nothing is being drawn to the canvas. Below is the code:
void Main::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton) {
QPointF pos = event->pos();
if( mStartPoint.isNull() ) {
if(josh.contains(pos))
mStartPoint = pos;
} else {
canvas.addLine(mStartPoint.x(),mStartPoint.y(),pos.x(),pos.y());
mStartPoint = QPointF();
}
}
}
bool Main::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (!mStartPoint.isNull()) {
m_targetImage = QImage(canvas.width(),canvas.height(),QImage::Format_ARGB32);
QPainter p;
p.begin(&m_targetImage);
p.drawLine(mStartPoint, mouseEvent->pos());
p.end();
}
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
}
return false;
}
void Main::paintEvent(QPaintEvent *pe)
{
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(10);
painter.setPen(pen);
painter.drawImage(0, 0, m_targetImage);
}
Any help is appreciated! Thanks! Josh
I think this is what you want. Change parameters according to your requirements.
//In your constructor
m_nInitialX = 0;
m_nInitialY = 0;
m_nFinalX = 0;
m_nFinalY = 0;
m_nPTargetPixmap = 0;
m_nPTargetPixmap = new QPixmap(400,400);
m_nbMousePressed = false;
void Main::mousePressEvent(QMouseEvent* event)
{
m_nbMousePressed = true;
m_nInitialX = event->pos().x();
m_nInitialY = event->pos().y();
}
void Main::mouseReleaseEvent(QMouseEvent *event)
{
m_nbMousePressed = false;
}
void Main::paintEvent(QPaintEvent *e)
{
if(m_nbMousePressed)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::green);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
}
QPainter painter(this);
painter.drawPixmap(0, 0, *m_nPTargetPixmap);
}
void Main::mouseMoveEvent(QMouseEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::black);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
update(); // update your view
m_nFinalX = event->pos().x();
m_nFinalY = event->pos().y();
}
update(); // update your view
}
This piece of code will draw a 2 point line that you want.
Here's my example of how to paint lines in your way directly on the widget.
Declaration:
private:
void drawLines(QPainter *p);
QPoint startPos;
QPoint endPos;
bool inDrawing;
QVector<QLine> lines;
Setting initial values in constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
startPos = QPoint();
endPos = QPoint();
inDrawing = false;
setMouseTracking(true);
}
Starting or ending the line drawing:
void Widget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
if (!inDrawing)
{
startPos = event->pos();
}
else
{
endPos = event->pos();
QLine line = QLine(startPos, event->pos());
lines.append(line);
}
inDrawing = !inDrawing;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (inDrawing)
{
endPos = event->pos();
update();
}
}
Drawing current and saved lines:
void Widget::drawLines(QPainter *p)
{
if (!startPos.isNull() && !endPos.isNull())
{
p->drawLine(startPos, endPos);
}
p->drawLines(lines);
}
Drawing:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter p(this);
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(4);
p.setPen(pen);
drawLines(&p);
}