QAbstractItemModel in QGraphicsView - qt

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.

Related

Adding a high number of QML objects in a QWidget application using QQuickView or QQuickWidget poses performance problem

I'm developping a Qt application in which the user can add QML objects inside a QGraphicsScene. The available QML objects are listed and the user can add as many as he wants.
Until now, I used QQuickWidgets. The QGraphicsScene contains a top-level widget which is the parent of all the QQuickWidgets I create. It works fine, but I have a performance problem. With a high number of objects, the application starts to slow down, and takes too much space in RAM (more than 1.5 GB with the first example I created, containing 400 objects).
I thought it comes from the way QQuickWidgets are handled by Qt, and wanted to try another way, with QQuickViews. To do so I created a root view, converted in a QWidget so I can embed it in my view, which is a QWidget. Then I add a new QQuickView in the root view for each created object.
The creation of the root view, its container and the engine:
_rootView = new QQuickWindow();
_rootView->resize(1024, 720);
_rootView->show();
QWidget *container = QWidget::createWindowContainer(_rootView, this);
container->resize(_rootView->size());
container->setObjectName("TopLevelQmlViewWidget");
_layout->addWidget(container);
_engine = new QQmlEngine(_rootView);
The creation of the QQuickViews representing the objects:
QQuickView *view = new QQuickView(_engine, _rootView);
view->setSource(url);
view->setResizeMode(QQuickView::SizeViewToRootObject);
view->show();
It works, but the problem is that each QQuickView creates its own thread, which doesn't change the way I handle it but takes place in memory. I don't understand why, because I reparent them to the root view.
So my questions are the following :
1 - Is there a way to prevent the QQuickViews to create their own threads ?
2 - Is using QQuickViews, indeed, less memory-consuming than using QQuickWidgets ?
3 - If no, how can I handle adding a big number of QML objects in a QWidget view without consuming too much memory ?
I think using multiple QQuickViews is a bad idea. An application usually only needs one. I would take a look at QQmlComponent instead. Here is an example:
QQmlComponent component(_engine, QUrl::fromLocalFile("MyItem.qml"));
QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
childItem->setParentItem(_rootView);
I'm by no means a QML expert. However, here are some pointers I can think of
Avoid mixing and matching QQuick: widget/View.
Consider creating objects dynamically
You can also make use of Loaders, but they have a small amount of extra overhead.
Consider using something like a stack/swipe view to minimize amount of loaded objects.
For best ROI, I'd first try implementing something like stack view and see how much it may help with the RAM. Then go on to create other objects dynamically as needed.
Finally I think QT has a tool that lets you see during runtime the amount of memory of the QML tree. You can look at that and see where your biggest memory hogs are.

Subclassing QStandardItemModel to avoid QAbstractItemModel

I'm implementing a Model/View for a tree like structure, and I've decided to try the QStandardItemModel on which I want to wrap on it a specific class, (which I call here "appSpecificClass").
Basically, I want part of that class (like names, or some data), to be shown in the model, and when I change the model (in edit role, or drag and drop), I want that to have consequences on the appSpecificClass (which is, when I change a name that is showing on the model, the name on the object associated with the model's item of the appSpecificClass also changes).
So, I started from subclassing the QStandardItem by a appSpecificItem, which only has a pointer to the appSpecificClass. When I construct the appSpecificItem, the text and icons are called from appSpecificClass and everything works fine.
However, when change data from appSpecificItem, naturally it does not change the appSpecificClass, because so far I didn't found any way of interacting with the appSpecificItem's pointer via overloading a virtual function (or else)
Does anyone knows how to do this/if this is possible? What can I do such that if for instance the signal
QStandardItemModel::itemChanged ( QStandardItem * item )
is emitted, I can change a the appSpecificItem's pointer.
If not, is there any good tutorial about implementing a Model from scratch? I've tried myself some, but it is not an easy task. Ideally I would like a QStandardItemModel like model, but a little more abstraction on it (such that I can put my appSpecificClass on it).

How to forward signals to the last-clicked QGraphicsScene

I have a few QGraphicsScene subclasses "CustomScene" all deriving from a common interface that contains the virtual functions cut(), copy(), paste(), and delete(). QGraphicsScene is the superclass, which is inherited by CustomSceneInterface, which is inherited by CustomScene. Each of the CustomScenes are shown in separate QGraphicsViews in the main window. I also have QActions for cut, copy, paste, and delete.
I'm having trouble figuring out how to send the QAction signals to whichever CustomScene was clicked on last (or whichever has "focus").
How can I do this?
I realized I can just send the QAction signals to slots that check which QGraphicsView has focus and then call its scene's appropriate method. I'll need to call
QWidget::setFocusPolicy(Qt::ClickFocus)
on the QGraphicsViews to get this to work properly. If someone can think of a better solution, please let me know.
EDIT:
With Qt5 and being able to use lambda expressions as slots, I can now employ a pretty spiffy approach. First, I make a function lastClickedScene(), which returns whichever scene was last clicked on. Then I do connect(actionCut, &QAction::triggered, [=]{lastClickedScene->cut();}).

Qt4 QMenu items sorting

I am using QT4 and dynamically adding entries to a QMenu. Is it possible to sort the entries in the QMenu without deleting it and creating a new one?
I originally thought there was a function to insert at a specific location so I could sort on insert, but I have not been able to locate it.
Once added, I don't think you can reorder. While you are creating though you could use the QWidget::insertAction method to place it exactly where you want it.
void QWidget::insertAction ( QAction * before, QAction * action )
Otherwise you could use QWidget::addActions. Create your list of Actions and sort it before adding to the QMenu.
void QWidget::addActions ( QList<QAction *> actions )
In one of my codes, I save the QActions into a separate List and generate the menus and submenus on demand. In theory, I can add "weight" to the items and have them re-ordered, but I have not implemented this yet.
Project page is available here: http://code.google.com/p/qtedit4/wiki/qmdilib
Please note that the actions of QWidget (and QMenu) are stored as a QList which can be "read", using QWidget::actions() . Remember that the list is copied, so you can modify the actions but not the list itself. (I hope I am not mistaking...)

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