suggestions for repeating display views



  • I couldn't think of a better way of phrasing my title. I'm writing an app that controls and reports on devices that are connected wirelessly. The needs are modest, but there's a bit of wrinkle in that the customer may have 1 or 100 of these wireless devices.

    I need a way to allow him to run through a list of these devices, and select one. Doing so would then give him a details screen with which he can perform other actions on the selected device.

    Would this be a good use of QStackedWidgets, or is there more appropriate construct for this?

    Thanks...


  • Lifetime Qt Champion

    Hi,

    The most minimal I can think of a QListView and a QLabel for displaying the information.

    Can you give more details about your setup ?



  • @mzimmers
    You could, but it doesn't sound like it scales, what if there were 10,000 of these devices? Eventually there's a memory price to pay.

    But are you going to create a separate widget for each device? Won't the displays be similar? So you'll want to write code which lets user pick which device and then show a "shared" widget for the information?



  • More information: the main window will contain a list/table of active devices on the network. Some very summary information (MAC address, device ID, last communication time) will appear in this table. When the user wants to get more information or to configure a particular device, he'll select it from this list.

    At this point, the display will change to a details view. There will be more information and several controls, but everything will pertain to just one device. JonB's point is valid about this probably using resources unnecessarily.

    I'll have to store a fair amount of data (some time period for each device) but I guess there's no reason the details UI needs to be replicated.

    SGaist: the list/label will be fine for the overview display (I'll use a table instead of a view), but I'll need another display for the details view.


  • Lifetime Qt Champion

    Keep all your device information in a model so you can easily and quickly get them.

    Then when your user selects a device, Load the corresponding data from your model and show it in the QLabel.



  • I'm not sure I understand what you're recommending -- are you proposing that I use a single QLabel object for the entirety of my details display?


  • Lifetime Qt Champion

    If you are only showing one device details at a time, yes. Just update the content of the label with what corresponds to the selected device.



  • The details view as I intend it will contain quite a bit of information. There will be ~10-15 fields, all of which can be modified by the user. There will also be a few push buttons to send other commands to the target device.


  • Lifetime Qt Champion

    Makes me thinking about the QtSQL module Book example.



  • Thanks for the reference. I've taken some of that example and adapted it.

    So, to summarize, I have a QTableView that is associated with a QAbstractItemModel. The table reveals a few details about all the devices (rows) found. Now I'd like to display some additional detail about any device that the user selects.

    A very simple start would be to display the MAC address in a QLineEdit when the user selects a row. I can envision a way to do this, but it's cumbersome and indirect (signalling the model to extract the information and signal it back to the widget). Can someone inform me of a preferred method to do this?

    Thanks...



  • @mzimmers

    but it's cumbersome and indirect (signalling the model to extract the information and signal it back to the widget)

    Why? What do you have in mind by "indirect" & "signalling"? You haven't mentioned threads anywhere, so assuming your UI, view & model are all in the same thread where is there any "signalling" involved (unless you mean the row selected signal, but that does not matter)?

    You just retrieve the data via QAbstractItemModel::data() and plonk it into your widget(s), explicitly.

    Depending, you can already have retrieved the "additional detail" into your model without having to display it in the QTableView, so you can access it without having to do work at that instant if you prefer (perhaps not relevant for your device-model, but often the case when it's a database-model).


  • Qt Champions 2017

    Well, I'd just fill the model data as it comes. And it'd come as the selection of the selection model changes ... I think just subscribing to the QItemSelectionModel::selectionChanged signal should be enough, no?



  • Here's what I'm trying to do in my display:

    // in my c'tor.
        QObject::connect(ui->tableView->selectionModel(), QItemSelectionModel::currentRowChanged, this, &Widget::updateDetails);
    }
    
    void Widget::updateDetails(QModelIndex i)
    {
        int row = i.row();
        QVariant qv;
        QString s;
        QModelIndex qmi;
        qmi = m_model->getModel()->index(row, DEV_MACADDR, QModelIndex());
        qv = m_model->getModel()->data(qmi, DEV_MACADDR);
        s = qv.toString();
        ui->macAddr->setText(s);
    
        qmi = m_model->getModel()->index(row, DEV_DEVNAME, QModelIndex());
        qv = m_model->getModel()->data(qmi, DEV_DEVNAME);
        s = qv.toString();
        ui->devName->setText(s);
    
        qmi = m_model->getModel()->index(row, DEV_LATEST_HB, QModelIndex());
        qv = m_model->getModel()->data(qmi, DEV_LATEST_HB);
        s = qv.toString();
        ui->heartbeat->setText(s);
    

    I'm sure this isn't the best way to do this (particularly since it doesn't work), but in any event, the first assignment of qv works, but the others give qv an invalid value. What am I doing wrong?

    EDIT: OK, I just realized I shouldn't be using the 2nd argument to the data() call. It's working now. Not ready to mark this solved yet, though, as I'm going to have other questions in a bit.

    Thanks...



  • So far, I've been experimenting entirely with QStrings as elements in my model, like this:

    struct DeviceDetails
    {
        uint8_t macAddr[6];
        char devName[25];
    ...
    } d;
    QAbstractItemModel *m_model;
    ...   
     m_model->setData(m_model->index(row, DEV_DEVNAME), d.devName); /
    

    I'm now trying to work with other data types, such as the macAddr, but I can't figure out how to do this. This is what I'm trying to do (but doesn't compile):

        m_model->setData(m_model->index(row, DEV_MACADDR), d.macAddr);
    

    How do I do this? Thanks...


  • Qt Champions 2017

    @mzimmers said in suggestions for repeating display views:

    but doesn't compile

    With what error?
    I could imagine QVariant isn't aware of how to serialize that type, but that's just a speculation at this point.



  • C:\Qt\5.10.1\mingw53_32\include\QtCore\qvariant.h:471: error: 'QVariant::QVariant(void*)' is private
         inline QVariant(void *) Q_DECL_EQ_DELETE;
                ^
    

    and:

    C:\Users\MZimmers\CD desktop apps\Qt projects\wb_utility\model.cpp:36: error: use of deleted function 'QVariant::QVariant(void*)'
    

  • Qt Champions 2017

    Well, I suggest you convert d.macAddr to QString before you pass it to setData ...



  • This post is deleted!


  • @mzimmers said in suggestions for repeating display views:

    'QVariant::QVariant(void*)' is private

    That is a "safeguard" operator to avoid people using pointers to non-QObjects into a QVariant.
    Basically macAddr is of type unsigned char* the d will still own the data into d.macAddr and will delete it when it goes out of scope. QVariant needs to own the data and know how to delete it. you should be able to hack your way around it by using a smart pointer but it's probably much easier to store it in a Qt container like QByteArray
    m_model->setData(m_model->index(row, DEV_MACADDR), QByteArray(d.macAddr,sizeof(d.macAddr));

    P.S.
    If the compiler complains about signed/unsigned you can use:
    QByteArray(static_cast<char*>(d.macAddr),sizeof(d.macAddr))



  • @VRonin ah that explains it (in fact, that's exactly what I was trying to do).

    I like your idea of changing my char array into QByteArray, but there's a snag - I'm attempting to use the same C struct for my target device as my host app, which restricts me to C++11 types.



  • @mzimmers
    But @VRonin is only asking you to make a copy into a QByteArray for passing to m_model->setData(); you're not changing what is actually in your struct d. So how does that affect your use of the struct in the two apps?



  • Aargh - I still get confused about using setdata() (and in fact the whole model/view paradigm). Let me play with this, and I'll get back with more confusion in a bit.



  • @mzimmers
    Maybe it's me who's misunderstanding you:

    I'm attempting to use the same C struct for my target device as my host app, which restricts me to C++11 types

    I am taking that as you have two, separate programs which wish to share a struct in a .h file, so you'd like the actual declaration & content to be accessible from a non-Qt program. Maybe you mean something quite different?!



  • Yes, you got it exactly. I'm kind of a fanatic when it comes to not duplicating data structures (not because I'm lazy, but because I'm afraid of updating one of them, but forgetting to update the other, down the road). This philosophy has occasionally caused headaches during initial implementation, but usually repays itself in simplified maintenance.



  • OK, I've been grinding through this stuff, and I understand it at least a little better now. My worker receives a message, converts it from XML into a struct, and passes the struct to the object containing the model. My model object then does stuff like this:

        mac_itoa(d.macAddr, macStr);
        m_model->setData(m_model->index(row, DEV_MACADDR), macStr);
    

    So, my summary information in the display widget updates automatically. But, as I mentioned, I have a details area. How do I go about updating the values in this area? I can't do a setModel, as they're just edit boxes, not lists, tables or trees.



  • You have 2 options:
    Do it manually: connect(view->selectionModel(),&QItemSelectionModel::selectionChanged and fill your widgets with data

    or use QDataWidgetMapper like explained here: http://doc.qt.io/qt-5/qdatawidgetmapper.html#setCurrentModelIndex



  • @VRonin thanks for the link. I looked at one of the mapper examples (simplewidgetmapper), and I think this is the way to go for me.

    In fact, I'm thinking about changing the entire UI. Instead of separate summary/detail areas, I could provide the user with two lists: one for the MAC address, and one for the device name. When the user makes a selection, all the details area (contained in a grid like the simplewidgetmapper example) would update. The user could then edit the details and press a "commit" button.

    How does this sound so far?

    EDIT:

    So, I've begun an in-place conversion from QTableView to using individual widgets (and QDataWidgetMapper). (I've only implemented 2 fields so far.) I think I must be missing a step, because the fields don't initialize on startup the way they did with the table view.

        mapper = new QDataWidgetMapper(this);
        mapper->setModel(d->getModel());
        mapper->addMapping(ui->comboBoxMacAddr, 0);
        mapper->addMapping(ui->deviceName, 1);
    
        ui->gridLayout->addWidget(ui->labelMacAddr, 0, 0, 1, 1);
        ui->gridLayout->addWidget(ui->comboBoxMacAddr, 0, 1, 1, 1);
        ui->gridLayout->addWidget(ui->labelDevName, 1, 0, 1, 1);
        ui->gridLayout->addWidget(ui->deviceName, 1, 1, 1, 1);
    

    Have I indeed left out a step? It may be noteworthy that, once I select a row in my table (I still have the table active), the update properly affects these fields.

    Thanks...



  • @mzimmers said in suggestions for repeating display views:

    the fields don't initialize on startup

    What would the expected behaviour be?



  • I was expecting that the widgets that I mapped to the model would be automatically updated with the model data. (This is how the view table worked.) Could the problem be that I have multiple views setting on the same model? (I still have my view table active.)

    If it looks like I misunderstood the correct use of the mapper, please let me know and I'll make whatever changes necessary.



  • You just missed the connection.

    @VRonin said in suggestions for repeating display views:

    like explained here: http://doc.qt.io/qt-5/qdatawidgetmapper.html#setCurrentModelIndex

    connect(view->selectionModel(),&QItemSelectionModel::currentRowChanged,mapper,&QDataWidgetMapper::setCurrentModelIndex);



  • I actually have that connection, and it handles updating the fields once a row is changed. What's missing is the initial setting of the fields before the user selects a row.

    In production, this app is going to be collecting data from remote devices all the time. When it gets the first message (no matter from which device), I'd like to populate the MAC address and device name fields with the information from that message, as it does the view table.



  • You can call view->selectionModel()->setCurrentIndex(view->model()->index(0,0)); when you insert the first row to populate the linked widgets



  • @VRonin: this solution seems to require the table view. I was hoping to eliminate the table view once I got my individually mapped widgets working. Am I barking up the wrong tree here?



  • How would you select which device to show details for?



  • Hi VRonin - I was thinking to use a combo box for the MAC address.

    I'm getting the impression that this isn't a great idea, though, so perhaps I'll keep the table after all. The summary area (above the fold) will be read-only, and the details area will permit editing (and a commit).



  • @mzimmers said in suggestions for repeating display views:

    I was thinking to use a combo box for the MAC address.

    Then it's just a small change to the connect statement:
    connect(combo,QOverload<int>::of(&QComboBox::currentIndexChanged),mapper,&QDataWidgetMapper::setCurrentIndex);


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.