如何支持 Qt 5.12 和 Qt Quick Controls 2 的 TableView 中的行选择?
How to support row selection in a TableView for Qt 5.12 and Qt Quick Controls 2?
我有一个简单的示例项目 here 来演示问题。
我在下面包含了我认为是相关来源的内容,但其余部分在上面的项目 link 中可用,或者我可以编辑并包含更多有用的内容。
我正在查看 TableView 文档 here. I do not see any mention of how to support row selection. If I look here, I see documentation for 5.15, where row selection is described. And, if I look 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 { }
}
}
我能够自己滚动 selection。所需的逻辑很简单,因为我只需要支持单行的 selection。
我的 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 属性 来保存项目是否 selected 和自定义选择角色的定义。
我的自定义角色需要一个 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;
}
这里的关键是检查角色是否是自定义选择角色,然后 return 根据 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 的代表的 Rectangle 的 color: selected
部分。 selected
指的是我在上面roleNames函数中设置的字符串selected
。系统知道使用正确的 ModelItem::Role 调用数据函数,因此 if ( role == int( ModelItem::Role::Selection ) )
解析为 true
.
对于每个 DelegateChoice,我为调用模型中 select 函数的单元格定义了一个 MouseArea。 select函数是:
void
ModelList::
select( int index )
{
beginResetModel();
for ( int x = 0; x < this->mList.length(); x++ )
{
this->mList[x].selected = ( x == index );
}
endResetModel();
}
begin/endResetModel 导致 table 在 selection 改变时重新绘制。
示例项目已更新。
欢迎提出对此解决方案的改进建议。
我有一个简单的示例项目 here 来演示问题。
我在下面包含了我认为是相关来源的内容,但其余部分在上面的项目 link 中可用,或者我可以编辑并包含更多有用的内容。
我正在查看 TableView 文档 here. I do not see any mention of how to support row selection. If I look here, I see documentation for 5.15, where row selection is described. And, if I look 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 { }
}
}
我能够自己滚动 selection。所需的逻辑很简单,因为我只需要支持单行的 selection。
我的 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 属性 来保存项目是否 selected 和自定义选择角色的定义。
我的自定义角色需要一个 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;
}
这里的关键是检查角色是否是自定义选择角色,然后 return 根据 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 的代表的 Rectangle 的 color: selected
部分。 selected
指的是我在上面roleNames函数中设置的字符串selected
。系统知道使用正确的 ModelItem::Role 调用数据函数,因此 if ( role == int( ModelItem::Role::Selection ) )
解析为 true
.
对于每个 DelegateChoice,我为调用模型中 select 函数的单元格定义了一个 MouseArea。 select函数是:
void
ModelList::
select( int index )
{
beginResetModel();
for ( int x = 0; x < this->mList.length(); x++ )
{
this->mList[x].selected = ( x == index );
}
endResetModel();
}
begin/endResetModel 导致 table 在 selection 改变时重新绘制。
示例项目已更新。
欢迎提出对此解决方案的改进建议。