【问题标题】:How to support row selection in a TableView for Qt 5.12 and Qt Quick Controls 2? [closed]如何在 Qt 5.12 和 Qt Quick Controls 2 的 TableView 中支持行选择? [关闭]
【发布时间】:2021-08-27 11:07:11
【问题描述】:

我有一个简单的示例项目here 来演示问题。

我在下面包含了我认为是相关来源的内容,但其余部分可以在上面的项目链接中找到,或者我可以编辑并在有用的情况下添加更多内容。

我正在查看 TableView 文档here。我没有看到任何关于如何支持行选择的提及。如果我查看here,我会看到 5.15 的文档,其中描述了行选择。而且,如果我查看 here,我会看到一些有关 Qt Quick Controls 1 行选择的文档,但这也不适用于我的情况。

对于 Qt 5.12 和 Qt Quick Controls 2,我无法找到相应的文档。

对于我的案例,如何支持 TableView 中的行选择?如何找到适合我情况的正确文档?

main.qml

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12

import Backend 1.0

ApplicationWindow
{
  id:      root
  visible: true

  width:  768
  height: 450

  minimumWidth:  768
  minimumHeight: 450

  property string backendReference: Backend.objectName

  TableView
  {
    id: tableView

    columnWidthProvider: function( column )
    {
      return 100;
    }

    rowHeightProvider: function( column )
    {
      return 23;
    }

    anchors.fill: parent
    topMargin:    columnsHeader.implicitHeight

    model: Backend.modelResults.list

    ScrollBar.horizontal: ScrollBar {}
    ScrollBar.vertical:   ScrollBar {}

    clip: true

    delegate: Rectangle
    {
      Text
      {
        text: display
        anchors.fill: parent
        anchors.margins: 10
        color: 'black'
        font.pixelSize: 15
        verticalAlignment: Text.AlignVCenter
      }
    }

    Rectangle // mask the headers
    {
      z: 3

      color: "#222222"

      y: tableView.contentY
      x: tableView.contentX

      width:  tableView.leftMargin
      height: tableView.topMargin
    }

    Row
    {
      id: columnsHeader
      y:  tableView.contentY

      z: 2

      Repeater
      {
        model: tableView.columns > 0 ? tableView.columns : 1

        Label
        {
          width:  tableView.columnWidthProvider(modelData)
          height: 35

          text: Backend.modelResults.list.headerData( modelData, Qt.Horizontal )

          font.pixelSize:    15
          padding:           10
          verticalAlignment: Text.AlignVCenter

          background: Rectangle
          {
            color: "#eeeeee"
          }
        }
      }
    }

    ScrollIndicator.horizontal: ScrollIndicator { }
    ScrollIndicator.vertical: ScrollIndicator { }
  }
}

【问题讨论】:

  • 由于性能原因,我相信 QtQuick 2 中的 TableView 与 QtQuick.Controls 1 中的 TableView 相比是完全重写的。根据您的问题以及提供的文档,我没有看到任何表明 TableView 2 实际上包含此功能的内容(相反,您可以自己实现它)。除了它出现在 TableView 1 API 中之外,您还有理由相信这一点吗?
  • 如何自己实现?是否有任何示例代码可以演示如何执行此操作?我已经看过了,但找不到任何东西。
  • “选择”是一个任意概念,因此如何选择某项内容取决于您。一个简单的例子:在你的模型条目中添加一个“selected”标志,然后通过交互来切换它——每个委托都可以包含一个 MouseArea,当单击它时,它将执行类似model.selected = !model.selected 的操作。当然,您需要为更复杂的选择生成任何进一步的逻辑。相关问题:stackoverflow.com/questions/65733392/…stackoverflow.com/questions/62633171/…
  • 看来我还需要了解角色如何工作并以某种方式使用 Qt::DecorationRole 来指示该行已被选中。但是,它们的工作方式也让我难以理解(stackoverflow.com/questions/67895259/…)。我只需要支持一次选择一行。

标签: qt qml qtquick2 qtquickcontrols2


【解决方案1】:

我能够滚动自己的选择。所需的逻辑很简单,因为我只需要支持单行的选择。

我的 ModelItem 看起来像:

struct ModelItem
{
    Q_GADGET

    Q_PROPERTY( QString population MEMBER population )
    Q_PROPERTY( int averageAge MEMBER averageAge )
    Q_PROPERTY( bool selected MEMBER selected )

public:

    enum class Role {
      Selection = Qt::UserRole,
      ColumnType,
      ColorValue
    };
    Q_ENUM(Role)

    QString population;
    int     averageAge;
    bool    selected    { false };

    bool operator!=( const ModelItem& other )
    {
        return other.population != this->population
            || other.averageAge != this->averageAge;
    }

};

这里的关键点是用来保存一个项目是否被选中的selected属性和自定义Selection角色的定义。

我的自定义角色需要一个 roleNames 函数

QHash<int, QByteArray>
ModelList::
roleNames() const
{
  return {
    { Qt::DisplayRole, "display" },
    { int( ModelItem::Role::Selection ), "selected" },
    { int( ModelItem::Role::ColumnType ), "type" },
    { int( ModelItem::Role::ColorValue ), "colorValue" }
  };
}

这里的关键是我使用字符串“selected”来指代我的自定义选择角色。

我的数据函数如下:

QVariant
ModelList::
data( const QModelIndex& index, int role ) const
{
    const ModelItem modelItem = mList.at( index.row() );

    QVariant result = QVariant();

    if ( role == Qt::DisplayRole )
    {
        if ( index.column() == 0 )
        {
          result = QVariant( QString( modelItem.population ) );
        }
        else
        {
          result = QVariant( QString::number( modelItem.averageAge ) );
        }
    }

    if ( role == int( ModelItem::Role::Selection ) )
    {
        result = QVariant( QString( modelItem.selected ? "#eeeeee" : "white" ) );
    }

    if ( role == int( ModelItem::Role::ColumnType ) )
    {
      if ( index.column() == 0 )
        result = QVariant( QString( "stringValue" ) );
      else
        result = QVariant( QString( "colorValue" ) );
    }

    if ( role == int( ModelItem::Role::ColorValue ) )
    {
      QString color;

      if ( modelItem.averageAge < 13 )
        color = "red";
      else if ( modelItem.averageAge < 35 )
        color = "yellow";
      else
        color = "green";

      result = QVariant( color );
    }

    qDebug() << role << " " << result;

    return result;
}

这里的关键是检查角色是否是自定义的Selection角色,然后根据modelItem中selected的值返回一个颜色。

最后一步是让 QML 使用这个自定义角色:

delegate: DelegateChooser
{
  role: "type"

  DelegateChoice
  {
    roleValue: "colorValue"

    delegate: Rectangle
    {
      color: selected

      Rectangle
      {
        color: colorValue

        width: parent.height
        height: parent.height

        radius: width * 0.5;

        anchors.horizontalCenter: parent.horizontalCenter;
      }

      MouseArea
      {
        anchors.fill: parent

        onClicked:
        {
          var idx = Backend.modelResults.list.index( row, column )

          console.log( "Clicked cell: ", idx.row, " ", Backend.modelResults.list.data( idx ) )

          Backend.modelResults.list.select( idx.row );
        }
      }
    }
  }

  DelegateChoice
  {
    delegate: Rectangle
    {
      color: selected

      Text
      {
        text: display
        anchors.fill: parent
        anchors.margins: 10
        color: 'black'
        font.pixelSize: 15
        verticalAlignment: Text.AlignVCenter
      }

      MouseArea
      {
        anchors.fill: parent

        onClicked:
        {
          var idx = Backend.modelResults.list.index( row, column )

          console.log( "Clicked cell: ", idx.row, " ", Backend.modelResults.list.data( idx ) )

          Backend.modelResults.list.select( idx.row );
        }
      }
    }
  }
}

这里的关键是每个 DelegateChoice 的代表矩形的 color: selected 部分。 selected 指的是我在上面的 roleNames 函数中设置的字符串 selected。系统知道使用正确的 ModelItem::Role 调用数据函数,因此 if ( role == int( ModelItem::Role::Selection ) ) 解析为 true

对于每个 DelegateChoice,我为调用模型中选择函数的单元格定义了一个 MouseArea。选择函数是:

void
ModelList::
select( int index )
{
  beginResetModel();

  for ( int x = 0; x < this->mList.length(); x++ )
  {
    this->mList[x].selected = ( x == index );
  }

  endResetModel();
}

begin/endResetModel 会在选择更改时重新绘制表格。

示例项目已更新。

欢迎提出对此解决方案的改进建议。

【讨论】:

    猜你喜欢
    • 2017-09-11
    • 1970-01-01
    • 2013-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    相关资源
    最近更新 更多