I wrote a QAbstractItemModel based class and implemented data(const QModelIndex & ar_index, int a_role) const function. I refresh the model using:
beginInsertRows(QModelIndex(), 0, 0);
// fill model
endInsertRows();
Inside data function I update some information X needed after refreshing.
I checked that data functions are invoked after refreshing the model and debugging I verified that my X variable is properly setted.
After refreshing, I need to fill another widget with this X value. How do I know that all data refresh was finished and correctly show Xvalue? Which signal is emmited after completelly refreshing the model?
There is no standard signal to use here -- the Qt's MVC API is designed to support not only static models, but also the dynamic ones, and for these, there cannot ever be a signal telling "all done". For example, in Trojita, a Qt IMAP e-mail client, rowsInserted is emitted to indicate that a new e-mail has arrived, but this happens even before the library knows anything about the new arrivals -- not even their UIDs. Only after the remote server has responded to the library's commands with data, the dataChanged is emitted to indicate that the "data represented by the model" has changed and that the attached views shall update.
You said you're reimplementing your own QAIM from scratch. This means that you can set any contracts you see fit here. Are some of the data immutable, i.e. guaranteed to never change after they have been loaded? If so, you can use a custom role like RoleIsItemFetched and after each change to the model (that is, after modelReset, rowsInserted, rowsMoved and dataChanged) check whether your data(someIndex, RoleIsItemFetched).toBool() function returns true. If so, you have your data, if not, you have to keep waiting.
An alternative to that might be to introduce your own signal. Don't be afraid of that, the MVC API is great for presenting data to users, but if you think you need more control over what is returned and have full control over the model and the "views", go ahead and make the code do what you need.
Related
I'm trying to implement an application in Qt with a model-view-controller scheme. I have some model classes (databases), one application controller, and a bunch of GUI classes. All GUI classes know the app controller and can call its public slots to send data to it that should be stored in the model. The app controller takes the data and calls the appropriate database to store it. The database classes check the data and return a bool to the app controller to tell if the data was okay or not.
Now, one GUI element has prepared some user data and wants to send it to the app controller. I am trying to figure out how to let the GUI-class know that the app controller validated the incoming data with the model and it accepted the data. My app controller shouldn't care nor know which GUI elements call its public API to commit data to the model. But how does the app controller communicate a response to the GUI element to tell it, that everything was fine, so the GUI element can close itself?
My idea would have been to let the GUI element create a random number token to send it to the app controller together with the data as a kind of "callback address". As soon as my GUI element receives an okay signal with the correct token attached, it can react to that.
I was dunno. All my solutions feel weird and not how it's supposed to be in Qt.
Edit 1: a pseudo-code example for clarification:
void Appcontroller::Slot(userdata)
{
bool okay = database->save(userdata)
};
void Appcontroller::CreateMasterWindow()
{
this->masterwindow_ = new MasterWindow(this);
// I am propagating the knowledge of the app controller to the
// MasterWindow and the latter can propagate that info further
// down, that's fine (everybody can know the responsible
// controller, but I don't think the controller should care about
// all his bastard children)
};
void MasterWindow::CreateSubWindow()
{
this->subwindow_ = new SubWindow(appcontroller_);
// the masterwindow creates a random subwindow that the controller
// should't have to care about
};
void SubWindow::SendUserData(userdata)
{
QObject::connect(this, &SubWindow::SendToControllerSignal,
appcontroller_, &Appcontroller::Slot);
emit SendToControllerSignal(userdata);
// how to get a callback (targeted to me/this) from this,
// if the appcontroller does not know me? what is de way?
};
TL;DR: Connections should be made outside of the components. To compose the parts of your system, you instantiate the objects, and then connect them for desired functionality. The objects should otherwise have no knowledge of each other.
Thus, the only means of communication between the objects are signals and slots.
All GUI classes know the app controller and can call its public slots to send data to it that should be stored in the model
Nope.
The GUI classes should know nothing. When a GUI class needs something, it emits a signal. When a GUI class is supposed act on something (e.g. a change in the model), it exposes that as a slot.
I am propagating the knowledge of the app controller to the MasterWindow
Why? The MasterWindow exposes its functionality via signals and slots. Composing it into a bigger system is accomplished by signal/slot connections. No other knowledge is needed.
The app controller takes the data and calls the appropriate database to store it.
Does there even need to be a controller? All it seems to do is act like a signal-slot connection. You can directly connect the GUI classes to the database, and the controller could be what sets the connections up initially.
Otherwise, the application controller can act as a viewmodel, i.e. a QAbstractItemModel that adapts the model exposed by the database, and tweaks it for display/interaction (e.g. adds color properties, icons, etc.).
The database classes check the data and return a bool to the app controller to tell if the data was okay or not.
The database classes should expose slots that are used to modify the database state. They should also expose signals that indicate any changes. To indicate failure, the database can indicate a "change" with the same value.
how to get a callback (targeted to me/this) from this, if the appcontroller does not know me? what is de way?
The signals from the model/database are connected to the GUI classes. A GUI class can go into a "change pending" state once it emits a "change requested" signal. It will then react accordingly when its "new value" slot is called with the updated value: if the value didn't change, it indicates a failure, otherwise it indicates success. If it didn't request a change, it simply updates the displayed value - something else changed it.
This can be all done using nothing more than the Q_PROPERTY mechanism, and the change notification signals.
If the values aren't simple discretes, but rather have some structure, expose those as indices in a custom QAbstractItemModel. Each GUI item can then act on one or more model indices: requesting the changes, and reacting to value updates.
Multiple GUI elements can be hooked up to the same database variable (or no GUI elements!), and the GUI must support this and update its state to reflect the state of the model.
The "tickets" or "request identifiers" you propose are mostly spurious. The requesting GUI element has enough state to know that it requested a change, and can always act predictably on subsequent feedback from the model/database.
I am building an application using Qt/QML. The QML of my main window is very complex and depends on lots of data which must be loaded via HTTP and then be processed by a C++ backend before it is ready to be displayed.
The C++ backend provides a signal which is fired when the data is ready. Until then, I want the window to be empty except for a simple loading indicator being displayed. Of course, I could use a simple overlay which hides my actual interface until the data is available, but this would mean that the QML code of my actual user interface is already loaded and tries to access the not-yet-available data, which is causing a lot of errors, so I would need to add dozens of dummy values and NOTIFY signals for each single property which might not yet be available.
What is the best way to completely deactivate a portion of QML code and to enable it as soon as a signal is triggered?
My personal experience is to not give data to your view components, don't bind them. For example, set your text value to an empty string or don't set it, set your image component source to an empty string or don't set it at first. When your signal comes in with data ready, you assign the data to the views at that time.
I have a combo which is disabled, but adding an element to it will emit the currentIndexChanged(int) signal.
I expected signals to be naturally turned off when a widget is disabled, but it's not the case. I know there is blockSignals(bool), but if there are many widgets whose signals must be "blocked when disabled", blockSignals would require a Boolean state for each widget.
How can I disable the signals sent by a widget when it is disabled (and not alter its blockSignals state)?
EDIT
To clarify: since this is a widget, user cannot interact with it when it's disabled, but some signals are emitted when altering the widget programmatically. In my case there are two interesting signals:
currentIndexChanged(int) and activated(int)
The problem in my code is that I sometimes alter the combo programmatically AND I wish it to emit a signal, and sometimes it's the user that alters the combo by interacting. That's why I am using currentIndexChanged and not activated.
In both cases, anyway, I don't want the signals to be emitted when widget is disabled.
The QComboBox signals are user interaction based from end user point of view if you only have a QComboBox and nothing else as your question seems to imply.
I simply cannot reproduce the issue. I have just made a short program where I cannot get any of the QComboBox signals emitted since I cannot simply interact with the widget.
Edit: It might be a good idea to upate your question with more context for the casual readers, but based on further clarification in comments, yes, programatically it might be the case, but then signals might be useful to process programmatically, too, with corresponding slots, so it is not a major improvement if Qt blocks them automatically.
Luckily, the feature you wish to have is already available:
bool QObject::blockSignals(bool block)
If block is true, signals emitted by this object are blocked (i.e., emitting a signal will not invoke anything connected to it). If block is false, no such blocking will occur.
The return value is the previous value of signalsBlocked().
Note that the destroyed() signal will be emitted even if the signals for this object have been blocked.
If you want to do it for many widgets, create a simple function that you call instead of myWidget->setDisabled(true);:
inline bool disableAndBlockSignals(QWidget *widget)
{
widget->setDisabled(true);
return widget->blockSignals(true);
}
If you want to disable only some of them, say, currentIndexChanged, you can use disconnect manually then.
You can disconnect signals with the QObject::disconnect(); when you want to block them and then reconnecting them when you want to unblock them.
In your case, the signals are the sole source of state information carried on to other objects. If you disable them, the other objects won't ever get any notification that the state was changed. This can cause bugs in the objects that depend on being informed of your widget's state.
There are at least two solutions:
Don't change the widget's state. You can certainly defer the update of the widget's contents until after it gets reenabled.
Create a proxy that monitors the originating widget's state, and queues up the signals (with compression) until the widget gets reenabled.
Due to those workarounds, your design may require a rework. Perhaps it'd be better if you could cope with signals from those disabled widgets. You should also evaluate whether disabling a widget doesn't break the user experience. What if the user wants to see the contents of a widget, but doesn't mean to change the current setting? A disabled widget in such a case is going too far. You can make you own, perhaps subclassed, widget, acting so that the control is not disabled, but the current element stays fixed. This could even be a separate object, applicable to any control - through judicious leverage of the user property.
I have a QPlainTextEdit widget in my application which has a QSyntaxHighlighter assigned to it. Upon each content change within that text edit area, I need to get a notification (to update the global application save/changed state). However, the signal textChanged() also gets emitted each time the highlighter gets to work, which I need to filter out somehow.
I already had a look at modificationChanged(), but that doesn't seem to work either. It ignores the highlighting changes and successfully notifies me upon the first content change, but not of any subsequent changes. The documentation mentions, that I should be able to reset the internal state with setModified(false) but that method doesn't seem to exist.
Any ideas on how to filter the changes?
Do I have to switch to QTextDocument which seems to have a single contentsChanged() that is said to ignore syntax highlighting changes?
It turns out I already was on the right track...just not all the way:
I indeed need to listen to modificationChanged signals since they are emitted on content changes (which are the relevant events for my application save state handling).
I however originally did not see a way to reset the internal modification state (e.g. when my application saves its state). The reason was that setModified(bool) does not exist for the QPlainTextEdit, but I realized that each of those objects has a QTextDocument internally which does have that method. So I simply call that each time I need to reset the state to non-modified:
m_pPlainTextEdit->document()->setModified(false);
As a result, when the content is changed the next time, modificationChanged will get emitted again so that I can react to it and for example enable the "Save" icon.
BTW: The signal contentsChanged from QTextDocument is also emitted upon formatting changes, so not helpful in my scenario.
I have not tested it, it is just basically an idea.
When the user modifies the text, it is a QKeyEvent.
When the highlighter does, it is some sort of QInputMethodEvent (?)
What you could do is, check if the event is a QKeyEvent, and if it is not, block it.
You can create a filterobject class, or just define the following method in the class that contains the QTextEdit.
bool MyClass::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QKeyEvent) //The user modified the text edit
return false;
else
return true;
}
And you have to install it (for example in the constructor), if you defined it in the class that contains QTextEdit:
myTextEdit->installEventFilter(this);
Instead of hooking into modificationChanged(), and resetting the modified flag everytime, you could just hook into textChanged(). It's triggered anytime you make a change to the document, regardless if had been previously changed or not...
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.