Is it fine to change model data internally before displaying views? For example
struct MainWindow : QMainWindow
{
MainWindow()
{
mTreeView->setModel(mModel);
mModel->appendChild(...); // No beginInsertRows() and similars are called
}
};
Although the model is set to the view before changing the model data, the view is not displayed since it is done in the constructor of main window. If the view is updated when it is displayed, I think the code should be okay.
When the model is already connected to one or more views I totally recommend calling the corresponding begin... and end... methods before and after the model modification. Those functions emit the signals which connected views (or proxies) must handle before and after the data is modified. Otherwise, the views may end up in an invalid state.
When no views (or proxies) are connected it is safe to do so.
It is fine to do so - your program will not crash, at least. However when you add new items to the model in the way you show in your example, your view will not display the update, especially if you do not use beginInsertRows().
If you want the view properly show the actual data, try to set model after you insert items in it. Otherwise you will need to call beginInsertRows() and endInsertRows() in your model class.
Related
I have an MFC app that uses a CSplitterWnd to create multiple-pane views. That process is done in the CFrameWnd::OnCreateClient() call. After that, at some point, the various views have their CView::OnInitialUpdate() function called. One of the views is a CTreeView and another a CListView with multiple columns that are setup within the OnInitialUpdate(). I need to setup the default CTreeView Item which updates the CListView as well. However, if I set the default within OnInitialUpdate the CListView has not yet setup the various columns. Therefore it seems I need to set the default CTreeView Item after all that is done. I presume I'd do this in some CFrameWnd callback. My question is at what point do I do that?
TIA!
It appears that you have dependencies between your views; that's not right.
The data should be stored in the document (even the current selection, if it supposed to be shared between views).
Than the view that is responsible for the change should call CDocument::UpdateAllViews(). As a result, all other views OnUpdate() will be called.
Please see https://learn.microsoft.com/en-us/cpp/mfc/reference/cview-class?view=vs-2019
I have a really big tree structure and I cant load the complete tree into Ram on client side. I am using Qt's QTreeView.
I want to load the sub elements of an item dynamically when the user expands the element.
Is there some signal that triggers when the user opens an item in the tree?
I am using the tutorial Simple Tree Model Example. When I do changes to the model the view has also to be updated. And I dont want to loose the focus to avoid user confusion!
and after some time (request to the server)...
You need to implement a subclass of QAbstractItemModel that can process needed amount of data. When an item is expanded in the view, it calls QAbstractItemModel::rowCount to determine children count and then QAbstractItemModel::flags and QAbstractItemModel::data to get children data. If requested data is not available at the moment, you should return placeholder data (i.e. 1 row containing "Loading"), and start a request. When the data is received, emit rowsAboutToBeInserted and rowsInserted signals to notify the view about new data (you should also notify it about removing "Loading" row). The view will then call rowCount, data, and flags methods again, and your model should now provide loaded data. Use QCache to keep last accessed data in memory.
EDIT:
what I did instead was to move the "Main Window" creation and message handler into the controller, now only controller needs to know about Model and View. Since main window messages are processed in the controller itself, it can easily call view to change, and call model to do the logic, view never needs to call model, and if model needs to talk to View, then there are 3 options:
It can do so by returning a value to the calls from Model and then Model calls View according to what is returned from Model.
Model can post WM_APP messages to the main window and then Model calls View accordingly.
If the changes are very small, for instance, changing text of a static control, the Controller can pass handles to those controls to Model and Model can do the changes itself.
Any advices ?
I am trying to understand how MVC work in c++ and pure winapi, so am developing a demo app, and trying to implement my own interpretation/version of MVC.
The idea is to keep controller in total control, where everything is relayed through the controller.
Design :
Every class will be in it's own file .h and .cpp
Our main.cpp,view classes and model classes all will only be able to communicate to controller and controller only, there won't be any communication between then directly.
The view classes(there will be more then 1 gui(child windows) including the main window gui) will only create, show, hide, and destroy the windows(views) the messageloop of the main window and any sub or superclassesed child window will reside either in the controller folder or in the model folder.
Controller will further have sub folders with files views.h, views.cpp for communicating to view classes and model.h, model.cpp for communicating to model classes, similar in structure as in views.
Model will have files with different logics related to the application in different files.
It will look something like :
Problem :
There are too many co-dependent classes in different files.
What kind of approach would you suggest, I want to keep the idea of "Everything relaying through controller", if possible.
What's missing is some kind of abstract interfaces for the view and model to communicate. I.e., you rely on concrete types for communication.
This can be solved in a few different ways:
Abstract base classes as listeners/observers, (i.e. IModelListener, IViewListener) which the controller implements.
Events (i.e. ValueEnteredEvent from view and ValueChangedEvent from model).
Slots & signals ala Qt (or Boost), which allows a listener to connect to single output methods of an object.
The controller connects to the view and model object respectively, using whatever method you prefer. Hence, these do not know about the controller directly, they only talk to these given interfaces.
Avoid concrete dependencies & connections.
Abstract them away through interfaces (defined by the object it self)
Make sure each object knows as little as possible (but no less) in order to communicate effectively with it's peers.
I believe you can do this using pipes
The controller will have open pipes to the model class and the view class.
The view will have open pipes to the child view classes.
When a child view makes a change to the data, it sends the change to the main view through the pipe.
The view in turn sends the change to the controller through the pipe.
The controller updates the model by writing to the pipe.
The model updates the data and sends the refreshed data to the controller.
The controller sends the refreshed data to the main view.
The view then sends the refreshed data to the child views.
All communication is done by reading and writing to the respective pipes.
What I'm doing on MVC pattern is I create a ViewDelegate which is an abstract class. Make my controller inherited from ViewDelegate, and set controller as View's delegate.
Class ViewDelegate{
Public:
Virtual void onViewCallBack = 0;
}
Class controller : public ViewDelegate{
Public:
Static Controller* create();
Private:
Bool init();
Void onViewCallBack();
}
Class view {
Public:
static view* create();
Bool initWithDelegate(ViewDelegate*)
Private:
Bool init();
}
Use the delegate in View to call method declare in Controller.
At the beginning I want to tell that I just started learning QT so my knowledge about this is really not deep. I wrote simple tasks management it's a console application of course. I used logic which resembles MVC pattern (controllers, views, actions, models).
For example let's take user login. I create instance of LoginController class, then LoginController creates instance of LoginView who is waiting for user to enter data - login, password. Login and password is saved as LoginView members. Then in LoginController I read this data and passes them as parameters to UserVerificationAction constructor. Constructor of this class saved this data as members of their class. Next in LoginController I calls method of class UserVerification - action() which validates login and password. Then depending on the result of validation I create instance of MenuController or instance of LoginFailiedView. This mechanism is user throughout the program (CreateUserController, AddTaskController) etc. I used virtual methods so MenuController consists of about 20 lines of code and is very easy to read.
I want to use Qt to implement a GUI to be more precise I want to use signals and slots mechanism but I have a dilemma. Maybe it would by better to create a slot in the LoginView class and then creates action instance instead passes entered data to LoginView members and then in LoginController creates instance od action class. maybe there is a better way to do this. I want you to give me some tips on how I should do it properly
p.s.
Sorry for my English
In Qt, the concept of a "controller" is slightly blurred. It tends to be part of both the model and view. This does not mean that you can't write a controller to link a model and view logic.
Normally what you will see is a view that emits signals for its actions. And then you wire these either directly into compatible slots on a model or a subclass where you have written your own slots.
If for instance you have a main window. This window might create a model and a view as children. And it may then define slots on the window subclass that wire between the model and view. This means your window is a view and a controller.
Qt provides Model/View architecture.
It introduces 3 classes: Model, View and Delegate to store, present and edit data.
I believe that is what you are looking for.
I'm implementing my 3rd "naive" model as QAbstractItemModel inheriting class.
So far it worked well.
However, I've been using read-only "static" models in my views and only changed the model of some views depending on the user's actions. So I used my_view->setModel( a_model ); to update a view.
Now I need to have one of the views to keep a unique model but that model needs to be update sometimes, using a special function "update()" function that I call in the code when required.
At the end of the update() function, I just call emit dataChanged( ... ); with the corresponding data.
It seems that it doesn't update the view this model is connected to. The only way to make the view update seems to do something like my_view->setModel( nullptr ); then my_view->setModel( a_model ); again.
What are the possible reasons for an emit dataChanged( ... ); to not trigger view's display update?
I've been debugging my model implementation functions and the index() function get called but not the data(). I'm a bit worried that maybe I didn't understood something about the model/view system in the case of changing model (that is not changed through the view but programmatically).
It's an open-source project so you chan check the full model code there (it's a bit hacky I think, not used to model/view system of Qt) : http://code.google.com/p/art-of-sequence/source/browse/tools/aosdesigner/view/model/LayerObjectsModel.cpp?spec=svn4fe209aa3e82f2c7cd42192581a890e28bada9b0&r=4fe209aa3e82f2c7cd42192581a890e28bada9b0
The code of the widget managing the view is available there : http://code.google.com/p/art-of-sequence/source/browse/tools/aosdesigner/view/LayersView.cpp?spec=svn4fe209aa3e82f2c7cd42192581a890e28bada9b0&r=4fe209aa3e82f2c7cd42192581a890e28bada9b0
I checked a bit Qt documentation. dataChanged() should be emitted when existing data of the model changes.
If you are adding rows to the model check beginInsertRows and endInsertRows
From the insertRows Qt documentation:
If you implement your own model, you can reimplement this function if
you want to support insertions. Alternatively, you can provide your
own API for altering the data. In either case, you will need to call
beginInsertRows() and endInsertRows() to notify other components that
the model has changed.
If you are removing rows from the model check correspondingly beginRemoveRows and endRemoveRows
Also have a look at beginResetData
When a model radically changes its data it can sometimes be easier to
just call this function rather than emit dataChanged() to inform other
components when the underlying data source, or its structure, has
changed.