用QT/C++寫一個簡易文字編輯器
學習QT的小練習,先看一下目前實現的效果。
功能:
- 編輯文字儲存為txt。
- 開啟一個txt文字檔案,可編輯可儲存。
- 文字編輯功能:剪下,複製,貼上,加粗,斜體,下劃線,設定顏色,字型。
要點:
- QT Designer的UI視覺化設計:基本控制元件佈局,資源匯入,選單&動作,訊號&槽的配置;
- QT訊號和槽的機制:視覺化配置以及程式碼手動連線,實現槽函式;
- plainTextEdit控制元件相關:展示文字,設定文字格式,顏色等;
- 檔案對話方塊:讀寫檔案,開啟和儲存功能;
- QT常用快捷鍵:F4在原始檔和標頭檔案之間切換,右擊選單refactor轉到定義等;
1. UI佈局設計
整體介面佈局如下。
設計思路:
新建mainWindow窗體,自帶選單欄,工具欄和狀態列。
(1)選單欄。依次新增需要的選單。
(2)工具欄。每個小塊為一個動作(action),新建動作後新增到對應選單。
(3)字型大小用spinBox控制元件(QSpinBox類),字型設定用FontComboBox控制元件(QFontComboBox類)。不能直接在視覺化設計裡拖到工具欄,只能通過程式碼的方式新增。
(4)編輯區。用一個plainTextEdit控制元件即可。
(5)狀態列。拖一個Qlabel控制元件進去顯示當前正在編輯的檔案。
2. 新增資原始檔
新建一個資原始檔。
隨便起個名字,新增到當前工程裡面。
在新建的資原始檔裡新增字首,然後就可以把所有資原始檔(圖示)匯入進來。
3. 新增選單(menu)和動作(action)
視覺化設計介面裡逐個新增選單和動作。action的屬性如下,可以設定快捷鍵,如果把checkable勾上,那action按下就不會彈起,像開關一樣(比如加粗,斜體,下劃線這三個按鈕)。
每個aciton預設的風格是隻顯示圖示,可以更改下面的屬性讓文字顯示在下方。
4.新增spinBox控制元件和FontComboBox控制元件
這兩個控制元件是分別用於設定字型大小和字型的,沒法直接拖到工具欄裡,需要在程式碼裡實現。最底下顯示當前檔案的label也同理,用addWidget方法新增到工具欄和狀態列。
void MainWindow::initUI()
{
//設定字型大小控制元件
spinFontSize = new QSpinBox();
spinFontSize->setMinimum(5); //最小值5
spinFontSize->setMaximum(50); //最大值50
ui->mainToolBar->addWidget(new QLabel("字型大小:")); //先新增一個label提示
ui->mainToolBar->addWidget(spinFontSize); //新增到工具欄
//設定字型控制元件
comboFont = new QFontComboBox();
ui->mainToolBar->addWidget(new QLabel("字型:"));
ui->mainToolBar->addWidget(comboFont);
//當前檔案控制元件
currentFile = new QLabel();
currentFile->setText("當前檔案:"); //設定文字
ui->statusBar->addWidget(currentFile); //新增到狀態列
}
5. 實現清空、剪下、複製、貼上功能
這四個功能很簡單,在訊號/槽窗口裡直接編輯。plainTextEdit自帶的槽函式可以直接實現這些功能,把訊號和槽配置好。
6. 實現粗體、斜體、下劃線功能
程式碼實現。在視覺化介面裡右擊動作,轉到slot,編寫槽函式,選擇帶bool引數的。使用QTextCharFormat
設定字型格式,該bool引數會被傳遞到其方法。
實現字型加粗:
void MainWindow::on_actionBold_triggered(bool checked)
{
QTextCharFormat fmt;
if(checked)
fmt.setFontWeight(QFont::Bold);
else
fmt.setFontWeight(QFont::Normal);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
呼叫plainTextEdit的mergeCurrentCharFormat()
方法融合字型設定。
同理,斜體實現:
void MainWindow::on_actionItalic_triggered(bool checked)
{
QTextCharFormat fmt;
fmt.setFontItalic(checked);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
下劃線實現:
void MainWindow::on_actionUnderline_triggered(bool checked)
{
QTextCharFormat fmt;
fmt.setFontUnderline(checked);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
7.實現字型大小和樣式設定
設定字型大小使用spinBox控制元件,字型樣式用fontComboBox控制元件,兩個控制元件都是自己用程式碼實現的,所以要自己實現一下槽函式以及配置好訊號和槽的連線。
把訊號和槽的函式寫到initConnections()裡,和initUI()一樣一起初始化。
void MainWindow::initConnections()
{
//連線字型spinBox
connect(spinFontSize,SIGNAL(valueChanged(int)),this,SLOT(on_spinFontSize_valueChanged(int)));
//連線字型comboBox
connect(comboFont,SIGNAL(currentIndexChanged(const QString &)),this,SLOT(on_comboFont_currentIndexChanged(const QString &)));
}
設定字型大小槽函式,呼叫setFontPointSize()
方法:
void MainWindow::on_spinFontSize_valueChanged(int aFontsize)
{
QTextCharFormat fmt;
fmt.setFontPointSize(aFontsize);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
設定字型樣式槽函式,呼叫setFontFamily()
方法:
void MainWindow::on_comboFont_currentIndexChanged(const QString &text)
{
QTextCharFormat fmt;
fmt.setFontFamily(text);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
8.實現字型顏色設定
plainTextEdit控制元件的palette()
方法可以拿到一個調色盤物件,用調色盤可以獲取和設定顏色,然後再呼叫setPalette()
方法應用到plainTextEdit控制元件,但這樣會把整個文字的顏色改變。
我想只把選中的文字顏色改變,可以參考上面類似的實現,用QTextCharFormat
的setForeground()
方法設定字型顏色,然後合併過去。
槽函式:
void MainWindow::on_actionColor_triggered()
{
QPalette pal = ui->plainTextEdit->palette();
QColor iniColor = pal.color(QPalette::Text);
QColor color = QColorDialog::getColor(iniColor,this,"選擇顏色");
if(!color.isValid())
return;
//pal.setColor(QPalette::Text,color);
QTextCharFormat fmt;
fmt.setForeground(color); //設定字型顏色
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
//ui->plainTextEdit->setPalette(pal);
}
9.實現開啟和儲存檔案
使用QFileDialog
的getOpenFileName()
方法,會彈出檔案選擇對話方塊,讓使用者選擇一個檔案。
這裡設定4個引數:
- 父物件
- 對話方塊標題
- 當前路徑(開啟視窗時顯示的路徑)
- 過濾器
呼叫QDir::currentPath()
方法可以拿到專案當前路徑。
過濾器我只寫文字檔案,如果想再新增其它格式檔案,用雙分號隔開,舉例:QString filter = "文字檔案(*.txt);;其它檔案(*.*)"
獲取到檔名後,在最下方的label上顯示當前處理的檔案。
初始化一個QFile物件,呼叫file.open(QIODevice::ReadWrite)
方法按可讀可寫方式開啟檔案。呼叫readAll()
方法讀取到所有文字填充到plainTextEdit控制元件中。
開啟檔案的槽函式:
void MainWindow::on_actionOpen_triggered()
{
QString curPath = QDir::currentPath();
QString filter = "文字檔案(*.txt)";
QString fileName = QFileDialog::getOpenFileName(this,"開啟文字檔案",curPath,filter);
if(fileName.isEmpty())
return;
QString curFileName = "當前檔案:" + fileName;
currentFile->setText(curFileName);
QFile file(fileName);
if(file.open(QIODevice::ReadWrite)){
ui->plainTextEdit->setPlainText(file.readAll());
file.close();
}
}
儲存檔案類似,用getSaveFileName()
方法,得到檔名後,初始化QFile物件寫檔案。注意下面的寫方法。
儲存檔案的槽函式:
void MainWindow::on_actionSave_triggered()
{
QString curPath = QDir::currentPath();
QString filter = "文字檔案(*.txt)";
QString fileName = QFileDialog::getSaveFileName(this,"另存為",curPath,filter);
if(fileName.isEmpty())
return;
QFile file(fileName);
if(file.open(QIODevice::WriteOnly)){
QString content = ui->plainTextEdit->toPlainText();
QByteArray strBytes = content.toUtf8();
file.write(strBytes,strBytes.length());
}
}
10.總結
很簡單的小專案,只是熟悉QT的入門知識,但是基於上述要點的理解,知道怎麼視覺化佈局,匯入資原始檔,編寫訊號和槽,自定義資料結構,已經能寫一些簡單的小程式了,下面想要做出更酷炫的介面,只要針對控制元件去學習就行。
完整的mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSpinBox>
#include <QFontComboBox>
#include <QLabel>
#include <QColorDialog>
#include <QFileDialog>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_actionBold_triggered(bool checked);
void on_plainTextEdit_copyAvailable(bool b);
void on_spinFontSize_valueChanged(int aFontsize);
void on_comboFont_currentIndexChanged(const QString &text);
void on_actionItalic_triggered(bool checked);
void on_actionUnderline_triggered(bool checked);
void on_actionColor_triggered();
void on_actionOpen_triggered();
void on_actionSave_triggered();
private:
Ui::MainWindow *ui;
QSpinBox *spinFontSize;
QFontComboBox *comboFont;
QLabel *currentFile;
void initUI();
void initConnections();
};
#endif // MAINWINDOW_H
完整的mainwindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("簡易文字編輯器");
initUI();
initConnections();
setCentralWidget(ui->plainTextEdit);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initUI()
{
//設定字型大小控制元件
spinFontSize = new QSpinBox();
spinFontSize->setMinimum(5); //最小值5
spinFontSize->setMaximum(50); //最大值50
ui->mainToolBar->addWidget(new QLabel("字型大小:")); //先新增一個label提示
ui->mainToolBar->addWidget(spinFontSize); //新增到工具欄
//設定字型控制元件
comboFont = new QFontComboBox();
ui->mainToolBar->addWidget(new QLabel("字型:"));
ui->mainToolBar->addWidget(comboFont);
//當前檔案控制元件
currentFile = new QLabel();
currentFile->setText("當前檔案:"); //設定文字
ui->statusBar->addWidget(currentFile); //新增到狀態列
}
void MainWindow::initConnections()
{
//連線字型spinBox
connect(spinFontSize,SIGNAL(valueChanged(int)),this,SLOT(on_spinFontSize_valueChanged(int)));
//連線字型comboBox
connect(comboFont,SIGNAL(currentIndexChanged(const QString &)),this,SLOT(on_comboFont_currentIndexChanged(const QString &)));
}
void MainWindow::on_actionBold_triggered(bool checked)
{
QTextCharFormat fmt;
if(checked)
fmt.setFontWeight(QFont::Bold);
else
fmt.setFontWeight(QFont::Normal);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_plainTextEdit_copyAvailable(bool b)
{
ui->actionCut->setEnabled(b);
ui->actionCopy->setEnabled(b);
ui->actionPaste->setEnabled(ui->plainTextEdit->canPaste());
}
void MainWindow::on_spinFontSize_valueChanged(int aFontsize)
{
QTextCharFormat fmt;
fmt.setFontPointSize(aFontsize);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_comboFont_currentIndexChanged(const QString &text)
{
QTextCharFormat fmt;
fmt.setFontFamily(text);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_actionItalic_triggered(bool checked)
{
QTextCharFormat fmt;
fmt.setFontItalic(checked);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_actionUnderline_triggered(bool checked)
{
QTextCharFormat fmt;
fmt.setFontUnderline(checked);
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
}
void MainWindow::on_actionColor_triggered()
{
QPalette pal = ui->plainTextEdit->palette();
QColor iniColor = pal.color(QPalette::Text);
QColor color = QColorDialog::getColor(iniColor,this,"選擇顏色");
if(!color.isValid())
return;
//pal.setColor(QPalette::Text,color);
QTextCharFormat fmt;
fmt.setForeground(color); //設定字型顏色
ui->plainTextEdit->mergeCurrentCharFormat(fmt);
//ui->plainTextEdit->setPalette(pal);
}
void MainWindow::on_actionOpen_triggered()
{
QString curPath = QDir::currentPath();
QString filter = "文字檔案(*.txt)";
QString fileName = QFileDialog::getOpenFileName(this,"開啟文字檔案",curPath,filter);
if(fileName.isEmpty())
return;
QString curFileName = "當前檔案:" + fileName;
currentFile->setText(curFileName);
QFile file(fileName);
if(file.open(QIODevice::ReadWrite)){
ui->plainTextEdit->setPlainText(file.readAll());
file.close();
}
}
void MainWindow::on_actionSave_triggered()
{
QString curPath = QDir::currentPath();
QString filter = "文字檔案(*.txt)";
QString fileName = QFileDialog::getSaveFileName(this,"另存為",curPath,filter);
if(fileName.isEmpty())
return;
QFile file(fileName);
if(file.open(QIODevice::WriteOnly)){
QString content = ui->plainTextEdit->toPlainText();
QByteArray strBytes = content.toUtf8();
file.write(strBytes,strBytes.length());
}
}