I want to draw a visualization of the stack at certain points while executing a program, so some kind of rectangles, filled with an address and value and in some color, depending on some events. I'm using C++ with Qt.
Right now, my Stackview class holds all StackItems and then uses the paintEvent to draw all of those into my Dialog-window. This window isn't scrollable, so if the stack is bigger, I can't see all stack items or scroll down!
My question is should I use a GraphicsView and a GraphicsScene and add my Rects there or should I do something with a QScrollArea, I'm not sure, what fits my needs best and how to implement it?
The end result should be, that my Debugger can step through code line by line and update the stack, depending on the line.
Related
I can't seem to find a guide on Qt's widget / layout system that explains how it all fits together. Only examples of what to do in typical use-cases.
Slightly more specifically:
When my widget (which has a QGridLayout of child widgets) is resized, I want to:
layout the children, so obtain their sizes,
do some stuff before everything gets repainted
have the children painted, presumably by calling update()
How do I slot number 2 between 1 and 3? If the answer is, "Your design is bad, don't do this", then this is my specific problem:
I have a PlotFrame class that shows Cartesian graphs. It houses a PlotPane (the main area of the graph) and a dynamic number of axes. The PlotPane and Axis classes are QWidgets laid out with a QGridLayout and I'm using explicit double buffering so I can display transient mouse-driven features on top of the base buffered image.
When the PlotFrame is resized, the number 2 in the list above is this:
Create the buffered image of the axes whose ticks and labels dynamically depend on the length of the axis on the screen, this also calculates positions of grid lines for display in the main PlotPane.
Get the grid line positions just calculated and pass them to the PlotPane.
Create the buffered image of the PlotPane.
Once this is done, I'm happy to have Qt do its asynchronous stuff, and to deal with any transient graphical features in each of the widgets' paintEvent() functions.
Thanks in advance. I'm want to get a better understanding of how this all works but don't really want to be digging in the source code if I can help it. I find that Qt's own docs lack depth / are confusing to navigate. There are some great guides I've found (eg. flylib.com) but they are well out of date, therefore misleading. I'm currently using Qt6.
EDIT: After further experimentation, it seems that the parent widget (PlotFrame) resizeEvent() gets called after its children have been laid out and before they are painted. Is this so? Can I rely on this?
I am new to C++,Qt and Visual Studio and this is my first post on Stack Overflow.
I apologize in advance if this is a repeated question, I tried searching for a similar question but couldn't find one. Let me know if this is a repeated question and I will delete it.
I am trying to create a line plot using QWidget::paintEvent(). The line plot I am drawing is actually a QPainterPath. I want to detect when the mouse hovers over my line plot and so I create a small rectangle where my mouse cursor is and detect when this rectangle intersects with my line plot using bool QPainterPath::intersects() function. The problem is that this function returns true even when my mouse is not exactly over my line plot. In the Image 1 (I am not allowed to embed images yet) my line plot is the thick black curve and the bool QPainterPath::intersects() returns true even when my cursor is over the yellow region. As per the Qt document this is because:
There is an intersection if any of the lines making up the rectangle crosses a part of the path or if any part of the rectangle overlaps with any area enclosed by the path.
There is no way to have a QPainterPath without any enclosed area as Qt only provides two types of fill for QPainterPath: Qt::OddEvenFill or Qt::WindingFill. (To be honest, I find this kind of annoying, since an open path is a series of line segments connected end-to-end, if someone wants to enclose an area they can easily connect the first and last point using either QPainterPath::lineTo() or QPainterPath::moveTo() functions)
Anyway, I decided to get smarter than Qt and drew two extra QPainterPath with pathUp being a few pixels above my line plot and pathDn being a few pixels below my line plot. Image 2 shows these 3 line plots, red one is pathUp, black one is real line plot and green one is pathDn. I thought I coould detect the intersection in the QWidget::mouseMoveEvent() by using the following code:
// cRect: Rectangle at mouse cursor position
if((pathUp.intersects(cRect) && (!pathDn.intersects(cRect))) || ((!pathUp.intersects(cRect)) && pathDn.intersects(cRect)))
{
qDebug() << "Intersects";
}
But this still produces wrong results because now the enclosed area is different, as you can see in Image 3 the green area is an enclosed area of pathDn and red area is the enclosed area of pathUp. The thick black curve is again the line plot that I want to detect my mouse hover on. This enclosed area is not affected by Qt::setFillRule of QPainterPath.
What's even more frustrating is that I tried this technique using QPolygonF instead of QPainterPath on QWidget and the results were exactly the same. I also tried QGraphicsView, there I used QGraphicsPathItem to create my line plot and then used QGraphicsScene::focusItemChanged() signal to detect when I click on my line plot. It again produced the same result of detecting the click when my cursor is over the enclosed area. I do not want to create a custom QGraphicsItem (unless I absolutely have to) just to reimplement it's hoverEnterEvent() and hoverLeaveEvent() method because of the limitations imposed on the boundingRect() of the QGraphicsItem as explained in Qt Docs:
QGraphicsScene expects all items boundingRect() and shape() to remain unchanged unless it is notified. If you want to change an item's geometry in any way, you must first call prepareGeometryChange() to allow QGraphicsScene to update its bookkeeping.
Since I making a plot in real-time the boundingRect() will change quite frequently (> 20 Hz), which will result in an extra computational burden on the software. Is there any way I can solve my problem without creating a custom QGraphicsItem?
P.S. I have been using Stack Overflow for many years whenever I got stuck. I just never made an account here because I never needed to post anything. You guys are the best and I am very happy to be a part of this community!
I am currently working on a simple CAD-like drawing program using Qt and openGL.
What I am doing is that I maintain a list of objects which is on the canvas. The paintGL() function is just loop through the list and render the objects one by one.
objects are fed to the list via slot drawObject(Object obj), in which there is an update() function to schedule an update event to update the scene.
Now, I want to do some rubberband drawing of lines:
After pick one endpoint of the line, whenever I move the cursor, a mouseMoveEvent() is triggered and it will generate an object for the line and emit a signal to drawObject(Object) slot. what the slot does is to erase the old line by doing xor drawing, and draw the new line in xor mode as well.
What I expect to happen is that every time the mouse is Moved, a new object is rendered to the scene. However, it is not. For example, if I move the mouse
fast, then before the update() function actually update the scene, multiple mouseMove events has been triggered and it seems that these events are never been handled, i.e., the correspondence objects never goes to screen. What the program actually does is that a lot of random artifacts is left on the screen after a fast rubberband dragging.
It seems that this is due to the fact that what update() function of QOpenGLWidget does is that it generate an event to inform the widget to redraw later for performance purpose.
During the course of me writing this question, I discovered the repaint() function which do an immediate update. However, the lagging is quite significant: when I move the mouse fast, the rubberband line is not following.
So, my question is, how to implement the rubberband drawing so that it could take advantage of the update() machanism to boost the performance while not having those glitches on the screen?
I have searching around on this but I could find a single article talking about this fast-moving mouse stuff.
Thank you in advance!
I am working on a window which displays a number of objects (a graph) (C++/Win32API). This is in GDI+ and I have written a test piece of code with Direct2d as I want to improve the performance when dragging objects in the window. The best approach I have found (to date) is the following (using a graph of 1000 nodes and 999 edges).
(bascially buffer static content to a bitmap buffer and only draw whats moving)
When dragging starts (e.g lbuttondown state), create a base rendertarget with the full graph excluding the node being dragged and the attached edges, call GetBitmap and store for later use. When I need to draw (due to mousemove event and lbuttondown state true), then clear the current hwndrendertarget (background wash white), then draw the edges that are connected to the node being moved into the hwndrendertarget, then copy the save bitmap to the hwndrendertarget, then copy the node bitmap (created when the node is first created in the DB, saves actually drawing it) being moved to the hwndrendertarget, then call EndDraw.
Now this works ok (ish), what I don't like it that, when the node is dragged quickly the mouse cursor moves ahead of the node being dragged (distance depending on speed of drag/mousemove bu worst case up to about 1/2 inch). My reference app is MS Visio, dragging a single object on this shows the cursor staying in the same position over the object being dragged maybe +/- 1/2 pixels.
What I have not tried yet is moving all the (and only) the drawing operations to a separate thread, but before I try this I would like to research other methods if other single threaded approaches would trump this way.
Update:
I have optimized this a bit more with improvement, I found I was allocating and de-allocating the edge brush in the draw function which I have moved out to a class wide object and initialize for the life of the class as with other brushes etc. The cursor now only gets a little way (2 pixels or so) outside of the object being dragged when being dragged quickly, the object is a 15px radius circle. so the cursor is able to move up to 17px away from the middle of the object (the point the cursor should stick to) when being dragged. In testing I found an interesting thing, on my main monitor the drag is worse in that the cursor can get ahead of the object being dragged by more than 17px, say up to maybe 25px from the center point of the object where the cursor should be fixed. On the second monitor for the extended desktop (i.e. no taskbar) the drag is better in that described previously. If I hide the taskbar on the main monitor and run the app on that monitor and drag, the performance is the same as the second monitor.
I subclassed QGraphicsItem and reimplemented paint.
In paint I wrote something like this for labeling the item:
painter->drawText("Test",10,40);
After some time I think It may be useful to handle labeling with seperate item. So I wrote something like this.
QGraphicsTextItem *label = new QGraphicsTextItem("TEST",this);
setPos(10,40);
But two "TEST" drawing do not appear in the same place on screen. I guess difference may be related with item coordinates - scene coordinates. I tried all mapFrom... and mapTo... combinations inside QGraphicsItem interface but no progress. I want to drawings to appear in the same place on screen.
What I miss?
I assume that you are using the same font size and type in both cases. If the difference in position is very small the reason can be the QGraphicTextItem is using some padding for the text it contains. I would try to use QGraphicsSimpleTextItem that is not going to add fancy stuff internally and see if you still have the same problem. The coordinates system is the same one if you use painter or setPost so that is not the problem. If this doesn't help I will suggest to specify the same rect for both to avoid Qt adding it owns separation spaces.