Model - View - Controller in Qt - c++

I understand more or less how does MPV works.
But I don't get what classes:
QAbstractItemModel
QAbstractItemView
QAbstractItemDelegate / QItemDelegate
Can do for me?
If that is relevant, I'm using
QGraphicsScene / QGraphicsView with some elements (visual representation of game board) that user can interact with while the interaction logic (game rules) are encapsulated in other class.

AbstractItemModel QAbstractItemView QAbstractItemDelegate
Are from the "Mode/View framework"
This is a very powerful framework for the data part of your application, here is a presentation of the framework.
QAbstractItemModel
Is the base class for the model of the MVC. Has a global interface for accessing and altering the data and takes care of the Observable part.
QAbstractItemView
Is the base class for the view of the MVC. Has aglobal interface for the view/selections part and it takes care of the Observer part. You don't have to worry about the observer pattern, the framework does it for you.
QAbstractItemDelegate
Is the base class for the controller of the MVC.
Is the Strategy pattern for painting, editing the elements, ...
QGraphicsScene / QGraphicsView
Are from the "The Graphics View Framework" and is independent of the Model/View framework.
This is also a very powerful framework for the graphics part.
The Scene
QGraphicsScene provides the Graphics
View scene. The scene has the
following responsibilities:
Providing a fast interface for
managing a large number of items
Propagating events to each item
Managing item state, such as selection
and focus handling Providing
untransformed rendering functionality;
mainly for printing
The View
QGraphicsView provides the view
widget, which visualizes the contents
of a scene. You can attach several
views to the same scene, to provide
several viewports into the same data
set
If you want a Model to be visible in a QGraphicsView than you will have to write your own view based on the QAbstractItemView.
Take a QGraphicsView as view port widget QAbstractScrollArea::setViewport(QWidget * widget)) and then you can
add QAbstractItemView::rowsInserted,
remove QAbstractItemView::rowsAboutToBeRemoved
and change QAbstractItemView::dataChanged
the items in the scene. Don't forget to take care of the reset an layout change events.

Related

Qt Application with QTabWidget, QGraphicsScene and QPushButton

I want to create a Qt application with a QTabWidget, where each tab contains a QGraphicsView and a QGraphicsScene to draw a polygon on a Cartesian plane. example: first tab for a triangle, second for square, etc...
So, I create my qt application and then:
- inside my class derived from QDialog, I create a QVBoxLayout, I've added my widget to this, and I've inserted it into my QTabWidget, created with QDesigner: I repeat this operation for each tab of QTabWidget.
I want to know if it's a bad practice to add a QGraphicsScene into a tab in this way or to redefine all the classes from qwidget, view, and scene.
out of the QTabwidget, i have some QPushButtons that do some operations on the QGraphicsScene's polygons: when i click on a polygon, i want to do some operations through the QpushButton, like sum the angles (only on the polygon that I've clicked).
How do I communicate to the QPushButton (out of the QTabWidget) that I've clicked a polygon and which one I've clicked(inside QTabWidget)?
the private data field of my_scene contains a QList of triangles (derived from QGraphicsPolygonItem)
https://i.stack.imgur.com/Pu329.jpg
I want to know if it's a bad practice
Generally speaking no, it isn't.
If you are ok with the functionality of some class you should use this class. If you need to extend it in any way you should subclass from this class and add everything you need.
It's true not only for Qt classes but for any other classes written by you or somebody else. It's one of the basic principles of inheritance.

Qt how to redirect widget painting to parent widget?

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.

Draw more than one widget in QListView item

I created my own data model based on QAbstractListModel and connected it with QListView by simple ui->listView->setModel(prodModel) - it works fine.
Now I want to create custom view in QListView for each item. For example, I want to reach that every data row from model will be shown in style like Stack Overflow (question with rating). I want to use Qt widgets and arrange it on my own idea - using layouts etc.
Unfortunately, a Qt's guide about model/view doesn't have described how to use QItemDelegate to show my widgets - I'm not interested about editing data in view (but I want to interact like click - ideally if I could click and get signal/sth in each widget in my custom view).
I found some examples, but they describe drawing only one widget (QProgressBar) without any layouts.
Any help will be appreciable, thanks!

Customizing QT QTreeView with custom widgets

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.

Layers on QGraphicsView?

Hi I'm making an application that pulls data from a WFS and then displays those layers of data on a QGraphicsView on a widget. At the moment all layers are rendered and added to the same view meaning if I want to turn a layer of it means re-rendering all of it except that layer.
At the moment im adding a QGraphicsScene with Ellipse Items and Polygon Items added to it, to the graphics scene. I'm wondering if its possible to add multiple scenes to a graphics view or layers to a scene or something that would allow me to just hide/show certain points/polygons from a check box or something that simply hides a layer?
I know this is kind of vague but I'd appreciate any help.
Thanks.
You only need one QGraphicsScene, but the key here is that all QGraphicsItems and QGraphicsObjects can be parented.
If you create a single QGraphicsItem or QGraphicsObject as a parent object, it doesn't need to draw anything, but can be used as the root for a layer's items.
Therefore, subclass from QGraphicsItem to create a QGraphicsItemLayer class that doesn't render anything and add all the ellipses, polygons etc that are required in the same layer as children of that QGraphicsItemLayer.
When you want to hide a layer, just hide the parent QGraphicsItemLayer object and all its children will be hidden too.
-------- Edited --------------
Inherit from QGraphicsItem: -
class QGraphicsItemLayer : public QGraphicsItem
{
public:
virtual QRectF boundingRect()
{
return QRectF(0,0,0,0);
}
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
{
}
};
Create a layer item:
QGraphicsItemLayer* pLayer = new QGraphicsItemLayer;
Add the objects you want to the layer, note that pLayer is passed as the parent
QGraphicsEllipseItem = new QGraphicsEllipseItem(pLayer);
Assuming you've created the QGraphicsScene with a pointer to it called pScene: -
pScene->addItem(pLayer);
Then when you want to hide the layer
pLayer->hide();
Or display the layer: -
pLayer->show();
Another way to go is QGraphicsItemGroup
Something like:
// Group all selected items together
QGraphicsItemGroup *group = scene->createItemGroup(scene->selecteditems());
...
// Destroy the group, and delete the group item
scene->destroyItemGroup(group);
So you can treat group as a layer and since group is also QGraphicsItem have all features like show()/hide() etc.
UPDATE: Changing Z-val for a group will allow you to implement things like 'move layer to top/bottom'
I think you could try to partition your objects according to z value: see setZValue.
Then introduce a mapping between layer id and indexing. A simple QStringList could do.
Of course, there are many details and variations that a practical solution will need to account for.