??本文是我在學(xué)習(xí)QT的GUI界面設(shè)計(jì)過(guò)程當(dāng)中的心得和學(xué)習(xí)筆記,在學(xué)習(xí)時(shí)已經(jīng)有C, C++,Python的基礎(chǔ)。文章附上了學(xué)習(xí)的代碼,僅供大家參考。如果有問(wèn)題,有錯(cuò)誤歡迎大家留言。此外,博主還有另外幾篇文章,分別關(guān)于 Python基礎(chǔ)知識(shí)、 Python的具體應(yīng)用、 C語(yǔ)言指針結(jié)構(gòu)體的難點(diǎn)、 C++入門和進(jìn)階知識(shí)點(diǎn)和 C++高階知識(shí)點(diǎn),大家點(diǎn)擊即可翻閱。
一、Qt簡(jiǎn)介和下載安裝
??Qt是一個(gè)1991年由Qt Company開(kāi)發(fā)的跨平臺(tái)C++圖形用戶界面(Graphic User Interface, GUI)應(yīng)用程序開(kāi)發(fā)框架。QT包括但不僅限于GUI的開(kāi)發(fā),也包含了諸如系統(tǒng)調(diào)用、網(wǎng)絡(luò)編程、數(shù)據(jù)庫(kù)編程,2D/3D圖形處理等等。QT具有強(qiáng)大的跨平臺(tái)運(yùn)行的性能,幾乎囊括了所有的操作系統(tǒng),例如Linux、Windows、Mac OS、Android、IOS。我們所熟知的金山WPS、Google Earth谷歌地圖、SKype網(wǎng)絡(luò)電話就是用Qt開(kāi)發(fā)的。
??博主使用的版本是Qt 5.14.2, 下載、安裝 參照B站視頻。
二、Qt入門
2.1 創(chuàng)建第一個(gè)項(xiàng)目
??第一步,選擇new->Application->Qt Widgets Application->Choose:
??第二步,修改項(xiàng)目名稱和項(xiàng)目路徑,點(diǎn)擊下一步。
??第三步,修改類名稱,其中基類有三種,分別是QMainWindow(菜單類), QWidget, QDialog(對(duì)話框類),表示創(chuàng)建的類繼承的基類,例如,圖中所示mywidget類的父類就是QWidget。QDialog和QMainWindow是QWidget的子類。QMainWindow是菜單類,左上角有一些菜單選項(xiàng),右上角有最小化最大化按鈕。QDialog是對(duì)話框類,下圖所示就是一個(gè)對(duì)話框類。
??第四步,選擇MinGW 64-bit 編譯器,32位和64位的區(qū)別在于32位能在64位的機(jī)器上跑,64位不能在32位的機(jī)器上跑,初始項(xiàng)目選擇任意一個(gè)就可以,點(diǎn)擊下一步,然后在點(diǎn)擊完成,就可以產(chǎn)生一個(gè)名為Qt_test的項(xiàng)目,項(xiàng)目底下有一個(gè)Qt_test.pro的項(xiàng)目文件。
??之后的步驟默認(rèn)就可以,一直點(diǎn)下一步,然后編譯運(yùn)行,出現(xiàn)一個(gè)空白窗口,創(chuàng)建完畢。
??編譯成功后,會(huì)在項(xiàng)目目錄底下生成build文件,然后點(diǎn)擊debug文件,里面有生成的.exe可執(zhí)行文件,點(diǎn)擊即可運(yùn)行,結(jié)果就是一個(gè)空白圖窗。博主在運(yùn)行.exe的時(shí)候碰到了錯(cuò)誤彈窗,顯示程序“無(wú)法找到入口”,添加了環(huán)境變量之后還需要將環(huán)境變量上移,具體解決參考解決Qt生成exe錯(cuò)誤:無(wú)法定位程序輸入點(diǎn)。
# QT_hello.pro文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
# 版本大于4以上的添加widgets模塊
CONFIG += c++11 # 用C++11標(biāo)準(zhǔn)來(lái)解釋代碼
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mywidget.cpp
HEADERS += \
mywidget.h
TARGET = UAV # 生成的.exe文件名稱
TEMPLATE = app # 應(yīng)用程序模板 Application
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
// mywidget.h文件
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class mywidget : public QWidget // 公共繼承
{
Q_OBJECT // Q_OBJECT宏,允許類中使用信號(hào)和槽的機(jī)制
public:
mywidget(QWidget *parent = nullptr); // 構(gòu)造函數(shù)
~mywidget(); // 析構(gòu)函數(shù)
};
#endif // MYWIDGET_H
// main.cpp文件
# include <QApplication>
# include <QtWidgets>
# include <QDebug>
// main程序入口 argc命令行變量數(shù)量, argv命令行變量的數(shù)組
int main(int argc, char *argv[])
{
QApplication a(argc, argv); // 應(yīng)用程序?qū)ο螅?在QT中,應(yīng)用程序?qū)ο笥星覂H有一個(gè)
mywidget w; // 窗口對(duì)象 mywidget是Qwidget的子類
w.show(); // show方法, 窗口對(duì)象默認(rèn)不會(huì)顯示
qDebug()<<"hello world"; // 在控制臺(tái)輸出, 用于調(diào)試
return a.exec(); // 讓應(yīng)用程序?qū)ο筮M(jìn)入消息循環(huán)
}
// mywidget.cpp文件
#include "mywidget.h"
mywidget::mywidget(QWidget *parent)
: QWidget(parent)
{
}
mywidget::~mywidget()
{
}
2.2 快捷鍵和命名規(guī)范
??QT如下,可以提高編碼效率:
/* 快捷鍵
* 運(yùn)行代碼: Ctrl + r
* 編譯: Ctrl + b
* 注釋: Ctrl + /
* 縮放字體:Ctrl + 滾輪
* 查找/替換字體: Ctrl + f
* 整行移動(dòng)代碼: Ctrl + Shift + 上/下鍵
* 自動(dòng)對(duì)齊:Ctrl + i
* 在同名文件和源文件之間切換: F4
* 快速添加函數(shù)定義:鼠標(biāo)移到聲明的那一行,按Alt + Enter
* 修改變量名,并應(yīng)用到所有:Ctrl + Shift + r
* 快捷打開(kāi)輸出窗口: Alt + number(1-8)
* 書簽功能: 快速跳到代碼
* Ctrl + M 添加/刪除標(biāo)簽
* Ctrl + . 查找并移動(dòng)到下一個(gè)標(biāo)簽處
* 查看幫助文檔:
* 第一種:Qt Creator 查看 F1
* 第二種:獨(dú)立的幫助文檔程序查看
*/
/*
類名: 首字母大寫,單詞和單詞之間首字母大寫
函數(shù)名和變量名稱: 首字母小寫,單詞和單詞之間首字母大寫
*/
??Qt設(shè)計(jì)師創(chuàng)建的文件后綴為.ui, 無(wú)法直接運(yùn)用到C++中,因此引入一個(gè)uic工具,可以將.ui文件轉(zhuǎn)換為.c文件。rcc moc同樣是這樣類型的工具,將一些qt文件轉(zhuǎn)換成C++語(yǔ)法格式的文件。
2.3 Qt項(xiàng)目和VS2022項(xiàng)目相互轉(zhuǎn)換
??博主最近需要使用QT和VS 2022聯(lián)合編程,大家有需要也可以參考視頻 VS項(xiàng)目和QT項(xiàng)目相互轉(zhuǎn)換。
??使用VS2022創(chuàng)建的QT項(xiàng)目,輸出為.pro文件,利用Qt createor打開(kāi),需要在.pro文件中加載模塊(添加如下代碼),因?yàn)閂S2022是在項(xiàng)目配置的時(shí)候加載的。如下圖所示。
# ----------------------------------------------------
# This file is generated by the Qt Visual Studio Tools.
# ------------------------------------------------------
QT += core gui widgets
# 模塊加載, 使用VS2022創(chuàng)建的項(xiàng)目,輸出為.pro文件,利用Qt createor打開(kāi),需要在.pro文件中加載模塊
# 因?yàn)閂S2022是在項(xiàng)目配置的時(shí)候加載的。
TEMPLATE = app
TARGET = QtWidgetsTest
##########################################
# 以下代碼可以不要
DESTDIR = ../x64/Debug
CONFIG += debug
LIBS += -L"."
DEPENDPATH += .
MOC_DIR += GeneratedFiles/$(ConfigurationName)
OBJECTS_DIR += debug
UI_DIR += GeneratedFiles
RCC_DIR += GeneratedFiles
###########################################
HEADERS += ./qtwidgetstest.h
SOURCES += ./qtwidgetstest.cpp \
./main.cpp
FORMS += ./qtwidgetstest.ui
RESOURCES += qtwidgetstest.qrc
w.setWindowTitle(u8"VS2022 QT 窗口"); // 不亂碼
/*
產(chǎn)生亂碼, 英文不會(huì)有亂碼,英文編碼格式都是同意的ASCII,QT中文編碼格式是UTF-8,
windows中文編碼格式是GB2312,u8為轉(zhuǎn)換成UTF-8,QT就可以識(shí)別了
*/
三、Qt基礎(chǔ)
3.1 Qt對(duì)象樹(shù)和窗口坐標(biāo)系概念
??當(dāng)創(chuàng)建的對(duì)象在堆區(qū)的時(shí)候,如果指定的父親是QObject派生下來(lái)的類或者QObject子類派生下來(lái)的類,可以不用管理釋放操作,QT會(huì)對(duì)象會(huì)放入到對(duì)象樹(shù)中,會(huì)自動(dòng)釋放內(nèi)存,一定程度上簡(jiǎn)化了內(nèi)存回收機(jī)制。這也是QT的優(yōu)點(diǎn)之一,因此,我們?cè)跇?gòu)造時(shí)候就指定parent對(duì)象,就不需要操心內(nèi)存釋放問(wèn)題。
??Qt窗口的坐標(biāo)系:以左上角頂點(diǎn)為原點(diǎn)(0, 0),X向右增加,Y向下增加。對(duì)于嵌套窗口,其坐標(biāo)系是相對(duì)于父窗口而言。
3.2 QPushButton
??在編寫這部分代碼時(shí),博主的編輯器竟然沒(méi)有代碼補(bǔ)全功能,于是又在網(wǎng)上找了解決辦法,這里給出鏈接。
??在學(xué)習(xí)QT的各種類的過(guò)程中,最重要的是 學(xué)會(huì)如何查找?guī)椭臋n以及看懂幫助文檔。例如QPushButton類,幫助文檔中給出詳細(xì)解釋:添加頭文件,同時(shí)要在.pro文件中加入widgets模塊,其父類是QAbstractButton,其子類是QCommandLinkButton等等信息。
3.3 信號(hào)和槽(signals and slots)
3.3.1 pushbutton關(guān)閉窗口
??信號(hào)和槽是學(xué)習(xí)Qt的一個(gè)非常重要知識(shí)點(diǎn),在信號(hào)和槽當(dāng)中,我們引入一個(gè)連接函數(shù)connect( ),將信號(hào)發(fā)送者和信號(hào)接收者鏈接起來(lái)。connect( )一共有四個(gè)參數(shù):
- 參數(shù)1:信號(hào)發(fā)送者;
- 參數(shù)2:發(fā)送的信號(hào)(函數(shù)地址);
- 參數(shù)3:信號(hào)接收者;
- 參數(shù)4:處理的槽函數(shù)(函數(shù)地址)。
??在空白項(xiàng)目的基礎(chǔ)上改寫mywidget.cpp函數(shù),實(shí)現(xiàn)點(diǎn)擊按鈕,關(guān)閉窗口案例:
# include "mywidget.h"
# include <QPushButton>
# include <QDebug>
mywidget::mywidget(QWidget *parent)
: QWidget(parent)
{
qDebug() << "hello world"; // 調(diào)試信息
// 創(chuàng)建一個(gè)按鈕
QPushButton * btn = new QPushButton;
//btn->show(); // show以頂層(新窗口)的方式彈出窗口控件
btn->setParent(this); // 讓btn對(duì)象依賴在mywidget窗口中
btn->setText("第一個(gè)按鈕");
QPushButton *btn2 = new QPushButton("第二個(gè)按鈕",this);
btn2->move(100,100); // 移動(dòng)btn2按鈕
resize(600,400); // 重置窗口大小
btn2->resize(50,50); // 設(shè)置btn2按鈕大小
setWindowTitle("第一個(gè)窗口應(yīng)用"); // 設(shè)置窗口名稱
connect(btn,&QPushButton::clicked, this, &mywidget::close);
}
mywidget::~mywidget()
{
}
3.3.2 自定義信號(hào)和槽
??創(chuàng)建一個(gè)下課,老師餓了,學(xué)生請(qǐng)客的案例。添加自定義的老師類和學(xué)生類,選擇C++類,這個(gè)兩個(gè)類不是窗口類,直接繼承QObject類。分別產(chǎn)生了teacher和student的.cpp .h文件。
??頭文件中定義,變量名稱和函數(shù)聲明,在.cpp文件中寫實(shí)現(xiàn)。
// mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
# include "teacher.h"
# include "student.h"
class mywidget : public QWidget // 公共繼承
{
Q_OBJECT // Q_OBJECT宏,允許類中使用信號(hào)和槽的機(jī)制
public:
mywidget(QWidget *parent = nullptr); // 構(gòu)造函數(shù)
~mywidget(); // 析構(gòu)函數(shù)
Teacher *t; // 在頭文件中聲明變量和函數(shù)
Student *s;
void ClassIsOver();
};
#endif // MYWIDGET_H
// student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
signals:
public slots:
/* 早期的QT版本必須要寫到,public slots,高級(jí)版本可以寫到public或全局下
* 返回值void,需要聲明,也需要實(shí)現(xiàn)
* 可以有參數(shù),可以發(fā)生重載
*/
void treat();
void treat(QString foodName);
};
#endif // STUDENT_H
// teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
/* 自定義信號(hào)類,沒(méi)有返回值
* 只需要聲明,不需要實(shí)現(xiàn)
* 可以有參數(shù),可以重載
*/
void hungry();
void hungry(QString foodName);
public slots:
/* 早期的QT版本必須要寫到,public slots,高級(jí)版本可以寫到public或全局下
* 返回值void,需要聲明,也需要實(shí)現(xiàn)
* 可以有參數(shù),可以發(fā)生重載
*/
};
#endif // TEACHER_H
// mywidget.cpp
# include "mywidget.h"
# include <QPushButton>
# include <QDebug>
mywidget::mywidget(QWidget *parent)
: QWidget(parent)
{
// 創(chuàng)建老師和學(xué)生對(duì)象
this->t = new Teacher(this);
this->s = new Student(this);
// 無(wú)參
// //ClassIsOver(); // 調(diào)用下課函數(shù) 沒(méi)有鏈接,沒(méi)有任何響應(yīng)
// connect(t,&Teacher::hungry, s, &Student::treat); // 先鏈接后觸發(fā)信號(hào),才能響應(yīng)
// ClassIsOver(); // 調(diào)用下課函數(shù)
// 鏈接代參數(shù)的函數(shù),
void (Teacher:: *f1)(QString) = &Teacher::hungry;
void (Student:: *f2)(QString) = &Student::treat;
// connect(t,f1, s, f2); // 因?yàn)榘l(fā)生了函數(shù)重載,不能簡(jiǎn)單的用取地址符,編譯器判斷不了是哪個(gè)函數(shù),用函數(shù)指針
ClassIsOver(); // 調(diào)用下課函數(shù)
// 點(diǎn)擊一個(gè)按鈕, 觸發(fā)下課
QPushButton *btn = new QPushButton("下課",this);
this->resize(800,600); // 重置窗口大小
connect(btn,&QPushButton::clicked,this, &mywidget::ClassIsOver);
// disconnect(btn,&QPushButton::clicked,this, &mywidget::ClassIsOver); // 斷開(kāi)鏈接
/* 1、信號(hào)可以鏈接信號(hào)
* 2、一個(gè)信號(hào)可以鏈接到多個(gè)槽函數(shù)
* 3、多個(gè)信號(hào)可以鏈接到一個(gè)槽函數(shù)
* 4、信號(hào)和槽函數(shù)的參數(shù)必須類型一一對(duì)應(yīng)(槽函數(shù)要接收信號(hào)的參數(shù))
* 5、信號(hào)參數(shù)個(gè)數(shù)可以多于槽函數(shù)參數(shù)個(gè)數(shù),但是類型也要一一對(duì)應(yīng)
*/
// QT5 6 向下兼容 QT4版本以前的信號(hào)和槽的鏈接方式
connect(t,SIGNAL(hungry()), s, SLOT(treat(QString))); // 優(yōu)點(diǎn),直觀,缺點(diǎn),類型不做檢測(cè)
}
mywidget::~mywidget()
{
}
void mywidget::ClassIsOver()
{
// emit t->hungry();
emit t->hungry("宮保雞丁");
}
// student.cpp
#include "student.h"
# include <QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat()
{
qDebug() << "請(qǐng)老師吃飯";
}
void Student::treat(QString foodName)
{
// qDebug() << "請(qǐng)老師吃 :" << foodName; // 帶引號(hào),用toUtf8()先將它轉(zhuǎn)成QbyteArray類型,然后用data()在轉(zhuǎn)成char *類型
qDebug() << "請(qǐng)老師吃 :" << foodName.toUtf8().data();
}
??main.cpp和teacher.cpp默認(rèn)即可。信號(hào)和槽使用時(shí)必須先鏈接后觸發(fā)信號(hào),才能響應(yīng)。
3.4 Lambda表達(dá)式
??Lambda表達(dá)式是C++11中用來(lái)定義并創(chuàng)建匿名的函數(shù)對(duì)象。實(shí)際上是一個(gè)匿名方法,用來(lái)聲明一個(gè)只在此次使用的匿名函數(shù)。[函數(shù)對(duì)象參數(shù)](操作符重載函數(shù)參數(shù))mutable->返回值(函數(shù)體)
- 1、函數(shù)對(duì)象參數(shù):[],標(biāo)識(shí)一個(gè)lambda的開(kāi)始,這部分不能省略。函數(shù)對(duì)象參數(shù)是傳遞給編譯器自動(dòng)生成的函數(shù)對(duì)象類的構(gòu)迨函數(shù)。函數(shù)對(duì)象參數(shù)只能使用那些到定義Lambda 為止時(shí)Lambda所在作用范圍內(nèi)可見(jiàn)的局部變量(包括Lambda所在類的this)。函數(shù)對(duì)象參數(shù)有以下幾種形式:
參數(shù) | 作用 |
---|---|
空 | 沒(méi)有使用任何函數(shù)對(duì)象參數(shù) |
= | 函數(shù)體可以使用lambda所在作用范圍內(nèi)所有可見(jiàn)的局部變量 |
this | 函數(shù)體可以使用lambda所在類中的成員變量 |
a | 將a按值進(jìn)行傳遞 |
&a | 將a按引用進(jìn)行傳遞 |
a, &b | 將a按值傳遞,b按引用傳遞 |
=, &a, &b | 除a b按引用傳遞外,其余值按值進(jìn)行傳遞 |
&, a, b | 除a b按值進(jìn)行傳遞外,其余值按引用進(jìn)行傳遞 |
??其中=傳遞了包括Lambda所在類的this,并且是引用傳遞方式,相當(dāng)于編譯器自動(dòng)為我們引用傳遞了所有局部變量。按值進(jìn)行傳遞時(shí),函數(shù)體內(nèi)不能修改傳遞進(jìn)來(lái)的a的拷貝,因?yàn)槟J(rèn)情況下函數(shù)是const的。要修改傳遞進(jìn)來(lái)的a的拷貝,可以添加mutable修飾符。
- 2、操作符重載函數(shù)參數(shù):標(biāo)識(shí)函數(shù)重載的()參數(shù),沒(méi)有參數(shù)時(shí),可以省略。參數(shù)可以用過(guò)按值傳遞和按引用兩種方式進(jìn)行傳遞。
- 3、可修改標(biāo)識(shí)符:mutable聲明,這部分可以省略。按值傳遞函數(shù)對(duì)象參數(shù)時(shí),加上mutable修飾符,可以修改按值傳遞進(jìn)來(lái)的拷貝(注意僅僅是能修改拷貝, 而不是修改值本身)
- 4、函數(shù)返回值:->返回值類型, 標(biāo)識(shí)函數(shù)返回值的類型,當(dāng)返回值為void,或者函數(shù)體中只有溢出return的地方(此時(shí)編譯器可以自動(dòng)推斷出返回值類型)時(shí),這部分可以省略。
- 5、函數(shù)體: {},標(biāo)識(shí)函數(shù)的實(shí)現(xiàn),這部分不能省略,但函數(shù)體可以為空。
[capture](parameters)mutable->return-type
{
statement;
}
??在空項(xiàng)目的基礎(chǔ)之上改變mywidget.cpp函數(shù):
#include "mywidget.h"
#include <QPushButton>
mywidget::mywidget(QWidget *parent)
: QWidget(parent)
{
// 利用lambda表達(dá)式,點(diǎn)擊按鈕,關(guān)閉窗口
QPushButton *btn = new QPushButton("關(guān)閉", this);
btn->move(100,100);
connect(btn,&QPushButton::clicked,this, [=](){this->close();});
}
mywidget::~mywidget()
{
}
3.5 菜單欄工具欄的創(chuàng)建
??在空項(xiàng)目的基礎(chǔ)之上改變mywidget.cpp函數(shù):
#include "mainwindow.h"
#include <QPushButton>
#include <QMenuBar>
#include <QToolBar>
#include <QStatusBar>
#include <QDockWidget>
#include <QLabel>
#include <QTextEdit>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
resize(1400,900); // 重置窗口大小
/********** 菜單欄創(chuàng)建 **********/
QMenuBar *bar = menuBar();
setMenuBar(bar); // 將菜單欄放置到窗口中
QMenu *fileMenu = bar->addMenu("文件"); // 創(chuàng)建菜單
QMenu *editMenu = bar->addMenu("編輯"); // 創(chuàng)建菜單
QAction *newAction = fileMenu->addAction("新建");
fileMenu->addSeparator(); // 添加分隔線
QAction *openAction = fileMenu->addAction("打開(kāi)");
/********** 工具欄創(chuàng)建 **********/
QToolBar *toolBar = new QToolBar(this); // 工具欄,可以有多個(gè)
addToolBar(Qt::LeftToolBarArea, toolBar);
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);// 后期設(shè)置只允許左右???/span>
toolBar->setFloatable(false);// 設(shè)置浮動(dòng)
toolBar->setMovable(false); // 允許移動(dòng)(總開(kāi)關(guān))
toolBar->addAction(newAction);
toolBar->addSeparator();
toolBar->addAction(openAction);
QPushButton *btn = new QPushButton("aaa",this);
toolBar->addWidget(btn);
/********** 狀態(tài)欄創(chuàng)建,最多一個(gè) **********/
QStatusBar *stBar = statusBar();
setStatusBar(stBar);
QLabel *label = new QLabel("提示信息",this);
stBar->addWidget(label);
QLabel *label2 = new QLabel("右側(cè)提示信息",this);
stBar->addPermanentWidget(label2);
/********** 鉚接部件(浮動(dòng)窗口,可以有多個(gè))**********/
QDockWidget *dockWidget = new QDockWidget("浮動(dòng)窗口",this);
addDockWidget(Qt::BottomDockWidgetArea,dockWidget);
dockWidget->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea); // 設(shè)置??繀^(qū)域,只允許上下
/********** 設(shè)置中心部件,只能有一個(gè) **********/
QTextEdit *edit = new QTextEdit(this);
setCentralWidget(edit);
/*總結(jié): 使用set加入窗口的部件智能有一個(gè),而add加入的能有多個(gè)*/
}
MainWindow::~MainWindow()
{
}
3.6 資源文件添加和UI界面使用
??在新建空白項(xiàng)目的第三步點(diǎn)擊generate form,生成項(xiàng)目后就會(huì)產(chǎn)生一個(gè).ui文件。UI界面可以直接拖拽控件,輸入文本,我們開(kāi)發(fā)窗口應(yīng)用就變得很方便。
??在此界面的基礎(chǔ)上,創(chuàng)建文件,編輯,工具,幫助等菜單,菜單的一級(jí)目錄是無(wú)法鍵入中文的,只能輸入英文,然后在創(chuàng)建好的對(duì)象中將文本改成中文,建立完成后的文件如下。點(diǎn)擊項(xiàng)目添加文件,add new file -> Qt -> Qt resource file -> choose,然后更改文件名稱,一般設(shè)置為res,然后會(huì)在Resources底下生成一個(gè)res.qrc的文件。
??將圖片復(fù)制到項(xiàng)目目錄底下的Image文件(所有圖片文件都放進(jìn)去),以資源編輯器的方式打開(kāi)res.qrc,添加前綴(可以直接使用默認(rèn)或者“/”),添加文件,使用“ : + 前綴名 + 文件名稱” 。mainwindow.cpp文件如下所示:
// mainwindow.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ui->actionnew->setIcon(QIcon("C:/Users/19080/Pictures/Camera Roll/文件圖標(biāo).JPEG") ); // 絕對(duì)路徑
// 使用添加Qt資源文件
ui->actionnew->setIcon(QIcon(":Image\\fileIcon.JPEG") );
ui->actionopen->setIcon(QIcon(":Image\\Luffy.jpg") );
}
MainWindow::~MainWindow()
{
delete ui;
}
3.7 對(duì)話框
3.7.1 模態(tài)和非模態(tài)
??mainwindow.cpp文件如下所示:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 點(diǎn)擊新建按鈕 彈出一個(gè)對(duì)話框
connect(ui->actionnew,&QAction::triggered,[=](){
/* 對(duì)話框 分類
* 模態(tài)對(duì)話框(不可以對(duì)其他窗口進(jìn)行操作) 非模態(tài)對(duì)話框則相反
*/
/* 模態(tài)創(chuàng)建 阻塞 */
// QDialog dig(this);
// dig.resize(200,100);
// dig.exec();
// qDebug() <<"模態(tài)對(duì)話框彈出";
/* 非模態(tài)創(chuàng)建 */
QDialog *dig2 = new QDialog(this);
dig2->resize(200,100);
dig2->show();
dig2->setAttribute(Qt::WA_DeleteOnClose); // 55號(hào)屬性
qDebug() << "非模態(tài)對(duì)話框彈出";
});
}
MainWindow::~MainWindow()
{
delete ui;
}
3.7.2 消息對(duì)話框
??目前Qt內(nèi)置對(duì)話框有:
名稱 | 作用 |
---|---|
QColorDialog | 選擇顏色 |
QFileDialog | 選擇文件或者目錄 |
QFontDialog | 選擇字體 |
QInputDialog | 允許用戶輸入一個(gè)值,并將值返回 |
QMessageBox | 模態(tài)對(duì)話框,用于顯示信息、詢問(wèn)信息等等 |
QPageSetupDialog | 為打印機(jī)提供紙張相關(guān)的選項(xiàng) |
QPrintDialog | 打印機(jī)配置 |
QPrintPreviewDialog | 打印預(yù)覽 |
QProgressDialog | 顯示操作過(guò)程 |
??mainwindow.cpp文件如下所示,其中QMessageBox::question的返回值是QMessageBox::StandardButton類型,我們就可以利用if語(yǔ)句去判斷返回值是否為QMessageBox::Save,從而進(jìn)一步做其他操作。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QMessageBox>
#include <QDebug>
#include <QColorDialog>
#include <QFileDialog>
#include <QFontDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->actionnew,&QAction::triggered,[=](){
// 錯(cuò)誤對(duì)話框
// QMessageBox::critical(this, "critical", "錯(cuò)誤");
// 信息對(duì)話框
// QMessageBox::information(this, "info", "信息");
// 提問(wèn)對(duì)話框 參數(shù)1:父類,參數(shù)2:title, 參數(shù)3:提示信息, 參數(shù)4:按鍵選項(xiàng), 參數(shù)5: 默認(rèn)選項(xiàng)(關(guān)聯(lián)回車選項(xiàng))
// if(QMessageBox::Save == QMessageBox::question(this, "ques", "提問(wèn)",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Save))
// {
// qDebug() << "選擇的是保存";
// }
// else
// {
// qDebug() << "選擇的是取消";
// }
// 警告對(duì)話框
//QMessageBox::warning(this,"warning","警告");
// 其他標(biāo)準(zhǔn)對(duì)話框
// 顏色對(duì)話框
// QColor color = QColorDialog::getColor(QColor(255, 0, 0));
// qDebug() << " r = " << color.red() << " g = "<< color.green() << " b = " << color.blue();
// 文件對(duì)話框 參數(shù)1: 父類, 參數(shù)2: 對(duì)話框標(biāo)題 參數(shù)3:默認(rèn)打開(kāi)路徑,參數(shù)4:過(guò)濾器(僅能選取該類型文件) 返回值是選取文件路徑
// QString str= QFileDialog::getOpenFileName(this, "打開(kāi)文件", "C:\\Users\\19080\\Desktop", "(*.txt)");
// qDebug() << str;
// 字體對(duì)話框
bool flag;
QFont font = QFontDialog::getFont(&flag, QFont("華文云彩", 36) );
qDebug() <<" 字體: "<< font.family() <<" 大小:"<< font.pointSize()<< "是否加粗:"<< font.bold() << "是否傾斜:"<< font.italic();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
3.8 設(shè)計(jì)登錄界面
??首先我們現(xiàn)在UI界面創(chuàng)建如下控件,用戶名和密碼用Label控件,輸入框用Line Edit控件,登錄和退出用PushButton控件。
??然后在左側(cè)工具欄Containers中選擇Widget控件,將用戶名、密碼和輸入框拖入Widget中選擇,在上方工具欄中選擇柵格布局(適用于多行多列的,如果是單行或單列可以選擇水平布局或垂直布局),布局之后就變得更整齊。登錄和退出就選擇水平布局。為了在窗口縮放是保持各個(gè)空間的相對(duì)位置不變,可以加入Spacers控件(也可以不加),其效果于彈簧。
??登錄界面一般開(kāi)發(fā)時(shí)就確定大小,我們找到MainWindow->sizePolicy->水平和垂直策略都選擇Fixed,然后將minnumSize和maxiumSize都選擇固定的尺寸(具體數(shù)值任意,大小合適即可)。操作完畢后窗口大小就固定下來(lái)。
??最后修改窗口名稱,選中密碼對(duì)應(yīng)的編輯框,QLineEdit->echoMode->Password(輸入密碼的編程一個(gè)個(gè)黑圈圈),到目前為止我們將登錄窗口的UI界面設(shè)計(jì)完畢,但是具體的功能還需要底層代碼才能實(shí)現(xiàn)。
3.9 各類控件
3.9.1 按鈕組
??Qt的UI設(shè)計(jì)界面的按鈕組有:PushButton,ToolButton, Radio Button, Check Box等等。
??ToolButton建立后,可以添加圖片和修改文本,選擇Icon->選擇資源文件(前面部分有介紹),然后選擇QToolButton->autoRaise,其效果是當(dāng)光標(biāo)移動(dòng)到該按鈕時(shí),按鈕自動(dòng)高光亮起。
??依次創(chuàng)建4個(gè)Radio Button,分別命名為男 女, 已婚, 未婚。然后添加Group Box, 將男女添加今一個(gè)Group Box, 修改文本為性別。同理,已婚和未婚這兩個(gè)Radio Button為另外一組。
??創(chuàng)建4個(gè)Check Box依次修改文本,放入Group Box中,設(shè)置垂直布局。然后在添加一個(gè)ListWidget,在預(yù)覽圖中顯示為白色框。
??在mainwindow.cpp中輸入如下代碼,形成代碼和界面的聯(lián)動(dòng):
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 設(shè)置單選按鈕 默認(rèn)男選中
ui->radioButton->setChecked(true);
// 選中女后打印信息
connect(ui->radioButton_2, &QRadioButton::clicked,[=](){
qDebug() << "選中了女性按鈕";
});
// 多選按鈕 2:選中 1:半選中 0:未選中
// 信號(hào)為stateChanged, 槽函數(shù)為lambda表達(dá)式, 信號(hào)的參數(shù)會(huì)自動(dòng)傳給槽函數(shù)
connect(ui->checkBox_4, &QCheckBox::stateChanged,[=](int state){
qDebug() << state;
});
// 利用listWidget寫詩(shī)
QListWidgetItem *item = new QListWidgetItem("窗前明月光");
// 將一行詩(shī)放到listWidget控件中
ui->listWidget->addItem(item);
item->setTextAlignment(Qt::AlignHCenter); // 設(shè)置為水平居中
// QStringList QList<QString>
QStringList list;
list << "疑是地上霜"<< "舉頭望明月"<<"低頭思故鄉(xiāng)"; // 將這幾句詩(shī)加入鏈表類中
ui->listWidget->addItems(list); // 這種方法無(wú)法設(shè)置對(duì)齊格式
}
MainWindow::~MainWindow()
{
delete ui;
}
3.9.2 QTreeWidget和QTableWidget控件
??QTreeWidget控件代碼如下:
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// treeWidget樹(shù)控件使用
// 設(shè)置水平頭
// 從下面兩行代碼可以看出,str相當(dāng)于一個(gè)string類型的列表(例如,vector<string>) <<操作符相當(dāng)于 append()函數(shù)的作用
// QStringList str = QStringList()<<"英雄"<<"英雄介紹";
// qDebug() << str;
ui->treeWidget->setHeaderLabels (QStringList()<<"卡牌類型"<<"卡牌介紹");
QTreeWidgetItem *monster = new QTreeWidgetItem(QStringList()<< " 怪獸");
QTreeWidgetItem *magic = new QTreeWidgetItem(QStringList()<< " 魔法");
QTreeWidgetItem *trap = new QTreeWidgetItem(QStringList()<< " 陷阱");
// 加載頂層節(jié)點(diǎn)
ui->treeWidget->addTopLevelItem(monster);
ui->treeWidget->addTopLevelItem(magic);
ui->treeWidget->addTopLevelItem(trap);
// 加載子節(jié)點(diǎn)
QStringList m1 = QStringList()<< "增殖的G"<< "效果怪獸,每次對(duì)方對(duì)怪獸的特殊召喚成功,自己從卡組抽1張";
QTreeWidgetItem *monster1 = new QTreeWidgetItem(m1);
monster->addChild(monster1);
QStringList m2 = QStringList()<< "效果遮蒙者"<< "效果怪獸,以對(duì)方場(chǎng)上1只效果怪獸為對(duì)象,其效果直到回合結(jié)束時(shí)無(wú)效。";
QTreeWidgetItem *monster2 = new QTreeWidgetItem(m2);
monster->addChild(monster2);
QStringList ma1 = QStringList()<< "強(qiáng)欲之壺"<< "通常魔法,從卡組抽兩張牌";
QTreeWidgetItem *magic1 = new QTreeWidgetItem(ma1);
magic->addChild(magic1);
QStringList ma2 = QStringList()<< "天使的施舍"<< "通常魔法,從卡組抽三張,然后丟棄兩張手牌";
QTreeWidgetItem *magic2 = new QTreeWidgetItem(ma2);
magic->addChild(magic2);
QStringList t1 = QStringList()<< "技能抽取"<< "永續(xù)陷阱,能夠使場(chǎng)上表側(cè)表示的怪獸卡效果無(wú)效";
QTreeWidgetItem *trap1 = new QTreeWidgetItem(t1);
trap->addChild(trap1);
QStringList t2 = QStringList()<< "王宮的敕命"<< "永續(xù)陷阱,能夠使場(chǎng)上的魔法卡效果無(wú)效";
QTreeWidgetItem *trap2 = new QTreeWidgetItem(t2);
trap->addChild(trap2);
}
Widget::~Widget()
{
delete ui;
}
??效果圖:
??QTableWidget控件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// TableWidget控件
// 設(shè)置列數(shù),一定要設(shè)置,不然會(huì)出現(xiàn)未知錯(cuò)誤
ui->tableWidget->setColumnCount(3);
// 設(shè)置水平表頭
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<<"性別"<<"年齡");
// 設(shè)置行數(shù)
ui->tableWidget->setRowCount(5);
// 設(shè)置正文
//ui->tableWidget->setItem(0,0,new QTableWidgetItem("張三"));
QStringList nameList = QStringList()<<"李大"<<"柳二"<<"張三"<<"劉四"<<"王五";
QStringList sexList = QStringList()<<"男"<<"男"<<"女"<<"女"<<"女";
for(int i=0;i<5;i++)
{
int col = 0;
ui->tableWidget->setItem(i,col++,new QTableWidgetItem(nameList[i]));
ui->tableWidget->setItem(i,col++,new QTableWidgetItem(sexList.at(i)));
ui->tableWidget->setItem(i,col++,new QTableWidgetItem(QString::number(i+18)));
}
}
Widget::~Widget()
{
delete ui;
}
??效果圖:
3.9.3 其他控件
??主要包括了scroll area、 tool box、tab widget、stacked widget等控件。其中scroll area 是滾動(dòng)條控件,toolbox是列表頁(yè)面(例如QQ的聯(lián)系人列表),tab widget是類似網(wǎng)頁(yè)頁(yè)面的控件。stacked widget是??丶梢詫⒁陨先齻€(gè)頁(yè)面全部放到??丶校缓髮?shí)現(xiàn)多個(gè)頁(yè)面的切換。代碼中還包括了combo box下拉框,QLabel的簡(jiǎn)單使用。其中,QLabel可以用作顯示圖片,播放動(dòng)態(tài)圖,視頻等等。
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QMovie>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// stacked Widget??丶氖褂?/span>
// 默認(rèn)定位
ui->stackedWidget->setCurrentIndex(0);
// scroll area 按鈕
connect(ui->btn_scroll,&QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(0);
});
// toolBox 按鈕
connect(ui->btn_toolBox,&QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(1);
});
// tabWidge 按鈕
connect(ui->btn_tabWidget,&QPushButton::clicked, [=](){
ui->stackedWidget->setCurrentIndex(2);
});
// combo box 下拉框
ui->comboBox->addItem("奔馳");
ui->comboBox->addItem("寶馬");
ui->comboBox->addItem("拖拉機(jī)");
// 點(diǎn)擊按鈕選中拖拉機(jī)
connect(ui->pushButton_16,&QPushButton::clicked, [=](){
// ui->comboBox->setCurrentIndex(2);
ui->comboBox->setCurrentText("拖拉機(jī)"); // 兩句代碼效果一樣
});
// 利用QLabel顯示圖片
ui->imaLabel->setPixmap(QPixmap(":/Image/fileIcon.JPEG").scaled(ui->imaLabel->size()));
//利用QLabel顯示gif動(dòng)態(tài)圖片
QMovie *movie = new QMovie(":/Image/picaqu.gif");
movie->setScaledSize(ui->movieLabel->size());
ui->movieLabel->setMovie(movie);
// 播放動(dòng)圖
movie->start();
}
Widget::~Widget()
{
delete ui;
}
??效果圖:
3.9.4 自定義控件封裝
??建立一個(gè)自定義的控件,將QSpinBox和QSlider聯(lián)動(dòng)起來(lái):QSpinBox移動(dòng),QSlider跟著移動(dòng),QSlider跟著移動(dòng),QSpinBox也跟著移動(dòng)。
??項(xiàng)目文件點(diǎn)擊->新建->Qt->設(shè)計(jì)師界面->widget,然后修改文件名(SmallWidget)。在smallwidget.up界面中添加,QSpinBox和QSlider兩個(gè)控件:
??右鍵SmallWidget窗口->提升為->添加->提升,成功以后,widget類底下就會(huì)包含SmallWidget類。
??修改smallwidget.cpp文件:
// smallwidget.cpp文件
#include "smallwidget.h"
#include "ui_smallwidget.h"
SmallWidget::SmallWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SmallWidget)
{
ui->setupUi(this);
// QSpinBox移動(dòng),QSlider跟著移動(dòng)
void (QSpinBox:: *spSignal)(int) = &QSpinBox::valueChanged; // valueChanged有重載版本,因此需要確定輸入?yún)?shù)是哪種版本的,這里需要的是int輸入
connect(ui->spinBox, spSignal, ui->horizontalSlider, &QSlider::setValue);
// QSlider跟著移動(dòng) QSpinBox移動(dòng)
connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue);
}
SmallWidget::~SmallWidget()
{
delete ui;
}
3.10 鼠標(biāo)和定時(shí)器事件以及事件分發(fā)器、過(guò)濾器
??添加myLabel類->C+±>C++Class,父類選擇為QWidget,因?yàn)樾陆?xiàng)目窗口能夠選擇父類有限,這里我們就選擇QWidget類,項(xiàng)目文件建好后,修改mylabel.h文件中include頭文件和父類,mylabel.cpp中構(gòu)造函數(shù)的父類,全部修改為QLabel。
??進(jìn)入U(xiǎn)I界面,拖拽一個(gè)Label控件,修改為合適大小,文字刪除,此時(shí)控件消失,為了方便觀察,我們?cè)O(shè)置控件的邊框?yàn)锽ox類型,屬性頁(yè)面QFrame->frameShape->Box,如下圖所示。
??UI界面的設(shè)置創(chuàng)建兩個(gè)label控件,用來(lái)顯示定時(shí)器數(shù)字。定時(shí)器主要使用到timerEvent(QTimerEvent *ev)函數(shù),多個(gè)定時(shí)器之間用timeId來(lái)區(qū)分。
??多個(gè)事件之間通過(guò)bool event(QEvent *ev)來(lái)進(jìn)行事件分發(fā),返回值是bool類型,如果返回值是true代表用戶要處理這個(gè)事件,不向下分發(fā)事件。
// mylabel.h文件
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>
class myLabel : public QLabel
{
public:
explicit myLabel(QWidget *parent = nullptr);
// 聲明
// 鼠標(biāo)進(jìn)入事件
void enterEvent(QEvent *event);
// 鼠標(biāo)離開(kāi)事件
void leaveEvent(QEvent *);
// 鼠標(biāo)按下事件
void mousePressEvent(QMouseEvent *event);
// 鼠標(biāo)釋放事件
void mouseReleaseEvent(QMouseEvent *event);
// 鼠標(biāo)移動(dòng)事件
void mouseMoveEvent(QMouseEvent *event);
// 通過(guò)event事件分發(fā)器攔截 鼠標(biāo)按下事件
bool event(QEvent *e);
signals:
};
#endif // MYLABEL_H
// widget.h文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// 定時(shí)器事件
void timerEvent(QTimerEvent *);
int id1; // 定時(shí)器1的唯一標(biāo)識(shí)
int id2; // 定時(shí)器2的唯一標(biāo)識(shí)
// 重寫事件過(guò)濾器的時(shí)間
bool eventFilter(QObject *obj, QEvent *e);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
// mylabe.cpp文件
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>
myLabel::myLabel(QWidget *parent) : QLabel(parent)
{
// 設(shè)置鼠標(biāo)追蹤
setMouseTracking (true); // 原來(lái)是點(diǎn)擊后鼠標(biāo)移動(dòng)才能觸發(fā),現(xiàn)在只要鼠標(biāo)移動(dòng)就能觸發(fā)鼠標(biāo)移動(dòng)事件。
}
// 鼠標(biāo)進(jìn)入事件
void myLabel::enterEvent(QEvent *event)
{
qDebug() << "鼠標(biāo)進(jìn)入";
}
// 鼠標(biāo)離開(kāi)事件
void myLabel::leaveEvent(QEvent *)
{
qDebug()<< "鼠標(biāo)離開(kāi)";
}
// 鼠標(biāo)按下事件
void myLabel::mousePressEvent(QMouseEvent *event)
{
// 要求:當(dāng)鼠標(biāo)左鍵按下時(shí),打印信息,右鍵按下不打印
if(event->button() == Qt::LeftButton)
{
QString str = QString("鼠標(biāo)按下了 x = %1, y = %2, globalx = %3, globaly = %4").arg(event->x()).arg (event->y()).arg (event->globalX()).arg (event->globalY());
qDebug()<< str;
}
}
// 鼠標(biāo)釋放事件
void myLabel::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
qDebug()<< "鼠標(biāo)釋放";
}
}
// 鼠標(biāo)移動(dòng)事件
void myLabel::mouseMoveEvent(QMouseEvent *event)
{
qDebug()<< "鼠標(biāo)移動(dòng)";
// if(event->buttons() & Qt::LeftButton) // &位與操作,buttons用于同時(shí)按下多個(gè)按鈕,只要按下的按鈕中包含左鍵,執(zhí)行下面的操作。
// {
// qDebug()<< "鼠標(biāo)移動(dòng)";
// }
}
// 通過(guò)event事件分發(fā)器攔截 鼠標(biāo)按下事件
bool myLabel::event(QEvent *e)
{
// 如果是鼠標(biāo)按下,在event中做攔截操作,也就是說(shuō)在這一層做處理,后面的 鼠標(biāo)按下 相關(guān)代碼就不會(huì)觸發(fā)
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent *ev = static_cast<QMouseEvent *>(e); // static_cast是C++的強(qiáng)制類型轉(zhuǎn)換,大精度類型轉(zhuǎn)小精度類型,有損
QString str = QString("Event函數(shù)中,鼠標(biāo)按下了 x = %1, y = %2, globalx = %3, globaly = %4").arg(ev->x()).arg (ev->y()).arg (ev->globalX()).arg (ev->globalY());
qDebug() << str;
return true; // true代表用戶自己處理這個(gè)事件,不向下分發(fā)
}
// 其他事件交給父類處理, 其余事件都正常傳給后面的代碼處理
return QLabel::event(e);
}
// widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QMouseEvent>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
id1 = startTimer(1000); // 啟動(dòng)計(jì)時(shí)器,參數(shù)1:間隔,單位毫秒
id2 = startTimer(2000);
// 定時(shí)器的第二種方式
QTimer *timer = new QTimer(this);
// 啟動(dòng)定時(shí)器
timer->start(500);
connect(timer, &QTimer::timeout, [=](){
static int num3 = 1;
// label4 每隔0.5秒+1
ui->label_4->setText(QString::number(num3++));
});
// 點(diǎn)擊按鈕暫停定時(shí)器,第三個(gè)暫停
connect(ui->btn1, &QPushButton::clicked, [=](){
timer->stop();
});
// 給label 安裝事件過(guò)濾器, 實(shí)際上是一個(gè)比event攔截器更高級(jí)的攔截器
// 第一步
ui->label->installEventFilter(this);
}
// 第二步 重寫 eventfiler事件
bool Widget::eventFilter(QObject *obj, QEvent *e)
{
if(obj == ui->label)
{
if(e->type() == QEvent::MouseButtonPress)
{
QMouseEvent *ev = static_cast<QMouseEvent * >(e);
QString str = QString("事件過(guò)濾器中,鼠標(biāo)按下了 x = %1, y = %2, globalx = %3, globaly = %4").arg(ev->x()).arg (ev->y()).arg (ev->globalX()).arg (ev->globalY());
qDebug() << str;
return true; // true代表用戶自己處理這個(gè)事件,不向下分發(fā)
}
}
// 其他事件交給父類處理, 其余事件都正常傳給后面的代碼處理
return QWidget::eventFilter(obj, e);
}
// 定時(shí)器事件
void Widget::timerEvent(QTimerEvent *ev)
{
if(ev->timerId() == id1)
{
static int num1 = 1;
ui->label_2->setText(QString::number(num1++));
}
if(ev->timerId() == id2)
{
static int num2 = 1;
ui->label_3->setText(QString::number(num2++));
}
}
Widget::~Widget()
{
delete ui;
}
3.11 繪畫
3.11.1 繪畫設(shè)置
??這里主要介紹的是畫類操作,畫直線,畫圓,畫矩形等等,畫筆,毛刷等等設(shè)置,widget.cpp文件如下,此外,還需要在widget.h文件聲明函數(shù)void paintEvent(QPaintEvent *event)。
// widget.cpp文件
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 點(diǎn)擊移動(dòng)按鈕,移動(dòng)圖片 ,paintEvent函數(shù)默認(rèn)會(huì)調(diào)用一次,然后就是調(diào)用repaint函數(shù),即重新繪畫函數(shù)
connect (ui->pushButton, &QPushButton::clicked, [=](){
// 手動(dòng)調(diào)用繪圖事件函數(shù),實(shí)際上是調(diào)用repaint函數(shù)
PosX += 20; // 需要在widget.h中聲明, int PosX = 150;
update();
});
}
// 繪圖事件
void Widget::paintEvent(QPaintEvent *event)
{
// // 實(shí)例化畫家對(duì)象, this制定繪圖設(shè)備
// QPainter painter(this);
// // 設(shè)置畫筆
// QPen pen(QColor(255,0,0)); // 設(shè)置顏色
// pen.setWidth (5); // 設(shè)置寬度
// pen.setStyle (Qt::DotLine); // 設(shè)置風(fēng)格
// painter.setPen (pen);
// // 設(shè)置畫刷
// QBrush brush(QColor(0,255,0));
// brush.setStyle (Qt::Dense1Pattern);
// painter.setBrush (brush);
// // 畫了一條線(兩個(gè)點(diǎn)確定)
// painter.drawLine(QPoint(0,0),QPoint(100,100));
// // 畫橢圓圓,圓心和長(zhǎng)短軸焦點(diǎn)a,b確定 a=b就是圓
// painter.drawEllipse (QPoint(100,100),50,50);
// // 畫矩形
// painter.drawRect (QRect(20,20,50,50));
// // 畫文字
// painter.drawText (QRect(10,200,150,50),"好好學(xué)習(xí),天天向上");
****高級(jí)設(shè)置*******/
// QPainter painter(this);
// painter.drawEllipse (QPoint(200,200),100,100);
// // 設(shè)置 抗鋸齒能力,即畫的仔細(xì)一點(diǎn),毛邊少一點(diǎn),但是效率低一點(diǎn)
// painter.setRenderHint (QPainter::Antialiasing);
// painter.drawEllipse (QPoint(400,200),100,100);
// // 畫矩形
// painter.drawRect (QRect(20,20,50,50));
// painter.translate (100,0); // 畫家從0,0開(kāi)始作畫,變成從100,0開(kāi)始作畫
// // 保存畫家狀態(tài)
// painter.save();
// painter.drawRect (QRect(20,20,50,50));
// // 還原畫家保存狀態(tài)
// painter.restore ();
// painter.drawRect (QRect(20,20,50,50));
/ ******* 利用畫家調(diào)用圖片資源 **** //
QPainter painter(this);
// 如果超過(guò)屏幕寬度 ,從0開(kāi)始
if(PosX > this->width())
{
PosX = 0;
}
painter.drawPixmap(PosX,20,QPixmap(":/Image/Luffy.jpg"));
}
Widget::~Widget()
{
delete ui;
}
3.11.2 繪圖設(shè)備
??主要有QPixmap,QPicture,QImage三種,需要在widget.h文件聲明函數(shù)void paintEvent(QPaintEvent *event)
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// //
// // Pixmap繪圖設(shè)備,專門為平臺(tái)做了顯示的優(yōu)化
// QPixmap pix(300,300); // 說(shuō)明畫紙大小
// // 填充背景色
// pix.fill(Qt::white);
// // 聲明畫家
// QPainter painter(&pix);
// painter.setPen(QPen(Qt::green));
// painter.drawEllipse(QPoint(150,150),100,100);
// // 保存
// pix.save("D:\\software\\QT\\QT_Project\\qtDemo10\\pix.png");
// ///
// // QImage 繪圖設(shè)備 可以對(duì)每個(gè)像素進(jìn)行訪問(wèn)
// QImage img(300,300, QImage::Format_RGB32);
// img.fill(Qt::white);
// QPainter painter(&img);
// painter.setPen(QPen(Qt::blue));
// painter.drawEllipse(QPoint(150,150),100,100);
// // 保存
// img.save("D:\\software\\QT\\QT_Project\\qtDemo10\\img.png");
// QPicture 繪圖設(shè)備,可以記錄和重現(xiàn)繪圖指令
QPainter painter;
QPicture pic;
painter.begin(&pic); // 開(kāi)始往pic上畫畫
painter.setPen(QPen(Qt::blue));
painter.drawEllipse (QPoint(150,150),100,100);
painter.end(); // 結(jié)束畫畫
// 保存到磁盤
pic.save("D:\\software\\QT\\QT_Project\\qtDemo10\\pic.hyf");
// hyf是博主的姓名縮寫,在文件資源管理器中是無(wú)法打開(kāi)這個(gè)圖片的
// 我們?cè)诶L圖事件中使用load函數(shù)可以打開(kāi),準(zhǔn)確來(lái)說(shuō)是重新繪制,因此pic保存的不是圖片本身而是繪制圖片的指令
}
// 繪圖事件
void Widget::paintEvent(QPaintEvent *event)
{
// // 利用QImage 對(duì)像素進(jìn)行修改
// QPainter painter(this);
// QImage img;
// img.load(":/Image/fileIcon.JPEG");
// // 修改像素點(diǎn)
// for(int i=50; i<100; i++)
// {
// for (int j=50;j<100;j++)
// {
// QRgb value = qRgb(255,0,0);
// img.setPixel(i,j,value);
// }
// }
// painter.drawImage(0,0,img);
// 重現(xiàn)QPicture繪圖指令
QPainter painter(this);
QPicture pic;
pic.load("D:\\software\\QT\\QT_Project\\qtDemo10\\pic.hyf");
painter.drawPicture(0,0,pic);
}
Widget::~Widget()
{
delete ui;
}
3.12 文件讀取
??文件讀取是主要注意打開(kāi)文件,也要關(guān)閉文件,此外,QFile默認(rèn)是UTF-8格式類型。widget.cpp文件如下所示:
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QFile>
#include <QTextCodec>
#include <QFileInfo>
#include <QDebug>
#include <QDateTime>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect (ui->pushButton, &QPushButton::clicked, [=](){
QString path = QFileDialog::getOpenFileName (this,"打開(kāi)文件","C:/Users/19080/Desktop");
// 將路徑放到lineEdit中
ui->lineEdit->setText(path);
// 編碼格式類
QTextCodec *codec = QTextCodec::codecForName("gbk");
// 讀取內(nèi)容,放入到textEdit中
// QFile默認(rèn)支持的格式是UTF-8
QFile file(path); // 參數(shù)就是file path
file.open(QIODevice::ReadOnly); // 設(shè)置打開(kāi)方式
//QByteArray array = file.readAll();
QByteArray array;
while( !file.atEnd() )
{
array += file.readLine();
}
// 將讀取的數(shù)據(jù)放入到text Edit中
ui->textEdit->setText(array);
//ui->textEdit->setText(codec->toUnicode(array));
file.close(); // 對(duì)文件對(duì)象進(jìn)行關(guān)閉
文件寫入操作
file.open(QIODevice::Append); // 追加的方式寫入
file.write("123456789");
file.close();
QFileInfo ///
// QFileInfo 文件信息類
QFileInfo info(path);
qDebug() << "大小(字節(jié)):" << info.size()<< "后綴名:"<< info.suffix()<< "文件名稱:"<< info.fileName() <<"文件路徑:"<< info.filePath();
qDebug() << "文件創(chuàng)建日期: "<< info.created().toString ("yyyy/MM/dd hh:mm:ss"); // 按格式輸出 yyyy/MM/dd hh:mm:ss
qDebug() << "文件創(chuàng)建日期: "<< info.lastModified().toString ("yyyy/MM/dd hh:mm:ss");
});
}
Widget::~Widget()
{
delete ui;
}
四、翻金幣小游戲
4.1 出現(xiàn)的問(wèn)題
??如果沒(méi)有素材可以從網(wǎng)上找,圖片資源都可以根據(jù)實(shí)際變化,不是唯一的。資源網(wǎng)站這里推薦阿里的圖標(biāo)庫(kù)。
??博主在寫這個(gè)代碼的過(guò)程中出現(xiàn)了一些錯(cuò)誤,第一個(gè)錯(cuò)誤博主沒(méi)有解決,將第二個(gè)錯(cuò)誤解決后,第一個(gè)就沒(méi)有出現(xiàn)了。第二個(gè)和第三個(gè)錯(cuò)誤在頭文件前面加上Q_OBJECT成員變量,加在public前面,問(wèn)題解決參考自博客No Q_OBJECT in the class with the signal錯(cuò)誤解決辦法,然后在.pro文件末尾加上空格重新編譯。
error: 'QtPrivate::QFunctorSlotObject<Func, N, Args, R>::QFunctorSlotObject(Func) [with Func = MainScene::MainScene(QWidget*)::<lambda()>; int N = 0; Args = QtPrivate::List<>; R = void]', declared using local type 'MainScene::MainScene(QWidget*)::<lambda()>', is used but never defined [-fpermissive]
explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {}
^~~~~~~~~~~~~~~~~~
error: static assertion failed: No Q_OBJECT in the class with the signal
# define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message)
^
error: undefined reference to `vtable for
4.2 源碼下載
??這里直接給大家分享一下成品,用百度網(wǎng)盤給出,步驟博主就不在一一介紹。
鏈接:https://pan.baidu.com/s/1QGdOGuyTYMGfSgi81O71vw?pwd=zajg
提取碼:zajg
4.3 NSIS打包程序
??當(dāng)我們寫好程序后將編譯運(yùn)行按鈕中的debug輸出改為release輸出,然后得到一個(gè)發(fā)布版本的.exe程序,如圖所示:
??我們將他單獨(dú)拎出來(lái)放到桌面的release文件夾(自己命名的空文件夾都可以)中,然后找到對(duì)應(yīng)編譯器的命令行窗口,如下圖所示,博主這里有兩個(gè)編譯器,博主的是MinGW 64-bit的。然后輸入windeployqt.exe CoinFlip.exe,按回車鍵,程序打包成功,之前的release文件就多了一些生成的打包文件。
windeployqt.exe CoinFlip.exe
??windeployqt.exe文件實(shí)際上是Qt編譯器提供的打包成window程序的可執(zhí)行文件,可以在對(duì)應(yīng)編譯器的bin文件中找到。
??這里需要注意不能使用普通的命令行窗口執(zhí)行這個(gè)命令,會(huì)出現(xiàn)無(wú)法找到入口的問(wèn)題。
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-783782.html
??然后我們使用HM NIS Edit軟件進(jìn)行Setup.exe文件的打包,需要配合NSIS軟件一起使用。
??NSIS是"Nullsoft 腳本安裝系統(tǒng)"(Nullsoft scriptable Installation System)的縮寫,它是是一個(gè)免費(fèi)的win32安裝、卸載系統(tǒng),可以很方便的打包windows應(yīng)用程序。它的特點(diǎn):腳本簡(jiǎn)潔高效;系統(tǒng)開(kāi)銷少;支持安裝、卸載、系統(tǒng)設(shè)置、解壓文件等功能。這里博主直接給出NSIS下載地址和HM NIS Edit下載地址,嫌麻煩的也可以用博主的百度網(wǎng)盤地址下載,鏈接:https://pan.baidu.com/s/1FrLENkVtB-B2lGslqw33bw?pwd=wybc
提取碼:wybc。
??參考博客 手把手教N(yùn)IS Edit安裝向?qū)У氖褂谩.?dāng)順著教程做到這一步的時(shí)候,點(diǎn)擊樹(shù)形圖,選擇release文件,將release文件中的所有文件添加進(jìn)來(lái)。其他部分按照教程或者默認(rèn)即可。最終會(huì)在項(xiàng)目目錄中生成Setup.exe文件。將Setup.exe安裝之后,就會(huì)在桌面生成快捷方式,點(diǎn)擊即可進(jìn)行游戲。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-783782.html
到了這里,關(guān)于【Qt設(shè)計(jì)開(kāi)發(fā)】GUI界面設(shè)計(jì)開(kāi)發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!