原文链接:
编写Qt Designer自定义控件(一)——如何创建并使用Qt自定义控件
https://blog.csdn.net/giselite/article/details/12622429
编写Qt Designer自定义控件(二)——编写自定义控件界面
https://blog.csdn.net/giselite/article/details/12622561
编写Qt Designer自定义控件(三)——给自定义控件添加属性
https://blog.csdn.net/giselite/article/details/12622625
编写Qt Designer自定义控件(四)——使用自定义控件
https://blog.csdn.net/giselite/article/details/12622659
Qt自定义控件的创建与初步使用(一)之自定义控件的创建步骤
https://blog.csdn.net/panshun888/article/details/51923927
Qt自定义控件的创建与初步使用(二)之图片上绘制文字、箭头、曲线
https://blog.csdn.net/panshun888/article/details/52074400
在使用Qt Designer设计窗体界面时,我们可以使用Widget Box里的窗体控件非常方便的绘制界面,比如拖进去一个按钮,一个文本编辑器等。虽然Qt Designer里的控件可以满足我们大部分的需求,但是有时候,也会产生一些特殊的需要,比如一个输入框,我们要输入的是经纬度,此时就会有两种输入方式,一种是小数形式,一种是度分秒的形式,此时只使用一个简单的LineEdit是无法满足需求的。我们设想构造这样一个输入控件,它可以支持浮点数输入,同时它还具有一个属性,更改这个属性可以使其切换为经纬度输入形式。如果我们的多个窗体上都需要输入经纬度,那么构造这样一个控件,将会非常方便。下面就以此为例,讲解一下如何创建自定义的窗体控件。
第一步:创建QtDesigner自定义控件工程
打开Qt Creator,创建一个Qt 设计师自定义控件,如下图所示:根据向导提示,创建好工程,这里取名为LogLatEdit,工程目录如下图所示:
第二步:编译控件工程
为了淌通整个自定义控件的编写流程,我们先不做任何更改,切换为Release版本,直接编译一下。
第三步:部署插件
编译完成后,在输出目录下,将生成的dll文件和lib文件一起拷贝到Qt的插件目录下,以我使用的Qt 4.8.4为例,在Qt 4.8.4的安装目录D:\Qt\4.8.4下,找到plugins目录,在其中找到designer目录,然后把dll和lib放进去,完整路径为:D:\Qt\4.8.4\plugins\designer。之后,启动D:\Qt\4.8.4\bin下的designer.exe,创建一个窗体,此时就会发现在左侧的Widget Box里出现了我们自己的LogLatEdit控件,我们可以像使用其它控件一样,把我们自己的控件拖绘到窗体上,如下图所示:
如果自定义控件没有出现在Widgetbox里,那么此时你可以通过【帮助-关于插件】菜单,打开插件信息对话框,点击刷新按钮,只要你没有忘记把dll和lib文件拷贝到正确的位置,插件都会自动识别并加载。对于其它版本的Qt也一样,比如我自己的电脑里安装了好几个版本的Qt,对于其它版本的Qt,做法也是一样,只需要把插件工程生成的dll和lib文件放置到相应版本的插件目录下去即可。
到此,我们就理清了如何创建一个自定义控件,并且知道了如何部署、加载并使用自定义控件。下面我们开始编写我们需要的控件,对于只想了解自定义控件开发过程的读者,至此就已经知道如何做了,那么后面的内容您可以略过不读了。既然是控件,就应该有界面,默认生成的控件类只是一个继承了QWidget的类,如下:
-
#ifndef LOGLATEDIT_H
-
#define LOGLATEDIT_H
-
-
#include
-
-
class LogLatEdit :
public QWidget
-
{
-
Q_OBJECT
-
public:
-
LogLatEdit(QWidget *parent =
0);
-
};
-
-
#endif
我们需要的是如下的控件组合:
该控件在输入浮点型模式下,是上面那样,直接输入以度为单位的浮点数即可,如果经纬度信息不是以度为单位的,此时自己换算的话非常麻烦,因此可以切换为度分秒的输入模式,也就是下面哪种样子。为此,我们需要添加一个LineEidt,两个SpinBox和一个DoubleSpinBox以及一些Label控件。
为了简单起见,我不想自己手动去写这些界面相关的代码,为此我们可以先删掉默认生成的loglatedit.h和loglatedit.cpp文件,这样我们就可以重新使用LogLatEdit这个名称重新新建一个ui类了。右键工程,选择“添加新文件”,使用Qt下的“Qt设计师界面类”模板,创建一个ui类,如下图所示:这个界面类,我们重新命名为我们需要的控件名称LogLatEdit,之后编辑ui文件,调整QWidget的大小,然后绘制控件,如图:
这里使用了HorizontalLayout控件对控件组合进行分组,为了演示方便,此处将二者拖放开来,最终的控件,实际上是两个水平布局叠加在一起,每次只有一个控件组合可见。
当然另一个做法也是可行的,那就是不要删除最初默认生成的loglatedit文件,而是把这个新建的ui类命名为别的名字,比如test,在绘制好控件之后,编译一下,然后找到Moc生成的ui_test.h文件,打开该文件,我们把相应的代码拷贝到我们的LogLatEdit控件类中去,然后做适当的修改,比如设定两个控件组合的位置及可见性,以及控件容器QWidget的大小等。这里为了把所有相关的代码都放在一起,便于读者测试,使用的就是这种方式。
使用ui类的方式比较方便,所有与界面相关的处理都可以在窗体设计器中完成,并且界面与代码分离,这种代码管理模式逻辑清晰,便于管理。不过使用ui类时,如果我们的ui类名称不是默认的LogLatEdit,则需要修改一下插件类中创建插件的代码,主要是修改loglateditplugin.cpp文件里createWidget方法中返回的控件对象。
经过修改之后我们的经纬度控件的代码如下:
-
#ifndef LOGLATEDIT_H
-
#define LOGLATEDIT_H
-
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
class LogLatEdit :
public QWidget
-
{
-
Q_OBJECT
-
public:
-
LogLatEdit(QWidget *parent =
0);
-
-
private:
-
QWidget *horizontalLayoutWidget;
-
QHBoxLayout *horizontalLayout;
-
QSpinBox *spinBox;
-
QLabel *label_7;
-
QSpinBox *spinBox_2;
-
QLabel *label_8;
-
QDoubleSpinBox *doubleSpinBox;
-
QLabel *label_9;
-
QWidget *horizontalLayoutWidget_2;
-
QHBoxLayout *horizontalLayout_2;
-
QLineEdit *lineEdit;
-
QLabel *label_6;
-
QSpacerItem *horizontalSpacer;
-
QSpacerItem *horizontalSpacer_2;
-
-
};
-
-
#endif
源文件:
-
#include "loglatedit.h"
-
-
LogLatEdit::LogLatEdit(QWidget *parent) :
-
QWidget(parent)
-
{
-
this->resize(
160,
22);
-
this->setMinimumSize(QSize(
160,
22));
//限定控件的大小
-
this->setMaximumSize(QSize(
200,
22));
-
horizontalLayoutWidget =
new QWidget(
this);
-
horizontalLayoutWidget->setObjectName(QString::fromUtf8(
"horizontalLayoutWidget"));
-
horizontalLayoutWidget->setGeometry(QRect(
0,
0,
160,
22));
-
horizontalLayout =
new QHBoxLayout(horizontalLayoutWidget);
-
horizontalLayout->setSpacing(
1);
-
horizontalLayout->setObjectName(QString::fromUtf8(
"horizontalLayout"));
-
horizontalLayout->setContentsMargins(
0,
0,
0,
0);
-
spinBox =
new QSpinBox(horizontalLayoutWidget);
-
spinBox->setObjectName(QString::fromUtf8(
"spinBox"));
-
spinBox->setMinimumSize(QSize(
35,
20));
-
spinBox->setMaximumSize(QSize(
35,
20));
-
spinBox->setMaximum(
90);
-
-
horizontalLayout->addWidget(spinBox);
-
-
label_7 =
new QLabel(horizontalLayoutWidget);
-
label_7->setObjectName(QString::fromUtf8(
"label_7"));
-
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
-
sizePolicy.setHorizontalStretch(
0);
-
sizePolicy.setVerticalStretch(
0);
-
sizePolicy.setHeightForWidth(label_7->sizePolicy().hasHeightForWidth());
-
label_7->setSizePolicy(sizePolicy);
-
label_7->setMinimumSize(QSize(
3,
20));
-
label_7->setSizeIncrement(QSize(
1,
0));
-
-
horizontalLayout->addWidget(label_7);
-
-
horizontalSpacer =
new QSpacerItem(
0,
20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
-
horizontalLayout->addItem(horizontalSpacer);
-
-
spinBox_2 =
new QSpinBox(horizontalLayoutWidget);
-
spinBox_2->setObjectName(QString::fromUtf8(
"spinBox_2"));
-
spinBox_2->setMinimumSize(QSize(
35,
20));
-
spinBox_2->setMaximumSize(QSize(
35,
20));
-
spinBox_2->setMaximum(
90);
-
-
horizontalLayout->addWidget(spinBox_2);
-
-
label_8 =
new QLabel(horizontalLayoutWidget);
-
label_8->setObjectName(QString::fromUtf8(
"label_8"));
-
sizePolicy.setHeightForWidth(label_8->sizePolicy().hasHeightForWidth());
-
label_8->setSizePolicy(sizePolicy);
-
label_8->setMinimumSize(QSize(
3,
20));
-
label_8->setSizeIncrement(QSize(
1,
0));
-
-
horizontalLayout->addWidget(label_8);
-
-
horizontalSpacer_2 =
new QSpacerItem(
0,
20, QSizePolicy::Expanding, QSizePolicy::Minimum);
-
-
horizontalLayout->addItem(horizontalSpacer_2);
-
-
doubleSpinBox =
new QDoubleSpinBox(horizontalLayoutWidget);
-
doubleSpinBox->setObjectName(QString::fromUtf8(
"doubleSpinBox"));
-
doubleSpinBox->setMinimumSize(QSize(
66,
20));
-
doubleSpinBox->setMaximumSize(QSize(
66,
20));
-
doubleSpinBox->setDecimals(
4);
-
doubleSpinBox->setMaximum(
90);
-
-
horizontalLayout->addWidget(doubleSpinBox);
-
-
label_9 =
new QLabel(horizontalLayoutWidget);
-
label_9->setObjectName(QString::fromUtf8(
"label_9"));
-
sizePolicy.setHeightForWidth(label_9->sizePolicy().hasHeightForWidth());
-
label_9->setSizePolicy(sizePolicy);
-
label_9->setMinimumSize(QSize(
3,
20));
-
label_9->setSizeIncrement(QSize(
1,
0));
-
-
horizontalLayout->addWidget(label_9);
-
-
horizontalLayoutWidget_2 =
new QWidget(
this);
-
horizontalLayoutWidget_2->setObjectName(QString::fromUtf8(
"horizontalLayoutWidget_2"));
-
horizontalLayoutWidget_2->setGeometry(QRect(
0,
0,
160,
22));
-
horizontalLayout_2 =
new QHBoxLayout(horizontalLayoutWidget_2);
-
horizontalLayout_2->setSpacing(
1);
-
horizontalLayout_2->setObjectName(QString::fromUtf8(
"horizontalLayout_2"));
-
horizontalLayout_2->setContentsMargins(
0,
0,
0,
0);
-
lineEdit =
new QLineEdit(horizontalLayoutWidget_2);
-
lineEdit->setObjectName(QString::fromUtf8(
"lineEdit"));
-
lineEdit->setInputMethodHints(Qt::ImhDigitsOnly|Qt::ImhFormattedNumbersOnly);
-
-
horizontalLayout_2->addWidget(lineEdit);
-
-
label_6 =
new QLabel(horizontalLayoutWidget_2);
-
label_6->setObjectName(QString::fromUtf8(
"label_6"));
-
-
horizontalLayout_2->addWidget(label_6);
-
-
label_7->setText(QApplication::translate(
"LogLatEdit",
"\302\260",
0, QApplication::UnicodeUTF8));
-
label_8->setText(QApplication::translate(
"LogLatEdit",
"\342\200\262",
0, QApplication::UnicodeUTF8));
-
label_9->setText(QApplication::translate(
"LogLatEdit",
"\342\200\263",
0, QApplication::UnicodeUTF8));
-
label_6->setText(QApplication::translate(
"LogLatEdit",
"\302\260",
0, QApplication::UnicodeUTF8));
-
-
horizontalLayoutWidget_2->setVisible(
false);
-
QMetaObject::connectSlotsByName(
this);
-
-
}
至此,界面相关的事情就做完了,后面我们需要给这个控件添加两个属性,一个输入模式属性,更改这个属性时,我们的控件可以在两种输入模式下切换,另一个属性就是经纬度值,我们要使其可以设置和返回经纬度值。
窗体控件都有属性,比如QLineEdit就有text属性,另外还有设置属性,比如QLineEdit的readOnly属性。下面就讲解一下如何给自己的控件添加属性的问题。对于我们的经纬度输入控件,它应该具有一个设置属性和一个值属性,我们把设置属性命名为inputMode,把值属性命名为value,先来讲解设置属性inputMode。
对于inputMode属性,它应该是一个枚举值,分别对应控件的两种状态,即浮点输入模式和经纬度输入模式,因此这个类型应该定义为枚举型。另外对于Qt的控件类,如何声明属性,我们可以参考Qt的源代码,比如D:\Qt\4.8.4\src\gui\widgets目录下QMainWindow的定义,我们可以仿照其做法,实现输入模式属性,这里不多做解释,直接给出代码:
-
class LogLatEdit :
public QWidget
-
{
-
Q_OBJECT
-
-
Q_ENUMS
(InputMode)
-
Q_PROPERTY
(InputMode inputMode READ inputMode WRITE setInputMode)
-
Q_PROPERTY
(float value READ value WRITE setValue)
-
public:
-
LogLatEdit
(QWidget *parent = 0);
-
-
enum InputMode
-
{
-
Float,
//float number mode
-
DegSecMin
//Degree second minute mode
-
};
-
InputMode inputMode()const;
-
void setInputMode(const InputMode mode);
-
-
double value() const;
-
void setValue(const double val);
-
//其余略
-
};
仿照QMainWindow的DockOptions属性和iconSize属性,我们实现我们的inputMode属性和value属性。这里注意一下使用Q_PROPERTY宏声明属性的方法,用法很简单,不多解释。下面是对应属性的实现代码:
-
LogLatEdit::InputMode LogLatEdit::inputMode()
const
-
{
-
return m_Mode;
-
}
-
-
-
void LogLatEdit::setInputMode(
const InputMode mode)
-
{
-
double val =
this->value();
-
if(mode==DegSecMin)
-
{
-
horizontalLayoutWidget->setVisible(
true);
-
horizontalLayoutWidget_2->setVisible(
false);
-
}
-
else
-
{
-
horizontalLayoutWidget->setVisible(
false);
-
horizontalLayoutWidget_2->setVisible(
true);
-
}
-
m_Mode = mode;
-
this->setValue(val);
-
}
-
-
-
double LogLatEdit::value()
const
-
{
-
if(
this->inputMode()==Float)
-
{
-
return
this->lineEdit->text().toDouble();
-
}
-
else
-
{
-
double val =
0;
-
val =
this->spinBox->value() +
-
double(
this->spinBox_2->value())/
60.0 +
-
this->doubleSpinBox->value()/
3600.0;
-
return val;
-
}
-
}
-
-
void LogLatEdit::setValue(
const
double val)
-
{
-
m_Value = val;
-
if(
this->inputMode()==Float)
-
{
-
this->lineEdit->setText(tr(
"%1").arg(val));
-
}
-
else
-
{
-
this->spinBox->setValue(
int(val));
-
this->spinBox_2->setValue(
int((val-
int(val))*
60));
-
this->doubleSpinBox->setValue(((val-
int(val))*
60-
int((val-
int(val))*
60)));
-
}
-
}
控件编写完毕以后,把生成的dll和lib文件一起拷贝到Qt安装目录下的插件目录里,比如我安装在D盘里的Qt 4.8.4,路径为:D:\Qt\4.8.4\plugins\designer,拷贝进去以后,这个插件就可以被Qt Designer加载了,此时这个自定义控件就可以像普通的控件一样使用了。当然如有必要,还可以给这个控件添加一些信号和槽,另外按照经纬度的取值范围不同,做一下区分经度和维度的处理,本例中不需要这么复杂,就不再深入探索了,下面是使用自定义的经纬度输入控件的效果:
到目前为止,我们已经可以在Qt Designer中使用自定义的控件绘制界面了,但是这个时候,事情还没有完,因为使用QtCreator时,会发现其界面设计器中并没有我们的自定义控件。另外当我们用Qt Designer绘制完控件后,编译时会发生找不到“loglatedit.h”头文件的编译错误。这个问题很容易理解,首先我们可以想到的是Qt Creator和Qt Designer的自定义控件目录是不同的,我自己的机器里,Qt和Qt Creator的安装目录如下:
当我把自定义控件的dll和lib文件拷贝到D:\Qt\4.8.4\plugins\designer目录下,再启动D:\Qt\4.8.4\bin目录下的designer.exe,这个插件可以加载到Widget Box里面去,但是如果我启动D:\Qt\qtcreator-2.8.1\bin目录下的Qt Creator,我们的插件并不会出现在Qt Creator的设计器中。因此,对于Qt Creator,我们也需要执行以下控件的安装,那么具体要安装到哪里去呢?经过在Qt Creator安装目录下一番查找,发现D:\Qt\qtcreator-2.8.1\bin\plugins\designer这个目录下存在和D:\Qt\4.8.4\plugins\designer目录下同名的dll文件,于是我们猜测这个目录就是Qt Creator的控件安装目录,接下来拷贝loglateditplugin.dll到这个目录下,之后重新打开Qt Creator,发现自定义控件加载成功了,于是第一个问题解决了。
对于第二个问题,我们很容易想到使用一个动态库时,除了要有dll和lib文件外,还需要头文件,而对于某一版本的Qt SDK,其界面相关的头文件都放置在QtGui目录下,于是我将工程中经纬度输入控件的头文件loglatedit.h拷贝到D:\Qt\4.8.4\include\QtGui目录下,再次试验,发现编译可以通过,但是连接失败,尝试将相应的lib文件放到D:\Qt\4.7.4\lib目录下并且把该lib库添加为工程的依赖库,依然连接失败。这表明Qt的自定义控件工程并没有导出我们的自定义控件,因此其生成的库文件loglateditplugin.lib里并没有自定义控件的信息。因此要使用自定义控件,只能引入源码。
通过以上实验,总结一下具体的做法,列举如下:
1. 创建Qt 设计师自定义控件工程,编写自定义控件;
2. 拷贝release版的dll和lib文件到Qt Designer的插件目录下,如D:\Qt\4.8.4\plugins\designer,这样自定义控件即可在Qt Designer中使用;
3. 拷贝dll文件到Qt Creator的集成Qt Designer的插件目录下,如:D:\Qt\qtcreator-2.8.1\bin\plugins\designer,使集成于Qt Creator中的Qt Designer可以加载并使用该控件;
4. 拷贝自定义控件的头文件和源文件到使用自定义控件的工程中,并且添加到工程里面去,这样就可以正确编译并连接了;
本篇博客的目的是简单介绍:创建一个用QLabel类来显示图片的自定义控件的编写。如果想实现在图片上绘制文字、箭头和曲线,请参考我的第二篇博客源码!!!给大家一个链接~【Qt自定义控件的创建与初步使用(二)之图片上绘制文字、箭头、曲线
】在写自定义控件的过程中遇到了很多的难题,但都慢慢解决了,本人对Qt自定义控件的认识还不深刻,做的不对的地方,还请大家指出,我会尽快修改,免得误导他人!同时推荐一篇关于自定义控件的博客连接:http://blog.csdn.net/giselite/article/details/12622429,写的还挺详细,大家可以参考,关于Qtcreator和vs2013的相关安装和配置,网上有很多,Qt的安装和配置也比较简单,简单的配置后即可用于创建Qt自定义控件,这里不再赘述,贴心的给初学者两个关于Qt安装和配置的参考教程吧。连接(http://blog.csdn.net/wangell/article/details/41117139
http://tieba.baidu.com/p/3451630520)
配置:Qt creator5.7,Qt 5.7+VS2013(64位)(有人喜欢用后者,全凭个人爱好,我用的是creator,本文也会关联到后者的使用),本篇博客是关于创建Qt自定义控件的内容,本篇案例的说明用的是Qt5.6,VS2013(因为本人的电脑装的是Qt5.6,公司的电脑装的是Qt5.7),建议最好用Qt5.7,但实际操作中不会有太大差别。
一、新建Qt4设计师自定义控件工程
1.打开creator,按红色箭头所示,新建Qt4设计师自定义控件工程;
2.设置项目名称,位置;
3.下一步默认就好;
4.双击修改控件类工程名称(注:本篇博文所有是”CV“名称命名,均采用的大写符号),即可生成红色箭头所指的几个文件;
5.下一步,就会自动生成名为cvplugin的插件,下面的步骤中会用到它;
6.至此就完成了新建Qt4设计师自定义控件工程,就会生成如图所示的工程。
二、编写自定义控件界面
既然是控件,就应该有界面,本篇博客的目的是想创建一个继承于QWidget类,并用QLabel类来显示图片的自定义控件;既然默认生成的控件类只是一个继承了QWidget的类,只是一个空壳控件,所以你应该按如下的操作来为此控件添加内容。
1.为了简单起见,我不想自己手动去写这些界面相关的代码,为此我们可以先删掉默认生成的cv.h和cv.cpp文件,选中cv.h和cv.cpp文件,右键选择”删除文件“,并勾上”彻底删除“选项,确定删除即可;
2.这样我们就可以重新使用”CV“这个名称为此控件添加一个ui类了。在上图所示的界面中选中CV项目,右键选择“添加新文件”,使用Qt下的“Qt设计师界面类”模板,创建一个ui类,如下图所示:
3.选中Widget即可,其他全部默认;
4.写上”CV“即可,点击下一步;
5.默认下一步;
6.至此就为自定义控件工程添加上了UI界面;
7.双击"cv.ui”,拖拽一个Label到界面上,并调整下大小,居中即可。在右边的列表中可以看到label控件,因为想显示图片,所以用的肯定是属于Display Widgets类的“QLabel”。这样就为自定义控件CV添加了一个显示图片的类QLabel了,接下来就可以为这个“CV”工程构建.dll和.lib文件了;
8.先将编译器改为“Release”模式,然后执行下qmake,为什么这样,我也不是很清楚,你可以自己去查一下;
9.再点击那个绿色三角形按钮,会弹出“自定义执行挡”,不要管,直接关闭即可(我查过好像是什么接口,我也不是很清楚,毕竟也是接触Qt不久),最后点击那个小锤子,等右下角那个绿色进度条变暗了,即可在你所建立的工程目录下见到如图所示的“build-CV-Desktop_Qt_5_6_0_MSVC2013_64bit-Release”插件工程;
10.打开该文件,并找到如图所示的两个文件“cvplugin.dll”、"cvplugin.lib”,将两个文件并拷贝到你安装的Qt5.7的designer中(注意路径!!!,根据自己安装的进行),如下第二个图所示。若想用“VS2013+Qt设计师”进行程序编写的,你就要将两个文件“cvplugin.dll”、"cvplugin.lib”拷贝到如下第三个图所示的路径的文件夹下(注意路径!!!,根据自己安装的进行);
11.最后你随便在Qt creator 或 Qt设计师中 新建一个工程就可以在Widgets窗口中看到“CV”插件了,于是你就可以想使用其他控件一样来随意使用“CV”了,这个控件目前的功能相当于“Label”控件(本次主要用来显示图片),当然你可以在创建的过程中为它添加任意想要的组合功能。
第二篇,“CV”插件的使用。未完待续......
本文目的:编辑自定义控件的界面ui,并在图片上添文字、箭头、曲线、打开、保存等功能。并说明了如何去使用这个编辑好的ui界面控件!
上次简单的说明了如何去创建Qt自定义控件,当时还是对其了解不够深刻,现在看来,QT自定义控件就是你事先把界面写好(一般基于QWidget基类),然后再把它写入QT自定义控件的工程【可参考Qt自定义控件的创建与初步使用(一)这篇博客】中去,最后放到别的工程中,通过简单的拖拽和拷贝就可以用了。那怎么样去做和使用这个自定义的控件呢?又要注意什么呢?具体分为三大步,下面给大家娓娓道来!
配置:Qt creator5.7,Qt 5.7+VS2013(64位)(有人喜欢用后者,全凭个人爱好,我用的是creator),本篇案例的说明用的是Qt5.6(因为本人的电脑装的是Qt5.6,公司的电脑装的是Qt5.7),建议最好用Qt5.7,但实际操作中不会有太大差别。
一、建立基类是QWidget的Qt Application工程,把界面写好。
1.建立一个Qt Application工程,命名为 sof_Interface,基类是QWidget,然后一路下一步,这样你就完成了一个工程的创建(先在这个工程上把需要的界面写好)
2.打开.ui界面,此处为5个功能:打开图片、保存图片、在图片上添加文字、曲线、箭头符号。拖拽5个pushbotton、1个QLabel、1个QLineEdit、1个QDialogbottonbox,并按图所示排号界面,并选中,右键点击转到槽,从而创建相关的槽函数。并添加代码。
sof_Interface.h代码
-
<span style="font-family:Times New Roman;font-size:14px;">#ifndef SOF_INTERFACE_H
-
#define SOF_INTERFACE_H
-
-
#include
<QWidget>
-
#include
<QAbstractButton>
-
namespace Ui {
-
class sof_Interface;
-
}
-
-
class sof_Interface : public QWidget
-
{
-
Q_OBJECT
-
-
public:
-
explicit sof_Interface(QWidget *parent = 0);
-
~sof_Interface();
-
-
void paint(QImage&theImage);
-
enum Type{
-
type1,
-
type2,
-
type3,
-
};
-
-
private slots:
-
void on_doOpen_clicked();//打开图片
-
void on_doSave_clicked();//保存图片
-
void on_buttonBox_clicked(QAbstractButton *button);//ok、cancel
-
void on_mou_Track_clicked();//曲线
-
void on_arrow_Edit_clicked();//箭头
-
void on_text_Edit_clicked();//文字
-
private:
-
Ui::sof_Interface *ui;
-
QString curFile; //打开文件名
-
bool isOpen;
-
Type path_type; //画的图形种类
-
QImage image,tempImage; //原图、缓存图片
-
QPixmap pic; //用于显示图片
-
bool isDrawing;
-
QPoint begin,end;
-
QPoint end_pos; //鼠标释放时的位置
-
QString Text; //存储文字变量
-
protected:
-
void mousePressEvent(QMouseEvent *e); //鼠标按下事件
-
void mouseMoveEvent(QMouseEvent *e); //鼠标移动事件
-
void mouseReleaseEvent(QMouseEvent *e); //鼠标释放事
-
void paintEvent(QPaintEvent *); //重绘事件
-
};
-
-
#endif // SOF_INTERFACE_H
-
span>
sof_Interface.cpp中代码
-
"font-family:Times New Roman;font-size:14px;">#include
"sof_interface.h"
-
#include
"ui_sof_interface.h"
-
#include
-
#include
-
#include
-
#include
-
#include
-
sof_Interface::sof_Interface(QWidget *parent) :
-
QWidget(parent),
-
ui(
new Ui::sof_Interface)
-
{
-
ui->setupUi(
this);
-
isOpen=
false;
-
path_type=type1;
//初始化
-
isDrawing=
false;
//初始化
-
}
-
-
sof_Interface::~sof_Interface()
-
{
-
delete ui;
-
}
-
/////////////////////////////槽函数/////////////////////////////
-
void sof_Interface::on_doOpen_clicked()
//打开图片槽函数
-
{
-
QString fileName = QFileDialog::getOpenFileName(
-
this,
"打开图片",
-
"",
-
"图片格式 (*.bmp *.jpg *.jpeg *.png)");
-
if(fileName !=
NULL)
-
{
-
image.load(fileName);
-
curFile=fileName;
-
isOpen=
true;
-
}
-
update();
-
}
-
-
void sof_Interface::on_doSave_clicked()
//保存图片槽函数
-
{
-
QString path = QFileDialog::getSaveFileName(
-
this, tr(
"保存图片"),
-
"",
"图片格式 (*.bmp *.jpg *.jpeg *.png)");
-
image.save(path);
-
}
-
-
void sof_Interface::on_buttonBox_clicked(QAbstractButton *button)
//lineEdit命令行输入槽函数
-
{
-
QString str;
-
-
if(ui->buttonBox->button(QDialogButtonBox::Ok) == button)
//判断按下的是否为"确定”按钮
-
{
-
if(!ui->lineEdit->text().isEmpty())
//判断lineEdit是否为空,不为空返回0
-
{
-
str += ui->lineEdit->text()+
"\n";
//str连接lineEdit中的内容
-
Text=str;
//在图片上写入lineEdit中输入的文字
-
str=
"";
-
}
-
-
}
-
else
if(button == ui->buttonBox->button((QDialogButtonBox::Cancel)))
-
{
-
ui->lineEdit->clear();
-
-
}
-
}
-
-
void sof_Interface::on_mou_Track_clicked()
//曲线槽函数
-
{
-
path_type=type1;
-
}
-
-
void sof_Interface::on_arrow_Edit_clicked()
//箭头槽函数
-
{
-
path_type=type2;
-
}
-
-
void sof_Interface::on_text_Edit_clicked()
//文字槽函数
-
{
-
path_type=type3;
-
}
-
-
////////////////////////////鼠标事件////////////////////////////
-
-
-
void sof_Interface::mousePressEvent(QMouseEvent *e)
//鼠标按下事件
-
{
-
begin=e->pos();
-
}
-
-
void sof_Interface::mouseMoveEvent(QMouseEvent *e)
//鼠标移动事件
-
{
-
end=e->pos();
-
-
if(path_type==type1)
//只有path_type==type1才绘制在image上(原图),这时isDrawing=false;
-
{
-
isDrawing=
false;
-
paint(image);
-
}
-
else
//其他path_type全绘制在tempImage上(缓冲图上)
-
{
-
isDrawing=
true;
-
tempImage=image;
-
paint(tempImage);
-
}
-
}
-
-
void sof_Interface::mouseReleaseEvent(QMouseEvent *e)
//鼠标释放事件
-
{
-
end=e->pos();
-
isDrawing=
false;
-
paint(image);
-
-
}
-
-
/////////////////////////paintEvent事件////////////////////////////
-
void sof_Interface::paintEvent(QPaintEvent *)
-
{
-
QPainter p(this);
-
if(isDrawing)
-
p.drawImage(
0,
0,tempImage);
-
else
-
p.drawImage(
0,
0,image);
-
-
if(isOpen=
true)
//打开图片操作
-
{
-
pic=QPixmap::fromImage(image);
-
QPainter painter(this);
-
painter.drawPixmap(
0,
0,
748,
480,pic);
-
}
-
}
-
-
////////////////////////每个画图的函数////////////////////////////
-
void sof_Interface::paint(QImage &theImage)
-
{
-
QPainter pp(&theImage);
-
-
pp.setCompositionMode(QPainter::CompositionMode_SourceIn);
//设置画刷的组合模式CompositionMode_SourceOut这个模式为目标图像在上。
-
pp.setPen(QPen(QBrush(Qt::red),
2, Qt::SolidLine));
//设置画笔(颜色,线宽,样式(实线))
-
pp.setRenderHint(QPainter::Antialiasing,
true);
//设置线段反锯齿
-
QFont font = pp.font();
//设置字体
-
font.setPixelSize(
20);
//改变字体大小
-
font.setBold(
false);
//字体是否加粗
-
pp.setFont(font);
//设置字体
-
-
if(path_type==type1)
//曲线
-
{
-
pp.drawLine(begin,end);
-
begin=end;
-
}
-
-
if(path_type==type2)
//箭头
-
{
-
float x1 = begin.x();
//取points[0]起点的x
-
float y1 = begin.y();
//取points[0]起点的y
-
float x2 = end.x();
//取points[count-1]终点的x
-
float y2 = end.y();
//取points[count-1]终点的y
-
float l =
10.0;
//箭头的长度
-
float a =
0.5;
//箭头与线段角度
-
float x3 = x2 - l *
cos(
atan2((y2 - y1) , (x2 - x1)) - a);
//计算箭头的终点(x3,y3)
-
float y3 = y2 - l *
sin(
atan2((y2 - y1) , (x2 - x1)) - a);
-
float x4 = x2 - l *
sin(
atan2((x2 - x1) , (y2 - y1)) - a);
//计算箭头的终点(x4,y4)
-
float y4 = y2 - l *
cos(
atan2((x2 - x1) , (y2 - y1)) - a);
-
pp.drawLine(x2,y2,x3,y3);
//绘制箭头(x2,y2,x3,y3)
-
pp.drawLine(x2,y2,x4,y4);
//绘制箭头(x2,y2,x4,y4)
-
pp.drawLine(begin,end);
//绘制主干箭头(begin,end)
-
}
-
-
if(path_type==type3)
//文字
-
{
-
pp.drawText(begin.x(),begin.y(),Text);
-
}
-
-
update();
-
}
-
main.pp中代码
-
"font-family:Times New Roman;font-size:14px;">#include
"sof_interface.h"
-
#include
-
-
int main(
int argc,
char *argv[])
-
{
-
QApplication a(argc, argv);
-
sof_Interface w;
-
w.show();
-
-
return a.exec();
-
}
3.界面运行效果,还可以保存哦,注意图片格式只能加载.jpg.jpeg.png.bmp,需要扩展的自己在代码中添加吧!当然还可以更改界面背景色等,自己去改哦~这只是个demo。
二、将自己第一步写的sof_Interface工程.h和.cpp的代码拷贝到自定义控件工程的.h和.cpp中即可写成自定义控件工程了,注意代码不能完全粘贴复制哦,注意自己建立工程的类名,除非你和我的一样。然后编译后见一下.dll和.lib,并放到相应的designer中,具体可参考【Qt自定义控件的创建与初步使用(一)这篇博客】。
三、这样你再新建立任意一个工程,你都可以在Qt creator或Qt 设计师中找到自己建立的控件,然后拖拽到工程的ui界面中,最后拷贝自定义控件.h和.cpp到这个新工程的工程目录下,注意和第二大步不一样哦~,就可以编译运行啦。再次就不给效果图啦~