1. 程式人生 > 其它 >用QT/C++寫一個簡易文字編輯器

用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控制元件,但這樣會把整個文字的顏色改變。
我想只把選中的文字顏色改變,可以參考上面類似的實現,用QTextCharFormatsetForeground()方法設定字型顏色,然後合併過去。

槽函式:

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.實現開啟和儲存檔案

使用QFileDialoggetOpenFileName()方法,會彈出檔案選擇對話方塊,讓使用者選擇一個檔案。
這裡設定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());
    }
}