Qt入门

一、从简单的Qt程序开始说起:

#include<QApplication>
#include<QLabel>

int main(int argc, char *argv[])
{
    // 应用程序类,用于管理整个应用程序所用到的资源
    QApplication app(argc, argv);
    QLabel* label = new QLabel("Hello Qt");
    label->show();  // 让Label处于课件状态
    // 程序进入事件循环状态,这是一种等待模式,不是一种貌似while(1)死循环的形式,其等待用户的动作作出相应的处理
    return app.exec();
}

注: 通过*.pro文件创建一个对应的VS工程文件使用命令 qmake -tp vc hello.pro
生成一个Xcode工程文件:qmake -spec macx-xcode hello.pro

#include<QApplication>
#include<QSpinBox>
#include<QSlider>
#include<QHBoxLayout>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv); // 应用程序实例
    QWidget *window = new QWidget; // 以QWidget对象为顶级窗口容器, 还可以以QMainWindow等来作为顶级窗口容器
    window->setWindowTitle("Enter your age"); // 设置窗口标题

    QSpinBox *spinBox = new QSpinBox = new QSpinBox; // 有上下箭头的微调器
    QSlider *slider = new QSlider(Qt::Horizontal); // 滑动杆
    spinBox->setRange(0, 130);
    slider->setRange(0, 130);

    /*
    connect();的声明
     QMetaObject::Connection QObject::connect(const QObject * sender, 
        const char[static] * signal, const QObject * receiver, const char * method, 
        Qt::ConnectionType type = Qt::AutoConnection)
    QObject是所有类的根。Qt使用这个QObject实现了一个单根继承的C++。它里面有一个connect静态函数,用于连接信号槽。
    */

    // Qt独有的信号与槽机制,类似C++11的std::bind和std::function
    QObject::connect(spinBox, SIGNAL(valueChanget(int)), slider, SLOT(setValue(int))); 
    QObject::connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));

    spinBox(30);

    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(spinBox);
    layout->addWidget(slider);

    /*
    QWidget::setLayout()函数调用会在窗口上安装该布局管理器,从软件的底层实现上来说,QSpinBox和QSlider会自动“重定义父窗口”, 他们会成为这个安装了布局的窗口部件的子对象,也正是基于这个原因,当创建一个需要放进某个布局中国的窗口部件是,就没有必要为其显式的指定父窗口了
    */
    window->setLayout(layout); // 设置主布局,相当于在窗口上安装该布局管理器
    window->show();

    // 进入消息循环的过程,Qt中的消息循环是是信号和槽机制,与Cocos2d-x或Win32的的while(1)死循环进行事件监听不同,
    // Qt中是有相应的改变才会发出相应的信号,进而调用信号所connect的槽函数
    return app.exec();

}

二、布局管理
Qt中四个布局管理器:
水平布局管理器:QHBoxLayout, 按照水平方向从左到右布局;
竖直布局管理器:QVBoxLayout, 按照竖直方向从上到下布局;
网格布局:QGridLayout,在一个网格中进行布局,布局是指定其位置类似二维数组的下标指定位置以及所占的空间即可;
分组布局:QStackLayout, 对一组子窗口部件进行摆放或者分页, 每次只显示其中的一个,其他的子窗口部件或者分页都隐藏起来;
另外还有以一些具有布局功能的类QSpliter(拆分), QScrollArea, QMAinWindow和QMdiArea(多文档);

FindFileDialog::FindFileDialog(QWidget *parent):QDialog(parent)
{
    ...
    QGridLayout *leftLayout = new QGridLayoutt;
    leftLayout->addWidget(namedLabel, 0, 0);
    leftLayout->addWidget(namedLineEdir, 0, 1);
    leftLayout->addWidget(lookInLabel, 1, 0);
    leftLayout->addWidget(lookInLineEfit, 1, 1);
    leftLayout->addWidget(subfoldersCheckBox, 2, 0, 1, 2);
    leftLayout->addWidget(tableWidget, 3, 0, 1, 2);
    leftLayout->addWidget(messageLabel, 4, 0, 1, 2);

    QVBoxLayout *rightLayout = new QVBoxLayout;
    rightLayout->addWidget(findButton);
    rightLayout->addWidget(stopButton); // 往布局管理器里添加窗口部件
    rightLayout->addWidget(closeButton);
    rightLayout->addStretch(); // 添加一个占位的标志,类似“弹簧”, 用于占位,不可见
    rightLayout->addWidget(helpButton);

    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->addLayout(leftLayout);  // 主布局中添加次布局
    mainLayout->addLayout(rightLayout);
    setLayout(mainLayout); // 设置主布局
    setWindowTitle("Find Files or Folders");

}

Qt为它所有的内置窗口部件都提供了合理的默认大小策略值,开发人员可以通过改变窗口部件的大小策略(QSizePolicy)来改变这些默认值;
一个QSizePolizy既包含一个水平分量,也包含一个垂直分量,其常见的取值如下:

  1. Fixed: 该窗口部件不能被拉伸或者压缩,窗口部件的大小尺寸总是保持为其大小提示的尺寸;
  2. Mnimum:该窗口部件的大小提示就是它的最小大小,再不能把窗口部件压缩到比这个大小提示还要小的大小,但是如有必要,可以拉伸它来填充尽可能多的空间;
  3. Maximum: 该窗口部件的大小提示就是它的最大大小,但是可以把该窗口部件压缩成它的最小大小提示的尺寸;
  4. Preferred: 该窗口部件的大小提示就是它比较合适的大小,但是如果需要,还是可以对该窗口的部件进行拉伸或者压缩;
  5. Expanding: 可以拉伸或者压缩该窗口部件,并且它特别希望能够变长变高;
  6. Ignored: 与Expanding相似, 只是它可以忽略窗口部件的大小提示和最小大小提示;
    注意: QSizePolicy类还保存了水平方向和垂直方向的一个拉伸因子, 这些拉伸因子可以用来说明在增大窗体时,对不同的子窗口部件应使用的不同放大比例;另外,如果考虑以上约束条件还不够的话,还可以对子窗口部件的类进行派生并且重新实现sizeHint()函数,由此获得所需的大小提示;
    这也是Qt中常用的策略,通过继承以及重载其中的虚函数来实现自己想要的功能,比如:
// MyWidget.h
class MyWidget : public QWidget
{
Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = 0);
    bool event(QEvent*);
    void mousePressEvent(QMouseEvent*);
    void mouseReleaseEvent(QMouseEvent*);
    void mouseMoveEvent(QMouseEvent*);
    void keyPressEvent(QKeyEvent*);
    void keyPressEvent(QKeyEvent*);
    void closeEvent(QCloseEvent*);
    void paintEvent(QPaintEvent*);
signals:
    void salaryChanged(int newSalary);
public slots:
    void setSalary(int newSalary);
private:
    int _salary;
};
// MyWidget.cpp
...
bool MyWidget::event(QEvent *ev)
{
    /* 
    拦截了MouseButtonPress时间不处理,因为消息都是从父类的QWidget的event一级一级往上传的,比如这里的的
        MouseButtonPress事件会往上传到mousePressEvent进行真正的处理,一般而言都是重载则一个层面的
        事件处理函数的,而不是重载event函数,因此可以看见重载enent()函数可以实现截断消息的功能
    另外还有专门的事件过滤器做消息截断工作:QObject::installEventFilter()允许一个对象中途截取发往
        另一个对象的事件,类似于Windows编程的钩子;
    */
    if(ev->type() == QEvent::MouseButtonPress) 
        return true;
    ev->accept();

    // 添加了自己想要实现的功能后别忘了以前该怎样的还是得做,所以得调用父类的event函数
    return QWidget::event(ev); 
}
/*
对QEvent::accept()的理解
void QEvent::accept()
设置事件对象的"accept"标志,等价于调用setAccepted(true)。
接收了该事件,不接收的事件可能会被传递给它的父窗口部件。

void QEvent::ignore()
清除事件对象的接受标志,等价于调用setAccepted(false)。
清除该标志意味着该事件接收器不接收该事件。不接收的事件可能会被继续传递给它的父窗口部件。

注:accepted: bool事件对象的接受标志
设置该参数表明事件接收器期望获得该事件。非预期的事件可能会传递给它的父对象。默认情况下isAccept()设置为true
*/

// 常规对功能键是否按下的代码结构演示
void MyWidget::mousePressEvent(QMouseEvent *ev)
{
    if(ev->button()==Qt::LeftButton)
    {
        if(ev->modifiers() == Qt::ControlModifier)
        {
            // handle with Control, 按下Contorl键的处理
            return;
        }
        // handle without Control,未按下Control键的处理
    }
    else
    {
        // ...
    }

}

三、信号和槽机制

/*
1) 信号的定义必须在signals:保留字下,并且不需要实现
2)槽的定义必须在slots:保留字下,需要实现
3)信号和槽通过QObject::connect函数连接
4)当信号被触发时,槽函数被调用

需要注意的是:
1)信号和槽,是QT的拓展,所以实现信号和槽的类,必须是QObject的子类
2)实现信号和槽的类,必须以宏Q_OBJECT开始,该宏提供了信号和槽机制的底层支持
3)连接信号和槽,要用到SIGNAL和SLOT宏,转换函数为字符串
4)一个信号可以和多个槽连接,槽函数调用的顺序是不确定的
5)多个信号可以同时连接一个槽
6)信号可以连接信号,形成信号传导
7)信号和槽的参数应该一样多,而且类型必须相同,要把信号成功连接到槽(或者连接到另一个信号),他们的参数必须具有相同的顺序和相同的类型,如:信号连接信号的示例:注意SIGNAL和SLOT中的函数如果有参数只写参数类型即可,不可以写上参数名
connnet(lineEdit, SIGNAL(textChanged(const QString&)), this, SIGNAL(updateRecord(const QString&)));
若信号的参数比它所连接的槽的参数多,那么多余的参数将会被简单的忽略掉,如:
connect(flp, SIGNAL(rawCommandReply(int, const QString&)), this, SLOT(checkError(int)));
若参数类型不匹配,或信号或槽不存在,或在connect中信号和槽的名字包含了参数名,Qt也会发出警告;
8)信号和槽都可以重载,信号只需要声明不需要实现;
9)信号和槽都可以有默认参数;
10)槽函数可以像普通成员函数一样被调用;
11)在槽函数中,调用sender可以获得信号调用者,receiver获得接受者;
12)信号可以使用disconnec移除:
disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
13)在Qt4.6及之前,若一个信号连接多个槽函数,则槽函数的执行顺序是随机的,但在Qt4.6后一个信号连接多个槽函数,则槽函数的执顺序是按照connect时的顺序顺势执行的(在网站http://www.cnblogs.com/MuyouSome/p/3515941.html评论上看到的细节问题,感谢);以下为Qt5.5的文档摘抄(注意第三段的叙述)
    Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.
    When a signal is emitted, the slots connected to it are usually executed immediately, just like a normal function call. When this happens, the signals and slots mechanism is totally independent of any GUI event loop. Execution of the code following the emit statement will occur once all slots have returned. The situation is slightly different when using queued connections; in such a case, the code following the emit keyword will continue immediately, and the slots will be executed later.
    If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted.
Signals are automatically generated by the moc and must not be implemented in the .cpp file. They can never have return types (i.e. use void).
    A note about arguments: Our experience shows that signals and slots are more reusable if they do not use special types. If QScrollBar::valueChanged() were to use a special type such as the hypothetical QScrollBar::Range, it could only be connected to slots designed specifically for QScrollBar. Connecting different input widgets together would be impossible.

14)connect也支持C++11支持的lambda表达式用法
    connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);
15)注意区分信号与事件
Qt 的事件和Qt中的signal不一样. 后者通常用来"使用"widget, 而前者用来"实现" widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的。但是如果我们要重载一个按钮的时候,我们就要面对event了。 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.。
信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。
信号通过emit发送,而事件除了系统事件如鼠标,键盘等事件,还有可以通过QApplication::postEvent或QApplication::sendEvent函数进行发送;
16)信号或者槽函数都可以加上形参默认值;
    如:
    signals:
        void sig(int i = 3); // emit sig(); 或者emit sig(5)

    public slots:
        // 这里的默认值形参似乎没意义,因为n的值还是由信号传递过来的值决定的,但这种语法经验证没问题
        void slot(int n = 1); 
*/

// 发送信号代码示例
void Employee::setSalary(int newSalary)
{
    if(newSalary != mySalary) // 加上判断条件,确保循环连接不会造成死循环
    {
        mySalary = newSalary;
        emit salaryChanged(mySalary);
    }
}

总结一下:
一个类: QObject
两个函数: connect , disconnect
三个宏: Q_OBJECT SIGNAL SLOT
三个保留字:signals , slots , emit

参考资料:
《Qt 5.5官方文档》
《C++ GUI Qt4编程 》
http://blog.csdn.net/chenlong12580/article/details/7710274
http://qimo601.iteye.com/blog/1407911
http://www.cnblogs.com/MuyouSome/p/3515941.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值