文本编辑器中查找对话框及功能实现

文章目录

    • 1 查找对话框界面实现
    • 2 查找对话框的功能实现

1 查找对话框界面实现

查找对话框需求分析:

  • 可复用软件部分。
  • 查找文本框中的指定字符串。
  • 能够指定查找方向。
  • 支持大小写敏感查找。

附加需求:

  • 点击关闭按钮后隐藏。
    文本编辑器中查找对话框及功能实现_第1张图片
    查找对话框的架构与设计:
    文本编辑器中查找对话框及功能实现_第2张图片
    查找对话框的界面与布局:
    文本编辑器中查找对话框及功能实现_第3张图片
    FindDialog.h:
#ifndef _FINDDIALOG_H_
#define _FINDDIALOG_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class FindDialog : public QDialog
{
    Q_OBJECT

protected:
	/*
	* 必须注意,这里的初始化顺序很重要,m_radioGrpBx必须在m_hbLayout之前声明,如果在其后m_radioGrpBx析构时会将m_hbLayout进行析构,但是m_hbLayout是栈对象,被delete就会出问题,一定要注意!!!
	*/
    QGroupBox m_radioGrpBx;

    QGridLayout m_layout;
    QHBoxLayout m_hbLayout;

    QLabel m_findLbl;
    QLineEdit m_findEdit;
    QPushButton m_findBtn;
    QPushButton m_closeBtn;
    QCheckBox m_matchChkBx;
    QRadioButton m_forwardBtn;
    QRadioButton m_backwardBtn;

public:
    explicit FindDialog(QWidget* parent = 0);
    bool event(QEvent* evt);
    
signals:
    
public slots:
    
};

#endif // _FINDDIALOG_H_

FindDialog.cpp:

#include "FindDialog.h"
#include 

FindDialog::FindDialog(QWidget *parent) : QDialog(parent, Qt::WindowCloseButtonHint | Qt::Drawer)
{
    m_findLbl.setText("Find What:");
    m_findBtn.setText("Find Next");
    m_closeBtn.setText("Close");
    m_matchChkBx.setText("Match Case");
    m_backwardBtn.setText("Backward");
    m_forwardBtn.setText("Forward");
    m_forwardBtn.setChecked(true);
    m_radioGrpBx.setTitle("Direction");

    m_hbLayout.addWidget(&m_forwardBtn);
    m_hbLayout.addWidget(&m_backwardBtn);

    m_radioGrpBx.setLayout(&m_hbLayout);

    m_layout.setSpacing(10);
    m_layout.addWidget(&m_findLbl, 0, 0);
    m_layout.addWidget(&m_findEdit, 0, 1);
    m_layout.addWidget(&m_findBtn, 0, 2);
    m_layout.addWidget(&m_matchChkBx, 1, 0);
    m_layout.addWidget(&m_radioGrpBx, 1, 1);
    m_layout.addWidget(&m_closeBtn, 1, 2);

    setLayout(&m_layout);
    setWindowTitle("Find");
}

bool FindDialog::event(QEvent* evt)
{
    if( evt->type() == QEvent::Close )
    {
        hide();

        return true;
    }

    return QDialog::event(evt);
}

MainWindow中需要增加成员:
文本编辑器中查找对话框及功能实现_第4张图片
文本编辑器中查找对话框及功能实现_第5张图片
槽函数的绑定与实现:
文本编辑器中查找对话框及功能实现_第6张图片
文本编辑器中查找对话框及功能实现_第7张图片


2 查找对话框的功能实现

文本查找功能的核心思想:

  1. 获取当前光标的位置并作为起始点。
  2. 向后(向前)查找目标第一次出现的位置。
  3. 通过目标位置以及目标长度在文本框中进行标记。

QString类中提供了子串查找的相关函数:

  • indexof:从指定位置向后查找目标子串的下标位置。
  • lastIndexof:从指定位置向前查找目标子串的下标位置。

QS同日那个类中查找函数所使用的下标位置:
文本编辑器中查找对话框及功能实现_第8张图片
Qt中的光标信息类QTextCursor:

  • 文本框中的光标是一个QTextCursor对象。
  • 所有与光标相关的信息都通过QTextCursor描述:
    • 如:光标位置、文本选择等等。
      文本编辑器中查找对话框及功能实现_第9张图片
      查找算法流程图:
      文本编辑器中查找对话框及功能实现_第10张图片
      MainWindow与FindDialog之间的关系图:
      文本编辑器中查找对话框及功能实现_第11张图片
      可以看出是一种弱耦合的关系,采用聚合关系比较好,也就是使用指针。

最终版本的实现如下:
FindDialog.h:

#ifndef _FINDDIALOG_H_
#define _FINDDIALOG_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class FindDialog : public QDialog
{
    Q_OBJECT

protected:
    QGroupBox m_radioGrpBx;

    QGridLayout m_layout;
    QHBoxLayout m_hbLayout;

    QLabel m_findLbl;
    QLineEdit m_findEdit;
    QPushButton m_findBtn;
    QPushButton m_closeBtn;
    QCheckBox m_matchChkBx;
    QRadioButton m_forwardBtn;
    QRadioButton m_backwardBtn;

    QPointer<QPlainTextEdit> m_pText;

    void initControl();
    void connectSlot();
protected slots:
    void onFindClicked();
    void onCloseClicked();
public:
    explicit FindDialog(QWidget* parent = 0, QPlainTextEdit* pText = 0);
    void setPlainTextEdit(QPlainTextEdit* pText);
    QPlainTextEdit* getPlainTextEdit();
    bool event(QEvent* evt);  
};

#endif // _FINDDIALOG_H_

FindDialog.cpp:

#include "FindDialog.h"
#include 
#include 
#include 

FindDialog::FindDialog(QWidget *parent, QPlainTextEdit* pText)
    : QDialog(parent, Qt::WindowCloseButtonHint | Qt::Drawer)
{
    initControl();
    connectSlot();
    setLayout(&m_layout);
    setWindowTitle("Find");
    setPlainTextEdit(pText);
}

void FindDialog::initControl()
{
    m_findLbl.setText("Find What:");
    m_findBtn.setText("Find Next");
    m_closeBtn.setText("Close");
    m_matchChkBx.setText("Match Case");
    m_backwardBtn.setText("Backward");
    m_forwardBtn.setText("Forward");
    m_forwardBtn.setChecked(true);
    m_radioGrpBx.setTitle("Direction");

    m_hbLayout.addWidget(&m_forwardBtn);
    m_hbLayout.addWidget(&m_backwardBtn);

    m_radioGrpBx.setLayout(&m_hbLayout);

    m_layout.setSpacing(10);
    m_layout.addWidget(&m_findLbl, 0, 0);
    m_layout.addWidget(&m_findEdit, 0, 1);
    m_layout.addWidget(&m_findBtn, 0, 2);
    m_layout.addWidget(&m_matchChkBx, 1, 0);
    m_layout.addWidget(&m_radioGrpBx, 1, 1);
    m_layout.addWidget(&m_closeBtn, 1, 2);

}

void FindDialog::connectSlot()
{
    connect(&m_findBtn, SIGNAL(clicked()), this, SLOT(onFindClicked()));
    connect(&m_closeBtn, SIGNAL(clicked()), this, SLOT(onCloseClicked()));
}

void FindDialog::setPlainTextEdit(QPlainTextEdit* pText)
{
    m_pText = pText;
}

QPlainTextEdit* FindDialog::getPlainTextEdit()
{
    return m_pText;
}

bool FindDialog::event(QEvent* evt)
{
    if( evt->type() == QEvent::Close )
    {
        hide();

        return true;
    }

    return QDialog::event(evt);
}

void FindDialog::onFindClicked()
{
    QString target = m_findEdit.text();

    if( (m_pText != NULL) && (target != "") )
    {
        QString text = m_pText->toPlainText();
        QTextCursor c = m_pText->textCursor();
        int index = -1;

        if( m_forwardBtn.isChecked() )
        {
            index = text.indexOf(target, c.position(), m_matchChkBx.isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);

            if( index >= 0 )
            {
                c.setPosition(index);
                c.setPosition(index + target.length(), QTextCursor::KeepAnchor);

                m_pText->setTextCursor(c);
            }
        }

        if( m_backwardBtn.isChecked() )
        {
            index = text.lastIndexOf(target, c.position() - text.length() - 1, m_matchChkBx.isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive);

            if( index >= 0 )
            {
                c.setPosition(index + target.length());
                c.setPosition(index, QTextCursor::KeepAnchor);

                m_pText->setTextCursor(c);
            }
        }

        if( index < 0 )
        {
            QMessageBox msg(this);

            msg.setWindowTitle("Find");
            msg.setText("Can not find \"" + target + "\" any more...");
            msg.setIcon(QMessageBox::Information);
            msg.setStandardButtons(QMessageBox::Ok);

            msg.exec();
        }
    }
}

void FindDialog::onCloseClicked()
{
    close();
}

MainWindow的构造函数需要修改:
文本编辑器中查找对话框及功能实现_第12张图片
为了解决文本框得到焦点和失去焦点被选择文本的样式保持一致,我们需要使用调色板对mainEditor进行设置,设置如下:

bool MainWindow::initMainEditor()
{
    bool ret = true;
    QPalette p = mainEditor.palette();

    p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight));
    p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText));

    mainEditor.setPalette(p);
    mainEditor.setParent(this);

    connect(&mainEditor, SIGNAL(textChanged()), this, SLOT(onTextChanged()));
    connect(&mainEditor, SIGNAL(copyAvailable(bool)), this, SLOT(onCopyAvailable(bool)));
    connect(&mainEditor, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool)));
    connect(&mainEditor, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool)));
    connect(&mainEditor, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged()));

    setCentralWidget(&mainEditor);

    return ret;
}

参考资料:

  1. QT实验分析教程

你可能感兴趣的:(所学所思所想)