how to use QT graphic view for drawing - qt

im trying to use the graphic veiw of QT to draw lines
it's possible to draw a multiple objects in the scene but is it possible to do a (real time lines ) drawing inside the Qt scene , and how ?
a sample code would be highly appreciated
thanks in advance

I'm creating a kind of "Framework" to do this. There are 2 approaches:
Handle mouse messages, create a QGraphicsLineItem object, add to Scene and modify it during the creation process.
Derive QGraphicsScene, create a QGraphicsLineItem but NOT added to scene, draw it when drawForeground, added it to scene after finished the creation.
Because QGraphicsScene will index objects in a BSP tree by default, and it will impact the performance when changing items frequently, you can get higher performance when using the 2nd approach during creation, but more code work.

1) Create GraphicsView and Scene
m_graphScen = new QGraphicsScene;
m_graphScen->setSceneRect(0,0,790,290);
m_graphView = new QGraphicsView;
m_graphView->setFixedSize(800, 300);
m_graphView->setScene(m_graphScen);
2) Create a slot which is doing the following by handling the mouse events:
m_graphScen->addLine(0, 250, 700, 250, QPen(QBrush(Qt::black),1));
m_graphView->show();
Also if you need to write or draw text see here.

Related

QAbstractItemModel in QGraphicsView

I have to create items based on the file system in the directory. it is mandatory that i have to use QGraphicsView and not (QTreeView/QListView) so how i can manage to hold a QModel for the graphicsScene. can any one help me suggest or refer an example of how i can load QFileModel with the QGraphicsScene.
There is only one easy way to do this, add view with model to the scene. Yes, it is still QTreeView/QListView, but you get all advantages of QGraphicsView and QGraphicsScene, such as rotation, interaction etc.
//fill the model and set model to view
ui->tableView->setParent(0);
QGraphicsProxyWidget * proxyView = ui->graphicsView->scene()->addWidget(ui->tableView);
proxyView->setRotation(45);
Result:
You don't need QFileModel for it. Use QDir::entryInfoList to get list of files and QFileSystemWatcher to track modification of file system.

Smoothly Updating QGraphicsscene

Hi I have managed to add a number of qgraphicsitems to a qgraphicsscene using the code below
def generate_graph_and_update_scene(self):
try:
local_params=locals() #for error log get local paramters
this_function_name=sys._getframe().f_code.co_name #for error log get function name
self.vertex_dict.clear()
self.clear() #clear graphicsscene
self.graph_pos.clear() #clear graph position holder object
#function that generates the node data
root_nodes=my_database_query.get_nodes_information()
for node in root_nodes:
# add nodes to nx.graph object
self.nx_graph.add_node(node['column1'])
# create networkx graph
self.graph_pos = nx.spring_layout(self.nx_graph, iterations=25,scale=10)
for node in self.nx_graph.nodes(): # Add nodes to qgraphicsscene
v=default_nodeobject.my_ellipse(node,self.graph_pos)
self.addItem(v) # Add ellipse to qgraphics scene
for edge in self.nx_graph.edges():
self.addItem(defaultedgeview.edgeview(edge[0], edge[1],self.graph_pos))#add edges to qgraphicscene
except:
#Exception handler
message=str(sys.exc_info())
message=message + str(local_params)+" "+ str(this_function_name)
print message
This allows me to add say 600 'nodes' to my qgraphics scene, however when I clear the scene and add another say 1500 nodes, adding the items blocks the UI and my whole application freezes for a few seconds.
Also whenever I am doing things like looping through the graphicsitems say looking for the nodes that have a certain property, again the main thread freezes while I am looping,
Could anyone suggest a good method of keeping the UI responsive while things are being done to the grpahicsscene/items in the scene.
Ideally would like to have smooth, non-blocking updates to the scene, even when I have a few thousand items showing.
The problem here is the management of each node as a graphics item. Adding and removing to a scene, as well as rendering each item is going to take time. With this many items, I suggest designing it differently.
Consider the node graph as a single, custom graphics item which stores a group of nodes and manages them as a single unit, rather than 600+ separate items.
Designed this way, you only add one item to the scene (the node graph) which allows rapid addition and removal of nodes and you will also see a performance improvement in rendering the scene, as all nodes are drawn in one call to paint().
Of-course, if you need to move nodes around by clicking and dragging them, you'll have to add additional code to handle detecting which node is being selected in the item and move it yourself.
However, this is the optimal way to handle such a large number of items in a scene.

installing event filters in QT

I am trying to avoid wasting time doing something if my lack of knowledge of QT could avoid me even to try.
Supposed a have a class X not derived from QGraphicsItems with fields containing among others several QGraphicsItems. Can I define event filters in X and install them on the QGraphicsItems in order to let X receive the event before the QGraphicsItems themselves?
Thanks.
If your design allows, rather than objects inheriting from QGraphicsItem, inherit from QGraphicsObject, which will allow you to use the standard QObject::installEventFilter.
Otherwise, you would need to have class 'X' inherit from QGraphicsItem.
Then, you can install an event filter from one GraphicsItem to another through the QGraphicsScene. This is detailed in the Qt Documentation here.
To filter another item's events, install this item as an event filter for the other item.
Example:
QGraphicsScene scene;
QGraphicsEllipseItem *ellipse = scene.addEllipse(QRectF(-10, -10, 20, 20));
QGraphicsLineItem *line = scene.addLine(QLineF(-10, -10, 20, 20));
line->installSceneEventFilter(ellipse);
// line's events are filtered by ellipse's sceneEventFilter() function.
ellipse->installSceneEventFilter(line);
// ellipse's events are filtered by line's sceneEventFilter() function.

Best way to create a long line (or cross line) cursor in Qt GraphicsView

The easy way to create the long cross line cursor (as long as viewport) is create a cross line graphicsItem, when mouse moved, set the item's pos property.
But this way will be very slow when the scene is complex, because it should update the whole viewport to update the cursor's pos.
The another easy way is setCursor(QCursor(..)),use a QPixmap to define the long cross line, this way will very fast , but the cursor will exceed the viewport rect.
Is there another way to show a long cross line cursor fastly?
Thanks very much!
If I understand correctly, you want to draw an horizontal line and a vertical line, crossing at the cursor position, and being as large as the viewport is.
A poosible solution would be to reimplement QGraphicsScene::drawForeground() to draw the two lines with the painter.
The problem is that the scene doesn't know about the mouse position. This means the view will have to track it and inform the scene when the mouse position has changed.
To do that, you'll have to create your own GraphicsScene (inheriting QGraphicsScene) and your own GraphicsView (inheriting QGraphicsView).
On your GraphicsView constructor, you'll have to start mouse tracking. This will make your you receive a mouseMoveEvent each time the mouse moves inside the view :
GraphicsViewTrack::GraphicsViewTrack(QWidget* parent) : QGraphicsView(parent) {
setMouseTracking(true);
}
void GraphicsViewTrack::mouseMoveEvent(QMouseEvent* pEvent) {
QPointF MousePos = this->mapToScene(pEvent->pos());
emit mousePosChanged(MousePos.toPoint());
}
As you can see in the code snippet above, the view is emitting a signal (mousePosChanged) to which the scene will be connected. This signal contains the mouse position, converted to the scene's coordinates.
Now, on the scene side, you have to add a slot which will be called when the mouse position changed, store the new mouse position in a member variable and reimplement QGraphicsScene::drawForeground() :
void GraphicsSceneCross::drawForeground(QPainter* painter, const QRectF& rect) {
QRectF SceneRect = this->sceneRect();
painter->setPen(QPen(Qt::black, 1));
painter->drawLine(SceneRect.left(), m_MousePos.y(), SceneRect.right(), m_MousePos.y());
painter->drawLine(m_MousePos.x(), SceneRect.top(), m_MousePos.x(), SceneRect.bottom());
}
void GraphicsSceneCross::onMouseChanged(QPoint NewMousePos) {
m_MousePos = NewMousePos; // Store the mouse position in a member variable
invalidate(); // Tells the scene it should be redrawn
}
The last thing to do is connect the GraphicsView's signal to the GraphicsScene slot.
I'll let you check if this solution is acceptable performance wise.
Based on Jerome answer and using python I created this code in my QGraphicsScene subclass:
def drawForeground(self, painter, rect):
if self.guidesEnabled:
painter.setClipRect(rect)
painter.setPen(self.guidePen)
painter.drawLine(self.coords.x(), rect.top(), self.coords.x(), rect.bottom())
painter.drawLine(rect.left(), self.coords.y(), rect.right(), self.coords.y())
def mouseMoveEvent(self, event):
self.coords = event.scenePos()
self.invalidate()
It should be straighforward for you to write its appropiate C++ code. Note that I take advantage of the rect argument passed by Qt Api framework and I clip the painter to that
area, since it's the visible area to be drawn.
I also cache the pen object, since I realized in other experiments that creating objects while painting will penalty performace and doing it so you also give the user the chance to set a custom pen in your program options.
I have found a way to do this!
I develops under Windows system, so can use lower GDI api jumping out of Qt's painting system.
The detail is get the HDC of the QGraphicsView's viewPort. Then in the QMouseEvent of QGraphicsView use "MoveToEx" and "LineTo" drawing two lines on the viewport,then I should do is erase the "old" cursor, It's easy to do this using "setROP2(HDC dc,R2_NOT)",then draw the old Cursor stored again.
This method doesn't enter the QPainter system, so the GraphicsItems under the cursor will not be repaint.
To solve the filker problem when the mouse moves fast, I don't use "double buffer". I used QTimer to update the cursor at CPU's idle. The detail is in QMouseEvent ,don't update the cursor at time ,but store the position to a list, When CPU idle, draw the cursor at the list of positions
I wish this will help others who meets the same problem with me.
Thanks Jérôme, who gave me useful tip of QGraphicsScene.

Sharing same model in two QGraphicScene instances in Qt

I have an application that displays an editor for a diagram using QGraphicsScene object. I would like to create a read only version of the same dialog but have ability for user to see both at the same time.
SimScene* pScene1 = new SimScene(model); // adds model to scene
SimScene* pScene2 = new SimScene(model); // adds model to scene
QGraphicsView* pView1 = new QGraphicsView();
pView1->setScene(pScene2);
QGraphicsView* pView1 = new QGraphicsView();
pView2->setScene(pScene2);
When I create 2 instances of QGraphicsScene and use addItem on the second one it removes all the items from the first one. Does Qt support any sort of sharing of model between scenes? Is my only choice to have same scene and try to customize the view? Later one doesn't seem to work because object selection information is within the graphics items being shared so if I disable flags on them they become read only in both views. Any advice is appreciated. Thanks.
If you just want an interactive and a read-only view on your model you can use a single QGraphicsScene and 2 QGraphicsViews. You just have to call QGraphicsView::setInteractive(false) on one of them. That way you don't have to change any item flags.
I think that you're storing QSceneItems in model classes. Because of that pScene1 and pScene2 are trying to share not only the model itself, but also the scene items. This won't work because any scene item can be placed only on one scene at any given moment.
How to fix it? Make model not aware of any GUI. Let it issue changed() notifications whenever something interesting happens.
Then let each SimScene wrap model into whatever QSceneItems it wants, and process changed() notifications.
Example:
Model:
Graph,
Edge,
Vertex
GUI
SimScene,
QEdge,
QVertex,
QSimInfo,
Qbackground, and so on ...
Also, you add pScene2 twice:
...
pView1->setScene(pScene2);
...
pView2->setScene(pScene2);
And allocate memory for the same pointer twice:
QGraphicsView* pView1 = new QGraphicsView();
...
QGraphicsView* pView1 = new QGraphicsView();

Resources