best practices: I'm doing it wrong
-
@mzimmers said in best practices: I'm doing it wrong:
If the details are mapped to the model, they're subject to being overwritten by remote updates. This can disrupt a user's editing session. I imagine this is a classic UI dilemma; how is it typically resolved?
If it were me I'd keep the updating part non-editable. I'd rather just open a dialog with the data loaded at the time of the edit trigger being fired and then leave the user either to commit or discard his changes. The other thing just seems to confusing to me as a user.
-
@JonB
I agree with @kshegunov that keeping the auto-updating and editing parts separate would simplify the experience for the end user.However, using a dialog and batch-updating will still leave you with decisions about what to really update, e.g.:
- A widget has an initial value from the device, copied into the dialog.
- The end-user does not change that, but changes some other widget's value, and clicks "commit".
- Meanwhile, the first widget's underlying value on the device has changed.
What do you do? Do you commit the value currently seen by the user in the dialog back to the device thus changing it on the device, or do you not do so, thus preserving the new value in the device but not what the user saw on the dialog? And how do you recognise this situation one way or the other?
An alternative approach which avoids all of this: Do you need/want to batch up multiple changes? Are they inter-dependent or quite separate? Could you instead send a change to one widget's value back to the device immediately (and perhaps even get rid of the dialog completely and/or no need for the "abstraction" layer)? Your code probably actually becomes a lot simpler. It could be rather nice for the user to see his changes applied immediately as he makes them, no "commit" button?
-
Here's an idea, for what I would do.
When the user selects an item from the summary field the details about that item are captured in a "snapshot" as of the moment of the click.
The user than can modify that captured data how ever allowed, and he/she can submit it via a special button.
However, while the user is editing, you keep on monitoring the data and if the new data varies from the original captured one, you blend in a small indicator item. For example a red
!
next to the edit field, to let the user now, that this entry is out of date. The use can than choose to update that field by clicking on the Icon.That updates the field and updates to captured data as well, reading it for the next potential change.
-
Lots of good information in these replies; thanks, guys. Jon: the information in question isn't going to be updated frequently (in fact, most of this app's use will be for initial provisioning), but any committed changes will involve a write to NVS (non volatile storage) on the target device. Since this is (relatively) slow, and because NVS has a limited number of writes, it's probably best to avoid on-the-fly commits, and let the user perform a manual commit instead.
Based on what all of you have said, it does seem like a separate window is probably the right choice.
-
I gather that a QDialog is the right UI object to use for this. I'd like to use Designer to create the dialog, so what is the mechanism for attaching it to my main display widget? Do I use an analog to the main widget:
namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT private: Ui::Widget *ui;
or is there a preferred method? One of the examples drew heavily on layouts and addWidget() calls, but this was for a considerably more elaborate dialog than I anticipate needing.
Thanks...
-
hi
Its 100% like a QWidget exceptclass MyDialog: public QDialog
I made a sample for some other poster about sending signals to mainwindow from dialog
But i guess it also shows how to use dialogs.
https://www.dropbox.com/s/w1qo7xpjhhxjzhi/myotherdialog.zip?dl=0note, there is a difference.
if you show it using exec() it blocks access to mainwindow while open.
if you show it with show() it wont.
That is different from QWidget windows as they cant block that way. -
@mrjj thanks for the example. It appears that I was on the right track.
I'm trying to use your example as a model, but I'm getting a compiler error. Here's the header file:
#ifndef EDITDIALOG_H #define EDITDIALOG_H #include <QDialog> namespace Ui { class EditDialog; } class EditDialog : public QDialog { Q_OBJECT public: explicit EditDialog(QWidget *parent = nullptr); private: Ui::EditDialog *ui; signals: public slots: }; #endif // EDITDIALOG_H
and here's the source:
#include "editdialog.h" EditDialog::EditDialog(QWidget *parent) : QDialog(parent), ui(new Ui::EditDialog) { }
But I'm getting a compiler error about an incomplete type Ui::EditDialog. I can't see what I'm missing; does anything look wrong to you?
Thanks...
-
@mzimmers
when you use the wizard to make a new class, you can select
and then base class
Then the besides .h and .cpp u have .ui file which you can use for visual designing.
that also adds the Ui::EditDialog *ui; to the class def.if you dont want it, you can simply remove all of it.
and create widgets in code. -
I'm doing something wrong. I deleted all my editdialog files, and recreated the .ui file. I then added the class, but Creator doesn't add the UI::EditDialog *ui for me.
EDIT: I think I found the problem -- used Qt Designer Form instead of Qt Designer Form Class. Will report back momentarily. -
Based on input from a few people here, I've decided to leave the details in the main display disabled. The user will press an "edit" button which will create a QDialog with the details enabled for editing.
It's not a huge deal, but I will be performing redundant work if I just re-create the display elements for the details in the QDialog. Does it make sense to create an aggregate display element with all the details, and use this element in both the main and edit windows? Can .ui files be nested?
-
I've created an edit dialog, but I'm not able to map the individual elements to the model as I did in the main widget. I create the edit dialog like this:
DeviceModel *m_d; ... void Widget::on_pushButtonEdit_clicked() { EditDialog editDialog(m_d); QObject::connect(&editDialog, &EditDialog::editCommitPressed, this, &Widget::sendCommit); editDialog.exec(); }
And I try to map to the model like this (seemingly identical to how I do it in the main widget):
EditDialog::EditDialog(DeviceModel *d, QWidget *parent) : QDialog(parent), ui(new Ui::EditDialog) { ui->setupUi(this); m_mapper = new QDataWidgetMapper(this); m_mapper->setModel(d->getModel()); m_mapper->addMapping(ui->deviceName, TAG_DEVICENAME);
The mapping works in the main widget, but not for my edit dialog. Any ideas what I'm doing wrong? Thanks.
-
Did you forget to connect to setCurrentModelIndex?
-
Well...maybe.
In my main widget, I have this line:
QObject::connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, m_mapper, &QDataWidgetMapper::setCurrentModelIndex);
Wouldn't that be sufficient for anything using the same model?
-
@mzimmers said in best practices: I'm doing it wrong:
for anything using the same model
The connect uses the selection of the view and the mapper so has nothing to do with the model.
If either the view or them_mapper
are different objects from those in the main window then you have to redo the connection -
Oh, OK, I think I see the problem: since my edit dialog doesn't have a QTableView, there's no way to select a row (no table --> no rows). I guess I can't just have the details mapped without some table to give it context, right?
So, do I need an invisible table in my edit dialog, or is there some more clever way around this?