How to synchronize movement between multiple QT graphics view widgets - c++

I am trying to build a tool that can compare images side-by-side. To do this, I have used three graphics view widgets in QT. They all zoom at the same rate, but I cannot seem to find anything online about how to link the movement of the images (aka - the drag and drop with the mouse). Is this feature even possible to construct?
Here is what the GUI looks like:
Image compare GUI

Well, every time I used a QGraphicsView, I ended up subclassing it cause it didn't provide everything I needed "as public".
In your case though, I believe this is as simple as doing:
connect(graphicsView1->verticalScrollBar(), SIGNAL(valueChanged(int)),
graphicsView2->verticalScrollBar(), SLOT(setValue(int)));
connect(graphicsView2->verticalScrollBar(), SIGNAL(valueChanged(int)),
graphicsView1->verticalScrollBar(), SLOT(setValue(int)));
If you prefer, call some slots to perform more than a 1-to-1 action:
void MyClass::scrollGraphicsView1(int value)
{
graphicsView1->verticalScrollBar()->setValue(value);
}
void MyClass::scrollGraphicsView2(int value)
{
graphicsView2->verticalScrollBar()->setValue(value);
}

Related

What Qt widgets should be used for sprite animation viewer

I'm looking to make a sprite animation editor. I have the loading of my custom animation file done but now need to get the actual ui started. I'm really just stuck on what widgets I would use to actually play my animation. I need to be able to go to certain frame, play, pause, loop, etc. Once I'm done with the viewing portion I plan on adding in the editing.
I've seen AnimatedSprite in qt docs but that seems to only allow playback of sprites in the same file. In my situation sprites can be from multiple image files and sometimes doesn't follow a grid like sprite cutter.
First of all, you should decide whether you want to use QML or Widgets. AnimatedSprite is QML related class. All widget-related classes starts with "Q" letter.
If you decide to use Qt Widgets, I would recommend to take a look at Qt Animation Framework in combination with Qt Graphics View Framework. Most likely it will not let you do everything you want out of box, but it should provide you with a rich set of useful tools.
If you need here are some examples.
Hope it helps.
Have a look at QMovie. This class may provide all the methods you need, as long as you only want to use it for viewing. The QMovie can be passed to a QLabel to show the animation.
QMovie however supports only gif out of the box (and there is a third party plugin for apng files). You would probably have to create your own image handle plugin to support your format.
If thats not applicable or to complicated, you will most likely have to create your own custom widget. Have a look at the painter example. Playing an animation is not that hard if you have all the frames. A simple QTimer to change the image to be drawn in a constant rate should work.

How to get widgets (QGraphicsProxyWidget) of QGraphicsScene?

I am making use of a QGraphicsScene and I am adding regular widgets (QLineEdit, QComboBox, etc.) to it via implicitly created QGraphicsProxyWidget objects:
m_pLineEdit = new QLineEdit("", 0);
m_pProxy = m_pGraphicsScene->addWidget(m_pLineEdit);
I am currently searching for a way to later retrieve those widgets from the scene again for processing, but am not able to find one.
I tried the following approaches already:
Since I cannot pass the graphics scene as parent to the widget constructor, retrieving the widgets via m_pGraphicsScene->findChildren(QLineEdit*) does not work, since there is no direct relation.
The graphics scene does have a QGraphicsSceneBspTreeIndex child, but that is not part of the official Qt API and therefore relying on it cannot be the way to go.
Bottom-line: How can I get all the QGraphicsProxyWidget objects from a Qt graphics scene? Can this be done in the Qt standard or do I have to subclass QGraphicsScene and try to manage the widgets myself?
Bottom-line: How can I get all the QGraphicsProxyWidget objects from a Qt graphics scene?
Get the list of all the items in the scene via scene->items() , then check if they're of the right class:
// Horrible C++98 code which doesn't even feature algorithms
QList<QGraphicsItem *> items = scene->items();
foreach (QGraphicsItem *item, items) {
QGraphicsProxyWidget *w;
if (w = qgraphicsitem_cast<QGraphicsProxyWidget *>(item)) {
use(w);
}
}
However, I'd like to stress that you should really keep track of the items you put in the scene. (At least, the ones you're interested in using afterwards). Walking over the scene and retrieving the items like this seems very fragile, and it's a signal of poor code quality and poor design. Note that you have the proxy returned by the addWidget call, just save it somewhere.
Just after posting the question I by chance found a solution in the Qt source code. The widget proxies are treated as regular QGraphicsItem internally and can be casted via qgraphicsitem_cast:
QList<QGraphicsItem*> graphicsItemList = m_pGraphicsScene->items();
foreach(QGraphicsItem* pGraphicsItems, graphicsItemList)
{
QGraphicsProxyWidget* pProxy = qgraphicsitem_cast<QGraphicsProxyWidget*>(pGraphicsItems);
if(pProxy)
{
QLineEdit* pLineEdit = qobject_cast<QLineEdit*>(pProxy->widget());
if(pLineEdit)
// do stuff
}
}
If someone knows a simpler/faster method, I'd be happy to hear about it. Until then I will use the approach outlined above.

Qt/Cocoa on MacOS - remove progress bar animation

in my application there is the QProgressBar I use, but it's not quite illustrating progress of a process, rather it represents a state. Therefore, I want it not to animate on Mac, as it always does. I have no experience with Cocoa, but Qt does not allow me to do that. And therefore I ask some more experienced Cocoa programmers: is it even possible to stop the progress bar animation? I looked for that for quite a while, but I didn't find anything.
Thank you in advance.
You could either call setStyle() on the QProgressBar to use a different kind of QStyle instead (i.e. one that doesn't animate); or you could ditch the QProgressBar altogether and use a different widget type entirely (e.g. a QLabel, or just subclass QWidget and implement paintEvent() do draw whatever you want the widget to look like)

Does a signal or event exist for when a QGraphicsView or QWidget is done being painted or rendered?

I'm trying to time an application to see how long it takes to load up some information, and paint a graph. My function loads up the data first, then draws the graph.
The timing is fairly simple, it calls an external function that gets msecs since some date.
The problem is even if I set t1 in the beginning and t2 right after I call the draw function, t2 will return before the QGraphicsView is actually updated. (I know, it makes sense why this should be asynchronous)
For instance when I load a large file, it will return with 700 msecs after I subtract the two values, but the actual rendering doesn't finish until a few seconds later.
I've looked all over the web and scoured the Qt documentation. I can find tons of information on updating widgets yourself, but nothing on any kind of signal or event that is fired off after rendering finishes.
Even the QGraphicsScene::changed signal appears to only be fired off when the scene changes underneath, not when rendering is done and the user can SEE the changes.
Any help on how to do this?
Does a signal or event exist for when a QGraphicsView or QWidget is done being painted or rendered?
As far as I know, it does not exist. (looked for something similar)
user can SEE the changes
As far as I know, Qt uses double buffering, so if painting is finished, it doesn't mean that user can see the changes.
Any help on how to do this?
If you want to know when painting has finished, then...
You can subclass QGraphicsScene and implement your own drawItems, drawBackground or drawForeground. This is NOT simple (because item painting algorithm is complicated), but you'll be able to tell when every item has finished painted.
You can fire/emit signals from within paintEvent (QWidget-based classes) or paint() (QGraphicsItem/QGraphicsObject-based classes). You'll need to use your own classes, obviously, and you'll have to subclass either QGraphicsView, or items you're drawing within view, or QGraphicsScene.
You could also create proxy QPainter class, and this way you'll be able to know what exactly is being paitned and when.
Having said that I suspect you're approaching your problem incorrectly.
If you're only trying to draw a graph, then there's no reason for you to know when painting is finished.
If painting is finished, it doesn't mean user can see the result.
Paint events might be called more than once.
Recommended approach:
Receive/read the data (you're drawing in your graph) from external source using threads or timer events (you'll need to read it in small chunks if you're using timer events, obviously), then update the graph from time to time, and let Qt handle repainting.
How exactly does this allow me to detect the amount of time it takes from when I choose to open a file to when all the data is loaded and the graph is drawn and is visible?
You can detect when paintEvent has finished painting by subclassing whatever widget you're using to paint Graph, overriding paintEvent and firing signal from within paintEvent, calling a subroutine or doing whatever you want.
There is no warranty that paintEvent will be called only once.
To measure how slow individual routine is and locate bottlenecks, use profilers. VerySleepy, AQTime, and so on.
Instead of measuring how long it takes to load AND display data, it will make much more sense to measure separately loading time and display time. This is a GUI application, not a game engine, so you do not control precisely when something is being drawn.
I have not testet it, but I think by subclassing QGraphicsScene and reimplementing the render method you can measure the render time.
#include <QGraphicsScene>
#include <QTime>
class MyGraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
void render ( QPainter * painter, const QRectF & target = QRectF(), const QRectF & source = QRectF(), Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio ) {
QTime t;
t.start();
QGraphicsScene::render (painter, target, source, aspectRatioMode);
qDebug("render time %d msec", t.elapsed());
}
};

Prevent QGraphicsItem::itemAt() On a "Background Item"

I am trying to set up a GUI using plugins. In my current phase I need the user to click and specify locations of points.
The plugin architecture I have setup requires the plugin to return QGraphicsItem that the plugin wishes to use. In the main program (which future plugin writers won't have access to), I set a background image to guide clicks. The plugins can sneakily access the scene() using an itemChange() and installing an eventFilter() on the scene being set. This allows for the plugins to have access to scene clicks and process whatever information it needs. However, the background picture is being returned by itemAt() calls.
I wish to prevent this background Pixmap from returning with an itemAt() for all future plugins. Is this possible?
Tried:
setEnabled(false); //No success
This doesn't answer your question directly, but it should solve your problem: I suggest reimplementing QGraphicsScene::drawBackground() and drawing your pixmap there. That's what the method is for.
A similar approach would be to put the pixmap in a resources file, then use style sheets to set it as the background of either the QGraphicsView or its viewport widget (I think these have slightly different effects).
To make an item invisible to itemAt() (or any of the other collision detection methods) you can make the boundingRect() method return a QRectF with zero width and height.
This though has the side effect of making any of the partial viewport update modes of QGraphicsView malfunction, because QGV doesn't have any idea where the item is going to paint itself. This can be remedied by having QGV do full updates on every repaint (setViewportUpdateMode(QGraphicsView::FullViewportUpdate)). Depending on your scene this might be an acceptable compromise. If you're using the OpenGL graphics backend the viewport updates will always be full updates anyway.
After so many years, here is the answer.
Make your own subclass of QGraphicsItem and override the shape() method
QPainterPath shape() const override
{
return {};
}
Ref to docs: https://doc.qt.io/qt-6/qgraphicsitem.html#shape
The shape is used for many things, including collision detection, hit tests, and for the QGraphicsScene::items() functions.
So this will effectively make the item invisible for itemAt() and others.
And you still have the boundingRect() which is used to filter what items should be painted.