I am implementing copy operation in graphicsView for different entities such as point, ellipse, circle etc. in qt but getting an error
error: invalid conversion from 'QGraphicsItem*' to 'QGraphicsItemGroup*
My code to copy operation in cadgraphicsview.cpp is:
void CadGraphicsScene::copy()
{
selectedItems.clear();
foreach(QGraphicsItemGroup *item, itemList){
QString str;
if (item->isSelected())
{
if(item->type() == Point::Type)
{
Point *pointItem = (Point *) item;
str = QString("Point %1 %2 ")
.arg(pointItem->scenePos().x())
.arg(pointItem->scenePos().y());
}
}
}
}
In cadgraphicsscene.h
#ifndef CADGRAPHICSSCENE_H
#define CADGRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QXmlStreamWriter>
#include <QUndoStack>
#include <QLabel>
#include <QFrame>
#include "cadcommandadd.h"
#include "cadcommanddelete.h"
#include "cadcommandmove.h"
class CadGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit CadGraphicsScene(QObject *parent, QUndoStack *);
enum Mode { NoMode, PointMode, LineMode, CircleMode, EllipseMode, TextMode };
QFont font() const
{
return myFont;
}
QColor textColor() const
{
return myTextColor;
}
void setTextColor(const QColor &color);
void setFont(const QFont &font);
void deleteItems();
void writeStream(QXmlStreamWriter *stream);
void readStream(QXmlStreamReader *stream);
public slots:
void setMode(Mode mode);
void selectItems();
void editorLostFocus(mText *item);
void cut();
void copy();
void paste();
void del();
protected:
void contentsContextMenuEvent(QContextMenuEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);
void setFlags();
void areItemsSelectable(bool);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
void mousePressEvent(QMouseEvent *event);
signals:
void textInserted(QGraphicsTextItem *item);
void itemSelected(QGraphicsItem *item);
private:
Mode entityMode;
QUndoStack *mUndoStack;
bool mFirstClick;
bool mSecondClick;
bool mThirdClick;
bool mPaintFlag;
QVector<QPointF> stuff;
QPointF start_p, mid_p, end_p, move_p, check_p;
QPen paintpen, linePen;
QList<QGraphicsItem *> itemList;
Point *pointItem;
Line *lineItem;
Circle *circleItem;
Ellipse *ellipseItem;
mText *textItem;
QColor myTextColor;
QFont myFont;
typedef QPair<QGraphicsItem *, QPointF> itemPos;
QList<itemPos> selectedItems;
private:
QAction *cutAct;
QAction *copyAct;
QAction *pasteAct;
};
#endif // CADGRAPHICSSCENE_H
Help me out to sought this :)
You define itemList as:
QList<QGraphicsItem *> itemList;
You are then trying to iterate it with:
foreach(QGraphicsItemGroup *item, itemList){
// ...
}
From the documentation you can see that QGraphicsItemGroup inherits from QGraphicsItem. So every QGraphicsItemGroup "is a" QGraphicsItem. But not every QGraphicsItem "is a" QGraphicsItemGroup.
If you want to iterate that itemList then item will have to be a QGraphicsItem, or itemList will have to be a QGraphicsItemGroup. It seems that since you are testing for specific classes then the former is probably what you want.
Related
I have a QGraphicsView in a QDockWidget, in which I'm displaying a PNG image. If I create a QPixMapItem in the dockwidget's constructor, and call a dockwidget's member function (a public slot) from within the constructor to display the image, it works fine.
However, if I invoke the slot from my MainWindow (a class called 'IView') via a signal, then the image does not show. What am I doing wrong?
Dockwidget construcor:
IvConfDockWidget::IvConfDockWidget(IView *parent) :
QDockWidget(parent),
ui(new Ui::IvConfDockWidget)
{
ui->setupUi(this);
MyGraphicsView *zoomGraphicsView = new MyGraphicsView();
MyGraphicsScene *zoomScene = new MyGraphicsScene();
ui->zoomLayout->addWidget(zoomGraphicsView);
QPixmap zoomPixmap = QPixmap("/home/mischa/logo.png");
QGraphicsPixmapItem *zoomPixmapItem = new QGraphicsPixmapItem(zoomPixmap);
updateZoomWindowReceived(zoomPixmapItem); // does not work if called from MainWindow
}
Here is the public slot:
void IvConfDockWidget::updateZoomWindowReceived(QGraphicsPixmapItem *zoomPixmapItem)
{
zoomGraphicsView->resetMatrix();
zoomScene->clear();
zoomScene->addItem(zoomPixmapItem);
zoomGraphicsView->setScene(zoomScene);
zoomGraphicsView->show();
}
If I comment out updateZoomWindowReceived() in the constructor, and call it via a signal from my MainWindow, then the graphics does not show. Here is the code in my MainWindow:
IvConfDockWidget *icdw = new IvConfDockWidget;
connect(this, &IView::updateZoomWindow, icdw, &IvConfDockWidget::updateZoomWindowReceived);
QPixmap zoomPixmap = QPixmap("/home/mischa/logo.png");
icdw->zoomGraphicsView->resetMatrix();
QGraphicsPixmapItem *newItem = new QGraphicsPixmapItem(zoomPixmap);
emit updateZoomWindow(newItem);
And for completeness, my derived versions of QGraphicsView and QGraphicsScene:
#define MYGRAPHICSVIEW_H
#include <QGraphicsView>
#include <QDebug>
#include <QMouseEvent>
#include <QScrollBar>
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT
private:
QPointF rightDragStartPos;
QPointF rightDragCurrentPos;
QPointF rightDragEndPos;
QPointF leftDragStartPos;
QPointF leftDragCurrentPos;
QPointF leftDragEndPos; // unused
QPointF middleDragStartPos;
QPointF middleDragCurrentPos;
QPointF middleDragEndPos;
bool rightButtonPressed = false;
bool leftButtonPressed = false;
bool middleButtonPressed = false;
QPoint previousMousePoint;
bool _pan = false;
int _panStartX = 0;
int _panStartY = 0;
public:
explicit MyGraphicsView();
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
QScrollBar *sxbar = nullptr;
QScrollBar *sybar = nullptr;
QString middleMouseMode = "DragMode";
int x = 0;
int y = 0;
signals:
void currentMousePos(QPointF);
void rightDragTravelled(QPointF);
void middleDragTravelled(QPointF pointStart, QPointF pointEnd);
void middleWCSTravelled(QPointF pointStart, QPointF pointEnd);
void middleWCSreleased();
void leftDragTravelled(QPointF pointStart, QPointF pointEnd);
void rightPress();
void leftPress(QPointF pointStart);
void middlePress(QPointF point);
void leftButtonReleased();
void rightButtonReleased();
void middleButtonReleased();
void middlePressResetCRPIX();
public slots:
void updateMiddleMouseMode(QString mode);
};
#endif // MYGRAPHICSVIEW_H
and
#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H
#include <QObject>
#include <QGraphicsScene>
#include <QKeyEvent>
class MyGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
MyGraphicsScene();
signals:
void itemDeleted();
protected:
void keyReleaseEvent(QKeyEvent * keyEvent);
};
#endif // MYGRAPHICSSCENE_H
I figured it out. My MainWindow uses a hybrid drive/RAM data model which can be in different states, and it happened that in one of the states the signal was never emitted. Once I fixed that, the behavior is as desired.
Sorry for the noise.
I derive custom item from QGraphicsItem and added some properties for QPropertyAnimation
Q_PROPERTY(qreal x READ x WRITE setX);
Q_PROPERTY(qreal y READ y WRITE setY);
Q_PROPERTY(QPointF pos READ pos WRITE setPos);
this is my header file
#ifndef MYSQUARE_H
#define MYSQUARE_H
#include<QObject>
#include<QPainter>
#include<QGraphicsItem>
#include<QGraphicsItemGroup>
#include<QGraphicsPixmapItem>
#include<QPainterPath>
#include<QPointF>
#include<QSizeF>
#include<QImage>
#include<QLabel>
#include<QCoreApplication>
#include <QStringBuilder>
#include <QString>
#include <QTranslator>
#include <QList>
#include <QVariant>
#include <QDebug>
#include <QtSvg>
#include <QSvgGenerator>
#include "arrow.h"
#include "mysvgitem.h"
class QGraphicsSvgItem;
class QPolygonF;
class Arrow;
class MySquare : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity);
Q_PROPERTY(QPointF pos READ pos WRITE setPos);
Q_PROPERTY(qreal x READ x WRITE setX);
Q_PROPERTY(qreal y READ y WRITE setY);
enum resize_direction
{
rd_none,
rd_left,
rd_top,
rd_right,
rd_bottom
1,1 Top
};
public:
MySquare();
QRectF boundingRect() const;
QPolygonF polygon() const { return myPolygon; }
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
bool Pressed;
void addArrow(Arrow *arrow);
QPointF returnCenter();
void setMyPolygon();
void setItemSize(qreal height,qreal width);
void setImage(const QString& str);
void setImage(const QString& str ,QSize);
void setImage(const QString& str ,QRectF rec);
void setSvgImage(const QString& str);
void setSvgImage(const QString& str ,QSizeF);
void setSvgImage(const QString& str ,QRectF rec);
void setText(const QString& str ,QRectF rec);
~MySquare();
signals:
void click();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
QVariant itemChange(GraphicsItemChange change, const QVariant &value) ;
private:
QPolygonF myPolygon;
QList<Arrow *> arrows;
QRectF myItemRect;
QString MyText;
QRectF MyTextRect;
QGraphicsTextItem *itemXY;
QString *posXY;
resize_direction resize_direction_;
};
#endif // MYSQUARE_H
Especially I want to create property to use it in animation, how is it possible to do.
I tried to use size property like
Q_PROPERTY(QSize size READ size WRITE setSize);
but I receive error
moc_mysquare.cpp: In member function ‘virtual int MySquare::qt_metacall(QMetaObject::Call, int, void**)’:
moc_mysquare.cpp:138:55: error: ‘size’ was not declared in this scope
case 1: *reinterpret_cast< QSize*>(_v) = size(); break;
^
moc_mysquare.cpp:149:55: error: ‘setSize’ was not declared in this scope
case 1: setSize(*reinterpret_cast< QSize*>(_v)); break;
Your property states that "there are functions named size() and setSize()". But you don't have them in your class.
I would like to emit a signal when hovering a QLabel with the mouse in QT.
Is it possible ?
If not, how can I do it ?
Thanks.
You can do hover handling without subclassing of QLabel. Try next code sample for it:
void LabelHoverHandler::attach(QLabel *label)
{
label->setAttribute(Qt::WA_Hover, true);
label->installEventFilter(this);
}
bool LabelHoverHandler::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::HoverEnter)
{
emit signalMouseHover(dynamic_cast<QLabel*>(obj) );
qDebug() << "HoverEnter";
}
return false;
}
LabelHoverHandler header file:
class LabelHoverHandler : public QObject
{
Q_OBJECT
public:
LabelHoverHandler(QObject *parent);
virtual ~LabelHoverHandler() {};
void attach(QLabel *label);
protected:
bool eventFilter(QObject *obj, QEvent *event);
signals:
void signalMouseHover(QLabel*);
};
Simply create an instance of LabelHoverHandler and call method attach() with a needed label as a parameter.
A possible implementation:
// mylabel.h
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel();
~MyLabel();
protected:
virtual void enterEvent(QEvent *ev);
virtual void leaveEvent(QEvent *ev);
private:
bool hover;
};
#endif // MYLABEL_H
and:
// mylabel.cpp
#include "mylabel.h"
#include <QEvent>
MyLabel::MyLabel()
{
hover = false;
setAttribute(Qt::WA_Hover, true);
}
MyLabel::~MyLabel()
{
}
void MyLabel::enterEvent(QEvent *ev)
{
if (!hover){
hover = true;
setText("Send signal here");
}
QLabel::enterEvent(ev);
}
void MyLabel::leaveEvent(QEvent *ev)
{
if (hover){
hover = false;
setText("Mouse leave area");
}
QLabel::leaveEvent(ev);
}
I have read this How to distinguish between mouseReleaseEvent and mousedoubleClickEvent on QGrapnhicsScene and this Distinguish between single and double click events in Qt in order to solve my problem. But they do not work. I created a class QLabel:
mouselabel.h
#ifndef MOUSELABEL_H
#define MOUSELABEL_H
#include <QLabel>
#include <QMouseEvent>
#include <QEvent>
#include <QDebug>
class MouseLabel : public QLabel
{
Q_OBJECT
public:
explicit MouseLabel(QWidget *parent = 0);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void leaveEvent(QMouseEvent *);
int x, y;
signals:
void Mouse_Pressed();
void Mouse_Position();
void Mouse_Left();
void Mouse_Release();
void Mouse_DoubleClick();
public slots:
};
#endif // MOUSELABEL_H
mouselabel.cpp
#include "mouselabel.h"
MouseLabel::MouseLabel(QWidget *parent) :
QLabel(parent)
{
}
void MouseLabel::mouseMoveEvent(QMouseEvent *event) {
this->x = event->x();
this->y = event->y();
emit Mouse_Position();
}
void MouseLabel::mousePressEvent(QMouseEvent *event)
{
emit Mouse_Pressed();
}
void MouseLabel::mouseReleaseEvent(QMouseEvent *event)
{
emit Mouse_Release();
}
void MouseLabel::mouseDoubleClickEvent(QMouseEvent *event)
{
emit Mouse_DoubleClick();
}
void MouseLabel::leaveEvent(QMouseEvent *)
{
emit Mouse_Left();
}
As described in the two links, in order to distinguish between a click and a double click, I created a timer and it starts when a mouserelease event happens and it stops when there is a double click mouse. But, this does not work. Can you help me to solve the problem?
The lightest possible solution is using a QBasicTimer, just have those 2 methods overloaded and the timer in your widget:
void mouseReleaseEvent(QMouseEvent *) {
if (timer.isActive()) {
timer.stop();
qDebug() << "double click";
}
else {
timer.start(300, this);
}
}
void timerEvent(QTimerEvent *) {
timer.stop();
qDebug() << "single click";
}
QBasicTimer timer;
The downside is a timer approach delays the single click, which is kind of annoying, but you could mask that, for example for selecting objects, using the mousePressEvent for immediate response.
I am having a problem where I emit a signal in a mouseMove event, but that signal does not get recieved by a slot on another object until i release the mouse.
Details:
I have a class called SelectableItem, which subclasses QGraphicsObject :
#pragma once
#if !defined PARTS_SELECTABLE_ITEM_H
#define PARTS_SELECTABLE_ITEM_H
#include <QGraphicsObject>
namespace Parts {
class SelectableItem : public QGraphicsObject {
Q_OBJECT
public:
SelectableItem();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) = 0;
QRectF boundingRect() const = 0;
void SetIsSelectedCustom(bool b);
bool IsSelectedCustom();
void SetIsSelectable(bool b);
bool IsSelectable();
void SetIsHovering(bool b);
bool IsHovering();
void Move(QPointF offset);
signals:
void moveItem(QGraphicsSceneMouseEvent *e);
void deselectAllItems();
void deselectItem(Parts::SelectableItem*);
void selectItem(Parts::SelectableItem*);
void stopSelectionDrag();
void stopMovingParts();
void deleteSelectedItems();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);
void mouseMoveEvent(QGraphicsSceneMouseEvent *e);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e);
void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
bool m_is_selected;
bool m_is_hovering;
bool m_is_selectable;
}; // class SelectableItem
} // namespace Parts
#endif // PARTS_SELECTABLE_ITEM_H
I have two other classes that subclass the SelectableItem class. They are Part and Terminal
Part:
#pragma once
#if !defined PARTS_PART_H
#define PARTS_PART_H
#include "PartLabel.h"
#include "PartDialog.h"
#include "Terminal.h"
#include "SelectableItem.h"
#include <QGraphicsObject>
#include <QGraphicsSvgItem>
#include <QSvgRenderer>
#include <map>
namespace Parts {
class Terminal;
class Part : public SelectableItem {
Q_OBJECT
public:
friend class PartLabel;
Part();
~Part();
enum PartLabelLocation {
North,
South,
East,
West
};
// Inherited Functions
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QRectF boundingRect() const;
void setPos(qreal x, qreal y);
// Part Functions
void SetData(std::map<std::string, std::string> data);
void SetTerminals(std::list<Terminal*> terms);
void AddTerminalsToScene(QGraphicsScene* s);
void SetPartGraphic(QGraphicsSvgItem *svg);
void SetDialog(PartDialog *d);
std::string GetName();
void SetName(const std::string &name);
void Move(QPointF offset);
PartLabel* GetLabel();
std::map<std::string, std::string>* GetData();
std::list<Terminal*> GetTerminalList();
QPointF GetCenterPointPos();
void SetLabelNorth();
void SetLabelSouth();
void SetLabelEast();
void SetLabelWest();
void ResetLabelPosition();
QGraphicsSvgItem* GetPartSvg();
signals:
void partRotated();
private:
// Inherited Functions
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e);
void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *e);
// Part Functions
void DrawTerminal(QPainter *painter, QPointF &point);
QRectF CalculateBoundingBox();
void Initialize();
void RotateCW();
void RotateCCW();
void OpenPropertiesDialog();
void SinglePartContextMenu(QGraphicsSceneContextMenuEvent *e);
void MultiplePartContextMenu(QGraphicsSceneContextMenuEvent *e);
// Part Members
QGraphicsSvgItem *m_part_graphic;
std::map<std::string, std::string> m_data;
std::list<Terminal*> m_terminals;
QRectF m_bounding_box;
PartLabel *m_label;
PartDialog *m_dialog;
bool m_is_initialized;
int m_label_position;
}; // class Part
}; // namespace Parts
#endif // PARTS_PART_H
Terminal:
#pragma once
#if !defined PARTS_TERMINAL_H
#define PARTS_TERMINAL_H
#define TERMINAL_RADIUS 15
#define CONNECTED_TERMINAL_RADIUS 7.5
#include "Part.h"
#include "SelectableItem.h"
#include <QGraphicsObject>
namespace Parts {
class Part;
class Connection;
class Terminal : public SelectableItem {
Q_OBJECT
public:
enum TerminalDirection {
North,
East,
South,
West
};
Terminal(const std::string &name, const QPointF &rel_coords, const std::string &direction);
// Inherited Functions
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QRectF boundingRect() const;
QPointF GetRelativeCoord();
QPointF GetConnectionCoord();
void SetPart(Part *p);
bool HasPart();
void Move();
void RotateCW();
void RotateCCW();
void SetIsConnected(bool b);
bool IsConnected();
void AddConnection(Connection *c);
void RemoveConnection(Connection *c);
std::list<Connection*> GetConnectionList();
signals:
void connectionBegun(Parts::Terminal *t);
void connectionCompleted(Parts::Terminal *t);
private:
// Inherited Functions
void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);
void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
//void contextMenuEvent(QGraphicsSceneContextMenuEvent *e);
void IncrementDirection();
void DecrementDirection();
QPointF m_rel_coords;
QPointF m_rotated_rel_coords;
bool m_is_connected;
int m_direction;
int m_rotation_angle;
QRectF m_bounding_rect;
Part *m_part;
std::string m_name;
std::list<Connection*> m_connections;
}; // class Terminal
} // namespace Parts
#endif // PARTS_TERMINAL_H
The SelectableItem class's mouseMove function is as follows:
void SelectableItem::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if(!m_is_selectable)
return;
if(m_is_selected)
emit moveItem(e);
else {
emit deselectAllItems();
emit selectItem(this);
}
}
The signal emit selectItem(this) is instantly recieved by another class SysGraphicsScene which handles item selection.
The problem I'm having is that the Part class emits this signal via its base class and the signal is recieved by the SysGraphicsScene class instantly. However, the Terminal class emits this signal via it's base class (SelectableItem, which is the same base class that Part has.) and the signal is not recieved instantly by SysGraphicsScene but only after mouseReleased.
I have no idea why two derived classes would work differently. Any ideas?
Ugh found the problem. Some of my Terminal objects were never getting connected to signals while som where. I knew it had to be something basic. :/