I have a collection of images, dynamically generated through QGraphicsView widgets, and i'd like my users to choose between them. For that purpose, i would display inside a custom widget available images in some kind of grid and have users click the one they are interested in.
Multiple questions arise :
is there an existing widget that already fits this purpose ?
should i find a way to disable all mouse event handling by QGraphicsView items, or could i add a transparent widget in front of graphic views which would intercept them ?
is there a performance issue displaying many QGraphicsView widgets (up to a few hundreds) ? Should i export them to plain images first ?
First off, no, there's no widget designed specifically for that purpose.
I don't think you are grasping what QGraphicsView is for. It's for displaying a QGraphicsScene, which is meant to hold many QGraphicsItems. Based on your post, I can't see why you would need multiple QGraphicsViews. You can simply have one QGraphicsView and display many images inside of its scene. For example, see QGraphicsPixmapItem.
You definitely should not have hundreds of QGraphicsViews. You probably just want one (although a few could be justified in certain circumstances), in which you display many QGraphicsItems in a QGraphicsScene. You can definitely have hundreds of QGraphicsItems visible at once. In your case, you probably want QGraphicsPixmapItems, which are a subclass of QGraphicsItem. You could even have multiple QGraphicsScenes, and display whichever one is relevant using QGraphicsView::setScene. If you want the user to be able to select an image from a grid, and then work with that image, I would look to the State Pattern.
I can't think of any reason to disable mouse handling in QGraphicsViews, QGraphicsScenes, or QGraphicsItems. Why should these not handle their own mouse events? You can (and should, where necessary) subclass them and reimplement mousePressEvent, mouseMoveEvent, mouseRelease event, etc. to obtain the functionality you want.
Good luck!
Related
I'm creating some custom qt designer widget plugin for drawing purpose. With these widgets, user can use qt designer for drawing just like Microsoft Visio (hopefully).
As shown in the screenshot below, there are one SvPage object page_0 as the container, it contains one SvArc widget and one SvCircle widget.
Every thing is good, except that when one widget (A) cover other widget (B), user cannot select widget B easily.
To solve this problem, I'm trying to do:
Set the size of each drawing widget (eg. SvArc,SvCircle) to very small (40px * 40 px);
Paint the content of drawing widget direct to its parent widget (SvPage). In SvPage::PaintEvent(QPaintEvent event), it iterates all children drawing widgets and call the doPaint(QPainter painter) method of each children.
3. To refresh the drawing widget automatically (eg, when SvArc widget is moved, its drawing on SvPage should be updated automatically), in the drawing widget's SvArc::PaintEvent(QPaintEvent *event), it will trigger the SvPage to update its painting.
But in step 3, there is a problem that it will lead to recursive repaint issue:
because SvArc::PaintEvent() trigger SvPage::PaintEvent(), and SvPage::PaintEvent() will then trigger SvArc::PaintEvent() again since SvArc widget is a child widget of SvPage widget.
So, the question is that is it a good idea to redirect widget painting to parent widget? If yes, how to solve the recursive repaint issue? If no, what's the good one?
Code (simplified):
void SvPage::paintEvent(QPaintEvent *event)
{
initPainter();
QList<SvWidget*> widgets = this->findChildren<SvWidget*>();
for (int i = 0; i < widgets.count(); i++)
{
SvWidget* w = widgets.at(i);
w->doPaint(this->painter);
}
destoryPainter();
}
void SvWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
emit signalDoPaint();
}
void SvArc::doPaint(QPainter* painter)
{
painter->drawArc(x, y, w, h, a alen);
}
You are messing things up here.
Every widget should be responsible for its own drawing. That's how Qt is designed to work.
You could use a single widget as a manager for some objects, and to draw them, but then those objects don't need to be widgets, they can be simple data representations. In that case, the objects will not concern themselves with any painting, it will be the manager widget that does it.
However that approach will be less efficient. Because when you have multiple independent widgets, the paint engine can easily detect changes and efficiently repaint only the parts that need updating.
In your case you will either have to do a whole lot of redundant repainting, or implement more sophisticated item management, which will be a complex task that will definitely not be worth the effort, if you are even up to it to begin with, which you are probably not.
Your current approach is very bad. I'd suggest to just stick to regular widgets, in their actual sizes, doing their actual painting. It will be much easier for you to implement and manage it, and it will be much easier for the computer to paint it.
As for selecting between overlapping widgets, QWidget wasn't really designed to facilitate that. Widgets are supposed to be put in layouts, not to overlap. Which is why its childAt() function can only return a single widget at a given coordinate.
What you should really do is use QGraphicsScene, QGraphicsView and QGraphicsItem. Similarly to widgets, graphics items will handle their own drawing efficiently, the difference is the API was designed for graphics, and when you have overlapping items, QGraphicsScene::items() will give you a list of all items at that position, so you can chose an item other than the topmost.
I'm creating some custom Qt Designer widget plugin for drawing purpose. With these widgets, user can use Qt Designer for drawing just like Microsoft Visio (hopefully).
The functionality you're reusing in Qt Designer is minimal, and could be easily factored out into a separate project. The only thing of any value for you is the property inspector pane.
For everything else, using widgets is about the most complicated way of implementing it. Use QGraphicsScene and QGraphicsView and start with 90% of your functionality already implemented and ready to go.
Implementing a rudimentary vector illustration system in QGraphicsScene is an afternoon job. You can have something with the functionality of early Corel Draw from Windows 2.x times done in a few days. It's the testament to the power of the scene framework and modern development frameworks in general.
How can I create a triangular pushbutton in Qt? What is the most simplest way of executing this? I use the designer to create buttons and not code.
Also, I read somewhere that shapes may be changed as long as the frame of the button is still rectangular but I want the frame to adjust according to the shape as well. How can I achieve this?
More detail: I want to place lots of small triangular buttons next to each other with every other triangle flipped. Each triangle button has it's own function, etc (no overlapping borders accepted). Can anyone give me a descriptive explanation for how I might go about this?
The geometry on a QWidget is always a rectangle.
It would be possible to create a QPushButton derivative, override its paintevent and do some nasty painting considering its neighborhood etc. but it would be really a pain...
it is much easier to use a QGraphicsView, QGraphicsScene and add appropriate QGraphicsItem (maybe the QGraphicsPolygonItem?), add them and use their signals/slots or create a derived class for your purposes.
It is not that hard to override the mouseevents to recognize clicks and you can even use the QStyleSheets to let the "button" look like it gets pressed.
So, if I figure out this correctly, QGraphicsItem are (abstract) graphics items which belong to one QGraphicsScene (which is scene manager basically).
QGraphicsView is specific "view" into that scene and multiple views can view same scene.
If I were to have 3 views viewing on same scene, where one view views 1-5/10 items, other 5-10/10 and third view views all of them, I would need to have one scene and three views with some kind of filters which items to draw. Is this possible ?
How can I filter which QGraphicsItems are displayed in specific QGraphicsView?
It's not possible to do directly, but is rather easy with viewscenes (akin to viewmodels).
Item visibility is an integral part of the scene, not a view. This makes sense: once you start letting scenes change item properties, there's never a sane place where one might stop. Next you want to move items a bit, etc. So this simply isn't supported in the current design.
You can have a prototype scene that has all items, and then viewscenes (viewmodels) that have copies of the items you wish visible. The items are small and cheap to copy, so even with a thousand items in a scene, the cost to implement it that way is minuscule. Just make a factory to make copies of all item types you're interested in, and run them on the prototype scene, ignoring the items you wish not shown.
In my raster drawing program I need to create a layers interface like in Photoshop or Sketchbook Pro. I read the documentation and figured out that I have to use QTreeView. But I didn't find a lot of information in the documentation about creating QTreeView with custom widgets. So:
1) How to insert custom widgets into tree view?
2) What is the difference between QTreeView and QTreeWidget?
3) What is the difference between QAbstractItemModel and qitemdelegate?
4) Any examples/articles/guides?
5) Maybe I should use something else?
QTreeWidget is a model and a view in one class, it's called a convenience view. It works against the good practice of separatng the views and the models, and probably shouldn't be used in a system where the notion of document layers belongs in the document handling code.
QTreeView is just a view, without any bundled models. When you have a model, you can set it on a view, making the view display the model.
A QAbstractItemModel is the data model. It has nothing to do with views or delegates at all - the model can exist and be useful without a view at all.
A delegate provides display and editing widgets for items of data in a view. It is a property of the view, not of the model. Different views can show the same model using different delegates, all at the same time.
Although the delegate lets you provide the custom widgets you're after, its use may be unnecessary. If the item you display has static contents, you can simply provide a QImage or a QPixmap as the data.
Special for your case (5): DON'T use any of QTreeView, QStandardItemModel and other such classes. If you need interaction with widgets + if you need widgets to be animated then you should use simple QScrollArea with QVBoxLayout inside of it.
Qt MVC is designed to process big amount of cognate data. It is not designed to provide widget-based interaction. So, if you want to "assign" one widget to each item and to interact with them - you will have a lot of problems with implementing delegates (tracking mouse events, providing editor's factory). Ofc, you may create your own delegates with custom drawing and custom processing of mouse events, but it's much easy to use simple widgets.
Just curious about scrolling complicated content inside web browser - like application. Lets assume i am using Qt and C++. This is not "how to" question, but more like "how does it work"? Completely derived from my curiosity irrational questions.
I did small experiment.
Created large QWidget 800x60000 px.
Added 300 QWidgets 800x200 px that are painting themselves using QPainter. Each widget prints its unique name to console when paintEvent() is called.
Added (1.) to a QScrollArea 800x800.
When scrolling, i notice redrawing only widgets that are not fully displayed on the screen. It is only 1 widget at a time (scene: http://savepic.ru/2670640.jpg).
So QScrollArea (or QWidget? Who deside what widget to repaint?) is smart - we do not have CPU loaded redrawing all the 300 widgets all the time or memory consumption storing 800x60000 pixmap (-;
Lets assume i want to use mouse to select text and other elements on my "webpage". So i want to be able to mark them (by changing background). How would i implement that? How different web browsers do that? Selecting pictures, text, tables... Should i think about tracking the mouse and drawing gray/blue/pink background boxes behind elements and my custom widgets?
I have another experiment - displaying stack of messages. The scheme is the same, except QPainter is not used here - only QLabels, QTextExits, QPushButtons (scene: http://savepic.ru/2632728.jpg). I can set a flag SelectableByMouse for QLabel, but how do i select more than 1 message?
You could suggest me to use some Qt HTML renderer, but this is not the answer for 'how does it work".