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 就好了。