I am trying something that would seem quite easy, but am struggling a bit with it. I want to be able to draw rectangles in a GUI. What I am doing now is:
I create a GUI with Qt Designer, in which I include a QGraphicsView.
In my main window I create a myGraphicsScene (class derived from QGraphicsScene, but with mouse press, move and release events overridden) and I set the scene to the QGraphicsView created in the UI.
The problem is that I am not able to properly control the refresh and update of the view when I change the scene.
Here is the relevant part of the code:
MainGUIWindow constructor and initialization:
MainGUIWindow::MainGUIWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainGUIWindow)
{
ui->setupUi(this);
_init();
}
void MainGUIWindow::_init()
{
scene = new myGraphicsScene(ui->frame_drawing);
scene->setSceneRect(QRectF(QPointF(-100, 100), QSizeF(200, 200)));
ui->graphicsView->setScene(scene);
QRect rect(10, 20, 80, 60);
scene->addText("Hello world!");
scene->addRect(rect, QPen(Qt::black), QBrush(Qt::blue));
}
I can perfectly see the HelloWorld text and this rectangle. However when I start with clicking events, I don't properly get updates anymore.
myGraphicsScene class header:
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include <QGraphicsScene>
class QGraphicsSceneMouseEvent;
class QPointF;
class QColor;
class myGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit myGraphicsScene(QObject *parent = 0);
public slots:
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
private:
QPen* pen;
QBrush* brush;
QRect* rect;
QPoint* p1;
QPoint* p2;
bool firstClick;
// bool startedRect;
};
#endif
myGraphicsScene class implementation:
#include "myGraphicsScene.h"
#include <QGraphicsSceneMouseEvent>
#include <QRect>
myGraphicsScene::myGraphicsScene(QObject *parent)
: QGraphicsScene(parent)
{
pen = new QPen(Qt::black);
brush = new QBrush(Qt::blue);
rect = 0;
// startedRect = false;
firstClick = true;
}
void myGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;
// rect = new QRect((mouseEvent->scenePos()).toPoint(), (mouseEvent->scenePos()).toPoint());
// addRect(*rect, *pen, *brush);
// startedRect = true;
if(firstClick)
{
p1 = new QPoint((mouseEvent->scenePos()).toPoint());
QRect tmp_rect(*p1, *p1);
addRect(tmp_rect, *pen, *brush);
}
else
{
p2 = new QPoint((mouseEvent->scenePos()).toPoint());
QRect tmp_rect(*p2, *p2);
addRect(tmp_rect, *pen, *brush);
}
QGraphicsScene::mousePressEvent(mouseEvent);
}
void myGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// if(startedRect)
// {
// rect->moveBottomRight((mouseEvent->scenePos()).toPoint());
// qDebug("Mouse Position: %d, %d", (mouseEvent->scenePos()).toPoint().x(), (mouseEvent->scenePos()).toPoint().y());
// qDebug("Rectangle BottomRight Position: %d, %d", rect->bottomRight().x(), rect->bottomRight().y());
// }
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void myGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;
// rect = 0;
// startedRect = false;
if(firstClick)
{
firstClick = false;
}
else
{
rect = new QRect(*p1, *p2);
addRect(*rect, *pen, *brush);
p1 = 0;
p2 = 0;
rect = 0;
firstClick = true;
}
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
My original idea was to draw the rectangles in a drag and drop fashion, but in the end ended up trying the two-clicks approach, which worked a bit better but still doesn't completely update the view when it should.
I am new to Qt, so I am not very familiar with how it should be done. Any help would be appreciated, since I have been a bit stuck here for some time now.
Thanks!
In each of your mouse*Event() overloads, call QGraphicsScene::update() method. It will schedule redraw on the view.
http://doc.qt.io/qt-5/qgraphicsscene.html#update
I just found the solution: I had to actually change the item added to the scene, and not the rectangle. So, now, my code looks like:
#include "myGraphicsScene.h"
#include <QGraphicsSceneMouseEvent>
#include <QRect>
#include <QGraphicsRectItem>
myGraphicsScene::myGraphicsScene(QObject *parent)
: QGraphicsScene(parent)
{
pen = new QPen(Qt::black);
brush = new QBrush(Qt::blue);
tmp_rect = 0;
startedRect = false;
// firstClick = true;
}
void myGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;
// Drag and drop approach
startedRect = true;
p1 = new QPointF(mouseEvent->scenePos());
tmp_rect = new QRectF(*p1, *p1);
// addRect(*tmp_rect, *pen, *brush);
tmp_rect_item = new QGraphicsRectItem(*tmp_rect);
rectangles.push_back(tmp_rect_item);
addItem(rectangles.back());
// Two-clicks approach
// if(firstClick)
// {
// p1 = new QPointF(mouseEvent->scenePos());
// tmp_rect_item = addRect(QRect(p1->toPoint(), p1->toPoint()), *pen, *brush); //save it to remove it after
// }
// else
// {
// p2 = new QPointF(mouseEvent->scenePos());
// // QRect tmp_rect(*p2, *p2);
// // addRect(tmp_rect, *pen, *brush);
// }
update();
QGraphicsScene::mousePressEvent(mouseEvent);
}
void myGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(startedRect)
{
tmp_rect_item->setRect(QRectF(*p1, mouseEvent->scenePos()));
qDebug("Mouse Position: %d, %d", (mouseEvent->scenePos()).toPoint().x(), (mouseEvent->scenePos()).toPoint().y());
qDebug("Rectangle BottomRight Position: %d, %d", tmp_rect->bottomRight().x(), tmp_rect->bottomRight().y());
update();
}
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void myGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if (mouseEvent->button() != Qt::LeftButton)
return;
// Drag and drop approach:
tmp_rect = 0;
startedRect = false;
// Two-clicks approach
// if(firstClick)
// {
// firstClick = false;
// }
// else
// {
// removeItem(tmp_rect_item);
// tmp_rect_item = new QGraphicsRectItem(QRectF(*p1, *p2));
// // *tmp_rect, *pen, *brush);
// rectangles.push_back(tmp_rect_item);
// addItem(rectangles.back());
// p1 = 0;
// p2 = 0;
// tmp_rect = 0;
// firstClick = true;
// }
update();
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
one other helpful hint I found was that when I extended my view, I had put the mouse event handler under "public" instead of "protected". I made the change the calls were made consistently.
Related
I try to make popup label which shows the name of vertical header item because name is large and I want header width to be just size of its number.
I made event filter and made popup dialog and popuplabel qlabel.
but size of popup is larger than size of header item.
if I make size equal to rect size ,the text disappear.
if i adjust the size of popup ,it makes greater rect showing text with offset downwards corresponding to visual index of header item.
this is the code:
#ifndef TABLEVIEW_H
#define TABLEVIEW_H
#include <QDialog>
#include <QEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QTimer>
class TableView : public QTableView {
Q_OBJECT
QDialog* popup;
QLabel* popupLabel;
int editor_index ;
public:
TableView(QWidget* parent = Q_NULLPTR) :QTableView(parent) {
viewport()->installEventFilter(this);
horizontalHeader()->viewport()->installEventFilter(this);
verticalHeader()->viewport()->installEventFilter(this);
setMouseTracking(true);
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
}
bool eventFilter(QObject* watched, QEvent* event) {
//if (viewport() == watched)
if ((watched == horizontalHeader()->viewport() ||
watched == verticalHeader()->viewport()))
{
if (event->type() == QEvent::MouseMove)
{
/*/
if (popup) { //delete previous popup just in case
popup->contentsRect().setRect(0,0,0,0) ;
}
if (popupLabel) { //delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0,0,0,0);
}
*/
/*
////////////////////////////////////////////////////////
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
////////////////////////////////////////////////////////
*/
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
int mouse_pos = header->orientation() == Qt::Horizontal ? mouseEvent->x() : mouseEvent->y();
int logical_index = header->logicalIndex(header->visualIndexAt(mouse_pos));
if (logical_index >= 0)
{ // if mouse is over an item
showPopup(logical_index, watched);
}
else
{
popup->hide();
}
}
else if (event->type() == QEvent::Leave) {
popup->hide();
}
}
else if (popup == watched)
{
if (event->type() == QEvent::Leave)
{
popup->hide();
}
}
return QTableView::eventFilter(watched, event);
}
private:
void showPopup(const int & logical_index, QObject* watched) const
{
if (logical_index >= 0)
{
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
QFont font("times", 12);
QFontMetrics fm(font);
int pixelsWide = fm.width("xxxxxx");
int pixelsHigh = fm.height();
QRect rect; // line edit rect in header's viewport's coordinates
if (header->orientation() == Qt::Horizontal) {
//rect.setLeft(header->sectionPosition(logical_index));
rect.setLeft(header->sectionViewportPosition(logical_index));
rect.setWidth(header->sectionSize(logical_index));
rect.setTop(0);
rect.setHeight(header->height());
}
else {
//rect.setTop((header->sectionPosition(logical_index)));
rect.setTop((header->sectionViewportPosition(logical_index)));
//QPoint point(0,header->sectionPosition(logical_index));
//rect.setTop((mapToGlobal(point)).y() );
//rect.topLeft())
int cy = header->sectionSize(logical_index);
rect.setHeight(header->sectionSize(logical_index));
rect.setLeft(0);
rect.setWidth(header->width());
//rect.setCoords(rect.left(),rect.top(),rect.left()+rect.width(),rect.top()+rect.height());
}
//rect.adjust(1, 1, 1, 1);
popupLabel->move(rect.bottomLeft());
//popupLabel->move(rect.topLeft());
//popupLabel->move(viewport()->mapToGlobal(rect.topLeft()));
popupLabel->resize(rect.size());
//popupLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
//popupLabel->show();
//popupLabel->setFixedHeight(rect.height());
//popupLabel->setFixedWidth(rect.width());
//popupLabel->setFrame(false);
//get current item text
QString text = header->model()->
headerData(logical_index, header->orientation()).toString();
//int z=text.size();
popupLabel->setText(text);
//popupLabel->setFocus();
///////////////////////////////////////////////////////////////////////////
//editor_index = logical_index; //save for future use
popupLabel->installEventFilter(this->parent()); //catch focus out event
//if user presses Enter it should close editor
//connect(header_editor, SIGNAL(returnPressed()),
// ui->tableWidget, SLOT(setFocus()));
//popupLabel->setGeometry(rect);
//popupLabel->adjustSize();
//popupLabel->setFixedSize(100, 100);
popupLabel->show();
////////////////////////////////////////////////////////////////////////////
//popup->move(viewport()->mapToGlobal(rect.bottomLeft()));
popup->move(viewport()->mapToGlobal(rect.topLeft()));
//popup->move(rect.topLeft());
//popup->resize(rect.size());
//popup->move(rect.bottomLeft());
//popup->move(rect.topLeft());
//popup->setFixedSize(100, popup->heightForWidth(100));
//popup->setFixedSize(100, 100);
//popupLabel->setText(text);
//popupLabel->show();
// popupLabel->setText(logical_index.data(Qt::DisplayRole).toString());
//popup->adjustSize();
//popup->setGeometry(rect);
popup->show();
//sleep 10 ms;
return;// true; // filter out event
}
else {
popup->hide();
//delete previous popup just in case
popup->contentsRect().setRect(0, 0, 0, 0);
//popup = nullptr;
//delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0, 0, 0, 0);
//popupLabel = nullptr;
return; //false;
}
}
};
#endif // TABLEVIEW_H
I use beyond compare to find out what I changed:
1.I move popupLabel to point 0,0
2.I resize popup to rect.size()
If not enuf space for display text,I will resize popupLabel and popup to a larger size.
code:
#include <QApplication>
#include <QTableWidget>
#include <QStyledItemDelegate>
#include <QDialog>
#include <QEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QTimer>
#include <QDebug>
class TableModel :public QAbstractTableModel
{
Q_OBJECT
public:
int rowCount(const QModelIndex& parent = QModelIndex()) const
{
return 4;
}
int columnCount(const QModelIndex& parent = QModelIndex()) const
{
return 2;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
{
if (role == Qt::DisplayRole)
return QString::number(index.row()) + QString::number(index.column());
return QVariant();
}
};
class TableView : public QTableView {
Q_OBJECT
QDialog* popup;
QLabel* popupLabel;
int editor_index;
public:
TableView(QWidget* parent = Q_NULLPTR) :QTableView(parent) {
viewport()->installEventFilter(this);
horizontalHeader()->viewport()->installEventFilter(this);
verticalHeader()->viewport()->installEventFilter(this);
setMouseTracking(true);
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
}
bool eventFilter(QObject* watched, QEvent* event) {
//if (viewport() == watched)
if ((watched == horizontalHeader()->viewport() ||
watched == verticalHeader()->viewport()))
{
if (event->type() == QEvent::MouseMove)
{
/*/
if (popup) { //delete previous popup just in case
popup->contentsRect().setRect(0,0,0,0) ;
}
if (popupLabel) { //delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0,0,0,0);
}
*/
/*
////////////////////////////////////////////////////////
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
////////////////////////////////////////////////////////
*/
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
int mouse_pos = header->orientation() == Qt::Horizontal ? mouseEvent->x() : mouseEvent->y();
int logical_index = header->logicalIndex(header->visualIndexAt(mouse_pos));
if (logical_index >= 0)
{ // if mouse is over an item
showPopup(logical_index, watched);
}
else
{
popup->hide();
}
}
else if (event->type() == QEvent::Leave) {
popup->hide();
}
}
else if (popup == watched)
{
if (event->type() == QEvent::Leave)
{
popup->hide();
}
}
return QTableView::eventFilter(watched, event);
}
private:
void showPopup(const int& logical_index, QObject* watched) const
{
if (logical_index >= 0)
{
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
QFont font("times", 12);
QFontMetrics fm(font);
int pixelsWide = fm.width("xxxxxx");
int pixelsHigh = fm.height();
QRect rect; // line edit rect in header's viewport's coordinates
if (header->orientation() == Qt::Horizontal) {
//rect.setLeft(header->sectionPosition(logical_index));
rect.setLeft(header->sectionViewportPosition(logical_index));
rect.setWidth(header->sectionSize(logical_index));
rect.setTop(0);
rect.setHeight(header->height());
}
else {
//rect.setTop((header->sectionPosition(logical_index)));
rect.setTop((header->sectionViewportPosition(logical_index)));
//QPoint point(0,header->sectionPosition(logical_index));
//rect.setTop((mapToGlobal(point)).y() );
//rect.topLeft())
int cy = header->sectionSize(logical_index);
rect.setHeight(header->sectionSize(logical_index));
rect.setLeft(0);
rect.setWidth(header->width());
//rect.setCoords(rect.left(),rect.top(),rect.left()+rect.width(),rect.top()+rect.height());
}
//rect.adjust(1, 1, 1, 1);
//popupLabel->move(rect.bottomLeft());
//popupLabel->move(rect.topLeft());
popupLabel->move(QPoint(0,0));
popupLabel->resize(rect.size());
//popupLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
//popupLabel->show();
//popupLabel->setFixedHeight(rect.height());
//popupLabel->setFixedWidth(rect.width());
//popupLabel->setFrame(false);
//get current item text
QString text = header->model()->
headerData(logical_index, header->orientation()).toString();
//int z=text.size();
popupLabel->setText(text);
qDebug() << rect << text;
//popupLabel->setFocus();
///////////////////////////////////////////////////////////////////////////
//editor_index = logical_index; //save for future use
popupLabel->installEventFilter(this->parent()); //catch focus out event
//if user presses Enter it should close editor
//connect(header_editor, SIGNAL(returnPressed()),
// ui->tableWidget, SLOT(setFocus()));
//popupLabel->setGeometry(rect);
//popupLabel->adjustSize();
//popupLabel->setFixedSize(100, 100);
popupLabel->show();
////////////////////////////////////////////////////////////////////////////
//popup->move(viewport()->mapToGlobal(rect.bottomLeft()));
popup->move(viewport()->mapToGlobal(rect.topLeft()));
//popup->move(rect.topLeft());
popup->resize(rect.size());
//popup->move(rect.bottomLeft());
//popup->move(rect.topLeft());
//popup->setFixedSize(100, popup->heightForWidth(100));
//popup->setFixedSize(100, 100);
//popupLabel->setText(text);
//popupLabel->show();
// popupLabel->setText(logical_index.data(Qt::DisplayRole).toString());
//popup->adjustSize();
//popup->setGeometry(rect);
popup->show();
//sleep 10 ms;
return;// true; // filter out event
}
else {
popup->hide();
//delete previous popup just in case
popup->contentsRect().setRect(0, 0, 0, 0);
//popup = nullptr;
//delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0, 0, 0, 0);
//popupLabel = nullptr;
return; //false;
}
}
};
#include"main.moc"
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
TableView view;
view.setModel(new TableModel);
view.show();
return a.exec();
}
Colleagues warned me that I was wrong to ask. That's why I completely redrafted my pervious question.
a) where best to create a new line
b) how to reliably select a specific line between 20 others
c) how best to move starting point of a line
d) how best to move end point of a line
e) how best to move line
f) how to delete a line
Everything is described in the documentation and examples, but each example chooses a different place for the actions. Someone uses the view, some scene and the other does most of the things in line. Someone uses bouding rect, another not, someone uses editing mode in view another not etc...
myscene.cpp
#include "myscene.h"
#include "myview.h"
#include "mymovingpoint.h"
#include <qgraphicsitem.h>
#include <qgraphicsview.h>
#include <qobject.h>
#include <qgraphicsview.h>
#include <qpoint.h>
#include <qmath.h>
/*
*
*
*/
myScene::myScene(QObject *parent)
: QGraphicsScene(parent)
{
myMode = myMode::InsertItem;
}
/*
*
*
*/
void myScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
# remove item added by clickevent, if doubleclicked
if(line)
{
removeItem(line);
}
# if doubleclick and left button, select existing item
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
//QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
# itemAt() returns only the object exactly under the mouse
# function getNearObject return object 3 points around
QGraphicsItem *item = getNearObject(mouseEvent);
if(item)
{
myMode = myMode::EditItem;
myLineItem *tempLine = dynamic_cast<myLineItem*>(item);
oldLine = line;
line = tempLine;
QColor myclr;
myclr.setRgb(255,0,0,255);
line->setPen(QPen(myclr, 2));
line->addMovingPoints();
}
else
{
myMode = myMode::InsertItem;
}
}
}
/*
*
*
*/
#return nearest objects
QGraphicsItem* myScene::getNearObject(QGraphicsSceneMouseEvent *mouseEvent)
{
int roundvalue = -3;
int roundx;
int roundy;
QPointF pointf = mouseEvent->scenePos();
QPointF pointtmp;
roundx = roundvalue;
roundy = roundvalue;
while(roundy <= roundvalue*-1)
{
while(roundx <= roundvalue*-1)
{
pointtmp.setX(pointf.x()-roundx);
pointtmp.setY(pointf.y()-roundy);
QGraphicsItem *item = itemAt(pointtmp, QTransform());
if(item)
{
return item;
}
roundx += 1;
}
roundx += roundvalue;
roundy += 1;
}
return NULL;
}
/*
*
*
*/
void myScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
if(myMode == myMode::InsertItem)
{
if(oldLine)
{
myLineColor.setRgb(213, 182, 10, 255);
oldLine->setPen(QPen(myLineColor, 2));
}
}
if(myMode == myMode::EditItem)
{
myLineItem *tempLine = dynamic_cast<myLineItem*>(item);
if(tempLine)
{
oldLine = line;
line = tempLine;
QColor myclr;
myclr.setRgb(255,0,0,255);
line->setPen(QPen(myclr, 2));
myMovingPoint myPoint(line);
}
}
else if(myMode == myMode::InsertItem)
{
oldLine = line;
if(mouseEvent->modifiers() & Qt::ControlModifier)
{
line = new myLineItem(QLineF(toNearest5(mouseEvent->scenePos()), toNearest5(mouseEvent->scenePos())));
}
else
{
line = new myLineItem(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos()));
}
myLineColor.setRgb(213, 182, 10, 255);
line->setPen(QPen(myLineColor, 2));
}
else
{
QGraphicsScene::mousePressEvent(mouseEvent);
}
}
}
/*
*
*
*/
void myScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(!items().contains(line))
{
addItem(line);
}
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
QLineF newLine;
if(myMode == myMode::InsertItem)
{
if(mouseEvent->modifiers() & Qt::ControlModifier)
{
newLine = QLineF(toNearest5(line->line().p1()), toNearest5(mouseEvent->scenePos()));
}
else
{
newLine = QLineF(line->line().p1(), mouseEvent->scenePos());
}
myLineColor.setRgb(213, 182, 10, 255);
line->setLine(newLine);
update();
}
else if(myMode == myMode::EditItem)
{
QLineF newLine = QLineF(line->line().p1(), mouseEvent->scenePos());
line->setLine(newLine);
update();
}
}
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
/*
*
*
*/
void myScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
myMode = myMode::InsertItem;
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
/*
*
*
*/
void myScene::keyPressEvent(QKeyEvent * keyEvent)
{
if(keyEvent->key() == Qt::Key_Delete)
{
if(line)
{
removeItem(line);
}
}
}
/*
*
*
*/
QPointF myScene::toNearest5(QPointF i)
{
int shift = 10;
int r = i.x();
int s = i.y();
r = (i.x()+5.0)/10;
s = (i.y()+5.0)/10;
r = r*10;
s = s*10;
QPointF output = QPointF(r, s);
return (output);
}
mysceneview.cpp
#include "myview.h"
#include <qmath.h>
#include <qmatrix.h>
myView::myView(QGraphicsScene *scene, QWidget *parent)
: QGraphicsView(scene, parent)
{
}
void myView::keyPressEvent(QKeyEvent *event)
{
QGraphicsView::keyPressEvent(event);
}
void myView::keyReleaseEvent(QKeyEvent *event)
{
QGraphicsView::keyReleaseEvent(event);
}
void myView::enterEvent(QEvent *event)
{
viewport()->setCursor(Qt::ArrowCursor);
QGraphicsView::enterEvent(event);
}
void myView::mousePressEvent(QMouseEvent* event)
{
if (event->modifiers() & Qt::ControlModifier)
{
viewport()->setCursor(Qt::ArrowCursor);
_lastPos = QPoint(event->pos());
//_lastPos = QPoint(event->pos().x()-event->pos().x()%5, event->pos().y()-event->pos().y()%5);
}
else
{
viewport()->setCursor(Qt::ArrowCursor);
_lastPos = event->pos();
}
QGraphicsView::mousePressEvent(event);
}
void myView::mouseMoveEvent(QMouseEvent* event)
{
viewport()->setCursor(Qt::ArrowCursor);
if (event->buttons().testFlag(Qt::RightButton))
{
QScrollBar *hBar = horizontalScrollBar();
QScrollBar *vBar = verticalScrollBar();
QPoint delta = event->pos() - _lastPos;
_lastPos = event->pos();
hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
vBar->setValue(vBar->value() - delta.y());
//event->ignore();
}
QGraphicsView::mouseMoveEvent(event);
}
void myView::mouseReleaseEvent(QMouseEvent* event)
{
viewport()->setCursor(Qt::ArrowCursor);
QGraphicsView::mouseReleaseEvent(event);
}
#if QT_CONFIG(wheelevent)
void myView::wheelEvent(QWheelEvent *e)
{
if (e->delta() > 0)
{
x=x+0.1;
qreal scale = x;
QMatrix matrix;
matrix.scale(scale, scale);
setMatrix(matrix);
//zoomIn(6);
}
else
{
x=x-0.1;
qreal scale = x;
QMatrix matrix;
matrix.scale(scale, scale);
setMatrix(matrix);
//zoomOut(6);
}
e->accept();
}
#endif
you should define in your model, what's a scene, what's a view, and what is and an item.
logically this is how it goes:
scene: collection of drawable objects, cannot be viewed.
view: a window on the scene, if the object belonging to the scene is in this window, it is shown, if it is not then it's occluded
item: is a draw-able object, stores the position of its vertex in object coordinate.
when you draw you draw in the view, then the object stores the points in its object space coordinate, they will be constant during zooms, moves, rotations and pans, they change only when you select the vertex and move it in the view.
so mylineitem is only a container for an absolute coordinate.
scene is a container of all the mylineitem, just a list of objects
view is your controller of the way the system draws the items of the scene.
this model allows you to export your drawable items either individually or as a scene without worrying about remaping the points to the new coordinate space of the new view.
Do all your object editing in the view, because it defines the distance between the points in its coordinate system.
expl:
view, coordinate system: Cartesian, 10x10x10 cm
your draw a line p1-p2
p1(0,0,0) origin of the object space, p2(1,1,1) and it maps in the view to p1view(3,5,4) expl and p2view(13,15,14) in view coordinate. means each unit in object space is 10cm in view space.
export this line to another view where system: 20x20x20 cm then line keeps its relative size and changes its coordinate to preserve it.
I want to create a scrollable widget which contains a collection of child widgets managed by QBoxLayout, and it must be easy to add and remove widgets from this collection. But when I add child widgets to it, viewport widget did not expand its size (remain initial size), instead children are overlapping on themselves. I do not know what to do to fix this.
Here is the code:
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QtGui/QScrollArea>
#include <QResizeEvent>
#include <QDebug>
class CMainWidget : public QScrollArea
{
Q_OBJECT
public:
CMainWidget(QWidget *parent = 0);
~CMainWidget();
protected:
virtual void resizeEvent(QResizeEvent *pEvent);
virtual void keyPressEvent(QKeyEvent *pEvent);
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "rootitem.h"
CMainWidget::CMainWidget(QWidget *parent)
: QScrollArea(parent)
{
QWidget* pViewport = new QWidget();
QBoxLayout* pLayout = new QBoxLayout(QBoxLayout::TopToBottom);
pLayout->setSizeConstraint(QLayout::SetNoConstraint);
for (int iWidgetIndex = 0; iWidgetIndex < 20; iWidgetIndex++)
pLayout->addWidget(new CRootItem());
pViewport->setLayout(pLayout);
pViewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
setWidget(pViewport);
}
CMainWidget::~CMainWidget()
{
}
void CMainWidget::resizeEvent(QResizeEvent *pEvent)
{
QWidget* pViewport = widget();
int iHeight = pViewport->height();
int iWidth = pEvent->size().width();
pViewport->resize(iWidth, iHeight);
}
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
pLayout->addWidget(new CRootItem());
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Here is the image which shows child widgets overlapping after inserting a couple of new items:
EDIT
Just solved my problem using dirty approach: subtraction and adding item fixed height from viewport's. Is there more fashion way to handle this problem?
Code:
void CMainWidget::keyPressEvent(QKeyEvent *pEvent)
{
QWidget* pViewport = widget();
QBoxLayout* pLayout = (QBoxLayout*)pViewport->layout();
if (pEvent->key() == Qt::Key_Space)
{
QWidget* pItem = new CRootItem();
pLayout->addWidget(pItem);
QSize Size = pViewport->size();
Size.rheight() += pItem->height() + pLayout->spacing();
pViewport->resize(Size);
qDebug() << "adding...";
}
if (pEvent->key() == Qt::Key_C)
{
if (!pLayout->isEmpty())
{
QLayoutItem* pItem = pLayout->itemAt(0);
pLayout->removeItem(pItem);
QSize Size = pViewport->size();
Size.rheight() -= pItem->widget()->height() + pLayout->spacing();
pViewport->resize(Size);
delete pItem->widget();
delete pItem;
qDebug() << "removing...";
}
}
}
Comment out your resizeEvent. You rarely need to set the explicit size or position of a widget unless it's a top level window. Let the layout do the work.
I have 5 entities that can be added in graphics view on mouse events and button clicks. Each entity has been assigned the unique id. I need to add the operations cut, copy and paste on
these entities. How to proceed with that. I didn't get any example for cut, copy paste operations in graphics View in Qt. How can I do that?
I have different classes for all entities my class line, circle and ellipse are inherited from QGraphicsItem and line and text from QgraphicsLineItem and QgraphicsEllipse Item. Please tell me how can I work with them too.
line.cpp
#include "line.h"
Line::Line(int i, QPointF p1, QPointF p2)
{
// assigns id
id = i;
// set values of start point and end point of line
startP = p1;
endP = p2;
}
int Line::type() const
{
// Enable the use of qgraphicsitem_cast with line item.
return Type;
}
QRectF Line::boundingRect() const
{
qreal extra = 1.0;
// bounding rectangle for line
return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(),
line().p2().y() - line().p1().y()))
.normalized()
.adjusted(-extra, -extra, extra, extra);
}
void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
// draws/paints the path of line
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(startP, 2, 2);
painter->drawEllipse(endP, 2, 2);
// sets pen for line path
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawLine(startP, endP);
}
else
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawEllipse(startP, 2, 2);
painter->drawEllipse(endP, 2, 2);
painter->drawLine(startP, endP);
}
}
How can I work with this QGraphicsLineItem?
I think you should use custom graphics scene. Create QGraphicsScene subclass. Reimplement keyPressEvent:
if (e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier)
{
listCopiedItems = this->selectedItems();
}
if (e->key() == Qt::Key_V && e->modifiers() & Qt::ControlModifier)
{
for(int i=0; i< listCopiedItems.count(); i++)
{
//create new objects, set position and properties
}
}
You can get all needed properties from old objects such as color, size etc and set to new. For cut do same thing but delete old objects from scene and when all work will be done, delete this objects from memory. Also you can create shortcuts with QShortcut class.
Edit. I want to say that it is very complicate task, so I can't get you code for all cases, for all types. I give just example, but this example works(I tested it). I post here absolutely full code.
Header:
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QStack>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void keyPressEvent(QKeyEvent *event);
public slots:
private:
QList<QGraphicsItem *> lst; QPoint last;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
QVector<QGraphicsEllipseItem * > vec;
};
#endif // GRAPHICSSCENE_H
Cpp:
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsItem>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
//add something
addPixmap(QPixmap("G:/2/qt.jpg"));
vec.push_back(addEllipse(0,0,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+100,0+100,50,50,QPen(Qt::red),QBrush(Qt::blue)));
vec.push_back(addEllipse(0+150,0+150,50,50,QPen(Qt::red),QBrush(Qt::blue)));
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
//qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
last = mouseEvent->scenePos().toPoint();//remember this point, we need it for copying
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
item->setFlags(QGraphicsItem::ItemIsSelectable);
item->setSelected(!item->isSelected());
}
QGraphicsScene::mousePressEvent(mouseEvent);
}
void GraphicsScene::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_C && e->modifiers() & Qt::ControlModifier)
{
lst = this->selectedItems();
}
if (e->key() == Qt::Key_V && e->modifiers() & Qt::ControlModifier)
{
for(int i=0; i< lst.count(); i++)
{
//if it is ellipse
QGraphicsEllipseItem *ell = qgraphicsitem_cast<QGraphicsEllipseItem *>(lst.at(i));
if(ell)
{//then add ellipse to scene with ell properties and new position
addEllipse(QRect(last,ell->rect().size().toSize()),ell->pen(),ell->brush());
qDebug() << "good";
}
}
}
QGraphicsScene::keyPressEvent(e);
}
It is so complicate because you have no any clone() method, so you can't clone object with all all properties and move it to the new position. If you have your specific items that you should provide something specific too. That's why it is complex and I can't get code for all cases.
EDIT
You can't show scene, you should use this instead:
QGraphicsView vview;
GraphicsScene ss;
vview.setScene(&ss);
vview.show();
I developing some kind of builder for our project. I want to use both drag and drop support and context menu in my application. Currently I use drag and drop support but no luck with context menu.
Left side my gui is toolbox. I am draging and droping widgets to the right side(QGraphicsScene) and I also want to use context menu inside QGraphicsScene. I use context menu inside graphics scene before. But before I did not use drap & drop operations. I write proper code but it does not work. What is missing?(Is it related drag & drop). Below is my code files.
//#dragwidget.cpp - Toolbox widgets
#include "dragwidget.h"
DragWidget::DragWidget(void)
{
}
DragWidget::~DragWidget(void)
{
}
void DragWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
QMimeData *data = new QMimeData;
data->setProperty("type",property("type"));
QDrag *drag = new QDrag(this);
drag->setMimeData(data);
drag->start();
}
}
//#view.cpp - Wrapper class for QGraphicsView
#include "view.h"
View::View(Scene *scene,QWidget *parent)
:QGraphicsView(scene,parent)
{
}
View::~View()
{
}
//#scene.cpp - Wrapper class for QGraphicsScene which catch drop events
#include "scene.h"
Scene::Scene(QObject *parent)
:QGraphicsScene(parent)
{
}
Scene::~Scene(void)
{
}
void Scene::setSceneRect(qreal x, qreal y, qreal w, qreal h)
{
QGraphicsScene::setSceneRect(x,y,w,h);
}
void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *e)
{
if(e->mimeData()->hasFormat("text/plain"));
e->acceptProposedAction();
}
void Scene::dragMoveEvent(QGraphicsSceneDragDropEvent *e)
{
}
void Scene::dropEvent(QGraphicsSceneDragDropEvent *e)
{
e->acceptProposedAction();
int itemType = e->mimeData()->property("type").toInt();
switch(itemType)
{
case BlockSegment:
drawStandartBlockSegment(e->scenePos());
break;
case Switch:
drawStandartSwitch(e->scenePos());
break;
case Signal:
drawStandartSignal(e->scenePos());
break;
}
}
void Scene::drawStandartBlockSegment(QPointF p)
{
BlockSegmentItem *blockSegment = new BlockSegmentItem(p.x(),p.y(),p.x()+100,p.y());
addItem(blockSegment);
}
void Scene::drawStandartSwitch(QPointF p)
{
SwitchItem *switchItem = new SwitchItem(":app/SS.svg");
switchItem->setPos(p);
addItem(switchItem);
}
void Scene::drawStandartSignal(QPointF p)
{
SignalItem *signalItem = new SignalItem(":app/sgs3lr.svg");
signalItem->setPos(p);
addItem(signalItem);
}
//#item.cpp - Base class for my custom graphics scene items
#include "item.h"
Item::Item(ItemType itemType, QGraphicsItem *parent)
:QGraphicsWidget(parent),m_type(itemType)
{
}
Item::~Item()
{
}
int Item::type()
{
return m_type;
}
QRectF Item::boundingRect() const
{
return QRectF(0,0,0,0);
}
QPainterPath Item::shape() const
{
QPainterPath path;
return path;
}
void Item::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(painter);
Q_UNUSED(option);
Q_UNUSED(widget);
}
void Item::deleteItem()
{
}
void Item::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
Q_UNUSED(event);
}
//#switchitem.cpp - my custom switch item.
#include "switchitem.h"
SwitchItem::SwitchItem(const QString& fileName,QGraphicsItem *parent /* = 0 */)
:Item(Switch,parent)
{
svg = new QGraphicsSvgItem(fileName,this);
createActions();
createContextMenu();
}
SwitchItem::~SwitchItem(void)
{
}
QRectF SwitchItem::boundingRect() const
{
return QRectF(pos().x(),pos().y(),svg->boundingRect().width(),
svg->boundingRect().height());
}
QPainterPath SwitchItem::shape() const
{
QPainterPath path;
path.addRect(boundingRect());
return path;
}
void SwitchItem::createActions()
{
deleteAct = new QAction("Sil",this);
deleteAct->setIcon(QIcon(":app/delete.png"));
connect(deleteAct,SIGNAL(triggered()),this,SLOT(deleteItem()));
}
void SwitchItem::createContextMenu()
{
contextMenu = new QMenu("SwitchContextMenu");
contextMenu->addAction(deleteAct);
}
void SwitchItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *e)
{
contextMenu->exec(e->screenPos());
}
//#app.cpp - Application class
#include "app.h"
#include "dragwidget.h"
App::App(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
Q_INIT_RESOURCE(app);
ui.setupUi(this);
canvasScene = new Scene(this);
canvasScene->setSceneRect(0,0,5000,5000);
canvasView = new View(canvasScene);
canvasView->setBackgroundBrush(Qt::black);
canvasView->ensureVisible(0,0,canvasView->geometry().width(),canvasView->geometry().height());
QHBoxLayout *layout = new QHBoxLayout;
toolBox = new QFrame(this);
layout->addWidget(toolBox,1);
layout->addWidget(canvasView,7);
ui.centralWidget->setLayout(layout);
createToolBox();
}
App::~App()
{
}
void App::createToolBox()
{
QVBoxLayout *layout = new QVBoxLayout;
QLabel *blockSegmentLabel = new QLabel("Block Segment");
DragWidget *blockSegmentWidget = new DragWidget;
blockSegmentWidget->setProperty("type",BlockSegment);
blockSegmentWidget->setPixmap(QPixmap(":app/blocksegment.png"));
QLabel *switchLabel = new QLabel("Switch");
DragWidget *switchWidget = new DragWidget;
switchWidget->setProperty("type",Switch);
switchWidget->setPixmap(QPixmap(":app/SS.png"));
QLabel *signalLabel = new QLabel("Signal");
DragWidget *signalWidget = new DragWidget;
signalWidget->setProperty("type",Signal);
signalWidget->setPixmap(QPixmap(":app/sgs3lr.png"));
layout->addWidget(blockSegmentLabel);
layout->addWidget(blockSegmentWidget);
layout->addWidget(switchLabel);
layout->addWidget(switchWidget);
layout->addWidget(signalLabel);
layout->addWidget(signalWidget);
toolBox->setLayout(layout);
}
void DragWidget::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
QMimeData *data = new QMimeData;
data->setProperty("type",property("type"));
QDrag *drag = new QDrag(this);
drag->setMimeData(data);
drag->start();
}
}
Problem is here. You've "eaten" right button :) Try fall back into base implementation of mousePressEvent