1、功能介绍
本篇主要使用Qt停靠控件QDockWidget、树控件QTreeWidget及Qt属性控件QtTreePropertyBrowser来搭建一个简单实用的主界面布局。效果如下所示。
2、控件使用详解
2.1 停靠控件QDockWidget
QDockWidget可以停靠在 QMainWindow 内或作为桌面上的顶级窗口浮动。默认值为 Qt::AllDockWidgetAreas。下面是停靠区域的枚举值。
Qt::LeftDockWidgetArea
Qt::RightDockWidgetArea
Qt::TopDockWidgetArea
Qt::BottomDockWidgetArea
Qt::AllDockWidgetAreas
Qt::NoDockWidgetArea
QDockWidget可以直接从Qt designer的部件列表中拖拽即可,或者用纯代码进行添加和布局,这两种方式都可以。此处采用的是拖拽的方式进行的添加。然后依据需要在dock控件内嵌入了树控件和属性控件。
2.2 树控件QTreeWidget
QTreeWidget是由Qt提供的树形表达控件,默认已经进行了一些封装,便于快速使用。如果想更高度化自由定制的话可以采用Tree view控件。需要注意的是
setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
设定好Qt::ItemIsAutoTristate属性后,父级子级的勾选复选框会自动进行变化,比较方便。
2.2.1 初步使用
下面是QTreeWidget的简单用法:
void QVisualSystem::InitialBrowserTree()
{
// 添加节点间连接线但会导致Qt::ItemIsAutoTristate失效
//ui.BrowserTree->setStyle(QStyleFactory::create("windows"));
ui.BrowserTree->setIconSize(QSize(32, 32));
ui.BrowserTree->setColumnCount(1);
ui.BrowserTree->setColumnWidth(0, 300);
QString sQssStyle = getQssStyle();
ui.BrowserTree->setStyleSheet(sQssStyle);
ui.BrowserTree->setHeaderHidden(true); //隐藏表头
//第一个分组
QTreeWidgetItem* group1 = new QTreeWidgetItem(ui.BrowserTree);
group1->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
group1->setText(0, QString::fromStdWString(L"专业"));
QTreeWidgetItem* subItem1 = new QTreeWidgetItem(group1);
subItem1->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem1->setText(0, QString::fromStdWString(L"热力"));
subItem1->setCheckState(0, Qt::Unchecked);
{
QTreeWidgetItem* subItem11 = new QTreeWidgetItem(subItem1);
subItem11->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem11->setText(0, QString::fromStdWString(L"供暖"));
subItem11->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem* subItem12 = new QTreeWidgetItem(subItem1);
subItem12->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem12->setText(0, QString::fromStdWString(L"地热"));
subItem12->setCheckState(0, Qt::Unchecked);
}
QTreeWidgetItem* subItem2 = new QTreeWidgetItem(group1);
subItem2->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem2->setText(0, QString::fromStdWString(L"给水"));
subItem2->setCheckState(0, Qt::Unchecked);
{
QTreeWidgetItem* subItem11 = new QTreeWidgetItem(subItem2);
subItem11->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem11->setText(0, QString::fromStdWString(L"家用水"));
subItem11->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem* subItem12 = new QTreeWidgetItem(subItem2);
subItem12->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem12->setText(0, QString::fromStdWString(L"工业水"));
subItem12->setCheckState(0, Qt::Unchecked);
}
QTreeWidgetItem* subItem3 = new QTreeWidgetItem(group1);
subItem3->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem3->setText(0, QString::fromStdWString(L"雨污"));
subItem3->setCheckState(0, Qt::Unchecked);
{
QTreeWidgetItem* subItem11 = new QTreeWidgetItem(subItem3);
subItem11->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem11->setText(0, QString::fromStdWString(L"雨水"));
subItem11->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem* subItem12 = new QTreeWidgetItem(subItem3);
subItem12->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate);
subItem12->setText(0, QString::fromStdWString(L"污水"));
subItem12->setCheckState(0, Qt::Unchecked);
}
ui.BrowserTree->expandAll();
}
2.2.2 实现连接线
假如我们想要使QTreeWidget实现类似于windows的叶子结点之间连线的效果,有两种实现方式,一种是直接代码处理,另外一种是使用QSS样式设置,个人推荐使用QSS,毕竟比较灵活且便于修改维护。
下面是直接写代码处理,但会导致Qt::ItemIsAutoTristate复选框勾选失效。
// 添加节点间连接线但会导致Qt::ItemIsAutoTristate失效
//ui.BrowserTree->setStyle(QStyleFactory::create("windows"));
下面是QSS样式设置。
ui.BrowserTree->setIconSize(QSize(32, 32));
ui.BrowserTree->setColumnCount(1);
ui.BrowserTree->setColumnWidth(0, 300);
QString sQssStyle = getQssStyle();
ui.BrowserTree->setStyleSheet(sQssStyle);
ui.BrowserTree->setHeaderHidden(true); //隐藏表头
QString QVisualSystem::getQssStyle()
{
QString qss = "QTreeWidget::branch:has-siblings:!adjoins-item{ \
image:url(:/res/images/branch-line.svg) 0;\
}\
QTreeWidget::branch:has-siblings:adjoins-item{\
image:url(:/res/images/branch-more.svg) 0;\
} \
QTreeWidget::branch:!has-children:!has-siblings:adjoins-item{\
image:url(:/res/images/branch-end.svg) 0;\
}\
QTreeWidget::branch:has-children:!has-siblings:closed,\
QTreeWidget::branch:closed:has-children:has-siblings{\
border-image:none;\
image:url(:/res/images/branch-closed.svg);\
}\
QTreeWidget::branch:open:has-children:!has-siblings,\
QTreeWidget::branch:open:has-children:has-siblings{\
border-image:none;\
image:url(:/res/images/branch-open.svg);\
}";
return qss;
}
2.3 属性控件QtTreePropertyBrowser
2.3.1 属性控件介绍
Qt库封装了很多控件,种类也比较多,其中容器控件包括:表格、树和列表。使用过QtDesigner的同学应该都知道,这个工具中有一个属性编辑器,是一个属性浏览器控件,就像vs中控件属性面板一样。实际上这是Qt基于QTreeWidget封装的属性控件QtTreePropertyBrowser,虽然这个控件现在还没有被Qt正式的收录,但Qt安装包的sourc资源中附带的有源码,我们可以直接复制过来进行使用。笔者的文件位于:C:\Qt\5.15.2\Src\qttools\src\shared\qtpropertybrowser
目录下。
2.3.2 控件用法
拉取一个QWidget控件,然后基于控件进行提升,将控件类别提升为QtTreePropertyBrowser类型。如下所示:
提升后有个编译问题,有的人可能遇到,原因跟Qt自身与VS的插件兼容性有关。假如采用的是QtCreator编辑器编码的话没有问题,但笔者使用的是VS+Qt + Qt VS Tools扩展,在VS-IDE中进行的Qt开发,因此编译出现下面报错:moc_xxxx文件使用了未定义类型“QtTreePropertyBrowserPrivate”。
1>moc_qttreepropertybrowser.cpp
1>F:\QtProject\QVisualSystem\x64\Debug\moc\moc_qttreepropertybrowser.cpp(136,29): error C2027: 使用了未定义类型“QtTreePropertyBrowserPrivate”
1>F:\QtProject\QVisualSystem\qtpropertybrowser\qttreepropertybrowser.h(48): message : 参见“QtTreePropertyBrowserPrivate”的声明
1>F:\QtProject\QVisualSystem\x64\Debug\moc\moc_qttreepropertybrowser.cpp(137,29): error C2027: 使用了未定义类型“QtTreePropertyBrowserPrivate”
1>F:\QtProject\QVisualSystem\qtpropertybrowser\qttreepropertybrowser.h(48): message : 参见“QtTreePropertyBrowserPrivate”的声明
1>F:\QtProject\QVisualSystem\x64\Debug\moc\moc_qttreepropertybrowser.cpp(138,29): error C2027: 使用了未定义类型“QtTreePropertyBrowserPrivate”
1>F:\QtProject\QVisualSystem\qtpropertybrowser\qttreepropertybrowser.h(48): message : 参见“QtTreePropertyBrowserPrivate”的声明
1>F:\QtProject\QVisualSystem\x64\Debug\moc\moc_qttreepropertybrowser.cpp(139,29): error C2027: 使用了未定义类型“QtTreePropertyBrowserPrivate”
1>F:\QtProject\QVisualSystem\qtpropertybrowser\qttreepropertybrowser.h(48): message : 参见“QtTreePropertyBrowserPrivate”的声明
这个问题实际上是由于宏Q_OBJECT设定引起的,此处不展开说明,只说解决方式。
找到报错的头文件,右键点击打开属性设置:进行如下更改即可解决。
2.3.2 示例源码
protected slots:
void PropertyValueChanged(QtProperty* property, const QVariant& value);
private:
QtVariantPropertyManager* m_pEditableManager;
QtVariantEditorFactory* m_pEditorFactory;
QtVariantPropertyManager* m_pNotEditableManager;
QMap<QtProperty*, QString> m_pPropertyToValue;
void QVisualSystem::InitialPropertyTree()
{
m_pPropertyToValue.clear();
m_pNotEditableManager = new QtVariantPropertyManager(ui.PropertyTree);
m_pEditableManager = new QtVariantPropertyManager(ui.PropertyTree);
m_pEditorFactory = new QtVariantEditorFactory(ui.PropertyTree);
ui.PropertyTree->setFactoryForManager(m_pEditableManager, m_pEditorFactory);
// 设定字段栏的宽度
ui.PropertyTree->setResizeMode(QtTreePropertyBrowser::ResizeToContents);
QStringList listHead;
listHead << QString::fromStdWString(L"字段") << QString::fromStdWString(L"值");
ui.PropertyTree->setHeaderLabels(listHead);
// 分组1
QtProperty* pGroup1 = m_pNotEditableManager->addProperty(QtVariantPropertyManager::groupTypeId(), QString::fromStdWString(L"基本资料"));
{
QtVariantProperty* pItem = m_pNotEditableManager->addProperty(QVariant::String, QString::fromStdWString(L"姓名"));
pItem->setValue(QString::fromStdWString(L"王无"));
pGroup1->addSubProperty(pItem);
pItem = m_pNotEditableManager->addProperty(QVariant::Int, QString::fromStdWString(L"年龄"));
pItem->setValue(31);
pGroup1->addSubProperty(pItem);
pItem = m_pNotEditableManager->addProperty(QVariant::String, QString::fromStdWString(L"性别"));
pItem->setValue(QString::fromStdWString(L"男"));
pGroup1->addSubProperty(pItem);
ui.PropertyTree->addProperty(pGroup1);
}
// 分组2
QtProperty* pGroup2 = m_pEditableManager->addProperty(QtVariantPropertyManager::groupTypeId(), QString::fromStdWString(L"社会特征"));
{
QtVariantProperty* pItem = m_pEditableManager->addProperty(QVariant::Bool, QString::fromStdWString(L"是否在职"));
pItem->setValue(true);
pGroup2->addSubProperty(pItem);
m_pPropertyToValue[pItem] = "true";
pItem = m_pEditableManager->addProperty(QtVariantPropertyManager::enumTypeId(), QString::fromStdWString(L"学历"));
QString sRange = QString::fromStdWString(L"本科,高中,硕士");
QStringList enumnameslist = sRange.split(",");
pItem->setAttribute("enumNames", enumnameslist);
pItem->setValue(0);
pGroup2->addSubProperty(pItem);
m_pPropertyToValue[pItem] = "本科";
pItem = m_pEditableManager->addProperty(QVariant::Double, QString::fromStdWString(L"从业年限"));
pItem->setValue(3.555);
pItem->setAttribute("decimals", 3);
pGroup2->addSubProperty(pItem);
m_pPropertyToValue[pItem] = "3.555";
ui.PropertyTree->addProperty(pGroup2);
}
// 设定属性栏是否展开
//QList<QtBrowserItem*> list1 = ui.PropertyTree->items(pGroup1);
//ui.PropertyTree->setExpanded(list1.at(0), true);
}
// 相响应属性值修改变化的槽函数
void QVisualSystem::PropertyValueChanged(QtProperty* property, const QVariant& value)
{
QString sName = property->propertyName();;
string sMsg = "PropertyName:" + sName.toStdString() + "value:" + value.toString().toStdString();
}