QtnProperty是一个开源的第三方Qt库,参考下面链接地址可以下载最新的版本。
git:https://github.com/lexxmark/QtnProperty
基础使用说明,以Qt5.8.0mingw开发环境为例:
1、编译静态链接库
目录Core和PropertyWidget是需要编译的两个静态库,将其编译后会在bin-win目录下生成libQtnPropertyCore.a和libQtnPropertyWidget.a两个静态链接库,注意的是config.pri中已经指定了目标路径,如果需要改变生成路径,请在config.pri中修改,同样的,其他所有的子项目都应该包含这个文件,方便将所有的生成文件都放到同一个地方;
2、在自己的项目中使用QtnProperty库
在自己的项目*.pro中,包含下面代码:
include(../Config.pri)
include(../QtnProperty.pri)
这里将config.pri包含进来正如前面说的是要将exe程序和.a库文件生成到同一个地方,引入QtnProperty.pri类似于VC中的库头文件一样。然后将库文件打包到pro配置文件:
INCLUDEPATH += $$PWD/../Core
INCLUDEPATH += $$PWD/../PropertyWidget
DEPENDPATH += $$PWD/../PropertyWidget
DEPENDPATH += $$PWD/../CorelibQtnPropertyCore.a
CONFIG(debug, debug|release): LIBS += $$PWD/../bin-win/libQtnPropertyCore.a
CONFIG(debug, debug|release): LIBS += $$PWD/../bin-win/libQtnPropertyWidget.a
到此就可以编译和使用QtnProperty库的啦!
3、属性设置项详解
在使用之前,需要先调用initQtnPropertyWidgetLibrary()这个函数,在PropertyDelegateFactory中声明。进入这个函数可以看到,该函数主要是注册属性项的代理类,如果想了解每个属性项是如何实现的,可以查看其中的实现。比如打开regBoolDelegates()这个函数:
void regBoolDelegates()
{
QtnPropertyDelegateFactory::staticInstance()
.registerDelegateDefault(&QtnPropertyBoolBase::staticMetaObject
, &qtnCreateDelegate<QtnPropertyDelegateBoolCheck, QtnPropertyBoolBase>
, "CheckBox");
QtnPropertyDelegateFactory::staticInstance()
.registerDelegate(&QtnPropertyBoolBase::staticMetaObject
, &qtnCreateDelegate<QtnPropertyDelegateBoolCombobox, QtnPropertyBoolBase>
, "ComboBox");
}
可以看到对于bool类型,提供了两个不同的实现,一种是CheckBox,一种是ComboBox,默认是CheckBox实现。如果需要自己实现,可以参考QtnPropertyDelegateBoolCheck的代码,仿照实现即可。如果需要选择ComboBox来实现bool,可以在代码中这样写:
auto boolValue = new QtnPropertyBool(propertySet);
QtnPropertyDelegateInfo boolNewDelegate;
boolNewDelegate.name = "ComboBox";
boolValue->setDelegate(boolNewDelegate);
再看regIntDelegates()这个函数,提供了SpinBox和SliderBox两个代理类实现:
void regIntDelegates()
{
QtnPropertyDelegateFactory::staticInstance()
.registerDelegateDefault(&QtnPropertyIntBase::staticMetaObject
, &qtnCreateDelegate<QtnPropertyDelegateInt, QtnPropertyIntBase>
, "SpinBox");
QtnPropertyDelegateFactory::staticInstance()
.registerDelegate(&QtnPropertyIntBase::staticMetaObject
, &qtnCreateDelegate<QtnPropertyDelegateSlideBoxTyped<QtnPropertyIntBase>, QtnPropertyIntBase>
, "SliderBox");
}
我们进入QtnPropertyDelegateInt这个代理类,发现所有的代理类其实都实现了两个接口:
QWidget* QtnPropertyDelegateInt::createValueEditorImpl(QWidget* parent, const QRect& rect, QtnInplaceInfo* inplaceInfo)
{
QSpinBox* spinBox = new QSpinBox(parent);
spinBox->setGeometry(rect);
new QtnPropertyIntSpinBoxHandler(owner(), *spinBox);
if (inplaceInfo)
{
spinBox->selectAll();
}
return spinBox;
}
bool QtnPropertyDelegateInt::propertyValueToStrImpl(QString& strValue) const
{
strValue = QString::number(owner().value());
return true;
}
这里最主要的实现就是这个createValueEditorImpl()函数了,可以看到SpinBox和SliderBox的区别也就主要在于创建的窗口不一样。同样,如果想提供ComboBox来实现下拉选择呢?当然也是一样的,仿照这个类,写一个类似的代理接口类,在这个函数中实现combobox来选择数据,那么问题来了?ComboBox中的数据如何提供?接下来看一个已经实现好的数据选择类QtnPropertyDelegateIntList,如何将ComboBox中的数据传递进去,实现代码:
QWidget* QtnPropertyDelegateIntList::createValueEditorImpl(QWidget* parent, const QRect& rect, QtnInplaceInfo* inplaceInfo)
{
if (owner().isEditableByUser())
{
QComboBox *comboBox = new QComboBox(parent);
auto delegate = owner().delegate();
if (delegate)
{
QList<int> values;
qtnGetAttribute(delegate->attributes, "values", values);
for (auto value : values)
{
comboBox->addItem(QString::number(value), value);
}
}
comboBox->setGeometry(rect);
// connect widget and property
new QtnPropertyIntComboBoxHandler(owner(), *comboBox);
if (inplaceInfo)
comboBox->showPopup();
return comboBox;
}
else
{
QLineEdit *lineEdit = new QLineEdit(parent);
lineEdit->setReadOnly(true);
lineEdit->setText(QString::number((int)owner()));
lineEdit->setGeometry(rect);
return lineEdit;
}
}
代码已经很清晰了,请注意看这两行:
QList<int> values;
qtnGetAttribute(delegate->attributes, "values", values);
创建ComboBox之后,从一个叫做delegate->attributes的属性中获取到QList<int>,然后将其加入到ComboBox中,这个delegate是一个QtnPropertyDelegateInfo类,这就是前面bool属性设置代码时用到的QtnPropertyDelegateInfo类,它除了有name属性外,还有一个attributes属性,支持QVariant类型,可以将任意类型的变量传递过去。当我们不知道如何传递属性参数时,就可以找到代理类,找到createValueEditorImpl()函数,看看中间究竟使用到了那些属性变量。在这里只需要传递一个QList<int>就可以了:
QList<int> list;
for (auto i=0; i<10; i++) {
list.append(i);
}
QtnPropertyDelegateInfo deleInfo;
deleInfo.name = "IntList";
deleInfo.attributes.insert("values", QVariant::fromValue(list));
intValue->setDelegate(deleInfo);
4、示例代码
#include "widget.h"
#include "ui_widget.h"
#include "PropertySet.h"
#include "PropertyCore.h"
#include "PropertyBase.h"
#include "Core/GUI/PropertyButton.h"
#include "Core/GUI/PropertyQBrush.h"
#include "Core/GUI/PropertyQColor.h"
#include "Core/GUI/PropertyQFont.h"
#include "Core/GUI/PropertyQPen.h"
#include <QMessageBox>
#include <QVector>
#include <QFileDialog>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
initQtnPropertyWidgetLibrary();
QtnPropertySet* propertySet = new QtnPropertySet(this);
// BOOL的两种表现形式
{
QtnPropertySet* propertyBOOL = new QtnPropertySet(propertySet);
propertyBOOL->setName("BOOL的两种表现形式");
//CheckBox形式
auto boolValue = new QtnPropertyBool(propertyBOOL);
boolValue->setName("Bool_CheckBox");
boolValue->setDescription("It is bool value");
boolValue->setValue(true);
//ComboBox形式
auto boolValue2 = new QtnPropertyBool(propertyBOOL);
boolValue2->setName("Bool_ComboBox");
boolValue2->setDescription("It is bool value");
boolValue2->setValue(false);
QtnPropertyDelegateInfo boolNewDelegate;
boolNewDelegate.name = "ComboBox";
boolNewDelegate.attributes["labelFalse"] = "否";
boolNewDelegate.attributes["labelTrue"] = "是";
boolValue2->setDelegate(boolNewDelegate);
}
//INT和FLOAT的表现形式
{
QtnPropertySet* propertyINT = new QtnPropertySet(propertySet);
propertyINT->setName("INT的三种表现形式");
//SpinBox形式
auto intValue = new QtnPropertyInt(propertyINT);
intValue->setName("Int_SpinBox");
intValue->setDescription("This is integer Value 0 ~ 9");
intValue->setMaxValue(9);
intValue->setMinValue(0);
intValue->setValue(1);
//SliderBox形式
auto intValue2 = new QtnPropertyInt(propertyINT);
intValue2->setName("Int_SliderBox");
intValue2->setDescription("This is integer Value 0 ~ 9");
intValue2->setMaxValue(9);
intValue2->setMinValue(0);
intValue2->setValue(2);
QtnPropertyDelegateInfo intNewDel1;
intNewDel1.name = "SliderBox";
intValue->setDelegate(intNewDel1);
//IntList形式
auto intValue3 = new QtnPropertyInt(propertyINT);
intValue3->setName("Int_List");
intValue3->setDescription("This is integer Value 0 ~ 9");
intValue3->setMaxValue(9);
intValue3->setMinValue(0);
intValue3->setValue(2);
QList<int> IntList;
for (auto i=0; i<10; i++) {
IntList.append(i);
}
QtnPropertyDelegateInfo intNewDel2;
intNewDel2.name = "IntList";
intNewDel2.attributes.insert("values", QVariant::fromValue(IntList));
intValue3->setDelegate(intNewDel2);
//FLOAT的默认形式
auto floatValue = new QtnPropertyFloat(propertyINT);
floatValue->setName("FloatValue");
floatValue->setDescription("Float value");
floatValue->setMaxValue(10.f);
floatValue->setMinValue(0.f);
floatValue->setStepValue(0.5f);
floatValue->setValue(5.0f);
}
//Enum的表现形式
{
QtnPropertySet* propertyENUM = new QtnPropertySet(propertySet);
propertyENUM->setName("Enum的表现形式");
auto enumValue = new QtnPropertyEnum(propertyENUM);
enumValue->setName("设备串口编号:");
enumValue->setDescription("请选择正确的设备连接端口");
QVector<QtnEnumValueInfo> vec;
vec.push_back(QtnEnumValueInfo(0, "NONE", "未选择"));
vec.push_back(QtnEnumValueInfo(1, "COM1", "COM1"));
vec.push_back(QtnEnumValueInfo(2, "COM2", "COM2"));
vec.push_back(QtnEnumValueInfo(3, "COM3", "COM3"));
auto enumInfo = new QtnEnumInfo("请选择串口", vec);
enumValue->setEnumInfo(enumInfo);
}
//窗口属性
{
QtnPropertySet* propertyWnd = new QtnPropertySet(propertySet);
propertyWnd->setName("Windows窗口属性");
//QPoint形式
auto wndPt = new QtnPropertyQPoint(propertyWnd);
wndPt->setName("Point");
wndPt->setDescription("This is Windows Point");
wndPt->setValue(QPoint(320, 240));
//QSize形式
auto wndSize = new QtnPropertyQSize(propertyWnd);
wndSize->setName("Size");
wndSize->setDescription("This is Windows Size");
wndSize->setValue(QSize(640, 480));
//QRect形式
auto wndRect = new QtnPropertyQRect(propertyWnd);
wndRect->setName("Rect");
wndRect->setDescription("This is the windows rectange.");
wndRect->setValue(QRect(0, 0, 10, 20));
}
//String的三种形式
{
QtnPropertySet* propertyStr = new QtnPropertySet(propertySet);
propertyStr->setName("String的三种形式");
//LineEdit形式
auto strEdit = new QtnPropertyQString(propertyStr);
strEdit->setName("窗口标题");
strEdit->setDescription("请输入窗口的标题");
strEdit->setValue("未命名");
//File形式
auto strFile = new QtnPropertyQString(propertyStr);
strFile->setName("选择文件路径");
strFile->setDescription("请选择指定的文件");
QtnPropertyDelegateInfo fileDele;
fileDele.name = "File";
fileDele.attributes["acceptMode"] = QFileDialog::AcceptOpen;
fileDele.attributes["defaultSuffix"] = "Text File | *.txt";
fileDele.attributes["fileMode"] = QFileDialog::ExistingFile;
fileDele.attributes["options"] = QFileDialog::ExistingFile;
fileDele.attributes["viewMode"] = QFileDialog::Detail;
strFile->setDelegate(fileDele);
//List形式
auto strList = new QtnPropertyQString(propertyStr);
strList->setName("设备类型");
strList->setDescription("请选择设备的类型");
QtnPropertyDelegateInfo listDele;
listDele.name = "List";
listDele.attributes["items"] = QStringList()<<"单机设备"<<"联网设备"<<"其他类型";
strList->setDelegate(listDele);
}
//杂项
{
QtnPropertySet* propertyOther = new QtnPropertySet(propertySet);
propertyOther->setName("杂项");
//Button
auto button = new QtnPropertyButton(propertyOther);
button->setName("HelloButton");
button->setDescription("This is Button");
button->setClickHandler([this](const QtnPropertyButton* btn){
QMessageBox::information(this, "Message", QString("Button Message from %1 : Hello, I am Button Style !").arg(btn->name()));
});
//Brush
auto brush = new QtnPropertyQBrushStyle(propertyOther);
brush->setName("Brush");
brush->setDescription("This is Brush");
brush->setValue(Qt::SolidPattern);
//Color
auto color = new QtnPropertyQColor(propertyOther);
color->setName("Color");
color->setDescription("This is Color");
color->setValue(QColor(255, 0, 0));
//Font
auto font = new QtnPropertyQFont(propertyOther);
font->setName("Font");
font->setDescription("This is Font");
font->setValue(QFont("宋体", 10));
//PenStyle
auto penStyle = new QtnPropertyQPenStyle(propertyOther);
penStyle->setName("PenStyle");
penStyle->setDescription("This is Pen");
penStyle->setValue(Qt::SolidLine);
//Pen
// auto pen = new QtnPropertyQPen(propertyOther);
// pen->setName("Pen");
// pen->setValue(QPen(Qt::SolidLine));
}
ui->property->setPropertySet(propertySet);
}
Widget::~Widget()
{
delete ui;
}
最后上个效果截图: