QT知识整理

项目结构

moc(Meta-Object Compiler)用于元对象代码生成,适用于*.h,.cpp文件,当扫描到.h文件中存在Q_OBJECT 宏就会生成moc_xxx.cpp代码,并将依赖关系写到Makefile里。uic(User Interface Compiler)用于Widget布局生成,适用于.ui文件,rcc(Resource Compiler)用于虚拟文件系统内容生成, 适用于*.qrc文件。

  • 用cmake管理项目
project(Test LANGUAGES CXX)
set(CMAKE_PREFIX_PATH "/home/van/Qt/5.12.11/gcc_64")
file(GLOB_RECURSE QRC_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

#set(QRC_SOURCE_FILES Resources.qrc)

find_package(Qt5 COMPONENTS Widgets REQUIRED)

qt5_add_resources(QRC_FILES ${QRC_SOURCE_FILES})


SOURCE_GROUP("Resource Files" FILES ${QRC_SOURCE_FILES})


INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/linux_include)

qt5_wrap_cpp(mocfiles mycamera.h)
add_executable(Test
main.cpp
${QRC_FILES}
BaslerCamera/mycamera.cpp
BaslerCamera/mycamera.h
customgraphicsitem.cpp
fullscreenvideo.cpp
iconbutton.cpp
mainwindow.cpp
BaslerCamera/sbaslercameracontrol.cpp
onevideo.cpp
qwcomboboxdelegate.cpp
qwfloatspindelegate.cpp
qwintspindelegate.cpp
DarkStyle.cpp
#        ${mocfiles}
)

LINK_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/lib/linux_64)
set(LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/linux_64)
find_library(CAMER_LIB_1  libGCBase_gcc_v3_1_Basler_pylon.so ${LIB_DIR})
find_library(CAMER_LIB_2  libGenApi_gcc_v3_1_Basler_pylon.so ${LIB_DIR})
find_library(CAMER_LIB_3  libpylonbase-6.1.1.so ${LIB_DIR})
find_library(CAMER_LIB_4  libpylonutility-6.1.1.so ${LIB_DIR})


#find_library(RUNTIME_LIB rt ${CMAKE_CURRENT_SOURCE_DIR}/lib/linux_64 ${CMAKE_CURRENT_SOURCE_DIR}/lib/Qt )

MESSAGE(${CAMER_LIB_1})
MESSAGE(${CAMER_LIB_2})
MESSAGE(${CAMER_LIB_3})
MESSAGE(${CAMER_LIB_4})

target_link_libraries(Test
        Qt5::Widgets
        ${CAMER_LIB_1}
        ${CAMER_LIB_2}
        ${CAMER_LIB_3}
        ${CAMER_LIB_4}
        )

以上是一个qt项目的cmakelists.

  • ui 文件
    ui文件是用户为Qmainwindow或Qwidget类创建的,用来记录内部子窗口或空间的种类及样式。
    假设用户类继承自Qwidget, 命名为xxx, 那么UIC会根据xxx.ui文件生成ui_xxx.h头文件,并让xxx.cpp包含。xxx类在初始化时,用ui_xxx类初始化成员变量ui,然后调用ui->setupUi(this)真正让xxx类包含ui文件中记录的那些子窗口和控件。 之后这些子窗口和控件可以用ui->的形式拿到。

如果想查看QT源码的话,在项目的cmake文件中添加QT源码路径
include_directories("/home/xxx/Qt/5.12.11/Src"),虽然不能直接跳转,但是按ctrl加函数名时,会在源码路径中进行搜索。

元对象和事件框架

如果要使用元对象,类的定义必须放在头文件中,并且必须继承自Qobject,并且在类定义中包含Q_OBJECT宏,它的作用是向类中添加metaObject()这个虚函数,并且识别其它的slot signal之类的宏。 而moc_xxx.cpp负责实现虚函数。 如果要反射类的成员函数,需要在函数声明前加上Q_INVOKABLE宏。

通过反射可以知道类与类之间的继承关系。
还可以得到成员函数的参数以及返回类型的信息,但是这些类型不能是自定义类,比如以下的例子在运行时会报错

#ifndef TEST_QOBJECT_H
#define TEST_QOBJECT_H
#include<QObject>
class B;
class A:public QObject{ /*要使用元对象系统必须继承自 QObject 类,且 QObject 应位于基类
继承列表中的第一位。*/
Q_OBJECT //启动元对象系统。Q_OBJECT 必须位于私有区域。
public: A(){qDebug("initialize A\n");}
};
//class A:public QObject{Q_OBJECT};
class B:public QObject{ Q_OBJECT
public: B(){qDebug("initialize B\n");}
    Q_INVOKABLE A* g(int i,float j,A* a){return a;}
};
class C:public QObject{Q_OBJECT};
class D:public C{Q_OBJECT};
#endif //TEST_QOBJECT_H
int main(){ A ma;
    B mb;
    const QMetaObject *pa=ma.metaObject();
    const QMetaObject *pb=mb.metaObject();

    QMetaMethod m=pb->method(pb->indexOfMethod("g(int,float)"));
    QByteArray s= m.name(); //获取成员函数 g 的函数名。
    cout<<s.data()<<endl; //输出 g
    s=m.methodSignature(); //获取函数 g 的签名
    cout<<s.data()<<endl; //输出 g(int,float)
    auto i=m.methodType(); /*获取函数 g 的类型,此处返回的是 QMetaMethod::MethodType 中定义的枚举值,
其中 Method=0,表示类型为成员函数*/
    cout<<i<<endl; //输出 0(表示成员函数)。
//以下信息与函数的返回类型有关
    s=m.typeName(); //获取函数 g 的返回值的类型名
    cout<<s.data()<<endl; //输出 void
    auto j=m.returnType(); /*获取函数 g 返回值的类型,此处的类型是 QMetaType 中定义的枚举值,其中枚举
值 QMetaType::void=43*/
    cout<<j<<endl; //输出 43
//以下信息与函数的参数有关
    auto k=m.parameterType(1); /*获取函数 g 中索引号为 1 的参数类型,此处的类型是 QMetaType 中定义的
枚举值,其中枚举值 QMetaType::float=38*/
    cout<<k<<endl; //输出 38
    QList<QByteArray> q=m.parameterNames(); //获取函数 g 的参数名列表
    cout<<q[0].data()<<q[1].data()<<endl; //输出 ij
    q=m.parameterTypes(); //获取函数 g 的参数类型列表。
    cout<<q[0].data()<<q[1].data()<<endl; //输出 intfloat
    return 0; }

qvariant可以用来保存自定义类型,但前提是该类型必须通过Q_DECLARE_METATYPE进行注册。 当类的成员变量用Q_PROPERTY声明之后,它就同时称为了类的属性,可以通过instance.property(“x”)的方式,利用变量名拿到属性的拷贝,并储存在Qvariant中,所以自定义类作为属性必须要有public的默认构造函数和拷贝函数。还可以通过instance.setProperty(“x”,v)的方式属性或者动态增加新属性。动态属性是属于实例的,所以不会跟类的元对象(QmetaObject)没有关系, 更改动态属性的值,会发送 QDynamicPropertyChangeEvent 到该对象。

QT中的对象以对象树的形式组织,父对象用Qobject指针保存其子对象的引用,一个子对象只能有一个父对象。 父对象析构时会自动删除其子对象,子对象删除时,会把该子对象从父对象的列表中移除,当一个对象被删除时,会发送一个 destroyed()信号

这里有一个问题,假设在栈上按照先后顺序创建A和B,并且将A的父对象设置为B,当离开作用域时,B先进行析构,它会调用A的析构函数。然后A自己又会调用一次析构函数,造成double free 错误。所以一般只让顶级父类位于栈上,其它子对象位于堆上。

在这里插入图片描述QApplication对象的exec()内会进行事件循环,调用notify()将事件分发给对应的对象,而对象处理事件的接口是event(),其内部根据事件的不同会调用不同的事件处理函数,event()返回true表示这个事件被接受并进行了处理,否则会被进一步分发给父对象,直到这个事件被处理或到达顶层对象。

#include <QApplication>
#include<QWidget>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QWidget{public:
    bool event(QEvent* e); //事件处理方式 1:重写虚函数 QObject::event()
    void mousePressEvent(QMouseEvent* e); };//事件处理方式 2:重写 QWidget 类中的此虚函数

bool A::event(QEvent* e){ static int i=0; //i 用于计数
    cout<<"E"<<i++<<endl; //验证该函数能在事件到达目标对象之前被捕获
    if(e->type()==QEvent::KeyPress) //判断是否是键盘按下事件。
        cout<<"keyDwon"<<endl;
    return QWidget::event(e); /*此处应调用父类的 event 函数以对未处理的事件进行处理,若此处不
调用父类的 event,则本例 mousePressEvent 处理函数中的内容将不会
被执行。读者可使此处返回 1 或者 0 进行验证。*/
}
void A::mousePressEvent(QMouseEvent* e){//处理鼠标按下事件,这是 Qt 中最简单的事件处理方式。
    cout<<"mouseDwon"<<endl;
}
int main(int argc, char *argv[]){
    QApplication a(argc,argv); //在 Qt 中 QApplication 类型的对象只能有一个
    A ma; //创建一个部件
    ma.resize(333,222); //设置部件的大小
    ma.show(); //显示创建的部件
    a.exec(); //在此处进入事件主循环。
    return 0; }

在上面这个例子中,重载的event最终会调用Qwidget的event函数,当中又会调用重载的mousePressEvent事件处理函数。
在event()内调用QEvent::accept()或者QEvent::accept()是没用的,因为它们是用于事件处理函数和event()之间通信的,而event()和notify()之间通讯直接利用bool返回值即可。
在这里插入图片描述

事件过滤器用于把本来应该分发给目标对象事件,发给观察者对象。并由观察者的eventFilter函数进行处理,如果返回false,那么事件会返回给目标对象,在多观察者的情况下,如果为true则传递给下一个观察者,最后注册的观察者,最先激活,在单观察者情况下如果为true就不再传回目标对象。

#include<QMouseEvent>
#include<QPushButton>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{public: //该类的对象用作过滤器对象,使用事件过滤器需继承 QObject
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress)
 {cout<<w->objectName().toStdString(); //验证 w 为事件本应到达的目标对象
 cout<<"=Ak"<<endl;
 return 1; //返回 1 表示该事件不再进一步处理
 }
 return 0;} /*返回 0 表示其余事件交还给目标对象处理,本例应返回 0,否则添加了该
过滤器的安钮会无法显示。*/
 };
class B:public A{public: //继承自类 A
bool eventFilter(QObject *w, QEvent *e){
if(e->type()==QEvent::MouseButtonPress){
cout<<w->objectName().toStdString()<<"=Bk"<<endl;
return 0;}
return 0;}
};
class C:public QWidget{public: void mousePressEvent(QMouseEvent *e){cout<<"Ck"<<endl;}};
class D:public QPushButton{public:void mousePressEvent(QMouseEvent *e){cout<<"DK"<<endl;}};
int main(int argc, char *argv[]){
QApplication a(argc,argv);
//创建对象,注意:本例父对象应先创建,以避免生命期过早结束
 A ma; B mb; C mc; D *pd=new D; D *pd1=new D;
 pd->setText("AAA"); pd->move(22,22); pd1->setText("BBB"); pd1->move(99,22);
 //设置对象名
 ma.setObjectName("ma"); mb.setObjectName("mb"); mc.setObjectName("mc");
 pd->setObjectName("pd"); pd1->setObjectName("pd1");
 //设置父对象
 pd->setParent(&mc); pd1->setParent(&mc);
 mb.setParent(&ma); //①
 //注册过滤器对象
 pd->installEventFilter(&mb); //②
 pd1->installEventFilter(&ma); //③
mc.resize(333,222); mc.show(); a.exec(); 
return 0; }

自定义事件有两种,一种是postEvent,它会加入事件队列,并且按照优先级排列。而sendEvent是直接发送给目标对象。所以postevent必须创建在堆上,事件发布后他的所有权就归事件队列所有.

假设一个窗口有多个功能相似的按钮,如果为每一个按钮写一个槽函数会很麻烦,更方便的做法是将各个按钮的信号绑定到同一个QSignalMapper上,然后用同一个槽函数进行处理。 为了区分这些按钮,可以事先用setMapping将按钮和一个整数或字符串绑定在一起。

void Widget::InitUi()
{
    names << "宋江" << "卢俊义" << "吴用" << "公孙胜"
          << "关胜" << "林冲" << "秦明" << "呼延灼"
          << "花荣" << "柴进" << "李应" << "朱仝"
          << "鲁智深" << "武松" << "董平" << "张清";
    QGridLayout *gridLayout = new QGridLayout;
    for (int i = 0; i < names.size(); ++i)
    {
        QPushButton *button = new QPushButton(names[i]);
        // 使用setMapping()函数将一个特定的整数或字符串或对象(qt doc截图那些)和一个特定的对象关联起来。
        signalMapper->setMapping(button, names[i]);
        // 这就是可以将对象的信号连接到QSignalMapper对象的map()槽函数上
        connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
        gridLayout->addWidget(button, i / 4, i % 4);
    }
    // 将我们定义的槽函数连接到mapped()信号 // 注意看mapped的写法
    connect(signalMapper, SIGNAL(mapped(QString)),this, SLOT(ShowName(QString)));
    setLayout(gridLayout);
}
模型视图

QT使用代理来实现模型和视图之间数据的传输。

QWFloatSpinDelegate::QWFloatSpinDelegate(QObject *parent):QStyledItemDelegate(parent)
{
}

QWidget *QWFloatSpinDelegate::createEditor(QWidget *parent,
   const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setDecimals(2);
    editor->setMaximum(10000);

    return editor;
}

void QWFloatSpinDelegate::setEditorData(QWidget *editor,
                      const QModelIndex &index) const
{
    float value = index.model()->data(index, Qt::EditRole).toFloat();
    QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
    spinBox->setValue(value);
}

void QWFloatSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
    spinBox->interpretText();
    float value = spinBox->value();
    QString str=QString::asprintf("%.2f",value);

    model->setData(index, str, Qt::EditRole);
}

void QWFloatSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    editor->setGeometry(option.rect);
    qDebug() << editor->rect();
}

上面是一个spinbox代理,当视图中的数据更新时会调用setmodelData来把更新同步到model中,反之当model中数据更新时调用setEditorData. setEditorData的参数中,index用来在model中索引出被更新过的数据项,editor是负责把index对应的数据项展示出来的窗口组件,在这里是一个spinbox。

绘图

使用Qpainter可以在不同的绘图设备上绘图,包括QWidget、QPixmap、QImage等。对于QWidget,只能在其paintEven函数中使用Qpainter。

Graphics view 架构方便绘制由多个图形元件组成的图形,每个元件都是可选择和修改的。Graphics view由三个部分组成,即场景,视图和图形项。

QGraphicsView 坐标单位是像素,左上角为(0,0)。QGraphicsScene 可以看成是所有顶层图形项的父项,并且可以设置左上角坐标,以及长宽。 如果场景和某个视图绑定,那么场景会填满该视图,场景用来管理每个图形项的状态,并将事件传给每个图形项,视图用来显示场景中的内容,可以为一个场景设置多个不同的视图。每个QGraphicsItem使用自己的坐标系,通常以中心为0,0。 QGraphicsItem::pos()返回图形项中心在父项坐标系中的坐标,如果是顶层就返回场景中坐标。 每个图形项在场景中都有一个位置坐标,和一个矩形包围框。

scene本身没有事件处理函数,它只负责把view的事件通过sendEvent转发给内部的item.scene可以管理内部selected 或者focused的item。 focused的item可以接收键盘事件。item之间用ZValue属性判别前后关系,在堆叠情况下只有Zvalue最大的item能获得焦点和select。

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include "math.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;   // 定义一个场景,设置背景色为红色
//    scene.setBackgroundBrush(Qt::red);

    QPen pen;   // 定义一个画笔,设置画笔颜色和宽度
    pen.setColor(QColor(0, 160, 230));
    pen.setWidth(10);

    QGraphicsRectItem *m_rectItem = new QGraphicsRectItem();   // 定义一个矩形图元
    m_rectItem->setRect(0, 0, 80, 80);
    m_rectItem->setPen(pen);
    m_rectItem->setBrush(QBrush(QColor(0, 0, 255)));
    m_rectItem->setFlag(QGraphicsItem::ItemIsMovable);

    QGraphicsLineItem *m_lineItem = new QGraphicsLineItem();    // 定义一个直线图元
    m_lineItem->setLine(QLineF(0, 0, 100, 100));
    m_lineItem->setPen(pen);
    m_lineItem->setFlag(QGraphicsItem::ItemIsMovable);

    QGraphicsPathItem *m_pathItem = new QGraphicsPathItem();    // 定义一个路径图元
    QPainterPath path;
    path.moveTo(0, 0);
    for (int i = 1; i < 5; ++i) {
        path.lineTo(50 + 40 * cos(0.8 * i * M_PI), 50 + 40 * sin(0.8 * i * M_PI));
    }
    path.closeSubpath();
    m_pathItem->setPath(path);
    m_pathItem->setPen(pen);
    m_pathItem->setFlag(QGraphicsItem::ItemIsMovable);

    QGraphicsPolygonItem *m_polygonItem = new QGraphicsPolygonItem();   // 定义一个多边形图元
    QPolygonF polygon;
    polygon << QPointF(-100.0, -150.0) << QPointF(-120.0, 150.0)
            << QPointF(320.0, 160.0) << QPointF(220.0, -140.0);
    m_polygonItem->setPolygon(polygon);
    m_polygonItem->setPen(pen);
    m_polygonItem->setFlag(QGraphicsItem::ItemIsMovable);

    scene.addItem(m_rectItem);      // 把矩形图元添加到场景
    scene.addItem(m_lineItem);      // 把直线图元添加到场景
    scene.addItem(m_pathItem);      // 把路径图元添加到场景
    scene.addItem(m_polygonItem);   // 把多边形图元添加到场景

    QGraphicsView view(&scene); // 定义一个视图,并把场景添加到视图
    view.resize(1024, 768);
    view.show();

    return a.exec();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值