QML does not update when C++ Abstract Model Updates



  • Hello,

    I am working on a control application for an eight channel preamplifier and am having some troubles integrating QML and C++. I have an object representing the unit with eight child objects that represent the individual channels. I am displaying the the channels as a list view, but cannot get the display to update when the channel objects are modified. For example, I have bound a few boolean variables to button checked properties. When I toggle the values in the C++ model (reimplemented QAbstractListModel), the button states do not change until I force the ListView to redraw itself by shrinking the window and re-maximizing it. I originally attempted to use the built-in functionality of the QAbstractListModel to handle the updates, but had not success. To get to the point I am at, I have implemented a series of signals moving up the object tree until a call can be made to the C++ class to change the fields. To make the changes go the other way, I have manually sent signals to the QML and have been able to get the preamp status pane to update real time. Unfortunately I have been unable to do the same for the Channel List View. Below is some of the source for the project. Thanks in advance for any help you can provide!

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <qqmlcontext.h>
    #include <qqml.h>
    #include <iostream>
    #include <QDebug>
    #include <QQuickView>
    #include <QQmlComponent>
    #include <QTimer>
    #include "preampmodel.h"
    
    int main(int argc, char *argv[])
    {
      QGuiApplication app(argc, argv);
      QTextStream cin(stdin);
      QQmlApplicationEngine engine;
      QQmlContext* context = engine.rootContext();
      PreampModel * preamps = new PreampModel();
    
      context->setContextProperty("preampModel", preamps);
      engine.load(QUrl(QStringLiteral("qrc:/MainForm.qml")));
      return app.exec();
    }
    

    preampmodel.cpp

    /* Class: PreampModel
     * Author: JLimbocker
     * Description:  This class inherits QAbstractListModel to present data to the QML user interface.
     *      It contains eight channels and seven configuration fields.  Each of the eight channels has
     *      eleven roles which are accessed through role names.  The seven configuration fields are accessed
     *      through role names regardless of which index is selected.  This class will be updated when changes
     *      are made in QML and when changes are made in C++.  All changes should be reflected both in QML and
     *      in C++.
     */
    
    #include "preampmodel.h"
    #include "channel.h"
    #include <QAbstractListModel>
    #include <string>
    #include <QDebug>
    
    PreampModel::PreampModel(QObject * parent) : QAbstractListModel(parent)
    {
      preampID = 1;
      online = true;
      clkStatus = false;
      sampleRate = 2;
      clkSource = 1;
      outFormat = 0;
      clkOutSource = 0;
      for(int i = 0; i < 8; i++)
        channelList.append(new Channel((preampID-1)*8+(1+i), QString("Channel_" + (preampID-1)*8+1), true, false, true, true, 1, 23));
    }
    PreampModel::~PreampModel()
    {
    
    }
    /* void PreampModel::print()
     * This function prints debug information for the object
     * Returns: void
     * Parameters: none
     */
    void PreampModel::print()
    {
      qDebug() << "PreampID : ";
      qDebug() << preampID;
      qDebug() << "\nonline     : ";
      qDebug() << online;
      qDebug() << "\nclkStatus  : ";
      qDebug() << clkStatus;
      qDebug() << "\nsampleRate : ";
      qDebug() << sampleRate;
      qDebug() << "\nclkSource  : ";
      qDebug() << clkSource;
      qDebug() << "\noutFormat  : ";
      qDebug() << outFormat;
      qDebug() << "\nclkOutSrc  : ";
      qDebug() << clkOutSource;
      qDebug() << "\n";
    }
    /* roleNames()
     * This function is reimplemented from QAbstractItemModel.
     * It provides role names to the QML engine.
     * Returns: roles - a Hash relating the role to the role name
     * Parameters: none
     */
    QHash<int, QByteArray> PreampModel::roleNames() const
    {
      QHash<int, QByteArray> roles;
      roles[idRole]            = "preampID";
      roles[onlineRole]        = "online";
      roles[clkStatusRole]     = "clkStatus";
      roles[sampleRateRole]    = "sampleRate";
      roles[clkSourceRole]     = "clkSource";
      roles[outFormatRole]     = "outFormat";
      roles[clkOutSourceRole]  = "clkOutSource";
      roles[phantomRole]       = "phantom";
      roles[phaseRole]         = "phase";
      roles[ribbonRole]        = "ribbon";
      roles[groupRole]         = "group";
      roles[groupNameRole]     = "groupName";
      roles[gainRole]          = "gain";
      roles[nameRole]          = "name";
      roles[numberRole]        = "number";
      roles[meterLevelRole]    = "meterLevel";
      roles[peakValueRole]     = "peakValue";
      roles[scribbleStripRole] = "scribbleStrip";
      return roles;
    }
    /* data(index, role)
     * This function is reimplemented from QAbstractItemModel.
     * It provides access to the model data for the QML Engine.
     * If Index is out of range or the chosen role is not supported,
     * it returns an empty QVariant.
     * Returns: A QVariant containing the desired data, if available.
     * Parameters: index - the index of the data to be accessed
     *             role  - the role of the data to be accessed
     */
    QVariant PreampModel::data(const QModelIndex &index, int role) const
    {
      if(index.row() < channelList.size() && index.row() >= 0)
        switch (role){
          case idRole             : return preampID;
          case onlineRole         : return online;
          case clkStatusRole      : return clkStatus;
          case sampleRateRole     : return sampleRate;
          case clkSourceRole      : return clkSource;
          case outFormatRole      : return outFormat;
          case clkOutSourceRole   : return clkOutSource;
          case phantomRole        : return channelList[index.row()]->getPhantom();
          case phaseRole          : return channelList[index.row()]->getPhase();
          case ribbonRole         : return channelList[index.row()]->getRibbon();
          case groupRole          : return channelList[index.row()]->getGroup();
          case groupNameRole      : return channelList[index.row()]->getGroupName();
          case gainRole           : return channelList[index.row()]->getGain();
          case nameRole           : return channelList[index.row()]->getName();
          case numberRole         : return channelList[index.row()]->getNumber();
          case meterLevelRole     : return channelList[index.row()]->getMeterLevel();
          case peakValueRole      : return channelList[index.row()]->getPeakValue();
          case scribbleStripRole  : return channelList[index.row()]->getScribbleStrip();
          default: return QVariant();
        }
      else
        return QVariant();
    }
    /* rowCount(index)
     * This function is reimplemented from QAbstractItemModel.
     * It provides a count of the number of rows in the model.
     * In the case of this implementation, there are only eight
     * different indices - one per preamp channel.
     * Returns: 8 - the number of channels in the preamp.
     * Parameters: index - unused
     */
    int PreampModel::rowCount(const QModelIndex & index) const
    {
        return 8;
    }
    /* setData(index, value, role)
     * This function is reimplemented from QAbstractItemModel.
     * It provides an interface for the QML engine to set data
     * fields in the object.
     * Returns: True if data has been set, False otherwise.
     * Parameters: index - the index of the data to be set
     *             value - the value to which the data should be set
     *             role  - the role of the data to be set
     */
    bool PreampModel::setData(const QModelIndex & index, const QVariant & value, int role)
    {
      if(index.row() < channelList.size() && index.row() >= 0)
      {
        switch (role){
          case idRole             : preampID     = value.toInt(); break;
          case onlineRole         : online       = value.toBool();break;
          case clkStatusRole      : clkStatus    = value.toBool();break;
          case sampleRateRole     : sampleRate   = value.toInt();break;
          case clkSourceRole      : clkSource    = value.toInt();break;
          case outFormatRole      : outFormat    = value.toInt();break;
          case clkOutSourceRole   : clkOutSource = value.toInt();break;
          case phantomRole        : channelList[index.row()]->setPhantom(value.toBool());break;
          case phaseRole          : channelList[index.row()]->setPhase(value.toBool());break;
          case ribbonRole         : channelList[index.row()]->setRibbon(value.toBool());break;
          case groupRole          : channelList[index.row()]->setGroup(value.toBool());break;
          case groupNameRole      : channelList[index.row()]->setGroupName(value.toInt());break;
          case gainRole           : channelList[index.row()]->setGain(value.toInt());break;
          case nameRole           : channelList[index.row()]->setName(value.toString());break;
          case numberRole         : channelList[index.row()]->setNumber(value.toInt());break;
          case meterLevelRole     : channelList[index.row()]->setMeterLevel(value.toInt());break;
          case peakValueRole      : channelList[index.row()]->setPeakValue(value.toInt());break;
          case scribbleStripRole  : channelList[index.row()]->setScribbleStrip(value.toString()); break;
          default: return false;
        }
        qDebug() << "emitting datachanged";
        //If data has been changed, send signal to notify QML Engine
        emit dataChanged(index, index);
        return true;
      }
      else
        return false;
    }
    /* index(row, column, parent)
     * This function is reimplemented from QAbstractItemModel.
     * It created an index for the given row and column.
     * Returns: the index corresponding to the row and column
     * Parameters: row - the row at whic the item is located.
     *             column - the column at which the item is located.
     */
    QModelIndex PreampModel::index(int row, int column, const QModelIndex &parent) const
    {
      return QAbstractItemModel::createIndex(row, column);
    }
    /* setProperty(_index, name, value)
     * This function provides manual property setting from QML.
     * Returns: True if successful, false if unsuccessful.
     * Parameters: _index - the row to be accessed
     *             name - the role name to be accessed
     *             value - the value to write
     */
    bool PreampModel::setProperty( const QVariant _index, const QString name, const QVariant value)
    {
      QModelIndex index = PreampModel::index(_index.toInt(), 0, QModelIndex());
      qDebug() << index.row() << " " << name << " " << value << channelList.size();
    
      if(index.row() < channelList.size() && index.row() >= 0)
      {
        if(name == QString::fromUtf8("idRole"))
        {
          preampID = value.toInt();
        }
        else if(name == QString::fromUtf8("onlineRole"))
        {
          online = value.toBool();
        }
        else if(name == QString::fromUtf8("clkStatusRole"))
        {
          clkStatus = value.toBool();
        }
        else if(name == QString::fromUtf8("sampleRateRole"))
        {
          sampleRate = value.toInt();
        }
        else if(name == QString::fromUtf8("clkSourceRole"))
        {
          clkSource = value.toInt();
        }
        else if(name == QString::fromUtf8("outFormatRole"))
        {
          outFormat = value.toInt();
        }
        else if(name == QString::fromUtf8("clkOutSourceRole"))
        {
          clkOutSource = value.toInt();
        }
        else if(name == QString::fromUtf8("phantomRole"))
        {
          channelList[index.row()]->setPhantom(value.toBool());
        }
        else if(name ==  QString::fromUtf8("phaseRole"))
        {
          channelList[index.row()]->setPhase(value.toBool());
        }
        else if(name ==  QString::fromUtf8("groupRole"))
        {
          channelList[index.row()]->setGroup(value.toBool());
        }
        else if(name ==  QString::fromUtf8("ribbonRole"))
        {
          channelList[index.row()]->setRibbon(value.toBool());
        }
        else if(name ==  QString::fromUtf8("groupNameRole"))
        {
          channelList[index.row()]->setGroupName(value.toInt());
        }
        else if(name ==  QString::fromUtf8("gainRole"))
        {
          channelList[index.row()]->setGain(value.toInt());
        }
        else if(name ==  QString::fromUtf8("nameRole"))
        {
          channelList[index.row()]->setName(value.toString());
        }
        else if(name ==  QString::fromUtf8("scribbleStripRole"))
        {
          channelList[index.row()]->setScribbleStrip(value.toString());
        }
        else
        {
          qDebug() << "Property Set Failed: Unknown Role";
          return false;
        }
        emit dataChanged(index, index);
        qDebug() << name << " changed";
        return true;
      }
      else
      {
        qDebug() << "Property Set Failed: Index out of Bounds";
        return false;
      }
    }
    /* flags(index)
     * This function is reimplemented from QAbstractItemModel.
     * It tells the qml that all items are editable and selectable.
     * Returns: An OR combination of the necessary flags.
     * Parameters: index - unused.
     */
    Qt::ItemFlags PreampModel::flags(const QModelIndex &index) const
    {
      return Qt::ItemIsEditable | Qt::ItemIsSelectable;
    }
    
    

    channel.cpp

    Channel::Channel(QObject *parent) : QObject(parent)
    {
      phantom = false;
      phase = true;
      ribbon = false;
      group = false;
      groupName = 0;
      gain = 49;
      name = "Channel";
      number = 0;
      meterLevel = 0;
      peakValue = 0;
      scribbleStrip = "";
    
    }
    Channel::Channel(int chNum, QString chName, QObject *parent) : QObject(parent)
    {
      number = chNum;
      name = chName;
      meterLevel = 0;
      peakValue = 0;
      scribbleStrip = "";
      phantom = false;
      phase = true;
      ribbon = false;
      group = false;
      groupName = 0;
      gain = 49;
    }
    Channel::Channel(int chNum, QString chName, bool phant, bool pha, bool rib, bool grp, int grpNam, int gn, QObject *parent) : QObject(parent)
    {
      number = chNum;
      name = chName;
      meterLevel = 0;
      peakValue = 0;
      scribbleStrip = "";
      phantom = phant;
      phase = pha;
      ribbon = rib;
      group = grp;
      groupName = grpNam;
      gain = gn;
    }
    
    //Setters
    void Channel::setPhantom(bool newPhantom)
    {
      phantom = newPhantom;
      emit phantomChanged();
    }
    void Channel::setPhase(bool newPhase)
    {
      phase = newPhase;
      qDebug() << "Phase changed at channel level";
      emit phaseChanged();
    }
    void Channel::setRibbon(bool newRibbon)
    {
      ribbon = newRibbon;
      emit ribbonChanged();
    }
    void Channel::setGroup(bool newGroup)
    {
      group = newGroup;
      emit groupChanged();
    }
    void Channel::setGroupName(int newGroupName)
    {
      groupName = newGroupName;
      emit groupNameChanged();
    }
    void Channel::setGain(int newGain)
    {
      gain = newGain;
      emit gainChanged();
    }
    void Channel::incrementGain()
    {
      gain++;
      emit gainChanged();
    }
    void Channel::decrementGain()
    {
      gain--;
      emit gainChanged();
    }
    void Channel::setName(QString newName)
    {
      name = newName;
      emit nameChanged();
    }
    void Channel::setNumber(int newNumber)
    {
      number = newNumber;
      emit numberChanged();
    }
    void Channel::setMeterLevel(int newLevel)
    {
      meterLevel = newLevel;
      emit meterLevelChanged();
    }
    void Channel::setPeakValue(int newValue)
    {
      peakValue = newValue;
      emit peakValueChanged();
    }
    void Channel::setScribbleStrip(QString newString)
    {
      scribbleStrip = newString;
      emit scribbleStripChanged();
    }
    void Channel::setAll()
    {
    }
    //Getters
    bool Channel::getPhantom()
    {
      return phantom;
    }
    bool Channel::getPhase()
    {
      return phase;
    }
    bool Channel::getRibbon()
    {
      return ribbon;
    }
    bool Channel::getGroup()
    {
      return group;
    }
    int Channel::getGroupName()
    {
      return groupName;
    }
    int Channel::getGain()
    {
      return gain;
    }
    QString Channel::getName()
    {
      return name;
    }
    int Channel::getNumber()
    {
      return number;
    }
    int Channel::getMeterLevel()
    {
      return meterLevel;
    }
    int Channel::getPeakValue()
    {
      return peakValue;
    }
    QString Channel::getScribbleStrip()
    {
      return scribbleStrip;
    }
    

    QML in follow-up post

    JL



  • Preamp.qml

    import QtQuick 2.7
    import QtQuick.Layouts 1.1
    import QtQuick.Controls 2.0
    
    Rectangle {
      id: pre
      property var devName: "preamp";
      property int devID: 0;
      property int clkSource
      property int clkOutSource
      property int outFormat
      property int sampleRate
      property bool online
      property bool clkStatus
      property variant channelModel
      property int startChNum: (devID-1)*8 + 1;
    
      signal channelChange(var index, var prop, var value)
    
      height: 410
      width: parent.width
      color: "#00000000"
      ListView {
        Component.onCompleted:
        {
          currentIndex = 0;
        }
        id: channelView
        anchors.left: parent.left
        anchors.right: status.left
        anchors.rightMargin: 12
        contentWidth: 1488
        contentHeight: 400
        boundsBehavior: Flickable.StopAtBounds
        flickableDirection: Flickable.AutoFlickIfNeeded
        orientation: ListView.Horizontal
        interactive: true
        spacing: 12
        delegate: Channel {
          id: ch
          channelID: model.number;
          channelName: model.name;
          gainVal: model.gain;
          phantom: model.phantom;
          phase: model.phase;
          ribbon: model.ribbon;
          group: model.group;
          groupName: model.groupName;
          meterLevel: model.meterLevel;
          scribbleStrip: model.scribbleStrip;
          onPhantomChanged:
          {
            pre.channelChange(model.modelIndex, "phantomRole", phantom)
            channelView.currentIndex = index;
          }
          onPhaseChanged:
          {
            pre.channelChange(index, "phaseRole", phase)
            channelView.currentIndex = index;
          }
          onGroupChanged:
          {
            pre.channelChange(index, "groupRole", group)
            channelView.currentIndex = index;
          }
          onRibbonChanged:
          {
            pre.channelChange(index, "ribbonRole", ribbon)
            channelView.currentIndex = index;
          }
          onGroupNameChanged:
          {
            pre.channelChange(index, "groupNameRole", groupName)
            channelView.currentIndex = index;
          }
          onGainValChanged:
          {
            pre.channelChange(index, "gainRole", gainVal)
            channelView.currentIndex = index;
          }
          onChannelNameChanged:
          {
            pre.channelChange(index, "nameRole", channelName)
            channelView.currentIndex = index;
          }
          onScribbleStripChanged:
          {
            pre.channelChange(index, "scribbleStripRole", scribbleStrip)
            channelView.currentIndex = index;
          }
        }
        model: channelModel
        ScrollBar.horizontal: ScrollBar {
        }
      }
      Preamp_status {
        id: status
        anchors.top: parent.top
        anchors.right: parent.right
        anchors.rightMargin: 6
        anchors.leftMargin: 12
        online: parent.online
        clkStatus: parent.clkStatus
        clkSource: parent.clkSource
        clkOutSource: parent.clkOutSource
        sampleRate: parent.sampleRate
        outFormat: parent.outFormat
        devID: parent.devID
        onSampleRateChanged:
        {
          pre.channelChange(channelView.CurrentIndex, "sampleRateRole", sampleRate)
        }
        onOnlineChanged:
        {
          pre.channelChange(channelView.currentIndex, "onlineRole", online)
        }
        onClkStatusChanged:
        {
          pre.channelChange(channelView.currentIndex, "clkStatusRole", clkStatus)
        }
        onClkSourceChanged:
        {
          pre.channelChange(channelView.currentIndex, "clkSourceRole", clkSource)
        }
        onClkOutSourceChanged:
        {
          pre.channelChange(channelView.currentIndex, "clkOutSourceRole", clkOutSource)
        }
        onOutFormatChanged:
        {
          pre.channelChange(channelView.currentIndex, "outFormatRole", outFormat)
        }
      }
    }
    

    MainForm.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Window 2.2
    Window {
      id:top
      visible: true
      Connections
      {
        target: preampModel
        onDataChanged:{
          preamp1.devID = preampModel.preampID;
          preamp1.devName = "";
          preamp1.clkSource = preampModel.clkSource
          preamp1.clkOutSource = preampModel.clkOutSource
          preamp1.outFormat =preampModel.outFormat
          preamp1.sampleRate = preampModel.sampleRate
          preamp1.online = preampModel.online
          preamp1.clkStatus = preampModel.clkStatus
          preamp1.channelModel = preampModel
          preamp1.updated();
        }
      }
      Rectangle {
        id: main
        objectName: "mainRect"
        anchors.fill: parent
        color: "#505050"
        Rectangle {
          id: navBar
          anchors.top: parent.top
          width: parent.width*(7/12)
          height: 60
          z: 10
          color: "#000000"
          Row {
            id: row1
            x: parent.x
            y: parent.y
            width: parent.width
            height: parent.height
            Rectangle {
              id: preampsBtn
              width: parent.width/3
              height: parent.height
              color: parent.parent.color
              Text {
                id: preampTxt
                x: 40
                y: 9
                width: 240
                height: 43
                color: "#ffffff"
                text: qsTr("Preamp")
                font.pixelSize: 26
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
              }
              MouseArea {
                id: mouseArea_preamps
                width: 100
                height: 100
                anchors.fill: parent
              }
            }
            Rectangle {
              id: mixerBtn
              width: parent.width/3
              height: parent.height
              color: parent.parent.color
              Text {
                id: mixerTxt
                x: 40
                y: 9
                width: 240
                height: 43
                color: "#ffffff"
                text: "Mixer"
                font.pixelSize: 26
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
              }
              MouseArea {
                id: mouseArea_mixer
                width: 100
                height: 100
                anchors.fill: parent
              }
            }
            Rectangle {
              id: setupBtn
              width: parent.width/3
              height: parent.height
              color: parent.parent.color
              Text {
                id: setupTxt
                x: 40
                y: 9
                width: 240
                height: 43
                color: "#ffffff"
                text: qsTr("Setup")
                font.pixelSize: 26
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
              }
              MouseArea {
                id: mouseArea_setup
                width: 100
                height: 100
                anchors.fill: parent
              }
            }
          }
        }
        Rectangle {
          id: presetBar
          x: parent.width - width
          y: 0
          width: parent.width *(5/12)-20
          height: 60
          z:10
          color: "#000000"
          Row {
            id: row2
            x: 268
            y: 24
            width: 200
            height: 400
            anchors.fill: parent
            Rectangle {
              id: rectangle1
              width: parent.width*(3/14)
              height: parent.height
              color: parent.parent.color
              Text {
                id: text4
                color: "#ffffff"
                text: qsTr("Preset")
                font.pixelSize: 26
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                anchors.fill: parent
              }
            }
            Rectangle {
              id: rectangle2
              width: parent.width*(5/14)
              height: parent.height
              color: parent.parent.color
              ComboBox {
                id: presetBox
                y: 3
                model: presetList
                anchors.fill:parent
                anchors.margins: 3
                Keys.onPressed:
                {
                  if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
                  {
                    presetBox.focus = false
                  }
                }
                contentItem: Text {
                  color : "#496c89"
                  text: presetBox.displayText
                  font.family: "Arial";
                  font.pixelSize: 26;
                  verticalAlignment: Text.AlignVCenter;
                  horizontalAlignment: Text.AlignHCenter;
                }
                background: Rectangle{
                  color: "#16161d"
                }
                indicator: Rectangle { }
                delegate: ItemDelegate {
                  width: presetBox.width
                  contentItem: Text {
                    text: modelData;
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    font.family: "Arial";
                    font.pixelSize: 26;
                    color: "#7a9cb8"
                  }
                  onClicked:
                  {
                    presetBox.currentIndex = index
                    preamplistview.currentIndex = index
                    presetBox.focus = false
                  }
                }
                popup: Popup {
                  id: preampPopup
                  width: parent.width
                  height: preamplistview.contentHeight
                  margins: 0
                  background: Rectangle { color: "#16161d" }
                  contentItem: ListView {
                    id: preamplistview
                    anchors.fill: parent
                    model: presetBox.model
                    boundsBehavior: Flickable.StopAtBounds
                    highlight: Rectangle { color: "#69697C" }
                    spacing: 0
                    highlightFollowsCurrentItem: true
                    currentIndex: presetBox.highlightedIndex
                    delegate: presetBox.delegate
                  }
                  enter: Transition {
                    PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to: preampPopup.parent.height*preampPopup.parent.count }
                  }
                  exit: Transition {
                    PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: preampPopup.parent.height*preampPopup.parent.count; to: 0 }
                  }
                }
                textRole: "key"
                ListModel
                {
                  id: presetList
                  ListElement { key: "Defaults"; }
                  ListElement { key: "Preset 1"; }
                  ListElement { key: "Preset 2"; }
                }
              }
            }
            Rectangle {
              id: rectangle3
              width: parent.width*(3/14)
              height: parent.height
              color: parent.parent.color
              Text {
                id: text5
                x: 277
                y: 23
                color: "#ffffff"
                text: qsTr("store")
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                anchors.fill: parent
                font.pixelSize: 26
              }
            }
            Rectangle {
              id: rectangle4
              width: parent.width*(3/14)
              height: parent.height
              color: parent.parent.color
              Text {
                id: text6
                color: "#ffffff"
                text: qsTr("recall")
                anchors.fill: parent
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
                font.pixelSize: 26
              }
            }
          }
        }
        function updateModel(index, role, value)
        {
          preampModel.setProperty(index, role, value)
          //console.log("Model Updated")
        }
        Preamp {
          id: preamp1
          devID: preampModel.preampID;
          devName: "";
          clkSource: preampModel.clkSource
          clkOutSource: preampModel.clkOutSource
          outFormat:preampModel.outFormat
          sampleRate: preampModel.sampleRate
          online: preampModel.online
          clkStatus: preampModel.clkStatus
          channelModel: preampModel
          anchors.right: parent.right
          anchors.rightMargin: 0
          anchors.left: parent.left
          anchors.leftMargin: 6
          anchors.top: navBar.bottom
          anchors.topMargin: 6
          anchors.bottom: parent.bottom
          anchors.bottomMargin: 6
          onChannelChange:
          {
              main.updateModel(index, prop, value)
          }
        }
      }
    }
    


  • Channel.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtGraphicalEffects 1.0
    
    Rectangle {
      id: channel
      property var gainVal: 49
      property var gainDisp: valToDisp(gainVal)
      property var channelID: "#"
      property var channelName: "ChannelName"
      property var peakValue: false
      property var groupName
      property var group
      property var ribbon
      property var phantom
      property var phase
      property var meterLevel
      property var scribbleStrip
    
      onPhantomChanged: console.log("Delegate Phantom Changed")
      onPhaseChanged:   console.log("Delegate Phase Changed")
      onGroupChanged:   console.log("Group Changed")
      onRibbonChanged:  console.log("Ribbon Changed")
      onGroupNameChanged: console.log("Group Name Changed")
      onGainValChanged: console.log("Gain Changed")
      function valToDisp(val)
      {
        return (val*1 <= 0) ?  -6: val*1+1
      }
      function updateGain(change)
      {
        gainVal = gainVal + change;
        if(gainVal > 68)
          gainVal = 68;
        if(gainVal < 0)
          gainVal = 0;
        gainSlider.value = gainVal;
      }
      function updateGainValue(value)
      {
        if(value*1 > 68)
          gainVal = 68;
        else if(value*1 < 0)
          gainVal = 0;
        else
          gainVal = value;
        gainSlider.value = gainVal;
      }
      DropShadow {
        id : channelShadow
        color: qsTr("#df000000")
        source: channel
        horizontalOffset: 2
        verticalOffset: 4
        samples: 6
        radius: 3
        cached: true
        anchors.fill: channel
        visible: channel.visible
      }
      width: 174
      height: 399
      color: "#00000000"
      Rectangle {
        id: rectangle1
        x: 212
        y: 133
        width: 174
        height: 344
        color:  "#496c89"
        anchors.rightMargin: 0
        anchors.fill: parent
    
        GridView {
          id: gridView1
          anchors.right: parent.right
          anchors.rightMargin: 0
          anchors.left: parent.left
          anchors.leftMargin: 0
          anchors.bottom: parent.bottom
          anchors.bottomMargin: 0
          anchors.top: parent.top
          anchors.topMargin: 0
          cellHeight: 70
          cellWidth: 70
    
          Rectangle {
            id: channelRect
            width: 120
            height: 74
            color: "#333240"
            anchors.top: parent.top
            anchors.topMargin: 6
            anchors.right: meterRect.left
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
    
            Text {
              id: channelIDField
              height: 31
              color: "#ffffff"
              text: channelID
              anchors.right: parent.right
              anchors.rightMargin: 6
              anchors.left: channelNameField.left
              anchors.leftMargin: 6
              anchors.top: parent.top
              anchors.topMargin: 6
              font.pixelSize: 30
              font.bold: true
              horizontalAlignment: Text.AlignLeft
              verticalAlignment: Text.AlignVCenter
            }
            TextField {
              id: channelNameField
              text: channelName
              font.bold: false
              anchors.right: parent.right
              anchors.rightMargin: 3
              anchors.left: parent.left
              anchors.leftMargin: 3
              anchors.bottom: parent.bottom
              anchors.bottomMargin: 6
              anchors.top: channelIDField.bottom
              anchors.topMargin: 0
              font.pixelSize: 15
              selectByMouse: true
              verticalAlignment: TextInput.AlignVCenter
              horizontalAlignment: TextInput.AlignLeft
              validator: RegExpValidator { regExp: /([A-Za-z0-9-_]{1,11})/; }
              background: channelNameBackgroundLoader.item
              color: focus ? "#26c9ff" : "#ffffff"
              selectionColor: "#26c9ff"
              selectedTextColor: "#ffffff"
              Component {
                id: nameFieldNormal
                Rectangle { color: "#333240" }
              }
              Component {
                id: nameFieldSelected
                Rectangle { color: "#636378" }
              }
              Loader {
                id: channelNameBackgroundLoader
                sourceComponent: nameFieldNormal
              }
              onFocusChanged:
              {
                if(focus)
                {
                  selectAll();
                  channelNameBackgroundLoader.sourceComponent = nameFieldSelected
                }
                else
                {
                  channelNameBackgroundLoader.sourceComponent = nameFieldNormal
                }
              }
              onEditingFinished:
              {
                channelName = text;
                text: channelName;
                focus = false;
                channelNameBackgroundLoader.sourceComponent = nameFieldNormal
              }
            }
          }
          Rectangle {
            id: gainRect
            x: 7
            height: 153
            color: "#333240"
            anchors.top: channelRect.bottom
            anchors.topMargin: 6
            anchors.rightMargin: 0
            anchors.leftMargin: 0
            anchors.right: channelRect.right
            anchors.left: channelRect.left
    
            Text {
              id: gainLabel
              width: 40
              color: "#ffffff"
              text: "Gain"
              anchors.bottom: gainUp.top
              anchors.bottomMargin: 6
              font.pixelSize: 20
              horizontalAlignment: Text.AlignHCenter
              verticalAlignment: Text.AlignVCenter
              anchors.left: parent.left
              anchors.leftMargin: 6
              anchors.top: parent.top
              anchors.topMargin: 6
            }
            TextField {
              id: gainDisplay
              text: gainDisp
              echoMode: TextInput.Normal
              anchors.left: gainLabel.right
              anchors.leftMargin: 6
              anchors.right: parent.right
              anchors.rightMargin: 6
              anchors.bottom: gainUp.top
              anchors.bottomMargin: 6
              anchors.top: parent.top
              anchors.topMargin: 6
              horizontalAlignment: Text.AlignHCenter
              verticalAlignment: TextInput.AlignVCenter
              font.pixelSize: 28
              selectByMouse:true
              validator: IntValidator { bottom: -6; top: 69} //TODO Change behavior on entered fields
              background: gainDisplayBackgroundLoader.item
              color: focus ? "#26c9ff" : "#ffffff";
              selectionColor: "#26c9ff"
              selectedTextColor: "#ffffff"
              Loader {
                id: gainDisplayBackgroundLoader
                sourceComponent: gainDisplayNormal
              }
              Component {
                id: gainDisplayNormal
                Rectangle { color: "#333240"; }
              }
              Component {
                id: gainDisplaySelected
                Rectangle { color: "#636378"; }
              }
              onFocusChanged:
              {
                if(focus)
                {
                  selectAll();
                  gainDisplayBackgroundLoader.sourceComponent = gainDisplaySelected
                  gainSlider.visible = true;
                }
                else
                {
                  gainDisplayBackgroundLoader.sourceComponent = gainDisplayNormal;
                }
              }
              onEditingFinished:
              {
                gainSlider.visible = false;
                if(gainDisplay.text*1 < 0)
                {
                  updateGainValue(0);
                }
                else if (gainDisplay.text*1 < 2)
                {
                  updateGainValue(1);
                }
                else
                  updateGainValue(gainDisplay.text*1 - 1);
                focus = false;
                gainDisplayBackgroundLoader.sourceComponent = gainDisplayNormal
              }
            }
            Button {
              id: gainUp
              y: 57
              width: 90
              height: 40
              anchors.left: parent.left
              anchors.leftMargin: 6
              anchors.horizontalCenterOffset: 0
              anchors.horizontalCenter: parent.horizontalCenter
              background: gainUpBackgroundLoader.item
              contentItem: gainUpContentLoader.item
              Loader {
                id: gainUpBackgroundLoader
                sourceComponent: gainUpNormal
              }
              Loader {
                id: gainUpContentLoader
                sourceComponent: upArrow
              }
              Component {
                id: gainUpNormal
                Rectangle { gradient: Gradient { GradientStop { position: 0.0; color: "#69697C" } GradientStop { position: 1.0; color: "#424251" } } }
              }
              Component {
                id: gainUpClicked
                Rectangle { gradient: Gradient { GradientStop { position: 0.0; color: "#3e3e46" } GradientStop { position: 1.0; color: "#1f1f24" } } }
              }
              Component {
                id: upArrow
                Image {
                  source: "Images/upArrow.png"
                  width: 27
                  height: 20
                  fillMode: Image.Pad
                }
              }
              Component {
                id: upArrowClicked
                Image {
                  source: "Images/upArrowClicked.png"
                  fillMode: Image.Pad
                }
              }
              Timer {
                id: gainUpLongpress
                repeat: false
                running: false
                interval: 500
                onTriggered: { gainUpRepeat.restart() }
              }
              Timer {
                id: gainUpRepeat
                repeat: true
                running: false
                interval: 250
                onTriggered: { updateGain(1);  }
              }
              MouseArea {
                id: mouseArea1
                x: 18
                y: 16
                anchors.fill: parent
                hoverEnabled: true
                onPressed:
                {
                  updateGain(1);
                  gainUpLongpress.restart();
                  gainUpBackgroundLoader.sourceComponent = gainUpClicked
                  gainUpContentLoader.sourceComponent = upArrowClicked
                }
                onReleased:
                {
                  gainUpLongpress.stop();
                  gainUpRepeat.stop();
                  gainUpBackgroundLoader.sourceComponent = gainUpNormal
                  gainUpContentLoader.sourceComponent = upArrow
                }
                onExited:
                {
                  gainUpLongpress.stop();
                  gainUpRepeat.stop();
                  gainUpBackgroundLoader.sourceComponent = gainUpNormal
                  gainUpContentLoader.sourceComponent = upArrow
                }
                onCanceled:
                {
                  gainUpLongpress.stop();
                  gainUpRepeat.stop();
                  gainUpBackgroundLoader.sourceComponent = gainUpNormal
                  gainUpContentLoader.sourceComponent = upArrow
                }
              }
            }
            Button {
              id: gainDown
              width: 90
              height: 40
              anchors.top: gainUp.bottom
              anchors.topMargin: 6
              anchors.left: parent.left
              anchors.leftMargin: 6
              anchors.horizontalCenterOffset: 0
              anchors.horizontalCenter: parent.horizontalCenter
              contentItem: gainDownContentLoader.item
              background: gainDownBackgroundLoader.item
              Loader {
                id: gainDownBackgroundLoader
                sourceComponent: gainDownNormal
              }
              Loader {
                id: gainDownContentLoader
                sourceComponent: downArrow
              }
              Component {
                id: gainDownNormal
                Rectangle { gradient: Gradient { GradientStop { position: 0.0; color: "#69697C" } GradientStop { position: 1.0; color: "#424251" } } }
              }
              Component {
                id: gainDownClicked
                Rectangle { gradient: Gradient { GradientStop { position: 0.00; color: "#3e3e46"; } GradientStop { position: 1.00; color: "#1f1e24"; } } }
              }
              Component {
                id: downArrow
                Image {
                  source: "Images/downArrow.png"
                  width: 27
                  height: 20
                  fillMode: Image.Pad
                }
              }
              Component {
                id: downArrowClicked
                Image {
                  source: "Images/downArrowClicked.png"
                  fillMode: Image.Pad
                }
              }
              Timer {
                id: gainDownLongpress
                repeat: false
                running: false
                interval: 500
                onTriggered: { gainDownRepeat.restart() }
              }
              Timer {
                id: gainDownRepeat
                repeat: true
                running: false
                interval: 250
                onTriggered: { updateGain(-1) }
              }
              MouseArea {
                id: mouseArea2
                anchors.rightMargin: 0
                anchors.bottomMargin: -1
                anchors.leftMargin: 0
                anchors.topMargin: 1
                anchors.fill: parent
                hoverEnabled: true
                onPressed:
                {
                  updateGain(-1);
                  gainDownLongpress.restart();
                  gainDownBackgroundLoader.sourceComponent = gainDownClicked
                  gainDownContentLoader.sourceComponent = downArrowClicked
                }
                onReleased:
                {
                  gainDownLongpress.stop();
                  gainDownRepeat.stop();
                  gainDownBackgroundLoader.sourceComponent = gainDownNormal
                  gainDownContentLoader.sourceComponent = downArrow
                }
                onExited:
                {
                  gainDownLongpress.stop();
                  gainDownRepeat.stop();
                  gainDownBackgroundLoader.sourceComponent = gainDownNormal
                  gainDownContentLoader.sourceComponent = downArrow
                }
                onCanceled:
                {
                  gainDownLongpress.stop();
                  gainDownRepeat.stop();
                  gainDownBackgroundLoader.sourceComponent = gainDownNormal
                  gainDownContentLoader.sourceComponent = downArrow
                }
              }
            }
          }
          Rectangle {
            id: groupRect
            y: 293
            height: 43
            color: "#333240"
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: channelRect.left
            anchors.leftMargin: 0
            Button {
              id: btnGroup
              y: 3
              width: 55
              height: 33
              text: qsTr("")
              checkable: true
              checked: group
              anchors.verticalCenter: parent.verticalCenter
              anchors.left: parent.left
              anchors.leftMargin: 6
              background: groupLoader.item
              contentItem: Text {
                color: "#ffffff"
                text: "group"
                font.pixelSize: 16
                verticalAlignment: Text.AlignVCenter
                horizontalAlignment: Text.AlignHCenter
              }
              onCheckedChanged:
              {
                if(checked)
                  groupLoader.sourceComponent = grpStyleON
                else
                  groupLoader.sourceComponent = grpStyleOFF
              }
              onClicked:
              {
                group = !group;
              }
              Loader {
                id: groupLoader
                sourceComponent: grpStyleOFF
              }
              Component {
                id: grpStyleON
                Rectangle { gradient: Gradient { GradientStop { position: 1; color: "#2db201" } GradientStop { position: 0; color: "#238c01" } } }
              }
              Component {
                id: grpStyleOFF
                Rectangle { gradient: Gradient { GradientStop { position: 0; color: "#69697C" } GradientStop { position: 1; color: "#424251" } } }
              }
            }
            ComboBox {
              id: groupSelect
              y: 3
              width: 85
              height: 33
              model: groupList
              currentIndex: groupName
              anchors.verticalCenter: parent.verticalCenter
              anchors.right: parent.right
              anchors.rightMargin: 6
              anchors.left: btnGroup.right
              anchors.leftMargin: 6
              Keys.onPressed:
              {
                if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
                {
                  groupSelect.focus = false
                }
              }
              contentItem: Text {
                color : "#ffffff"
                text: parent.displayText
                font.family: "Arial";
                font.pixelSize: 16;
                verticalAlignment: Text.AlignVCenter;
                horizontalAlignment: Text.AlignLeft;
              }
              background: Rectangle{
                color: "#424251"
              }
              indicator: Rectangle { }
              delegate: ItemDelegate {
                width: groupSelect.width
                contentItem: Text {
                  text:modelData;
                  horizontalAlignment: Text.AlignHCenter
                  verticalAlignment: Text.AlignVCenter
                  font.family: "Arial";
                  font.pixelSize: 16;
                  color: "#ffffff"
                }
                onClicked:
                {
                  groupName = index
                  groupSelect.focus = false
                }
              }
              popup: Popup {
                id: groupPopup
                width: groupSelect.width
                height:listview.contentHeight 
                margins: 0
                background: Rectangle { color: "#424251" }
                contentItem: ListView {
                  id: listview
                  anchors.fill: parent
                  model: groupSelect.model
                  boundsBehavior: Flickable.StopAtBounds
                  highlight: Rectangle { color: "#69697C" }
                  spacing: 0
                  highlightFollowsCurrentItem: true
                  currentIndex: groupSelect.highlightedIndex
                  delegate: groupSelect.delegate
                }
                enter: Transition {
                  SequentialAnimation {
                    PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to: listview.contentHeight }
                    PropertyAction { properties: "contentItem.visible"; value: true }
                  }
                }
                exit: Transition {
                  SequentialAnimation{
                    PropertyAction { properties: "contentItem.visible"; value: false }
                    PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: listview.contentHeight; to: 0 }
                  }
                }
              }
              textRole: "key"
              ListModel
              {
                id: groupList
                ListElement { key: "Group 1"; }
                ListElement { key: "Group 2"; }
                ListElement { key: "Group 3"; }
              }
            }
          }
          Rectangle {
            id: configRect
            height: 44
            color: "#333240"
            anchors.bottom: groupRect.top
            anchors.bottomMargin: 6
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
            Row {
              id: configRow
              spacing: 6
              anchors.right: parent.right
              anchors.rightMargin: 6
              anchors.left: parent.left
              anchors.leftMargin: 6
              anchors.top: parent.top
              anchors.topMargin: 6
              anchors.bottom: parent.bottom
              anchors.bottomMargin: 6
              Button {
                id: btnPhantom
                width: (parent.width-parent.spacing*2)/3
                text: qsTr("")
                checkable: true
                checked: phantom
                anchors.bottom: parent.bottom
                anchors.bottomMargin: 0
                anchors.top: parent.top
                anchors.topMargin: 0
                background: phantomLoader.item
                contentItem: Text {
                  color: "#ffffff"
                  text: "48V"
                  font.pixelSize: 16
                  verticalAlignment: Text.AlignVCenter
                  horizontalAlignment: Text.AlignHCenter
                }
                Loader {
                  id: phantomLoader
                  sourceComponent: phantomStyleOFF
                }
                onCheckedChanged:
                {
                  if(checked)
                    phantomLoader.sourceComponent = phantomStyleON
                  else
                    phantomLoader.sourceComponent = phantomStyleOFF
                }
                onClicked:
                {
                 phantom = !phantom;
                 checked = phantom;
                }
                Component {
                  id: phantomStyleON 
                  Rectangle { gradient: Gradient { GradientStop { position: 1; color: "#d93600" } GradientStop { position: 0; color: "#8c2300" } } }
                }
                Component {
                  id: phantomStyleOFF
                  Rectangle { gradient: Gradient { GradientStop { position: 0; color: "#69697C" } GradientStop { position: 1; color: "#424251" } } }
                }
              }
              Button {
                id: btnPhase
                width: (parent.width-parent.spacing*2)/3
                text: qsTr("")
                state: "OFF"
                checkable: true
                checked: phase
                anchors.bottom: parent.bottom
                anchors.top: parent.top
                anchors.bottomMargin: 0
                anchors.topMargin: 0
                contentItem: Text {
                  color: "#ffffff"
                  text: "Ø"
                  font.pixelSize: 16
                  verticalAlignment: Text.AlignVCenter
                  horizontalAlignment: Text.AlignHCenter
                }
                background: phaseLoader.item
                Loader{
                  id: phaseLoader
                  sourceComponent: phaseStyleOFF
                }
                onCheckedChanged:
                {
                  if(checked)
                    phaseLoader.sourceComponent = phaseStyleON
                  else
                    phaseLoader.sourceComponent = phaseStyleOFF
                }
                onClicked:
                {
                  phase = !phase;
                  checked: phase;
                }
                Component {
                  id: phaseStyleON
                  Rectangle { gradient: Gradient { GradientStop { position: 1; color: "#d9a300" } GradientStop { position: 0; color: "#8c6900" } } }
                }
                Component {
                  id: phaseStyleOFF
                  Rectangle { gradient: Gradient { GradientStop { position: 0; color: "#69697C" } GradientStop { position: 1; color: "#424251" } } }
                }
              }
              Button {
                id: btnRibbon
                width: (parent.width-parent.spacing*2)/3
                text: qsTr("")
                checkable: true
                checked: ribbon
                anchors.bottom: parent.bottom
                anchors.top: parent.top
                anchors.bottomMargin: 0
                anchors.topMargin: 0
                background: ribbonLoader.item
                contentItem: Text {
                  color: "#ffffff"
                  text: "RBN"
                  font.pixelSize: 16
                  verticalAlignment: Text.AlignVCenter
                  horizontalAlignment: Text.AlignHCenter
                }
                Loader {
                  id: ribbonLoader
                  sourceComponent: ribbonStyleOFF
                }
                onCheckedChanged:
                {
                  if(checked)
                    ribbonLoader.sourceComponent = ribbonStyleON
                  else
                    ribbonLoader.sourceComponent = ribbonStyleOFF
                }
                onClicked:
                {
                  ribbon = !ribbon;
                  checked: ribbon;
                }
                Component {
                  id: ribbonStyleON
                  Rectangle { gradient: Gradient { GradientStop { position: 1; color: "#006dd9" } GradientStop { position: 0; color: "#00468c" } } }
                }
                Component {
                  id: ribbonStyleOFF
                  Rectangle { gradient: Gradient { GradientStop { position: 0; color: "#69697C" } GradientStop { position: 1; color: "#424251" } } }
                }
              }
            }
          }
          Rectangle {
            id: meterRect
            width: 46
            color: "#333240"
            anchors.topMargin: 6
            anchors.rightMargin: 6
            anchors.left: gainRect.right
            anchors.leftMargin: 6
            anchors.bottom: gainRect.bottom
            anchors.top: parent.top
            anchors.right: parent.right
            Meter {
              id: meter
              color: "#333240"
              anchors.topMargin: 0
              anchors.fill: parent
            }
          }
          DropShadow {
            id : sliderShadow
            color: qsTr("#df000000")
            source: gainSlider
            horizontalOffset: 3
            verticalOffset: 6
            samples: 16
            radius: 8
            cached: true
            anchors.fill: gainSlider
            visible: gainSlider.visible
          }
          Slider {
            id: gainSlider
            visible: false
            focusPolicy: Qt.NoFocus
            width: 55
            height: 300
            anchors.top: channelRect.bottom
            anchors.topMargin: 0
            anchors.leftMargin: 6
            anchors.bottomMargin: 10
            snapMode: Slider.SnapAlways
            stepSize: 1
            from: 0
            to: 68
            orientation: Qt.Vertical
            background:  Rectangle {
              x: gainSlider.leftPadding
              y: gainSlider.topPadding
              implicitWidth: 50
              implicitHeight: 300
              width: gainSlider.availableWidth
              height: gainSlider.availableHeight
              color: "#333240"; border.width: 3; border.color: "#898989";
              Rectangle{
                color: "#898989"
                //height: parent.height
                width: 6
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                anchors.topMargin: 12
                anchors.bottomMargin: 12
              }
            }
            handle:  Image {
              x: gainSlider.leftPadding + gainSlider.availableWidth/2 - width/2
              y: gainSlider.topPadding + gainSlider.visualPosition * (gainSlider.availableHeight - height)
              source: "/Images/fader_vert_png.png"; height: 26; width: 33
            }
            Component.onCompleted:
            {
              value = gainVal
            }
            onPositionChanged:
            {
              value = Math.floor(value)
              updateGainValue(Math.floor(gainSlider.position*69));
            }
          }
          Rectangle {
            id: rectangle2
            color: "#333240"
            z: -1
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.top: groupRect.bottom
            anchors.topMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            TextArea {
              id: scribbleBox
              x: 41
              y: 32
              width: 80
              height: 20
              color: focus ? "#26c9ff" : "#ffffff";
              bottomPadding: 3
              rightPadding: 3
              topPadding: 3
              leftPadding: 3
              wrapMode: Text.NoWrap
              clip: false
              selectionColor: "#26c9ff"
              selectedTextColor: "#ffffff"
              placeholderText: qsTr("notes...")
              anchors.rightMargin: 3
              anchors.leftMargin: 3
              anchors.bottomMargin: 3
              anchors.topMargin: 3
              anchors.fill: parent
              background: scribbleBackgroundLoader.item
              selectByMouse:true
              font.pixelSize: 14
              text: scribbleStrip
              Loader {
                id: scribbleBackgroundLoader
                sourceComponent: scribbleNormal
              }
              onFocusChanged:
              {
                if(focus)
                {
                  selectAll();
                  scribbleBackgroundLoader.sourceComponent = scribbleSelected
                }
                else
                {
                  color: "#ffffff";
                  scribbleBackgroundLoader.sourceComponent = scribbleNormal
                }
              }
              Keys.onPressed:
              {
                if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
                {
                  console.log("Enter")
                  if(lineCount == 2)
                  {
                    console.log("Line count = 2")
                    event.accepted = true
                  }
                }
              }
              onEditingFinished:
              {
                focus = false;
                scribbleBackgroundLoader.sourceComponent = scribbleNormal;
                scribbleStrip = text;
              }
              Component {
                id: scribbleNormal
                Rectangle { anchors.fill: parent; color: "#333240"; }
              }
              Component {
                id: scribbleSelected
                Rectangle { anchors.fill: parent; color: "#636378"; }
              }
            }
          }
        }
      }
    }
    


  • Last one. I imagine there is a better way to share the contents of a project, but I am not sure what that would be in this case.

    Preamp_status.qml

    import QtQuick 2.7
    import QtQuick.Layouts 1.1
    import QtQuick.Controls 2.0
    import QtGraphicalEffects 1.0
    
    Rectangle {
      id: rectangle2
      width: 174
      height: 399
      color: "#00000000"
      property bool clkStatus: false;
      property bool online: false;
      property int clkSource
      property int clkOutSource: 0;
      property int outFormat: 0;
      property int sampleRate
      property int devID: 0;
      DropShadow {
        id : adcShadow
        color: qsTr("#df000000")
        source: adc
        horizontalOffset: 2
        verticalOffset: 4
        samples: 6
        radius: 3
        cached: true
        anchors.fill: adc
        visible: adc.visible
      }
      Rectangle {
        id: adc
        color: "#496c88"
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        anchors.top: status.bottom
        anchors.leftMargin: 0
        anchors.bottomMargin: 0
        anchors.rightMargin: 0
        anchors.topMargin: 12
        width: parent.width
        Text {
          id: adcLabel
          height: 22
          color: "#ffffff"
          text: qsTr("ADC")
          verticalAlignment: Text.AlignVCenter
          font.bold: true
          anchors.left: parent.left
          anchors.leftMargin: 6
          anchors.top: parent.top
          anchors.topMargin: 6
          font.pixelSize: 24
        }
        Rectangle {
          id: srRect
          height: 55
          color: "#333240"
          anchors.right: parent.right
          anchors.rightMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          anchors.top: adcLabel.bottom
          anchors.topMargin: 6
          Text {
            id: srLabel
            color: "#ffffff"
            text: qsTr("Sample Rate")
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.top: parent.top
            anchors.topMargin: 3
            font.pixelSize: 14
          }
          ComboBox {
            id: srCombo
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            anchors.top: srLabel.bottom
            anchors.topMargin: 3
            model: srModel
            currentIndex: sampleRate
            Keys.onPressed:
            {
              if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
              {
               srCombo.focus = false
              }
            }
            contentItem: Text {
              color : "#ffffff"
              text: srCombo.displayText
              font.family: "Arial";
              font.pixelSize: 14;
              verticalAlignment: Text.AlignVCenter;
              horizontalAlignment: Text.AlignLeft;
            }
            background: Rectangle{
              color: "#424251"
            }
            indicator: Rectangle { }
            delegate: ItemDelegate {
              x: 0
              y: 0
              width:srCombo.width
              contentItem: Text {
                text:modelData;
                horizontalAlignment: Text.AlignLeft
                verticalAlignment: Text.AlignVCenter
                font.family: "Arial";
                font.pixelSize: 14;
                color: "#ffffff"
              }
              onClicked:
              {
                sampleRate = index
                srCombo.focus = false
              }
            }
            popup: Popup {
              id: srPopup
              width: srCombo.width
              height:srView.contentHeight 
              margins: 0
              background: Rectangle { color: "#424251" }
              contentItem: ListView {
                id: srView
                anchors.fill: parent
                model:srCombo.model
                boundsBehavior: Flickable.StopAtBounds
                highlight: Rectangle { color: "#69697C" }
                spacing: 0
                highlightFollowsCurrentItem: true
                currentIndex:sampleRate
                delegate:srCombo.delegate
              }
              enter: Transition {
                SequentialAnimation {
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to: srView.contentHeight }
                  PropertyAction { properties: "contentItem.visible"; value: true }
                }
              }
              exit: Transition {
                SequentialAnimation{
                  PropertyAction { properties: "contentItem.visible"; value: false }
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: srView.contentHeight; to: 0 }
                }
              }
            }
            textRole: "key"
            ListModel
            {
              id: srModel
              ListElement { key: "44.1 kHz"; }
              ListElement { key: "48 kHz"; }
              ListElement { key: "88.2 kHz"; }
              ListElement { key: "96 kHz"; }
              ListElement { key: "176.4 kHz"; }
              ListElement { key: "196 kHz"; }
            }
          }
        }
        Rectangle {
          id: clkSrcRect
          height: 55
          color: "#333240"
          anchors.top: srRect.bottom
          anchors.topMargin: 6
          anchors.right: parent.right
          anchors.rightMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          Text {
            id: clkSrcLabel
            color: "#ffffff"
            text: qsTr("Clock Source")
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.top: parent.top
            anchors.topMargin: 3
            font.pixelSize: 14
          }
          ComboBox {
            id: clkSrcCombo
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            anchors.top: clkSrcLabel.bottom
            anchors.topMargin: 3
            model:clkSrcModel
            currentIndex: clkSource
            Keys.onPressed:
            {
              if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
              {
               clkSrcCombo.focus = false
              }
            }
            contentItem: Text {
              color : "#ffffff"
              text: clkSrcCombo.displayText
              font.family: "Arial";
              font.pixelSize: 14;
              verticalAlignment: Text.AlignVCenter;
              horizontalAlignment: Text.AlignLeft;
            }
            background: Rectangle{
              color: "#424251"
            }
            indicator: Rectangle { }
            delegate: ItemDelegate {
              width:clkSrcCombo.width
              contentItem: Text {
                text:modelData;
                horizontalAlignment: Text.AlignLeft
                verticalAlignment: Text.AlignVCenter
                font.family: "Arial";
                font.pixelSize: 14;
                color: "#ffffff"
              }
              onClicked:
              {
                clkSource = index;
                clkSrcCombo.focus = false
              }
            }
            popup: Popup {
              id:clkSrcPopup
              width: clkSrcCombo.width
              height: clkSrcView.contentHeight 
              margins: 0
              background: Rectangle { color: "#424251" }
              contentItem: ListView {
                id: clkSrcView
                anchors.fill: parent
                model:clkSrcCombo.model
                boundsBehavior: Flickable.StopAtBounds
                highlight: Rectangle { color: "#69697C" }
                spacing: 0
                highlightFollowsCurrentItem: true
                currentIndex:clkSource
                delegate:clkSrcCombo.delegate
              }
              enter: Transition {
                SequentialAnimation {
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to: clkSrcView.contentHeight }
                  PropertyAction { properties: "contentItem.visible"; value: true }
                }
              }
              exit: Transition {
                SequentialAnimation{
                  PropertyAction { properties: "contentItem.visible"; value: false }
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: clkSrcView.contentHeight; to: 0 }
                }
              }
            }
            textRole: "key"
            ListModel
            {
              id:clkSrcModel
              ListElement { key: "Internal"; }
              ListElement { key: "Word"; }
              ListElement { key: "Word - 75Ω"; }
              ListElement { key: "Dante"; }
            }
          }
        }
        Rectangle {
          id: outRect
          height: 55
          color: "#333240"
          anchors.right: parent.right
          anchors.rightMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          anchors.top: clkSrcRect.bottom
          anchors.topMargin: 6
          Text {
            id: outLabel
            color: "#ffffff"
            text: qsTr("Output Format")
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.top: parent.top
            anchors.topMargin: 3
            font.pixelSize: 14
          }
          ComboBox {
            id: outCombo
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            anchors.top: outLabel.bottom
            anchors.topMargin: 3
            model:outModel
            currentIndex: outFormat
            Keys.onPressed:
            {
              if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
              {
               outCombo.focus = false
              }
            }
            contentItem: Text {
              color : "#ffffff"
              text: outCombo.displayText
              font.family: "Arial";
              font.pixelSize: 14;
              verticalAlignment: Text.AlignVCenter;
              horizontalAlignment: Text.AlignLeft;
            }
            background: Rectangle{
              color: "#424251"
            }
            indicator: Rectangle { }
            delegate: ItemDelegate {
              width:outCombo.width
              contentItem: Text {
                text:modelData;
                horizontalAlignment: Text.AlignLeft
                verticalAlignment: Text.AlignVCenter
                font.family: "Arial";
                font.pixelSize: 14;
                color: "#ffffff"
              }
              onClicked:
              {
                outFormat = index
                outCombo.focus = false
              }
            }
            popup: Popup {
              id:outPopup
              width: outCombo.width
              height : outView.contentHeight /*parent.height*parent.count*/
              margins: 0
              background: Rectangle { color: "#424251" }
              contentItem: ListView {
                id: outView
                anchors.fill: parent
                model:outCombo.model
                boundsBehavior: Flickable.StopAtBounds
                highlight: Rectangle { color: "#69697C" }
                spacing: 0
                highlightFollowsCurrentItem: true
                currentIndex:outFormat
                delegate:outCombo.delegate
              }
              enter: Transition {
                SequentialAnimation {
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to:outView.contentHeight }
                  PropertyAction { properties: "contentItem.visible"; value: true }
                }
              }
              exit: Transition {
                SequentialAnimation{
                  PropertyAction { properties: "contentItem.visible"; value: false }
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from:outView.contentHeight; to: 0 }
                }
              }
            }
            textRole: "key"
            ListModel
            {
              id:outModel
              ListElement { key: "Professional"; }
              ListElement { key: "Consumer"; }
            }
          }
        }
        Rectangle {
          id: clkOutRect
          height: 55
          color: "#333240"
          anchors.right: parent.right
          anchors.rightMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          anchors.top: outRect.bottom
          anchors.topMargin: 6
          Text {
            id: clkOutLabel
            color: "#ffffff"
            text: qsTr("Clock Out Source")
            anchors.top: parent.top
            anchors.topMargin: 3
            anchors.left: parent.left
            anchors.leftMargin: 6
            font.pixelSize: 14
          }
          ComboBox {
            id: clkOutCombo
            anchors.right: parent.right
            anchors.rightMargin: 6
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            anchors.top: clkOutLabel.bottom
            anchors.topMargin: 3
            model:clkOutModel
            currentIndex: clkOutSource
            Keys.onPressed:
            {
              if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return)
              {
               clkOutCombo.focus = false;
              }
            }
            contentItem: Text {
              color : "#ffffff"
              text: clkOutCombo.displayText
              font.family: "Arial";
              font.pixelSize: 14;
              verticalAlignment: Text.AlignVCenter;
              horizontalAlignment: Text.AlignLeft;
            }
            background: Rectangle{
              color: "#424251"
            }
            indicator: Rectangle { }
            delegate: ItemDelegate {
              width:clkOutCombo.width
              contentItem: Text {
                text:modelData;
                horizontalAlignment: Text.AlignLeft
                verticalAlignment: Text.AlignVCenter
                font.family: "Arial";
                font.pixelSize: 14;
                color: "#ffffff"
              }
              onClicked:
              {
                clkOutSource = index
                clkOutCombo.focus = false
              }
            }
            popup: Popup {
              id: clkOutPopup
              width: clkOutCombo.width
              height: clkOutView.contentHeight /*parent.height*parent.count*/
              //implicitHeight:clkOutView.contentHeight
              margins: 0
              background: Rectangle { color: "#424251" }
              contentItem: ListView {
                id:clkOutView
                anchors.fill: parent
    
                model:clkOutCombo.model
                boundsBehavior: Flickable.StopAtBounds
                highlight: Rectangle { color: "#69697C" }
                spacing: 0
                highlightFollowsCurrentItem: true
                currentIndex:clkOutSource
                delegate:clkOutCombo.delegate
              }
              enter: Transition {
                SequentialAnimation {
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: 0; to: clkOutView.contentHeight }
                  PropertyAction { properties: "contentItem.visible"; value: true }
                }
              }
              exit: Transition {
                SequentialAnimation{
                  PropertyAction { properties: "contentItem.visible"; value: false }
                  PropertyAnimation { properties: "height"; easing.type: Easing.OutSine; duration: 250; from: clkOutView.contentHeight; to: 0 }
                }
              }
            }
            textRole: "key"
            ListModel
            {
              id:clkOutModel
              ListElement { key: "Internal"; }
              ListElement { key: "External"; }
            }
          }
        }
        Rectangle {
          id: clkStatusRect
          height: 30
          color: "#333240"
          anchors.top: clkOutRect.bottom
          anchors.topMargin: 6
          anchors.right: parent.right
          anchors.rightMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          Text {
            id: clkStatusLabel
            color: "#ffffff"
            text: qsTr("Clock Status")
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.verticalCenter: parent.verticalCenter
            font.pixelSize: 14
          }
          Rectangle {
            id: clkStatusIndicator
            x: 86
            y: 8
            width: 16
            height: 16
            color: clkStatus ? "#00ff00" : "#767676"
            radius: 8
            anchors.right: parent.right
            anchors.rightMargin: 16
            anchors.verticalCenter: parent.verticalCenter
          }
        }
      }
      DropShadow {
        id : statusShadow
        color: qsTr("#df000000")
        source: status
        horizontalOffset: 2
        verticalOffset: 4
        samples: 6
        radius: 3
        anchors.rightMargin: 0
        anchors.bottomMargin: 0
        anchors.leftMargin: 0
        anchors.topMargin: 0
        cached: true
        anchors.fill: status
        visible: status.visible
      }
      Rectangle {
        id: status
        height: 73
        color: "#496c88"
        anchors.top: parent.top
        anchors.topMargin: 0
        anchors.right: parent.right
        anchors.rightMargin: 0
        anchors.left: parent.left
        anchors.leftMargin: 0
        Rectangle {
          id: statusRect
          y: 39
          color: "#333240"
          anchors.left: statusLabel.right
          anchors.leftMargin: 6
          anchors.bottom: parent.bottom
          anchors.bottomMargin: 6
          anchors.top: parent.top
          anchors.right: parent.right
          anchors.topMargin: 6
          anchors.rightMargin: 6
          Rectangle {
            id: onlineIndicator
            x: 138
            y: 16
            width: 16
            height: 16
            color: online ? "#00ff00" : "#767676"
            radius: 8
            anchors.verticalCenter: onlineLabel.verticalCenter
            anchors.right: parent.right
            anchors.rightMargin: 6
            Layout.maximumHeight: 16
            Layout.maximumWidth: 16
            Layout.minimumHeight: 16
            Layout.minimumWidth: 16
            Layout.preferredHeight: 16
            Layout.preferredWidth: 16
          }
          Text {
            id: onlineLabel
            y: 0
            color: "#ffffff"
            text: qsTr("Online")
            anchors.left: parent.left
            anchors.leftMargin: 6
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 6
            anchors.right: onlineIndicator.left
            anchors.rightMargin: 6
            verticalAlignment: Text.AlignVCenter
            Layout.columnSpan: 6
            Layout.rowSpan: 1
            Layout.fillHeight: true
            font.pixelSize: 18
          }
          Text {
            id: preampID
            y: 0
            color: "#ffffff"
            text: devID
            anchors.verticalCenterOffset: -15
            anchors.verticalCenter: parent.verticalCenter
            anchors.left: idLabel.right
            anchors.leftMargin: 6
            verticalAlignment: Text.AlignVCenter
            Layout.columnSpan: 3
            Layout.rowSpan: 1
            Layout.fillHeight: true
            font.pixelSize: 18
          }
          Text {
            id: idLabel
            color: "#ffffff"
            text: qsTr("ID:")
            anchors.top: parent.top
            anchors.topMargin: 6
            anchors.verticalCenterOffset: -15
            anchors.verticalCenter: parent.verticalCenter
            anchors.left: parent.left
            anchors.leftMargin: 5
            verticalAlignment: Text.AlignVCenter
            Layout.columnSpan: 3
            Layout.fillHeight: true
            font.pixelSize: 18
          }
        }
        Text {
          id: statusLabel
          width: 72
          height: 25
          color: "#ffffff"
          text: "Status"
          anchors.top: parent.top
          anchors.topMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          font.bold: true
          verticalAlignment: Text.AlignVCenter
          font.pixelSize: 22
        }
        Button {
          id: pkClearButton
          anchors.right: statusRect.left
          anchors.rightMargin: 6
          anchors.top: statusLabel.bottom
          anchors.topMargin: 6
          anchors.bottom: parent.bottom
          anchors.bottomMargin: 6
          anchors.left: parent.left
          anchors.leftMargin: 6
          background: pkClearBackgroundLoader.item
          contentItem: Text {
            color: "#ffffff"
            text: "Peak Clear"
            font.pixelSize: 12
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
          }
    
          Loader {
            id: pkClearBackgroundLoader
            sourceComponent: pkClearButton.pressed ? pkClearClicked : pkClearNormal
          }
          Component {
            id: pkClearNormal
            Rectangle { gradient: Gradient { GradientStop { position: 0.0; color: "#69697C" } GradientStop { position: 1.0; color: "#424251" } } }
          }
          Component {
            id: pkClearClicked
            Rectangle { gradient: Gradient { GradientStop { position: 0.0; color: "#3e3e46" } GradientStop { position: 1.0; color: "#1f1f24" } } }
          }
    
        }
      }
    }


  • according to the Qt Doc, when you want to modify the model, you'd better call the "beginInsertRows" and "endInsertRows" .



  • @cjmdaixi
    It was my understanding that this operation only needs to occur when the size of the model is being changed, i.e. when rows are being added to the structure. In this case, I simply want to access data members in the model. I will implement the begin/endInsertRows and see if it makes a difference.
    It is also worth mentioning that I am able to confirm that changes are being made to the C++ model. The problem seems to be that the QML is not seeing the updates. I believe a solution would be to find a way to manually update the delegates in the ListView, but there doesn't seem to be a straightforward way to do this.



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