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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Qt笔记总结

發布時間:2024/9/27 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Qt笔记总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Qt筆記總結

作者:hackett

微信公眾號:加班猿

一、常用控件
按鈕類
QPushButton

QtoolButton

QRadioButton

item
QListWidget

容器類
QStackWidget

QWidget

QFrame

編輯類
QComboBox

QLineEdit

QTextEdit

顯示類
QLabel

setOpenExternalLinks()設置為true自動打開,false要打開鏈接需要捕捉linkActivated()信號

//顯示普通文本字符串
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);

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

QProcessBar

QMessageBox

二、信號與槽
信號和槽
信號槽,實際就是觀察者模式。當某個事件發生之后,它就會發出一個信號(signal)將想要處理的信號和自己的一個函數(稱為槽(slot))綁定來處理這個信號。當信號發出時,被連接的槽函數會自動被回調。

1、connect(sender, signal, receiver, slot);
1
sender:發送信號的對象
signal:發送對象發出的信號
receiver:接收信號的對象
slot:接收對象在接收到信號之后所需要調用的函數
例如:

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

自定義信號槽
自定義信號槽需要注意的事項
發送者和接收者都需要是QObject的子類(當然,槽函數是全局函數、Lambda 表達式等無需接收者的時候除外);

使用 signals 標記信號函數,信號是一個函數聲明,返回 void,不需要實現函數代碼;

槽函數是普通的成員函數,作為成員函數,會受到 public、private、protected 的影響;

使用 emit 在恰當的位置發送信號;

使用QObject::connect()函數連接信號和槽。

任何成員函數、static 函數、全局函數和 Lambda 表達式都可以作為槽函數

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

Lambda表達式
C++11中的Lambda表達式用于定義并創建匿名的函數對象。

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

1、函數對象參數:

[ ] 標識一個Lambda的開始,這部分必須存在,不能省略。

空,沒有任何函數對象參數

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

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

this,函數體內可以可以使用Lambda所在類中的成員變量

a,把a按值進行傳遞(默認為const不可修改,可添加mutable修飾符修改)

&b,把b按引用進行傳遞

&,a,b,除a和b進行值傳遞,其他參數按引用進行傳遞

2、操作符重載函數參數:

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

3、可修改標示符:

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

4、錯誤拋出標示符:

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

5、函數返回值:

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

6、函數體:

{},標識函數的實現,這部分不能省略,但函數體可以為空。

?

三、Qt窗口系統
1、坐標體系
以左上角為原點,X向右增加,Y向下增加。

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

對象模型

Qt創建對象的時候會提供一個Parent對象指針

在創建QObject對象時,可以提供一個其父對象,我們創建的這個QObject對象會自動添加到其父對象的children()列表。當父對象析構的時候,這個列表中的所有對象也會被析構。(注意,這里的父對象并不是繼承意義上的父類!)

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

QWidget繼承自QObject,因此也繼承了這種對象樹關系。一個孩子自動地成為父組件的一個子組件,也可以自己刪除子對象,它們會自動從其父對象列表中刪除。

當一個QObject對象在堆上創建的時候,Qt 會同時為其創建一個對象樹。不過,對象樹中對象的順序是沒有定義的。這意味著,銷毀這些對象的順序也是未定義的

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

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

1、菜單欄

創建菜單欄,通過QMainWindow類的menubar() 函數獲取主窗口菜單欄指針

QMenuBar *?? ?menuBar() const
1
創建菜單,調用QMenu的成員函數addMenu() 來添加菜單

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

創建菜單項,調用QMenu的成員函數addAction() 來添加菜單項

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、工具欄

直接調用QMainWindow類的addToolBar() 函數獲取主窗口的工具條對象,每增加一個工具條都需要調用一次該函數。

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

n 工具條是一個可移動的窗口,它的停靠區域由QToolBar的allowAreas決定,包括:

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

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


3、狀態欄

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

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

4、對話框QDialog
Qt 中使用QDialog類實現對話框。

對話框分為模態對話框和非模態對話框。

模態對話框,就是會阻塞同一應用程序中其它窗口的輸入。

模態對話框很常見,比如“打開文件”功能。你可以嘗試一下記事本的打開文件,當打開文件對話框出現時,我們是不能對除此對話框之外的窗口部分進行操作的。

與此相反的是非模態對話框,例如查找對話框,我們可以在顯示著查找對話框的同時,繼續對記事本的內容進行編輯。

1、標準對話框

Qt 的內置對話框大致分為以下幾類:

QColorDialog: 選擇顏色;

QFileDialog: 選擇文件或者目錄;

QFontDialog: 選擇字體;

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

QMessageBox: 模態對話框,用于顯示信息、詢問問題等;

QPageSetupDialog: 為打印機提供紙張相關的選項;

QPrintDialog: 打印機配置;

QPrintPreviewDialog:打印預覽;

QProgressDialog: 顯示操作過程。

2、自定義消息框

Qt 支持模態對話框和非模態對話框。

模態與非模態的實現:

使用QDialog::exec()實現應用程序級別的模態對話框

使用QDialog::open()實現窗口級別的模態對話框

使用QDialog::show()實現非模態對話框。

3、模態對話框

下面的示例中,我們調用了exec()將對話框顯示出來,因此這就是一個模態對話框。當對話框出現時,我們不能與主窗口進行任何交互,直到我們關閉了該對話框。

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

4、非模態對話框

下面我們試著將exec()修改為show(),看看非模態對話框:

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

是不是事與愿違?對話框竟然一閃而過!這是因為,**show()函數不會阻塞當前線程,對話框會顯示出來,然后函數立即返回,代碼繼續執行。**注意,dialog 是建立在棧上的,show()函數返回,MainWindow::open()函數結束,dialog 超出作用域被析構,因此對話框消失了。知道了原因就好改了,我們將 dialog 改成堆上建立,當然就沒有這個問題了:

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

如果你足夠細心,應該發現上面的代碼是有問題的:dialog 存在內存泄露!dialog 使用 new 在堆上分配空間,卻一直沒有 delete。解決方案也很簡單:將 MainWindow 的指針賦給 dialog 即可。還記得我們前面說過的 Qt 的對象系統嗎?

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

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

setAttribute()函數設置對話框關閉時,自動銷毀對話框。

1、消息對話框

QMessageBox用于顯示消息提示。我們一般會使用其提供的幾個 static 函數:

顯示關于對話框。

void about(QWidget * parent, const QString & title, const QString & text)
1
這是一個最簡單的對話框,其標題是 title,內容是 text,父窗口是 parent。對話框只有一個 OK 按鈕。

顯示關于 Qt 對話框。該對話框用于顯示有關 Qt 的信息。

void aboutQt(QWidget * parent, const QString & title = QString())
1
顯示嚴重錯誤對話框。

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

這個對話框將顯示一個紅色的錯誤符號。我們可以通過 buttons 參數指明其顯示的按鈕。默認情況下只有一個 Ok 按鈕,我們可以使用StandardButtons類型指定多種按鈕。

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

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

與QMessageBox::critical()類似,不同之處在于這個對話框提供一個問號圖標,并且其顯示的按鈕是“是”和“否”。

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

與QMessageBox::critical()類似,不同之處在于這個對話框提供一個黃色嘆號圖標。

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

2、標準文件對話框

QFileDialog,也就是文件對話框。

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()函數,為這兩個QAction對象添加響應的動作:

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

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

//打開文件
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()來獲取需要打開的文件的路徑。這個函數原型如下:

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

六個參數分別是:

parent:父窗口

caption:對話框標題

dir:對話框打開時的默認目錄

“.” 代表程序運行目錄

“/” 代表當前盤符的根目錄(特指 Windows 平臺;Linux 平臺當然就是根目錄),這個參數也可以是平臺相關的,比如“C:\”等

filter:過濾器。

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

selectedFilter:默認選擇的過濾器

options:對話框的一些參數設定

比如只顯示文件夾等等,它的取值是enum QFileDialog::Option,每個選項可以使用 | 運算組合起來

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

絕對定位
絕對定位就是一種最原始的定位方法:給出這個組件的坐標和長寬值。

如果用戶改變了窗口大小,比如點擊最大化按鈕或者使用鼠標拖動窗口邊緣,采用絕對定位的組件是不會有任何響應的。

布局定位
只要把組件放入某一種布局,布局由專門的布局管理器進行管理。當需要調整大小或者位置的時候,Qt 使用對應的布局管理器進行調整。

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

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

選中UI控件 -> 提升

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

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

在所有組件的父類QWidget中,定義了很多事件處理的回調函數,如:

keyPressEvent()

keyReleaseEvent()

mouseDoubleClickEvent()

mouseMoveEvent()

mousePressEvent()

mouseReleaseEvent() 等。

這些函數都是 protected virtual 的,也就是說我們可以在子類中重新實現這些函數。下面來看一個例子:

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()三個函數。在鼠標按下(press)、鼠標移動(move)和鼠標釋放(release)的時候,把當前鼠標的坐標值顯示在這個Label上面。

運行上面的代碼,當我們點擊了一下鼠標之后,label 上將顯示鼠標當前坐標值。

為什么要點擊鼠標之后才能在mouseMoveEvent()函數中顯示鼠標坐標值?

這是因為QWidget中有一個mouseTracking屬性,該屬性用于設置是否追蹤鼠標。只有鼠標被追蹤時,mouseMoveEvent()才會發出。如果mouseTracking是 false(默認即是),組件在至少一次鼠標點擊之后,才能夠被追蹤,也就是能夠發出mouseMoveEvent()事件。如果mouseTracking為 true,則mouseMoveEvent()直接可以被發出。

在構造函數里面設置label->setMouseTracking(true);即可

event()
event()函數主要用于事件的分發

例如,我們希望在一個QWidget組件中監聽 tab 鍵的按下,那么就可以繼承QWidget,并重寫它的event()函數,來達到這個目的:

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是一個普通的QWidget子類。我們重寫了它的event()函數,這個函數有一個QEvent對象作為參數,也就是需要轉發的事件對象。函數返回值是 bool 類型:

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

事件過濾器
有時候,對象需要查看、甚至要攔截發送到另外對象的事件。

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

virtual bool QObject::eventFilter ( QObject * watched, QEvent * event );
1
這個函數返回一個 bool 類型,如果你想將參數 event 過濾出來,比如,**不想讓它繼續轉發,就返回 true,否則返回 false。**事件過濾器的調用時間是目標對象(也就是參數里面的watched對象)接收到事件對象之前。也就是說,如果你在事件過濾器中停止了某個事件,那么,watched對象以及以后所有的事件過濾器根本不會知道這么一個事件。

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

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;
}

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

總結
Qt 的事件處理,實際上是有五個層次:

重寫paintEvent()、mousePressEvent()等事件處理函數。這是最普通、最簡單的形式,同時功能也最簡單。
重寫event()函數。event()函數是所有對象的事件入口,QObject和QWidget中的實現,默認是把事件傳遞給特定的事件處理函數。
重寫event()函數。event()函數是所有對象的事件入口,QObject和QWidget中的實現,默認是把事件傳遞給特定的事件處理函數。
在QCoreApplication::instance()上面安裝事件過濾器。該過濾器將過濾所有對象的所有事件,因此和notify()函數一樣強大,但是它更靈活,因為可以安裝多個過濾器。全局的事件過濾器可以看到 disabled 組件上面發出的鼠標事件。全局過濾器有一個問題:只能用在主線程。
重寫QCoreApplication::notify()函數。這是最強大的,和全局事件過濾器一樣提供完全控制,并且不受線程的限制。但是全局范圍內只能有一個被使用(因為QCoreApplication是單例的)。
六、繪圖和繪圖設備
QPainter
QPainter用來執行繪制的操作;QPaintDevice是一個二維空間的抽象,QPaintEngine提供了畫筆(QPainter)在不同的設備上進行繪制的統一的接口

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

下面我們通過一個實例來介紹QPainter的使用:

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

注意我們重寫了QWidget的paintEvent()函數。接下來就是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);
}

構造函數中,我們僅僅設置了窗口的大小和標題。而paintEvent()函數則是繪制的代碼。

繪圖設備
繪圖設備是指繼承QPainterDevice的子類。

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

QPixmap:和平臺無關,針對屏幕進行了優化了,不能對圖片進行修改

QImage:和平臺無關,在線程中繪圖,可以對圖片進行優化

QPicture:保存繪圖的狀態(二進制文件)

1、QPixmap -> QImage

QPixmap a;

a.toImage();

2、QImage -> QPixmap

QImage b;

QPixmap::fromImage(b);

3、QPainter

QPainter p;

QPicture pic;

p.begin(&pic);

//繪圖動作

p.end();

pic.save("路徑");

4、加載圖片

QPicture temp;

temp.load("路徑");

不規則窗口
1、給窗口畫一張背景圖

2、去表框

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

4、移動坐標是相對于屏幕而言

七、文件系統
I/O 設備的類圖(Qt5):


QIODevice:所有 I/O 設備類的父類,提供了字節塊讀寫的通用操作以及基本接口;
QFileDevice:Qt5新增加的類,提供了有關文件操作的通用實現。
QFlie:訪問本地文件或者嵌入資源;
QTemporaryFile:創建和訪問本地文件系統的臨時文件;
QBuffer:讀寫QbyteArray, 內存文件;
QProcess:運行外部程序,處理進程間通訊;
QAbstractSocket:所有套接字類的父類;
QTcpSocket:TCP協議網絡數據傳輸;
QUdpSocket:傳輸 UDP 報文;
QSslSocket:使用 SSL/TLS 傳輸數據;
QFile提供了從文件中讀取和寫入數據的能力。

我們通常會將文件路徑作為參數傳給QFile的構造函數。不過也可以在創建好對象最后,使用setFileName()來修改

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

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

isDir()檢查該文件是否是目錄;
isExecutable() 檢查該文件是否是可執行文件等。
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:二進制方式

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

QBuffer:內存文件(內容放在內存)

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

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

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

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

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

TCP
在Qt中實現TCP服務器端通信的流程:

創建套接字

將套接字設置為監聽模式

等待并接受客戶端請求

可以通過QTcpServer提供的void newConnection()****信號來檢測是否有連接請求,如果有可以在對應的槽函數中調用nextPendingConnection函數獲取到客戶端的Socket信息(返回值為QTcpSocket*類型指針),通過此套接字與客戶端之間進行通信。

接收或者向客戶端發送數據

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

發送數據:使用write()函數

在Qt中實現TCP/IP客戶端通信的流程:

創建套接字

連接服務器

可以使用QTcpSocket類的**connectToHost()**函數來連接服務器。

向服務器發送或者接受數據

下面例子為簡單的TCP/IP通信的實現例子:

TCP服務端
//---------- 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;
? ? // 負責監聽的套接字
? ? QTcpServer* m_server;
? ? // 負責通信的套接字
? ? QTcpSocket* m_client;
};

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

? ? //創建套接字對象
? ? m_server = new QTcpServer(this);
? ? //將套接字設置為監聽模式
? ? m_server->listen(QHostAddress::Any, 9999);

? ? //通過信號接收客戶端請求
? ? connect(m_server, &QTcpServer::newConnection,?
this, &TCPServer::slotNewConnection);
}

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

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

void TCPServer::slotReadyRead()
{
? ? //接收數據
? ? 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);
? ? //創建套接字
? ? m_client = new QTcpSocket(this);
? ? //連接服務器
? ? m_client->connectToHost(QHostAddress("127.0.0.1"), 9999);

? ? //通過信號接收服務器數據
? ? connect(m_client, &QTcpSocket::readyRead,?
this, &TCPClient::slotReadyRead);
? ? //發送按鈕
? ? connect(ui->btnSend, &QPushButton::clicked,?
this, &TCPClient::slotSendMsg);
}

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

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

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

UDP
在UDP方式下,客戶端并不與服務器建立連接,它只負責調用發送函數向服務器發送數據。類似的服務器也不從客戶端接收連接,只負責調用接收函數,等待來自客戶端的數據的到達。

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

創建套接字

綁定套接字

在UDP中如果需要接收數據則需要對套接字進行綁定,只發送數據則不需要對套接字進行綁定。

通過調用bind()函數將套接字綁定到指定端口上。

接收或者發送數據

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

data: 接收數據的緩存地址

maxSize: 緩存接收的最大字節數

address: 數據發送方的地址(一般使用提供的默認值)

port: 數據發送方的端口號(一般使用提供的默認值)

使用pendingDatagramSize()可以獲取到將要接收的數據的大小,根據該函數返回值來準備對應大小的內存空間存放將要接收的數據。

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

datagram:要發送的字符串

host:數據接收方的地址

port:數據接收方的端口號

廣播
在使用QUdpSocket類的writeDatagram()函數發送數據的時候,其中第二個參數host應該指定為廣播地址:QHostAddress::Broadcast此設置相當于QHostAddress(“255.255.255.255”)

使用UDP廣播的的特點:

使用UDP進行廣播,局域網內的其他的UDP用戶全部可以收到廣播的消息

UDP廣播只能在局域網范圍內使用

組播
在使用QUdpSocket類的writeDatagram()函數發送數據的時候,其中第二個參數host應該指定為組播地址,關于組播地址的分類:

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

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

224.0.2.0~238.255.255.255為用戶可用的組播地址(臨時組地址),全網范圍內有效;

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

注冊加入到組播地址需要使用QUdpSocket類的成員函數:

bool joinMulticastGroup(const QHostAddress & groupAddress)

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

例子中,信號由主線程的QTimer對象發出,之后Qt會將關聯的事件放到worker所屬線程的事件隊列。由于隊列連接的作用,在不同線程間連接信號和槽是很安全的。

示例代碼如下:

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()));
?? ??? ? // 啟動定時器
? ? timer.start(1000);
? ?? ? // 將類對象移交個線程
? ? worker.moveToThread(&t);
? ? // 啟動線程
? ? t.start();
??
? ? return a.exec();
}

關于Qobject類的connect函數最后一個參數,連接類型:

自動連接(AutoConnection),默認的連接方式。
如果信號與槽,也就是發送者與接受者在同一線程,等同于直接連接;

如果發送者與接受者處在不同線程,等同于隊列連接。

直接連接(DirectConnection)
當信號發射時,槽函數立即直接調用。無論槽函數所屬對象在哪個線程,槽函數總在發送者所在線程執行。

隊列連接(QueuedConnection)
當控制權回到接受者所在線程的事件循環時,槽函數被調用。槽函數在接受者所在線程執行。

總結:

* 隊列連接:槽函數在接受者所在線程執行。

* 直接連接:槽函數在發送者所在線程執行。

*自動連接:二者不在同一線程時,等同于隊列連接

多線程使用過程中注意事項:

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

需要移動到子線程中處理的模塊類,創建的對象的時候不能指定父對象。

十、Qt數據庫操作
一、操作數據庫
Qt 提供了 QtSql 模塊來提供平臺獨立的基于 SQL 的數據庫操作。

Qt 使用QSqlDatabase表示一個數據庫連接。

可以通過:

//打印Qt支持的數據庫驅動
qDebug() << QSqlDatabase::drivers();
1
2
找到系統中所有可用的數據庫驅動的名字列表。

封裝一個連接數據庫的函數:

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;
}

創建數據庫表student后,插入數據,然后將其獨取出來:

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;
}

插入多條數據,此時可以使用QSqlQuery::exec()函數一條一條插入數據,但是這里我們選擇了另外一種方法:批量執行。首先,我們使用QSqlQuery::prepare()函數對這條 SQL 語句進行預處理,問號 ? 相當于占位符,預示著以后我們可以使用實際數據替換這些位置。

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

二、使用模型操作數據庫
基于QSqlTableModel 的模型處理更為高級,如果對 SQL 語句不熟悉,并且不需要很多復雜的查詢,這種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()函數設置所需要操作的表格;

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

例如上面代碼中的操作實際相當于 SQL 語句:

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

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);說明我們想在索引 0 的位置插入 1 行新的數據。使用setData()函數則開始準備實際需要插入的數據。最后,調用submitAll()函數提交所有修改。

例如上面代碼中的操作實際相當于 SQL 語句:

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 重新設置為 26,存入相同的位置(在這里都是索引 0 的位置),提交之后完成一次更新

例如上面代碼中的操作實際相當于 SQL 語句:

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()函數可以一次刪除多行。

例如上面代碼中的操作實際相當于 SQL 語句:

DELETE FROM student WHERE age = 25
1
如果你覺得文章還不錯,記得"點贊關注"

關注我的微信公眾號【 加班猿 】可以獲取更多內容


————————————————
版權聲明:本文為CSDN博主「加班猿」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_37824357/article/details/109341717

總結

以上是生活随笔為你收集整理的Qt笔记总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。