QTreeWidget-View¶
1 QTreeWidget 和 QTreeView 区别,应该使用哪一个¶
Qt 图表:QTreeWidget 和 QTreeView。
QTreeWidget 继承自 QTreeView。
Qt 表格显示使用的是 view/mode 模式,界面和数据分开,两者使用代理链接。QTreeView 就是界面,如果需要修改数据则应该通过代理。
比如 Qt 封装好的 QFileSystemModel在view中显示,就是典型的 mode/view 结构。
如果你需要给 QTreeWidget 增加新建后删除后各种 ui 变化,选中后各种 ui 变化等等特殊效果/事件,建议不要用 QTreeWidget 和 Item,使用 mode/view。
QTreeWidget 最好仅用在表格变化不大的地方。比如固定的列表信息、固定尺寸报表等。
QTreeView 提供了一个接口,setModel 用来设置 mode(也就是数据)。
QTreeWidget 作用就是默认包含了一个 mode,并增加了如果操作这个默认 mode 的接口。
QTableWidget 类提供具有默认模型的基于项目的表视图。
这样当使用 QTreeWidget 时候会简单很多,当然他也引入了一写新的问题,比如默认带了表视图,当我图表视图 ui 很复杂时候不太方便实现,而且数据没有分开逻辑不清晰。
自己的项目中应该使用那个看情况,如果你的图表 ui 不复杂,跟 QTableWidget 默认的图表视图差异不大,而且不需要跟这个 Mode 关联(比如 QSql、QFile),应该使用 QTableWidget,反之则用 QTreeView。
2 常用样式表¶
QTreeWidget {
border-radius:5px;
font-size:20px;
background: rgb(79, 79, 83);
outline:0px;
}
QTreeWidget::item {
color:rgb(233, 233, 233);
background: rgb(79, 79, 83);
min-height: 30px;
}
QTreeWidget::item:alternate {
background: rgb(79, 79, 83);
}
QHeaderView {
color: white;
}
QHeaderView::section {
background-color: rgb(105, 106, 111);
border:none;
font-size:20px;
}
QTreeWidget {
border-radius:5px;
font-size:14px;
background: rgb(79, 79, 83);
outline:0px;
}
QTreeWidget::item {
color:rgb(233, 233, 233);
background: rgb(87, 87, 91);
padding:0px 14px;
min-height: 40px;
}
QTreeWidget::item:alternate {
background: rgb(79, 79, 83);
}
QTreeWidget::item:selected, QTreeWidget::item:hover {
background: rgb(104, 104, 108);
}
QHeaderView {
color: white;
text-align:center;
}
QHeaderView::section {
background-color: rgb(105, 106, 111);
border:none;
font-size:14px;
padding:0px 14px;
}
QTreeView{
border:1px solid #0F1F2F;
selection-background-color:#265687;
selection-color:#4894C6;
alternate-background-color:#265687;
gridline-color:#0F1F2F;
}
QTreeView::branch:closed:has-children{
margin:4px;
border-image:url(:/qss/blackblue/branch_open.png);
}
QTreeView::branch:open:has-children{
margin:4px;
border-image:url(:/qss/blackblue/branch_close.png);
}
QTreeView,QTreeView::branch{
background:#1B3149;
}
QTreeView::item:selected{
color:#4894C6;
background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #243D5B,stop:1 #243D5B);
}
QTreeView::item:hover,QHeaderView{
color:#4894C6;
background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #265687,stop:1 #265687);
}
QTreeView::item{
padding:1px;
margin:0px;
}
3 常用属性¶
QTreeview 有的接口 QTreeWidget 基本都可以使用,下边这里几个是 QTreeWidget 可以使用 QTreeview 常用接口
ui->treeWidget->setAutoExpandDelay(-1);//节点展开鼠标悬停时间
ui->treeWidget->setIndentation(10);//缩进字节(添加自定义图标用的)
ui->treeWidget->setRootIsDecorated(1);//第一列是否缩进(添加自定义图标用的)
ui->treeWidget->setUniformRowHeights(1);//强制所有项等高
ui->treeWidget->setItemsExpandable(1);//是否支持展开折叠
ui->treeWidget->setSortingEnabled(1);////是否支持自动排序(如果需要排序的表格一般都常常改变,建议别用QTreeWidget了)
ui->treeWidget->setAnimated(1);//是否支持动画,这个需要单独在定义显示动画
ui->treeWidget->setAllColumnsShowFocus(1);//选中是焦点在本格还是本行(节点)
ui->treeWidget->setWordWrap(1);//自动换行,如果文字太多想用...省略的话,用矩形框把qstring封一下在画
ui->treeWidget->setHeaderHidden(0);//是否显示标题,下边有一个Visible,区别在于Visible是隐藏Hidden是在内存销毁
ui->treeWidget->setExpandsOnDoubleClick(0);//双击是否可以展开子节点。其实如果有节点的话还是建议用mode/view
QTreeWidget 专有的
1. Ui 上 Widget 显示几列
QTreeWidget 的 head 专有的
- Widget 表头是否可见
- Widget 表头顺序是否可以拖动改变
- Widget 表头文字属性(居中?靠左?靠右?)
- Widget 表头宽度
- Widget 被选部分是否高亮显示
- Widget 各个区域的最小值
- Widget 排序按钮是否显示
- Widget 最后一个区域是否占满表格余下的所有部分
for (qint32 i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
if (item) {
item->setFlags(item->flags() | Qt::ItemIsEditable);
}
}
4 设置表格状态:编辑、选中等¶
for (qint32 i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
if (item) {
item->setFlags(item->flags() | Qt::ItemIsEditable);
}
}
Qt.NoItemFlags 0 没有设置任何属性。
Qt.ItemIsSelectable 1 可以选择。
Qt.ItemIsEditable 2 可以编辑。
Qt.ItemIsDragEnabled 4 可以拖动它。
Qt.ItemIsDropEnabled 8 它可以用作放置目标。
Qt.ItemIsUserCheckable 16 用户可以选中或取消选中它。
Qt.ItemIsEnabled 32 用户可以与项目交互。
Qt.ItemIsTristate 64 该项可通过三个独立的状态进行检查。
可以看到上面没有不可以编辑这个选项,我现在用法是。如果需要把某几个设置为不可以编辑的话,直接自定义一个 QStyledItemDelegate,让后把 DisableEditor 返回空算了(比较傻哈),比如下边是设置第一列可以编辑,后边几列不可以编辑。其实如果你要是都不可以编辑的话,初始化默认就是不可编辑的,什么都不用加。
class DisableEditor : public QStyledItemDelegate {
public:
explicit DisableEditor(QWidget *parent = nullptr);
virtual QWidget *createEditor(
QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override ;
};
DisableEditor::DisableEditor(QWidget *parent)
: QStyledItemDelegate(parent) {
}
QWidget *DisableEditor::createEditor(
QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Q_UNUSED(parent);
Q_UNUSED(option);
Q_UNUSED(index);
return nullptr;
}
for (qint32 i = 1; i < ui->treeWidget->columnCount(); ++i) {
ui->treeWidget->setItemDelegateForColumn(
i, new DisableEditor(ui->treeWidget));
}
5 检索、右键菜单¶
- 高自由度的 Tree 视图:
QTreeView
+QAbstractItemModel
+QSortFilterProxyModel
+QStyledItemDelegate
QTreeWidget
=QTreeView
+QTreeWidgetItem
(无法增加自定义接口)- 图省事且明确知道需求不会增加,可以用
QTreeWidget
方便些。
QTreeWidget
是真方便,就怕你照着需求做完,项目经理让你加代理。
5.1 增加表头和右键菜单¶
class KissQTreeWidget : public QTreeWidget {
Q_OBJECT
public:
explicit KissQTreeWidget(QWidget *parent = nullptr);
virtual ~KissQTreeWidget();
protected:
void Initial();
virtual void contextMenuEvent(QContextMenuEvent *e);
QMenu *context_menu_;
QItemSelection selection_;
};
KissQTreeWidget::KissQTreeWidget(QWidget *parent): QTreeWidget(parent) {
this->Initial();
}
KissQTreeWidget::~KissQTreeWidget() {
}
void KissQTreeWidget::Initial() {
QStringList header_list = {"Tag ID", "VR", "VM", "Length", "Description", "value"};
QList<int> headerwidth_list = {200, 70, 100, 50, 300, 300};
this->setHeaderLabels(header_list);
qint32 i = 0;
foreach (auto var, headerwidth_list) {
this->setColumnWidth(i, var);
i++;
}
this->setGeometry(0, 0, 1200, 800);
this->header()->setDefaultAlignment(Qt::AlignCenter);
// 右键菜单
this->context_menu_ = new QMenu(this);
context_menu_->addAction(tr("Open the folder where DCM is located"), this, [ = ]() {
});
context_menu_->addAction(tr("Copy current selection"), this, [ = ]() {
});
context_menu_->addSeparator();
context_menu_->addAction(tr("Copy all values"), this, [ = ]() {
});
}
void KissQTreeWidget::contextMenuEvent(QContextMenuEvent *e) {
if (indexAt(e->pos()).isValid()) {
context_menu_->popup(e->globalPos());
}
}
5.2 增加检索功能¶
void DicomTagsWidget::SlotFilterChanged() {
QTreeWidgetItemIterator it(tree_wid_);
QString str = ui.filter->text();
if(str.isEmpty()) {
while (*it) {
(*it)->setHidden(false);
++it;
}
return;
}
while (*it) {
(*it)->setHidden(true);
++it;
}
QList<QTreeWidgetItem *> items;
for(qint32 i = 0; i < tree_wid_->columnCount(); i++) {
items << tree_wid_->findItems(str, Qt::MatchContains | Qt::MatchRecursive, i);
}
foreach (auto var, items) {
var->setHidden(false);
while (var->parent()) {
var->parent()->setHidden(false);
var = var->parent();
}
}
}
6 代理退出立即修改数据¶
6.1 需求¶
列表上有一些 QComboBox 和 QDoubleSpinBox,留下来的代码是用的代理(createEditor + setEditorData + setModelData)
最新的需求需要实时修改。如果是用的代理(paint + editorEvent)或者 QTableWidget,这个需求很好实现。
代理(createEditor + setEditorData + setModelData)则是在列表的 editorEvent 后便判断是否完成修改来发送 commitData 和 sizeHintChanged 来实现退出编辑后修改 Model 的数据。
SetEditorData 和 createEditor 都是 const 修饰的,信号发不出去,index 也是 const 传入的,无法调用 setModelData。
不想大该原来的代码,干脆在代理初始化的时候把 Model 传进去吧。
6.2 代码¶
QWidget *ComboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/, const QModelIndex &index) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItems(item_list_);
editor->installEventFilter(const_cast<ComboxDelegate *>(this));
connect(editor, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
this, [&, index](int data) {
model_->setData(index, data, Qt::EditRole);
});
return editor;
}
6.3 反思¶
感觉代理就是为了抽离 data 和 view,像上边那样操作就失去了代理的意义。直接用 QTableWidget 设置每个单元个的 Widget 就好了。