如何在 Qt QML TableView 单元格中绘制圆圈?
How do I draw a circle in a Qt QML TableView cell?
我有一个简单的示例项目 here 来演示问题。
我在下面包含了我认为是相关来源的内容,但其余部分在上面的项目 link 中可用,或者我可以编辑并包含更多有用的内容。
根据一些研究,当列为 1 时,我似乎需要在我的数据函数中使用 Qt::DecorationRole
和 return 图像。但是,那部分代码永远不会执行。我遗漏了一些关于角色概念如何与 Qt QML TableView
一起工作的重要且明显的内容。
我需要更改什么才能在第 1 列(平均年龄)中画一个圆圈?我希望这个圆圈在 age < 13
时为红色,在 < 35
时为黄色,否则为绿色。
main.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Qt.labs.qmlmodels 1.0
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: DelegateChooser {
// role: "type"
DelegateChoice {
roleValue: "decoration"
Rectangle
{
color: 'red'
anchors.fill: parent
}
}
DelegateChoice {
// roleValue: "display"
Rectangle
{
Text
{
text: display
anchors.fill: parent
anchors.margins: 10
color: 'black'
font.pixelSize: 15
verticalAlignment: Text.AlignVCenter
}
}
}
}
// 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 { }
}
}
modeldata.cpp
#include "modeldata.h"
//
// ModelList
//
ModelList::
ModelList( QObject* parent )
: QAbstractTableModel (parent )
{
// mRoleNames = QAbstractTableModel::roleNames();
// mRoleNames.insert( 1, QByteArray( "type" ) );
}
int
ModelList::
rowCount(const QModelIndex &) const
{
int size = mList.size();
return size;
}
int
ModelList::
columnCount( const QModelIndex & ) const
{
return 2;
}
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 == Qt::DecorationRole )
{
qDebug() << "decorate 1";
}
return result;
}
QVariant
ModelList::
headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( section == 0 )
return QVariant( QString( "Population" ) );
else
return QVariant( QString( "Average Age" ) );
}
int
ModelList::
size() const
{
return mList.size();
}
const QList<ModelItem>&
ModelList::
list() const
{
return mList;
}
void
ModelList::
removeAt( int index )
{
if ( index < 0 || index >= mList.size() )
return;
beginRemoveRows( QModelIndex(), index, index );
mList.removeAt( index );
endRemoveRows();
emit sizeChanged();
}
void
ModelList::
add( const QString& population, const int averageAge )
{
ModelItem item;
item.population = population;
item.averageAge = averageAge;
add( item );
}
QHash<int, QByteArray>
ModelList::
roleNames() const
{
return {
{ Qt::DisplayRole, "display" },
{ Qt::DecorationRole, "decorations" }
};
// return this->mRoleNames;
}
void
ModelList::
add(const ModelItem& item)
{
const int index = mList.size();
beginInsertRows( QModelIndex(), index, index );
mList.append( item );
endInsertRows();
emit sizeChanged();
}
void
ModelList::
reset()
{
if ( mList.isEmpty() )
return;
beginRemoveRows( QModelIndex(), 0, mList.size() - 1 );
mList.clear();
endRemoveRows();
emit sizeChanged();
}
//
// ModelResults
//
ModelResults::ModelResults(QObject* parent)
: QObject(parent)
{
mList = new ModelList( this );
qRegisterMetaType<ModelItem>("ModelItem");
}
ModelList* ModelResults::list() const
{
return mList;
}
void ModelResults::reset()
{
mList->reset();
}
我已经能够在 averageAge 字段中绘制正确的圆圈。
我的 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;
}
};
这里的重点是 ColumnType 和 ColorValue 角色的定义。
我的自定义角色需要一个 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" }
};
}
自定义角色需要由 roleNames 提供并指定字符串“type”和“colorValue”。
我的数据函数如下所示:
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;
}
这里的一个关键点是,当使用角色 ColumnType 时,我 return 该列是 stringValue 还是 colorValue。
此外,当使用角色 ColorValue 时,我会查看 modelItem 的 averageAge 和 return 包含要使用的颜色的字符串。
最后一步是让 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 );
}
}
}
}
}
首先,对于 DelegateChooser,角色由我们自定义的“类型”角色指定。系统知道用这个角色调用我们的数据函数。当数据函数returns "colorValue"时,第一个DelegateChoice被选中,因为roleValue是"colorValue"。第二个 DelegateChoice 没有 roleValue,因为它似乎需要一个默认的 DelegateChoice,而“stringValue”是默认值。
其次,“colorValue”委托选择已定义color: colorValue
。这会导致系统再次调用具有 ColorValue 角色的数据函数,然后 return 为单元格设置正确的颜色。
示例项目已更新。
欢迎提出对此解决方案的改进建议。
我有一个简单的示例项目 here 来演示问题。
我在下面包含了我认为是相关来源的内容,但其余部分在上面的项目 link 中可用,或者我可以编辑并包含更多有用的内容。
根据一些研究,当列为 1 时,我似乎需要在我的数据函数中使用 Qt::DecorationRole
和 return 图像。但是,那部分代码永远不会执行。我遗漏了一些关于角色概念如何与 Qt QML TableView
一起工作的重要且明显的内容。
我需要更改什么才能在第 1 列(平均年龄)中画一个圆圈?我希望这个圆圈在 age < 13
时为红色,在 < 35
时为黄色,否则为绿色。
main.qml
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import Qt.labs.qmlmodels 1.0
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: DelegateChooser {
// role: "type"
DelegateChoice {
roleValue: "decoration"
Rectangle
{
color: 'red'
anchors.fill: parent
}
}
DelegateChoice {
// roleValue: "display"
Rectangle
{
Text
{
text: display
anchors.fill: parent
anchors.margins: 10
color: 'black'
font.pixelSize: 15
verticalAlignment: Text.AlignVCenter
}
}
}
}
// 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 { }
}
}
modeldata.cpp
#include "modeldata.h"
//
// ModelList
//
ModelList::
ModelList( QObject* parent )
: QAbstractTableModel (parent )
{
// mRoleNames = QAbstractTableModel::roleNames();
// mRoleNames.insert( 1, QByteArray( "type" ) );
}
int
ModelList::
rowCount(const QModelIndex &) const
{
int size = mList.size();
return size;
}
int
ModelList::
columnCount( const QModelIndex & ) const
{
return 2;
}
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 == Qt::DecorationRole )
{
qDebug() << "decorate 1";
}
return result;
}
QVariant
ModelList::
headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( section == 0 )
return QVariant( QString( "Population" ) );
else
return QVariant( QString( "Average Age" ) );
}
int
ModelList::
size() const
{
return mList.size();
}
const QList<ModelItem>&
ModelList::
list() const
{
return mList;
}
void
ModelList::
removeAt( int index )
{
if ( index < 0 || index >= mList.size() )
return;
beginRemoveRows( QModelIndex(), index, index );
mList.removeAt( index );
endRemoveRows();
emit sizeChanged();
}
void
ModelList::
add( const QString& population, const int averageAge )
{
ModelItem item;
item.population = population;
item.averageAge = averageAge;
add( item );
}
QHash<int, QByteArray>
ModelList::
roleNames() const
{
return {
{ Qt::DisplayRole, "display" },
{ Qt::DecorationRole, "decorations" }
};
// return this->mRoleNames;
}
void
ModelList::
add(const ModelItem& item)
{
const int index = mList.size();
beginInsertRows( QModelIndex(), index, index );
mList.append( item );
endInsertRows();
emit sizeChanged();
}
void
ModelList::
reset()
{
if ( mList.isEmpty() )
return;
beginRemoveRows( QModelIndex(), 0, mList.size() - 1 );
mList.clear();
endRemoveRows();
emit sizeChanged();
}
//
// ModelResults
//
ModelResults::ModelResults(QObject* parent)
: QObject(parent)
{
mList = new ModelList( this );
qRegisterMetaType<ModelItem>("ModelItem");
}
ModelList* ModelResults::list() const
{
return mList;
}
void ModelResults::reset()
{
mList->reset();
}
我已经能够在 averageAge 字段中绘制正确的圆圈。
我的 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;
}
};
这里的重点是 ColumnType 和 ColorValue 角色的定义。
我的自定义角色需要一个 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" }
};
}
自定义角色需要由 roleNames 提供并指定字符串“type”和“colorValue”。
我的数据函数如下所示:
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;
}
这里的一个关键点是,当使用角色 ColumnType 时,我 return 该列是 stringValue 还是 colorValue。
此外,当使用角色 ColorValue 时,我会查看 modelItem 的 averageAge 和 return 包含要使用的颜色的字符串。
最后一步是让 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 );
}
}
}
}
}
首先,对于 DelegateChooser,角色由我们自定义的“类型”角色指定。系统知道用这个角色调用我们的数据函数。当数据函数returns "colorValue"时,第一个DelegateChoice被选中,因为roleValue是"colorValue"。第二个 DelegateChoice 没有 roleValue,因为它似乎需要一个默认的 DelegateChoice,而“stringValue”是默认值。
其次,“colorValue”委托选择已定义color: colorValue
。这会导致系统再次调用具有 ColorValue 角色的数据函数,然后 return 为单元格设置正确的颜色。
示例项目已更新。
欢迎提出对此解决方案的改进建议。