日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Qt笔记总结

發(fā)布時(shí)間:2024/9/27 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt笔记总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Qt筆記總結(jié)

作者:hackett

微信公眾號(hào):加班猿

一、常用控件
按鈕類
QPushButton

QtoolButton

QRadioButton

item
QListWidget

容器類
QStackWidget

QWidget

QFrame

編輯類
QComboBox

QLineEdit

QTextEdit

顯示類
QLabel

setOpenExternalLinks()設(shè)置為true自動(dòng)打開,false要打開鏈接需要捕捉linkActivated()信號(hào)

//顯示普通文本字符串
QLable *label = new QLable;
label->setText(“Hello, World!”);
//顯示HTML格式的字符串
QLabel * label = new QLabel(this);
label ->setText("Hello, World");
label ->setText("<h1><a href=\"https://www.baidu.com\">
百度一下</a></h1>");
label ->setOpenExternalLinks(true);

QLabel * label = new QLabel(this);
label ->setText("Hello, World");
label ->setText("<h1><a href=\"https://www.baidu.com\">
百度一下</a></h1>");
// label->setOpenExternalLinks(true);
connect(label, &QLabel::linkActivated,?
this, &MyWidget::slotOpenUrl);

//槽函數(shù)?? ?
void MyWidget::slotOpenUrl(const QString &link)
{
? ? QDesktopServices::openUrl(QUrl(link));
}

QProcessBar

QMessageBox

二、信號(hào)與槽
信號(hào)和槽
信號(hào)槽,實(shí)際就是觀察者模式。當(dāng)某個(gè)事件發(fā)生之后,它就會(huì)發(fā)出一個(gè)信號(hào)(signal)將想要處理的信號(hào)和自己的一個(gè)函數(shù)(稱為槽(slot))綁定來(lái)處理這個(gè)信號(hào)。當(dāng)信號(hào)發(fā)出時(shí),被連接的槽函數(shù)會(huì)自動(dòng)被回調(diào)。

1、connect(sender, signal, receiver, slot);
1
sender:發(fā)送信號(hào)的對(duì)象
signal:發(fā)送對(duì)象發(fā)出的信號(hào)
receiver:接收信號(hào)的對(duì)象
slot:接收對(duì)象在接收到信號(hào)之后所需要調(diào)用的函數(shù)
例如:

/* &b1: 信號(hào)發(fā)出者,指針類型
* &QPushButton::pressed:處理的信號(hào)?? ?(&發(fā)送者的類名::信號(hào)名字)
* this: 信號(hào)接收者
* &MainWidget::close:槽函數(shù),信號(hào)處理函數(shù) (&接收的類名::槽函數(shù)名字)
*/
connect(&b1, &QPushButton::pressed, this, &MainWidget::close);

自定義信號(hào)槽
自定義信號(hào)槽需要注意的事項(xiàng)
發(fā)送者和接收者都需要是QObject的子類(當(dāng)然,槽函數(shù)是全局函數(shù)、Lambda 表達(dá)式等無(wú)需接收者的時(shí)候除外);

使用 signals 標(biāo)記信號(hào)函數(shù),信號(hào)是一個(gè)函數(shù)聲明,返回 void,不需要實(shí)現(xiàn)函數(shù)代碼;

槽函數(shù)是普通的成員函數(shù),作為成員函數(shù),會(huì)受到 public、private、protected 的影響;

使用 emit 在恰當(dāng)?shù)奈恢冒l(fā)送信號(hào);

使用QObject::connect()函數(shù)連接信號(hào)和槽。

任何成員函數(shù)、static 函數(shù)、全局函數(shù)和 Lambda 表達(dá)式都可以作為槽函數(shù)

/* 自定義槽,普通函數(shù)的用法
* Qt5:任意的成員函數(shù),普通全局函數(shù),靜態(tài)函數(shù)
* 槽函數(shù)需要和信號(hào)一致(參數(shù),返回值)
* 由于信號(hào)都是沒有返回值,所以,槽函數(shù)一定沒有返回值
*/
connect(b2, &QPushButton::released, this, &MainWidget::mySlot);

Lambda表達(dá)式
C++11中的Lambda表達(dá)式用于定義并創(chuàng)建匿名的函數(shù)對(duì)象。

基本構(gòu)成:[函數(shù)對(duì)象參數(shù)](操作符重載函數(shù)參數(shù))mutable或exception ->返回值{函數(shù)體}

1、函數(shù)對(duì)象參數(shù):

[ ] 標(biāo)識(shí)一個(gè)Lambda的開始,這部分必須存在,不能省略。

空,沒有任何函數(shù)對(duì)象參數(shù)

=,值傳遞方式(作用范圍:所有可見的局部變量以及所在類的this)

&,引用傳遞方式(作用范圍:所有可見的局部變量以及所在類的this)

this,函數(shù)體內(nèi)可以可以使用Lambda所在類中的成員變量

a,把a(bǔ)按值進(jìn)行傳遞(默認(rèn)為const不可修改,可添加mutable修飾符修改)

&b,把b按引用進(jìn)行傳遞

&,a,b,除a和b進(jìn)行值傳遞,其他參數(shù)按引用進(jìn)行傳遞

2、操作符重載函數(shù)參數(shù):

標(biāo)識(shí)重載的()操作符的參數(shù),沒有參數(shù)時(shí),這部分可以省略。

3、可修改標(biāo)示符:

mutable聲明,這部分可以省略。按值傳遞函數(shù)對(duì)象參數(shù)時(shí),加上mutable修飾符后,可以修改按值傳遞進(jìn)來(lái)的拷貝(注意是能修改拷貝,而不是值本身)。

4、錯(cuò)誤拋出標(biāo)示符:

exception聲明,這部分也可以省略。exception聲明用于指定函數(shù)拋出的異常,如拋出整數(shù)類型的異常,可以使用throw(int)

5、函數(shù)返回值:

->返回值類型,標(biāo)識(shí)函數(shù)返回值的類型,當(dāng)返回值為void,或者函數(shù)體中只有一處return的地方(此時(shí)編譯器可以自動(dòng)推斷出返回值類型)時(shí),這部分可以省略。

6、函數(shù)體:

{},標(biāo)識(shí)函數(shù)的實(shí)現(xiàn),這部分不能省略,但函數(shù)體可以為空。

?

三、Qt窗口系統(tǒng)
1、坐標(biāo)體系
以左上角為原點(diǎn),X向右增加,Y向下增加。

2、QWidget
所有窗口及窗口控件都是從QWidget直接或間接派生出來(lái)的。

對(duì)象模型

Qt創(chuàng)建對(duì)象的時(shí)候會(huì)提供一個(gè)Parent對(duì)象指針

在創(chuàng)建QObject對(duì)象時(shí),可以提供一個(gè)其父對(duì)象,我們創(chuàng)建的這個(gè)QObject對(duì)象會(huì)自動(dòng)添加到其父對(duì)象的children()列表。當(dāng)父對(duì)象析構(gòu)的時(shí)候,這個(gè)列表中的所有對(duì)象也會(huì)被析構(gòu)。(注意,這里的父對(duì)象并不是繼承意義上的父類!)

QWidget是能夠在屏幕上顯示的一切組件的父類

QWidget繼承自QObject,因此也繼承了這種對(duì)象樹關(guān)系。一個(gè)孩子自動(dòng)地成為父組件的一個(gè)子組件,也可以自己刪除子對(duì)象,它們會(huì)自動(dòng)從其父對(duì)象列表中刪除。

當(dāng)一個(gè)QObject對(duì)象在堆上創(chuàng)建的時(shí)候,Qt 會(huì)同時(shí)為其創(chuàng)建一個(gè)對(duì)象樹。不過(guò),對(duì)象樹中對(duì)象的順序是沒有定義的。這意味著,銷毀這些對(duì)象的順序也是未定義的

任何對(duì)象樹中的 QObject對(duì)象 delete 的時(shí)候,如果這個(gè)對(duì)象有 parent,則自動(dòng)將其從 parent 的children()列表中刪除;如果有孩子,則自動(dòng) delete 每一個(gè)孩子。Qt 保證沒有QObject會(huì)被 delete 兩次,這是由析構(gòu)順序決定的。

3、QMainWindow
QMainWindow是一個(gè)為用戶提供主窗口程序的類,包含一個(gè)菜單欄(menu bar)、多個(gè)工具欄(tool bars)、多個(gè)錨接部件(dock widgets)、一個(gè)狀態(tài)欄(status bar)及一個(gè)中心部件(central widget)

1、菜單欄

創(chuàng)建菜單欄,通過(guò)QMainWindow類的menubar() 函數(shù)獲取主窗口菜單欄指針

QMenuBar *?? ?menuBar() const
1
創(chuàng)建菜單,調(diào)用QMenu的成員函數(shù)addMenu() 來(lái)添加菜單

QAction* addMenu(QMenu * menu)
QMenu* addMenu(const QString & title)
QMenu* addMenu(const QIcon & icon, const QString & title)

創(chuàng)建菜單項(xiàng),調(diào)用QMenu的成員函數(shù)addAction() 來(lái)添加菜單項(xiàng)

QAction* activeAction() const
QAction* addAction(const QString & text)
QAction* addAction(const QIcon & icon, const QString & text)
QAction* addAction(const QString & text, const QObject * receiver,
?const char * member, const QKeySequence & shortcut = 0)
QAction* addAction(const QIcon & icon, const QString & text,?
const QObject * receiver, const char * member,?
const QKeySequence & shortcut = 0)

2、工具欄

直接調(diào)用QMainWindow類的addToolBar() 函數(shù)獲取主窗口的工具條對(duì)象,每增加一個(gè)工具條都需要調(diào)用一次該函數(shù)。

n 插入屬于工具條的動(dòng)作,即在工具條上添加操作。通過(guò)QToolBar類的addAction() 函數(shù)添加。

n 工具條是一個(gè)可移動(dòng)的窗口,它的停靠區(qū)域由QToolBar的allowAreas決定,包括:

/*
Qt::LeftToolBarArea?? ??? ?//停靠在左側(cè)
Qt::RightToolBarArea?? ??? ?//停靠在右側(cè)
Qt::TopToolBarArea?? ??? ?//停靠在頂部
Qt::BottomToolBarArea ? ?? ?//停靠在底部
Qt::AllToolBarAreas?? ??? ?//以上四個(gè)位置都可停靠
*/

/*使用setAllowedAreas()函數(shù)指定停靠區(qū)域*/
setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
/*使用setMoveable()函數(shù)設(shè)定工具欄的可移動(dòng)性*/
setMoveable(false);//工具條不可移動(dòng), 只能停靠在初始化的位置上


3、狀態(tài)欄

派生自QWidget類,使用方法與QWidget類似,QStatusBar類常用成員函數(shù):

//添加小部件
void addWidget(QWidget * widget, int stretch = 0)
//插入小部件
int?? ?insertWidget(int index, QWidget * widget, int stretch = 0)
//刪除小部件
void removeWidget(QWidget * widget)

4、對(duì)話框QDialog
Qt 中使用QDialog類實(shí)現(xiàn)對(duì)話框。

對(duì)話框分為模態(tài)對(duì)話框和非模態(tài)對(duì)話框。

模態(tài)對(duì)話框,就是會(huì)阻塞同一應(yīng)用程序中其它窗口的輸入。

模態(tài)對(duì)話框很常見,比如“打開文件”功能。你可以嘗試一下記事本的打開文件,當(dāng)打開文件對(duì)話框出現(xiàn)時(shí),我們是不能對(duì)除此對(duì)話框之外的窗口部分進(jìn)行操作的。

與此相反的是非模態(tài)對(duì)話框,例如查找對(duì)話框,我們可以在顯示著查找對(duì)話框的同時(shí),繼續(xù)對(duì)記事本的內(nèi)容進(jìn)行編輯。

1、標(biāo)準(zhǔn)對(duì)話框

Qt 的內(nèi)置對(duì)話框大致分為以下幾類:

QColorDialog: 選擇顏色;

QFileDialog: 選擇文件或者目錄;

QFontDialog: 選擇字體;

QInputDialog: 允許用戶輸入一個(gè)值,并將其值返回;

QMessageBox: 模態(tài)對(duì)話框,用于顯示信息、詢問(wèn)問(wèn)題等;

QPageSetupDialog: 為打印機(jī)提供紙張相關(guān)的選項(xiàng);

QPrintDialog: 打印機(jī)配置;

QPrintPreviewDialog:打印預(yù)覽;

QProgressDialog: 顯示操作過(guò)程。

2、自定義消息框

Qt 支持模態(tài)對(duì)話框和非模態(tài)對(duì)話框。

模態(tài)與非模態(tài)的實(shí)現(xiàn):

使用QDialog::exec()實(shí)現(xiàn)應(yīng)用程序級(jí)別的模態(tài)對(duì)話框

使用QDialog::open()實(shí)現(xiàn)窗口級(jí)別的模態(tài)對(duì)話框

使用QDialog::show()實(shí)現(xiàn)非模態(tài)對(duì)話框。

3、模態(tài)對(duì)話框

下面的示例中,我們調(diào)用了exec()將對(duì)話框顯示出來(lái),因此這就是一個(gè)模態(tài)對(duì)話框。當(dāng)對(duì)話框出現(xiàn)時(shí),我們不能與主窗口進(jìn)行任何交互,直到我們關(guān)閉了該對(duì)話框。

void MainWindow::open()
{
? ? QDialog dialog;
? ? dialog.setWindowTitle(tr("Hello, dialog!"));
? ? dialog.exec();
}

4、非模態(tài)對(duì)話框

下面我們?cè)囍鴮xec()修改為show(),看看非模態(tài)對(duì)話框:

void MainWindow::open()
{
? ? QDialog dialog(this);
? ? dialog.setWindowTitle(tr("Hello, dialog!"));
? ? dialog.show();
}

是不是事與愿違?對(duì)話框竟然一閃而過(guò)!這是因?yàn)?#xff0c;**show()函數(shù)不會(huì)阻塞當(dāng)前線程,對(duì)話框會(huì)顯示出來(lái),然后函數(shù)立即返回,代碼繼續(xù)執(zhí)行。**注意,dialog 是建立在棧上的,show()函數(shù)返回,MainWindow::open()函數(shù)結(jié)束,dialog 超出作用域被析構(gòu),因此對(duì)話框消失了。知道了原因就好改了,我們將 dialog 改成堆上建立,當(dāng)然就沒有這個(gè)問(wèn)題了:

void MainWindow::open()
{
? ? QDialog *dialog = new QDialog;
? ? dialog->setWindowTitle(tr("Hello, dialog!"));
? ? dialog->show();
}

如果你足夠細(xì)心,應(yīng)該發(fā)現(xiàn)上面的代碼是有問(wèn)題的:dialog 存在內(nèi)存泄露!dialog 使用 new 在堆上分配空間,卻一直沒有 delete。解決方案也很簡(jiǎn)單:將 MainWindow 的指針賦給 dialog 即可。還記得我們前面說(shuō)過(guò)的 Qt 的對(duì)象系統(tǒng)嗎?

不過(guò),這樣做有一個(gè)問(wèn)題:如果我們的對(duì)話框不是在一個(gè)界面類中出現(xiàn)呢?由于QWidget的 parent 必須是QWidget指針,那就限制了我們不能將一個(gè)普通的 C++ 類指針傳給 Qt 對(duì)話框。另外,如果對(duì)內(nèi)存占用有嚴(yán)格限制的話,當(dāng)我們將主窗口作為 parent 時(shí),主窗口不關(guān)閉,對(duì)話框就不會(huì)被銷毀,所以會(huì)一直占用內(nèi)存。在這種情景下,我們可以設(shè)置 dialog 的WindowAttribute:

void MainWindow::open()
{
? ? QDialog *dialog = new QDialog;
? ? dialog->setAttribute(Qt::WA_DeleteOnClose);
? ? dialog->setWindowTitle(tr("Hello, dialog!"));
? ? dialog->show();
}

setAttribute()函數(shù)設(shè)置對(duì)話框關(guān)閉時(shí),自動(dòng)銷毀對(duì)話框。

1、消息對(duì)話框

QMessageBox用于顯示消息提示。我們一般會(huì)使用其提供的幾個(gè) static 函數(shù):

顯示關(guān)于對(duì)話框。

void about(QWidget * parent, const QString & title, const QString & text)
1
這是一個(gè)最簡(jiǎn)單的對(duì)話框,其標(biāo)題是 title,內(nèi)容是 text,父窗口是 parent。對(duì)話框只有一個(gè) OK 按鈕。

顯示關(guān)于 Qt 對(duì)話框。該對(duì)話框用于顯示有關(guān) Qt 的信息。

void aboutQt(QWidget * parent, const QString & title = QString())
1
顯示嚴(yán)重錯(cuò)誤對(duì)話框。

StandardButton critical(QWidget * parent,?
const QString & title,?
const QString & text,?
StandardButtons buttons = Ok,?
StandardButton defaultButton = NoButton)

這個(gè)對(duì)話框?qū)@示一個(gè)紅色的錯(cuò)誤符號(hào)。我們可以通過(guò) buttons 參數(shù)指明其顯示的按鈕。默認(rèn)情況下只有一個(gè) Ok 按鈕,我們可以使用StandardButtons類型指定多種按鈕。

與QMessageBox::critical()類似,不同之處在于這個(gè)對(duì)話框提供一個(gè)普通信息圖標(biāo)。

StandardButton information(QWidget * parent,?
const QString & title,?
const QString & text,?
StandardButtons buttons = Ok,?
StandardButton defaultButton = NoButton)

與QMessageBox::critical()類似,不同之處在于這個(gè)對(duì)話框提供一個(gè)問(wèn)號(hào)圖標(biāo),并且其顯示的按鈕是“是”和“否”。

StandardButton question(QWidget * parent,
const QString & title,?
const QString & text,?
StandardButtons buttons = StandardButtons( Yes | No ),?
StandardButton defaultButton = NoButton)

與QMessageBox::critical()類似,不同之處在于這個(gè)對(duì)話框提供一個(gè)黃色嘆號(hào)圖標(biāo)。

StandardButton warning(QWidget * parent,?
const QString & title,?
const QString & text,?
StandardButtons buttons = Ok,?
StandardButton defaultButton = NoButton)

2、標(biāo)準(zhǔn)文件對(duì)話框

QFileDialog,也就是文件對(duì)話框。

openAction = new QAction(QIcon(":/images/file-open"),
?tr("&Open..."), this);
openAction->setShortcuts(QKeySequence::Open);
openAction->setStatusTip(tr("Open an existing file"));

saveAction = new QAction(QIcon(":/images/file-save"),?
tr("&Save..."), this);
saveAction->setShortcuts(QKeySequence::Save);
saveAction->setStatusTip(tr("Save a new file"));

QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
file->addAction(saveAction);

QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);
toolBar->addAction(saveAction);

textEdit = new QTextEdit(this);
setCentralWidget(textEdit);

使用connect()函數(shù),為這兩個(gè)QAction對(duì)象添加響應(yīng)的動(dòng)作:

connect(openAction, &QAction::triggered,?
this, &MainWindow::openFile);
connect(saveAction, &QAction::triggered,?
this, &MainWindow::saveFile);

下面是最主要的openFile()和saveFile()這兩個(gè)函數(shù)的代碼:

//打開文件
void MainWindow::openFile()
{
? ? QString path = QFileDialog::getOpenFileName(this,
? ? ? ? ? ? ? ?tr("Open File"), ".", tr("Text Files(*.txt)"));
? ? if(!path.isEmpty())?
{
? ? ? ? QFile file(path);
? ? ? ? if (!file.open(QIODevice::ReadOnly | QIODevice::Text))?
{
? ? ? ? ? ? QMessageBox::warning(this, tr("Read File"),
? ? ? ? ? ? ? ? ? ? ? ? ?tr("Cannot open file:\n%1").arg(path));
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? QTextStream in(&file);
? ? ? ? textEdit->setText(in.readAll());
? ? ? ? file.close();
? ? }?
else?
{
? ? ? ? QMessageBox::warning(this, tr("Path"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?tr("You did not select any file."));
? ? ?}
}

//保存文件
void MainWindow::saveFile()
{
? ? QString path = QFileDialog::getSaveFileName(this,
? ? ? ? ? ? ? ?tr("Open File"), ".", tr("Text Files(*.txt)"));
? ? if(!path.isEmpty())?
{
? ? ? ? QFile file(path);
? ? ? ? if (!file.open(QIODevice::WriteOnly | QIODevice::Text))?
{
? ? ? ? ? ? QMessageBox::warning(this, tr("Write File"),
? ? ? ? ? ? ? ? ? ? ? ? ?tr("Cannot open file:\n%1").arg(path));
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? QTextStream out(&file);
? ? ? ? out << textEdit->toPlainText();
? ? ? ? file.close();
? ? }?
else?
{
? ? ? ? QMessageBox::warning(this, tr("Path"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?tr("You did not select any file."));
? ? }
}

QFileDialog::getOpenFileName()來(lái)獲取需要打開的文件的路徑。這個(gè)函數(shù)原型如下:

QString getOpenFileName(QWidget * parent = 0,
? ? ? ? ? ? ? ? ? ? ? ? const QString & caption = QString(),
? ? ? ? ? ? ? ? ? ? ? ? const QString & dir = QString(),
? ? ? ? ? ? ? ? ? ? ? ? const QString & filter = QString(),
? ? ? ? ? ? ? ? ? ? ? ? QString * selectedFilter = 0,
? ? ? ? ? ? ? ? ? ? ? ? Options options = 0)

六個(gè)參數(shù)分別是:

parent:父窗口

caption:對(duì)話框標(biāo)題

dir:對(duì)話框打開時(shí)的默認(rèn)目錄

“.” 代表程序運(yùn)行目錄

“/” 代表當(dāng)前盤符的根目錄(特指 Windows 平臺(tái);Linux 平臺(tái)當(dāng)然就是根目錄),這個(gè)參數(shù)也可以是平臺(tái)相關(guān)的,比如“C:\”等

filter:過(guò)濾器。

我們使用文件對(duì)話框可以瀏覽很多類型的文件,但是,很多時(shí)候我們僅希望打開特定類型的文件

selectedFilter:默認(rèn)選擇的過(guò)濾器

options:對(duì)話框的一些參數(shù)設(shè)定

比如只顯示文件夾等等,它的取值是enum QFileDialog::Option,每個(gè)選項(xiàng)可以使用 | 運(yùn)算組合起來(lái)

四、布局管理器
Qt提供了兩種組件定位機(jī)制:絕對(duì)定位和布局定位。

絕對(duì)定位
絕對(duì)定位就是一種最原始的定位方法:給出這個(gè)組件的坐標(biāo)和長(zhǎng)寬值。

如果用戶改變了窗口大小,比如點(diǎn)擊最大化按鈕或者使用鼠標(biāo)拖動(dòng)窗口邊緣,采用絕對(duì)定位的組件是不會(huì)有任何響應(yīng)的。

布局定位
只要把組件放入某一種布局,布局由專門的布局管理器進(jìn)行管理。當(dāng)需要調(diào)整大小或者位置的時(shí)候,Qt 使用對(duì)應(yīng)的布局管理器進(jìn)行調(diào)整。

Qt 提供的布局中以下三種是我們最常用的:

QHBoxLayout:按照水平方向從左到右布局;
QVBoxLayout:按照豎直方向從上到下布局;
QGridLayout:在一個(gè)網(wǎng)格中進(jìn)行布局,類似于 HTML 的 table;
自定義控件
UI的控件和自定義控件的父類(基類)要一樣

選中UI控件 -> 提升

五、消息機(jī)制和事件
事件
Qt 中所有事件類都繼承于QEvent

event()函數(shù)并不直接處理事件,而是按照事件對(duì)象的類型分派給特定的事件處理函數(shù)(event handler)

在所有組件的父類QWidget中,定義了很多事件處理的回調(diào)函數(shù),如:

keyPressEvent()

keyReleaseEvent()

mouseDoubleClickEvent()

mouseMoveEvent()

mousePressEvent()

mouseReleaseEvent() 等。

這些函數(shù)都是 protected virtual 的,也就是說(shuō)我們可以在子類中重新實(shí)現(xiàn)這些函數(shù)。下面來(lái)看一個(gè)例子:

class EventLabel : public QLabel
{
protected:
? ? void mouseMoveEvent(QMouseEvent *event);
? ? void mousePressEvent(QMouseEvent *event);
? ? void mouseReleaseEvent(QMouseEvent *event);
};
?
void EventLabel::mouseMoveEvent(QMouseEvent *event)
{
this->setText(QString("<center><h1>Move: (%1, %2)
</h1></center>").arg(QString::number(event->x()),
? ? ? ? ? ? QString::number(event->y())));
}
?
void EventLabel::mousePressEvent(QMouseEvent *event)
{
? ? this->setText(QString("<center><h1>Press:(%1, %2)
</h1></center>").arg(QString::number(event->x()),
? ? ? ? ? ? ? ? QString::number(event->y())));
}
?
void EventLabel::mouseReleaseEvent(QMouseEvent *event)
{
? ? QString msg;
? ? msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
? ? ? ? ? ? ? ? event->x(), event->y());
? ? this->setText(msg);
}
?
int main(int argc, char *argv[])
{
? ? QApplication a(argc, argv);
?
? ? EventLabel *label = new EventLabel;
? ? label->setWindowTitle("MouseEvent Demo");
? ? label->resize(300, 200);
? ? label->show();
?
? ? return a.exec();
}

EventLabel繼承了QLabel,重寫了mousePressEvent()、mouseMoveEvent()和MouseReleaseEvent()三個(gè)函數(shù)。在鼠標(biāo)按下(press)、鼠標(biāo)移動(dòng)(move)和鼠標(biāo)釋放(release)的時(shí)候,把當(dāng)前鼠標(biāo)的坐標(biāo)值顯示在這個(gè)Label上面。

運(yùn)行上面的代碼,當(dāng)我們點(diǎn)擊了一下鼠標(biāo)之后,label 上將顯示鼠標(biāo)當(dāng)前坐標(biāo)值。

為什么要點(diǎn)擊鼠標(biāo)之后才能在mouseMoveEvent()函數(shù)中顯示鼠標(biāo)坐標(biāo)值?

這是因?yàn)镼Widget中有一個(gè)mouseTracking屬性,該屬性用于設(shè)置是否追蹤鼠標(biāo)。只有鼠標(biāo)被追蹤時(shí),mouseMoveEvent()才會(huì)發(fā)出。如果mouseTracking是 false(默認(rèn)即是),組件在至少一次鼠標(biāo)點(diǎn)擊之后,才能夠被追蹤,也就是能夠發(fā)出mouseMoveEvent()事件。如果mouseTracking為 true,則mouseMoveEvent()直接可以被發(fā)出。

在構(gòu)造函數(shù)里面設(shè)置label->setMouseTracking(true);即可

event()
event()函數(shù)主要用于事件的分發(fā)

例如,我們希望在一個(gè)QWidget組件中監(jiān)聽 tab 鍵的按下,那么就可以繼承QWidget,并重寫它的event()函數(shù),來(lái)達(dá)到這個(gè)目的:

bool CustomWidget::event(QEvent *e)
{
? ? if (e->type() == QEvent::KeyPress) {
? ? ? ? QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
? ? ? ? if (keyEvent->key() == Qt::Key_Tab) {
? ? ? ? ? ? qDebug() << "You press tab.";
? ? ? ? ? ? return true;
? ? ? ? }
? ? }
? ? return QWidget::event(e);
}

CustomWidget是一個(gè)普通的QWidget子類。我們重寫了它的event()函數(shù),這個(gè)函數(shù)有一個(gè)QEvent對(duì)象作為參數(shù),也就是需要轉(zhuǎn)發(fā)的事件對(duì)象。函數(shù)返回值是 bool 類型:

如果傳入的事件已被識(shí)別并且處理,則需要返回 true,否則返回 false。如果返回值是 true,那么 Qt 會(huì)認(rèn)為這個(gè)事件已經(jīng)處理完畢,不會(huì)再將這個(gè)事件發(fā)送給其它對(duì)象,而是會(huì)繼續(xù)處理事件隊(duì)列中的下一事件
在event()函數(shù)中,調(diào)用事件對(duì)象的accept()和ignore()函數(shù)是沒有作用的,不會(huì)影響到事件的傳播
event()函數(shù)中實(shí)際是通過(guò)事件處理器來(lái)響應(yīng)一個(gè)具體的事件。這相當(dāng)于event()函數(shù)將具體事件的處理“委托”給具體的事件處理器。而這些事件處理器是 protected virtual 的,因此,我們重寫了某一個(gè)事件處理器,即可讓 Qt 調(diào)用我們自己實(shí)現(xiàn)的版本

事件過(guò)濾器
有時(shí)候,對(duì)象需要查看、甚至要攔截發(fā)送到另外對(duì)象的事件。

QObject有一個(gè)eventFilter()函數(shù),用于建立事件過(guò)濾器。函數(shù)原型如下:

virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
1
這個(gè)函數(shù)返回一個(gè) bool 類型,如果你想將參數(shù) event 過(guò)濾出來(lái),比如,**不想讓它繼續(xù)轉(zhuǎn)發(fā),就返回 true,否則返回 false。**事件過(guò)濾器的調(diào)用時(shí)間是目標(biāo)對(duì)象(也就是參數(shù)里面的watched對(duì)象)接收到事件對(duì)象之前。也就是說(shuō),如果你在事件過(guò)濾器中停止了某個(gè)事件,那么,watched對(duì)象以及以后所有的事件過(guò)濾器根本不會(huì)知道這么一個(gè)事件。

上面QWidget組件中event()函數(shù)監(jiān)聽 tab 鍵的按下修改為使用事件過(guò)濾器的版本:

bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
? ? if (object == target && event->type() == QEvent::KeyPress)?
{
? ? ? ? QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
? ? ? ? if (keyEvent->key() == Qt::Key_Tab) {
? ? ? ? ? ? qDebug() << "You press tab.";
? ? ? ? ? ? return true;
? ? ? ? } else {
? ? ? ? ? ? return false;
? ? ? ? }
? ? }
? ? return false;
}

注意:事件過(guò)濾器和被安裝過(guò)濾器的組件必須在同一線程,否則,過(guò)濾器將不起作用。另外,如果在安裝過(guò)濾器之后,這兩個(gè)組件到了不同的線程,那么,只有等到二者重新回到同一線程的時(shí)候過(guò)濾器才會(huì)有效。

總結(jié)
Qt 的事件處理,實(shí)際上是有五個(gè)層次:

重寫paintEvent()、mousePressEvent()等事件處理函數(shù)。這是最普通、最簡(jiǎn)單的形式,同時(shí)功能也最簡(jiǎn)單。
重寫event()函數(shù)。event()函數(shù)是所有對(duì)象的事件入口,QObject和QWidget中的實(shí)現(xiàn),默認(rèn)是把事件傳遞給特定的事件處理函數(shù)。
重寫event()函數(shù)。event()函數(shù)是所有對(duì)象的事件入口,QObject和QWidget中的實(shí)現(xiàn),默認(rèn)是把事件傳遞給特定的事件處理函數(shù)。
在QCoreApplication::instance()上面安裝事件過(guò)濾器。該過(guò)濾器將過(guò)濾所有對(duì)象的所有事件,因此和notify()函數(shù)一樣強(qiáng)大,但是它更靈活,因?yàn)榭梢园惭b多個(gè)過(guò)濾器。全局的事件過(guò)濾器可以看到 disabled 組件上面發(fā)出的鼠標(biāo)事件。全局過(guò)濾器有一個(gè)問(wèn)題:只能用在主線程。
重寫QCoreApplication::notify()函數(shù)。這是最強(qiáng)大的,和全局事件過(guò)濾器一樣提供完全控制,并且不受線程的限制。但是全局范圍內(nèi)只能有一個(gè)被使用(因?yàn)镼CoreApplication是單例的)。
六、繪圖和繪圖設(shè)備
QPainter
QPainter用來(lái)執(zhí)行繪制的操作;QPaintDevice是一個(gè)二維空間的抽象,QPaintEngine提供了畫筆(QPainter)在不同的設(shè)備上進(jìn)行繪制的統(tǒng)一的接口

上面的示意圖告訴我們,Qt 的繪圖系統(tǒng)實(shí)際上是,使用QPainter在QPainterDevice上進(jìn)行繪制,它們之間使用QPaintEngine進(jìn)行通訊(也就是翻譯QPainter的指令)。

下面我們通過(guò)一個(gè)實(shí)例來(lái)介紹QPainter的使用:

class PaintedWidget : public QWidget
{
? ? Q_OBJECT
public:
? ? PaintedWidget(QWidget *parent = 0);
protected:
? ? void paintEvent(QPaintEvent *);
}

注意我們重寫了QWidget的paintEvent()函數(shù)。接下來(lái)就是PaintedWidget的源代碼:

PaintedWidget::PaintedWidget(QWidget *parent) :
? ? QWidget(parent)
{
? ? resize(800, 600);
? ? setWindowTitle(tr("Paint Demo"));
}

void PaintedWidget::paintEvent(QPaintEvent *)
{
? ? QPainter painter(this);
? ? painter.drawLine(80, 100, 650, 500);
? ? painter.setPen(Qt::red);
? ? painter.drawRect(10, 10, 100, 400);
? ? painter.setPen(QPen(Qt::green, 5));
? ? painter.setBrush(Qt::blue);
? ? painter.drawEllipse(50, 150, 400, 200);
}

構(gòu)造函數(shù)中,我們僅僅設(shè)置了窗口的大小和標(biāo)題。而paintEvent()函數(shù)則是繪制的代碼。

繪圖設(shè)備
繪圖設(shè)備是指繼承QPainterDevice的子類。

QBitmap是QPixmap的一個(gè)子類,它的色深限定為1,可以使用 QPixmap的isQBitmap()函數(shù)來(lái)確定這個(gè)QPixmap是不是一個(gè)QBitmap。

QPixmap:和平臺(tái)無(wú)關(guān),針對(duì)屏幕進(jìn)行了優(yōu)化了,不能對(duì)圖片進(jìn)行修改

QImage:和平臺(tái)無(wú)關(guān),在線程中繪圖,可以對(duì)圖片進(jìn)行優(yōu)化

QPicture:保存繪圖的狀態(tài)(二進(jìn)制文件)

1、QPixmap -> QImage

QPixmap a;

a.toImage();

2、QImage -> QPixmap

QImage b;

QPixmap::fromImage(b);

3、QPainter

QPainter p;

QPicture pic;

p.begin(&pic);

//繪圖動(dòng)作

p.end();

pic.save("路徑");

4、加載圖片

QPicture temp;

temp.load("路徑");

不規(guī)則窗口
1、給窗口畫一張背景圖

2、去表框

3、設(shè)定屬性(背景透明)

4、移動(dòng)坐標(biāo)是相對(duì)于屏幕而言

七、文件系統(tǒng)
I/O 設(shè)備的類圖(Qt5):


QIODevice:所有 I/O 設(shè)備類的父類,提供了字節(jié)塊讀寫的通用操作以及基本接口;
QFileDevice:Qt5新增加的類,提供了有關(guān)文件操作的通用實(shí)現(xiàn)。
QFlie:訪問(wèn)本地文件或者嵌入資源;
QTemporaryFile:創(chuàng)建和訪問(wèn)本地文件系統(tǒng)的臨時(shí)文件;
QBuffer:讀寫QbyteArray, 內(nèi)存文件;
QProcess:運(yùn)行外部程序,處理進(jìn)程間通訊;
QAbstractSocket:所有套接字類的父類;
QTcpSocket:TCP協(xié)議網(wǎng)絡(luò)數(shù)據(jù)傳輸;
QUdpSocket:傳輸 UDP 報(bào)文;
QSslSocket:使用 SSL/TLS 傳輸數(shù)據(jù);
QFile提供了從文件中讀取和寫入數(shù)據(jù)的能力。

我們通常會(huì)將文件路徑作為參數(shù)傳給QFile的構(gòu)造函數(shù)。不過(guò)也可以在創(chuàng)建好對(duì)象最后,使用setFileName()來(lái)修改

**我們可以使用QDataStream或QTextStream類來(lái)讀寫文件,也可以使用QIODevice類提供的read()、readLine()、readAll()以及write()這樣的函數(shù)。**值得注意的是,有關(guān)文件本身的信息,比如文件名、文件所在目錄的名字等,則是通過(guò)QFileInfo獲取。

QFileInfo有很多類型的函數(shù),舉一些常用例子。比如:

isDir()檢查該文件是否是目錄;
isExecutable() 檢查該文件是否是可執(zhí)行文件等。
baseName() 可以直接獲得文件名;
completeBaseName() 獲取完整的文件名
suffix() 則直接獲取文件后綴名。
completeSuffix() 獲取完整的文件后綴
文件
1、QFile file;

file.setFileName();

file.open();

file.write();

file.read();

file.close();

2、QFileInfo info;

info.size();

info.fileName();

QDataStream:二進(jìn)制方式

QTextStream:文本方式(指定編碼)

QBuffer:內(nèi)存文件(內(nèi)容放在內(nèi)存)

八、Socket通信
Qt中提供的所有的Socket類都是非阻塞的。

Qt中常用的用于socket通信的套接字類:

QTcpServer 用于TCP通信, 作為服務(wù)器端套接字使用

QTcpSocket 用于TCP通信,作為客戶端套接字使用。

QUdpSocket 用于UDP通信,服務(wù)器,客戶端均使用此套接字。

TCP
在Qt中實(shí)現(xiàn)TCP服務(wù)器端通信的流程:

創(chuàng)建套接字

將套接字設(shè)置為監(jiān)聽模式

等待并接受客戶端請(qǐng)求

可以通過(guò)QTcpServer提供的void newConnection()****信號(hào)來(lái)檢測(cè)是否有連接請(qǐng)求,如果有可以在對(duì)應(yīng)的槽函數(shù)中調(diào)用nextPendingConnection函數(shù)獲取到客戶端的Socket信息(返回值為QTcpSocket*類型指針),通過(guò)此套接字與客戶端之間進(jìn)行通信。

接收或者向客戶端發(fā)送數(shù)據(jù)

接收數(shù)據(jù):使用read()或者readAll()函數(shù)

發(fā)送數(shù)據(jù):使用write()函數(shù)

在Qt中實(shí)現(xiàn)TCP/IP客戶端通信的流程:

創(chuàng)建套接字

連接服務(wù)器

可以使用QTcpSocket類的**connectToHost()**函數(shù)來(lái)連接服務(wù)器。

向服務(wù)器發(fā)送或者接受數(shù)據(jù)

下面例子為簡(jiǎn)單的TCP/IP通信的實(shí)現(xiàn)例子:

TCP服務(wù)端
//---------- tcpserver.h ------------
class TCPServer : public QMainWindow
{
? ? Q_OBJECT

public:
? ? explicit TCPServer(QWidget *parent = 0);
? ? ~TCPServer();

public slots:
? ? void slotNewConnection();
? ? void slotReadyRead();

private:
? ? Ui::TCPServer *ui;
? ? // 負(fù)責(zé)監(jiān)聽的套接字
? ? QTcpServer* m_server;
? ? // 負(fù)責(zé)通信的套接字
? ? QTcpSocket* m_client;
};

//---------- tcpserver.cpp ------------
TCPServer::TCPServer(QWidget *parent) :
? ? QMainWindow(parent),
? ? ui(new Ui::TCPServer),
? ? m_server(NULL),
? ? m_client(NULL)
{
? ? ui->setupUi(this);

? ? //創(chuàng)建套接字對(duì)象
? ? m_server = new QTcpServer(this);
? ? //將套接字設(shè)置為監(jiān)聽模式
? ? m_server->listen(QHostAddress::Any, 9999);

? ? //通過(guò)信號(hào)接收客戶端請(qǐng)求
? ? connect(m_server, &QTcpServer::newConnection,?
this, &TCPServer::slotNewConnection);
}

TCPServer::~TCPServer()
{
? ? delete ui;
}

void TCPServer::slotNewConnection()
{
? ? if(m_client == NULL)
? ? {
? ? ? ? //處理客戶端的連接請(qǐng)求
? ? ? ? m_client = m_server->nextPendingConnection();
? ? ? ? //發(fā)送數(shù)據(jù)
? ? ? ? m_client->write("服務(wù)器連接成功!!!");
? ? ? ? //連接信號(hào), 接收客戶端數(shù)據(jù)
? ? ? ? connect(m_client, &QTcpSocket::readyRead,?
this, &TCPServer::slotReadyRead);
? ? }
}

void TCPServer::slotReadyRead()
{
? ? //接收數(shù)據(jù)
? ? QByteArray array = m_client->readAll();
? ? QMessageBox::information(this, "Client Message", array);
}

TCP客戶端
//------------- tcpclient.h ------------
class TCPClient : public QMainWindow
{
? ? Q_OBJECT

public:
? ? explicit TCPClient(QWidget *parent = 0);
? ? ~TCPClient();

public slots:
? ? void slotReadyRead();
? ? void slotSendMsg();

private:
? ? Ui::TCPClient *ui;
? ? QTcpSocket* m_client;
};

//------------- tcpclient.cpp --------------
TCPClient::TCPClient(QWidget *parent) :
? ? QMainWindow(parent),
? ? ui(new Ui::TCPClient)
{
? ? ui->setupUi(this);
? ? //創(chuàng)建套接字
? ? m_client = new QTcpSocket(this);
? ? //連接服務(wù)器
? ? m_client->connectToHost(QHostAddress("127.0.0.1"), 9999);

? ? //通過(guò)信號(hào)接收服務(wù)器數(shù)據(jù)
? ? connect(m_client, &QTcpSocket::readyRead,?
this, &TCPClient::slotReadyRead);
? ? //發(fā)送按鈕
? ? connect(ui->btnSend, &QPushButton::clicked,?
this, &TCPClient::slotSendMsg);
}

TCPClient::~TCPClient()
{
? ? delete ui;
}

void TCPClient::slotReadyRead()
{
?? ? //接收數(shù)據(jù)
? ? QByteArray array = m_client->readAll();
? ? QMessageBox::information(this, "Server Message", array);
}

void TCPClient::slotSendMsg()
{
? ? QString text = ui->textEdit->toPlainText();
?? ? //發(fā)送數(shù)據(jù)
? ? m_client->write(text.toUtf8());
? ? ui->textEdit->clear();
}

UDP
在UDP方式下,客戶端并不與服務(wù)器建立連接,它只負(fù)責(zé)調(diào)用發(fā)送函數(shù)向服務(wù)器發(fā)送數(shù)據(jù)。類似的服務(wù)器也不從客戶端接收連接,只負(fù)責(zé)調(diào)用接收函數(shù),等待來(lái)自客戶端的數(shù)據(jù)的到達(dá)。

在UDP通信中,服務(wù)器端和客戶端的概念已經(jīng)顯得有些淡化,兩部分做的工作都大致相同:

創(chuàng)建套接字

綁定套接字

在UDP中如果需要接收數(shù)據(jù)則需要對(duì)套接字進(jìn)行綁定,只發(fā)送數(shù)據(jù)則不需要對(duì)套接字進(jìn)行綁定。

通過(guò)調(diào)用bind()函數(shù)將套接字綁定到指定端口上。

接收或者發(fā)送數(shù)據(jù)

接收數(shù)據(jù):使用readDatagram()接收數(shù)據(jù),函數(shù)聲明如下:
qint64 readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0)
1
? 參數(shù):

data: 接收數(shù)據(jù)的緩存地址

maxSize: 緩存接收的最大字節(jié)數(shù)

address: 數(shù)據(jù)發(fā)送方的地址(一般使用提供的默認(rèn)值)

port: 數(shù)據(jù)發(fā)送方的端口號(hào)(一般使用提供的默認(rèn)值)

使用pendingDatagramSize()可以獲取到將要接收的數(shù)據(jù)的大小,根據(jù)該函數(shù)返回值來(lái)準(zhǔn)備對(duì)應(yīng)大小的內(nèi)存空間存放將要接收的數(shù)據(jù)。

發(fā)送數(shù)據(jù): 使用writeDatagram()函數(shù)發(fā)送數(shù)據(jù),函數(shù)聲明如下:
qint64 writeDatagram(const QByteArray & datagram, const QHostAddress & host, quint16 port)
1
? 參數(shù):

datagram:要發(fā)送的字符串

host:數(shù)據(jù)接收方的地址

port:數(shù)據(jù)接收方的端口號(hào)

廣播
在使用QUdpSocket類的writeDatagram()函數(shù)發(fā)送數(shù)據(jù)的時(shí)候,其中第二個(gè)參數(shù)host應(yīng)該指定為廣播地址:QHostAddress::Broadcast此設(shè)置相當(dāng)于QHostAddress(“255.255.255.255”)

使用UDP廣播的的特點(diǎn):

使用UDP進(jìn)行廣播,局域網(wǎng)內(nèi)的其他的UDP用戶全部可以收到廣播的消息

UDP廣播只能在局域網(wǎng)范圍內(nèi)使用

組播
在使用QUdpSocket類的writeDatagram()函數(shù)發(fā)送數(shù)據(jù)的時(shí)候,其中第二個(gè)參數(shù)host應(yīng)該指定為組播地址,關(guān)于組播地址的分類:

224.0.0.0~224.0.0.255為預(yù)留的組播地址(永久組地址),地址224.0.0.0保留不做分配,其它地址供路由協(xié)議使用;

224.0.1.0~224.0.1.255是公用組播地址,可以用于Internet;

224.0.2.0~238.255.255.255為用戶可用的組播地址(臨時(shí)組地址),全網(wǎng)范圍內(nèi)有效;

239.0.0.0~239.255.255.255為本地管理組播地址,僅在特定的本地范圍內(nèi)有效。

注冊(cè)加入到組播地址需要使用QUdpSocket類的成員函數(shù):

bool joinMulticastGroup(const QHostAddress & groupAddress)

TCP 和 UDP的區(qū)別
TCP?? ?UDP
是否連接?? ?面向連接?? ?無(wú)連接
傳輸方式?? ?基于流?? ?基于數(shù)據(jù)報(bào)
傳輸可靠性?? ?可靠?? ?不可靠
傳輸效率?? ?效率低?? ?效率高
能否廣播?? ?不能?? ?能
九、多線程的使用
在次線程中處理的業(yè)務(wù)放在獨(dú)立的模塊(類)中,由主線程創(chuàng)建完該對(duì)象后,將其移交給指定的線程,且可以將多個(gè)類似的對(duì)象移交給同一個(gè)線程。

例子中,信號(hào)由主線程的QTimer對(duì)象發(fā)出,之后Qt會(huì)將關(guān)聯(lián)的事件放到worker所屬線程的事件隊(duì)列。由于隊(duì)列連接的作用,在不同線程間連接信號(hào)和槽是很安全的。

示例代碼如下:

class Worker : public QObject
{
? ? Q_OBJECT
private slots:
? ? void onTimeout()
? ? {
? ? ? ? qDebug()<<"Worker::onTimeout get called from?: "
<<QThread::currentThreadId();
? ? }
};
? ??
int main(int argc, char *argv[])
{
? ? QApplication a(argc, argv);
? ? qDebug()<<"From main thread: "<<QThread::currentThreadId();
??
? ? QThread t;
? ? QTimer timer;
? ? Worker worker;
??
? ? QObject::connect(&timer, SIGNAL(timeout()),?
&worker, SLOT(onTimeout()));
?? ??? ? // 啟動(dòng)定時(shí)器
? ? timer.start(1000);
? ?? ? // 將類對(duì)象移交個(gè)線程
? ? worker.moveToThread(&t);
? ? // 啟動(dòng)線程
? ? t.start();
??
? ? return a.exec();
}

關(guān)于Qobject類的connect函數(shù)最后一個(gè)參數(shù),連接類型:

自動(dòng)連接(AutoConnection),默認(rèn)的連接方式。
如果信號(hào)與槽,也就是發(fā)送者與接受者在同一線程,等同于直接連接;

如果發(fā)送者與接受者處在不同線程,等同于隊(duì)列連接。

直接連接(DirectConnection)
當(dāng)信號(hào)發(fā)射時(shí),槽函數(shù)立即直接調(diào)用。無(wú)論槽函數(shù)所屬對(duì)象在哪個(gè)線程,槽函數(shù)總在發(fā)送者所在線程執(zhí)行。

隊(duì)列連接(QueuedConnection)
當(dāng)控制權(quán)回到接受者所在線程的事件循環(huán)時(shí),槽函數(shù)被調(diào)用。槽函數(shù)在接受者所在線程執(zhí)行。

總結(jié):

* 隊(duì)列連接:槽函數(shù)在接受者所在線程執(zhí)行。

* 直接連接:槽函數(shù)在發(fā)送者所在線程執(zhí)行。

*自動(dòng)連接:二者不在同一線程時(shí),等同于隊(duì)列連接

多線程使用過(guò)程中注意事項(xiàng):

線程不能操作UI對(duì)象(從Qwidget直接或間接派生的窗口對(duì)象)

需要移動(dòng)到子線程中處理的模塊類,創(chuàng)建的對(duì)象的時(shí)候不能指定父對(duì)象。

十、Qt數(shù)據(jù)庫(kù)操作
一、操作數(shù)據(jù)庫(kù)
Qt 提供了 QtSql 模塊來(lái)提供平臺(tái)獨(dú)立的基于 SQL 的數(shù)據(jù)庫(kù)操作。

Qt 使用QSqlDatabase表示一個(gè)數(shù)據(jù)庫(kù)連接。

可以通過(guò):

//打印Qt支持的數(shù)據(jù)庫(kù)驅(qū)動(dòng)
qDebug() << QSqlDatabase::drivers();
1
2
找到系統(tǒng)中所有可用的數(shù)據(jù)庫(kù)驅(qū)動(dòng)的名字列表。

封裝一個(gè)連接數(shù)據(jù)庫(kù)的函數(shù):

bool connect(const QString &dbName)
{
? ? QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
// ? ?db.setHostName("host");
// ? ?db.setDatabaseName("dbname");
// ? ?db.setUserName("username");
// ? ?db.setPassword("password");
? ? db.setDatabaseName(dbName);
? ? if (!db.open()) {
? ? ? ? QMessageBox::critical(0, QObject::tr("Database Error"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? db.lastError().text());
? ? ? ? return false;
? ? }
? ? return true;
}

創(chuàng)建數(shù)據(jù)庫(kù)表student后,插入數(shù)據(jù),然后將其獨(dú)取出來(lái):

if (connect("demo.db"))?
{
? ? QSqlQuery query;
? ? query.prepare("INSERT INTO student (name, age) VALUES (?, ?)");
? ? QVariantList names;
? ? names << "Tom" << "Jack" << "Jane" << "Jerry";
? ? query.addBindValue(names);
? ? QVariantList ages;
? ? ages << 20 << 23 << 22 << 25;
? ? query.addBindValue(ages);
? ? if (!query.execBatch()) {
? ? ? ? QMessageBox::critical(0, QObject::tr("Database Error"),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? query.lastError().text());
? ? }
?
? ? query.exec("SELECT name, age FROM student");
? ? while (query.next()) {
? ? ? ? QString name = query.value(0).toString();
? ? ? ? int age = query.value(1).toInt();
? ? ? ? qDebug() << name << ": " << age;
? ? }
}?
else?
{
? ? return 1;
}

插入多條數(shù)據(jù),此時(shí)可以使用QSqlQuery::exec()函數(shù)一條一條插入數(shù)據(jù),但是這里我們選擇了另外一種方法:批量執(zhí)行。首先,我們使用QSqlQuery::prepare()函數(shù)對(duì)這條 SQL 語(yǔ)句進(jìn)行預(yù)處理,問(wèn)號(hào) ? 相當(dāng)于占位符,預(yù)示著以后我們可以使用實(shí)際數(shù)據(jù)替換這些位置。

在上面的代碼中,我們使用一個(gè)字符串列表 names 替換掉第一個(gè)問(wèn)號(hào)的位置,一個(gè)整型列表 ages 替換掉第二個(gè)問(wèn)號(hào)的位置,利用QSqlQuery::addBindValue()我們將實(shí)際數(shù)據(jù)綁定到這個(gè)預(yù)處理的 SQL 語(yǔ)句上。需要注意的是,names 和 ages 這兩個(gè)列表里面的數(shù)據(jù)需要一一對(duì)應(yīng)。然后我們調(diào)用**QSqlQuery::execBatch()批量執(zhí)行 SQL,之后結(jié)束該對(duì)象。**這樣,插入操作便完成了。

二、使用模型操作數(shù)據(jù)庫(kù)
基于QSqlTableModel 的模型處理更為高級(jí),如果對(duì) SQL 語(yǔ)句不熟悉,并且不需要很多復(fù)雜的查詢,這種QSqlTableModel模型基本可以滿足一般的需求。

1、查詢操作
if (connect("demo.db"))?
{
? ? QSqlTableModel model;
? ? model.setTable("student");
? ? model.setFilter("age > 20 and age < 25");
? ? if (model.select()) {
? ? ? ? for (int i = 0; i < model.rowCount(); ++i) {
? ? ? ? ? ? QSqlRecord record = model.record(i);
? ? ? ? ? ? QString name = record.value("name").toString();
? ? ? ? ? ? int age = record.value("age").toInt();
? ? ? ? ? ? qDebug() << name << ": " << age;
? ? ? ? }
? ? }
}?
else?
{
? ? return 1;
}

setTable()函數(shù)設(shè)置所需要操作的表格;

setFilter()函數(shù)則是添加過(guò)濾器,也就是 WHERE 語(yǔ)句所需要的部分。

例如上面代碼中的操作實(shí)際相當(dāng)于 SQL 語(yǔ)句:

SELECT * FROM student WHERE age > 20 and age < 25
1
注意:我們使用QSqlTableModel只能進(jìn)行 SELECT * 的查詢,不能只查詢其中某些列的數(shù)據(jù)。

2、插入操作
QSqlTableModel model;
model.setTable("student");
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 1), "Cheng");
model.setData(model.index(row, 2), 24);
model.submitAll();

model.insertRows(row, 1);說(shuō)明我們想在索引 0 的位置插入 1 行新的數(shù)據(jù)。使用setData()函數(shù)則開始準(zhǔn)備實(shí)際需要插入的數(shù)據(jù)。最后,調(diào)用submitAll()函數(shù)提交所有修改。

例如上面代碼中的操作實(shí)際相當(dāng)于 SQL 語(yǔ)句:

INSERT INTO student (name, age) VALUES ('Cheng', 24)
1
3、更新操作
QSqlTableModel model;
model.setTable("student");
model.setFilter("age = 25");
if (model.select()) {
? ? if (model.rowCount() == 1) {
? ? ? ? QSqlRecord record = model.record(0);
? ? ? ? record.setValue("age", 26);
? ? ? ? model.setRecord(0, record);
? ? ? ? model.submitAll();
? ? }
}

找到 age = 25 的記錄,然后將 age 重新設(shè)置為 26,存入相同的位置(在這里都是索引 0 的位置),提交之后完成一次更新

例如上面代碼中的操作實(shí)際相當(dāng)于 SQL 語(yǔ)句:

UPDATE student SET age = 26 WHERE age = 25
1
4、刪除操作
QSqlTableModel model;
model.setTable("student");
model.setFilter("age = 25");
if (model.select()) {
? ? if (model.rowCount() == 1) {
? ? ? ? model.removeRows(0, 1);
? ? ? ? model.submitAll();
? ? }
}

removeRows()函數(shù)可以一次刪除多行。

例如上面代碼中的操作實(shí)際相當(dāng)于 SQL 語(yǔ)句:

DELETE FROM student WHERE age = 25
1
如果你覺得文章還不錯(cuò),記得"點(diǎn)贊關(guān)注"

關(guān)注我的微信公眾號(hào)【 加班猿 】可以獲取更多內(nèi)容


————————————————
版權(quán)聲明:本文為CSDN博主「加班猿」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_37824357/article/details/109341717

總結(jié)

以上是生活随笔為你收集整理的Qt笔记总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产成人精品国内自产拍免费看 | 都市激情亚洲综合 | 99热在线播放| av2018| 国产又粗又猛又黄又爽视频 | ass极品国模人体欣赏 | 国产欧美一区二区三区在线 | 欧美大片黄色 | 午夜影剧院 | 波多野结衣在线免费视频 | 精品人妻互换一区二区三区 | 欧美黄在线 | 五月天综合社区 | 久草a在线| 国产麻豆剧果冻传媒白晶晶 | 久爱视频在线 | 免费观看av网址 | youjizz国产精品 | 午夜小视频在线 | 在线免费观看a级片 | 绿帽人妻精品一区二区 | 毛片基地站 | 日本午夜电影网站 | 亚洲成人aaa | 亚洲AV不卡无码一区二区三区 | 中文字幕欧美视频 | 在线成人免费电影 | 黄页网站免费在线观看 | 久久99久久99精品免观看粉嫩 | 午夜一区二区三区四区 | 久久久精品人妻无码专区 | 日韩午夜毛片 | 久久精品亚洲一区 | 爱情岛论坛亚洲品质自拍 | 伊人一区 | 日韩免费小视频 | 超碰77| 一级片中文字幕 | jizz教师| 爱逼综合 | 亚洲一区二区三区免费视频 | 天天干天天色天天射 | 日韩av毛片 | 男同激情视频 | 国产一区在线观看视频 | 亚洲国产视频一区二区 | 日本乱偷中文字幕 | 成人v片| 久草视频免费在线播放 | 性久久久久久久久久 | 一区二区三区视频免费看 | 二区三区偷拍浴室洗澡视频 | yy111122少妇光屁股影院 | 日日爱99| 精品国产69 | 秘密基地在线观看完整版免费 | 日韩五月天 | 91调教打屁股xxxx网站 | 日韩超碰 | 69久久精品无码一区二区 | 国产精品视频免费在线观看 | 精品在线二区 | 毛片9| 香蕉视频网址 | 国产精品毛片一区二区 | 花房姑娘免费全集 | 男人懂得网站 | 亚洲一区二区不卡视频 | 琪琪色影音先锋 | 成年视频在线播放 | 成年网站在线播放 | 中文字幕欧美日韩 | 日韩精品2区| 一级伦理片 | 人妻洗澡被强公日日澡电影 | 一区二区视频观看 | 大胸美女无遮挡 | 欧美综合区 | 性生生活性生交a级 | 97免费公开视频 | 亚洲AV综合色区国产精品天天 | 免费视频毛片 | 国产精品成人无码 | 免费视频a | 久久精品视频免费观看 | 40一50一60老女人毛片 | 亚洲午夜久久久久久久久久久 | 国产激情二区 | 国产性生活片 | 野外一级片 | 久久精品久久久精品美女 | 91麻豆成人精品国产 | 国产午夜免费福利 | 国产伦精品一区二区三区网站 | 色啪视频 | 亚洲中文字幕无码av永久 | 三年中文在线观看免费观看 | 岛国av毛片 | 狠狠干2020 |