界面编程之实例学习,系统记事本是个极好的参考,初学Delphi及后之c#,皆以记事本为参考,今以Qt学习,亦是如此。
期间搭建开发环境,复习c++知识,寻找模块对应功能,不一而足;现刻录其模块代码,以做助记,功能接近系统记事本之95%。
学习了Qt事件驱动之信号槽机投、窗体间数据传递方法、文件编码、本地化等功能,然而初接触,仍不能得心应手。
IDE: VS2015+Qt5.8.0
界面如下:
直贴源代码吧!完成源码包,附于文后。
1、入口程序(main.cpp):
#pragma execution_character_set("utf-8") #include <QtWidgets/QApplication>
#include "qtranslator.h"
#include "notepad.h" int main(int argc, char *argv[])
{
QApplication a(argc, argv); //对话框类应用中文
QTranslator user;
bool ok = user.load("qt_zh_CN.qm", ".");
a.installTranslator(&user); NotePad np;
if (argc >= )
{
QString s = QString::fromLocal8Bit(argv[]);
np.loadFromFile(s);
} np.show();
return a.exec();
}
2、主模块(notepad.cpp):
#include <Windows.h>
#include <qfileinfo.h>
#include <qfile.h>
#include <qmimedata.h>
#include <qtextstream.h>
#include <qprinter.h>
#include <qprintdialog.h>
#include <qpagesetupdialog.h>
#include <qfiledialog.h>
#include <qfontdialog.h>
#include <qmessagebox.h>
#include <qtextobject.h>
#include <qdatetime.h>
#include <qsettings.h> #include "notepad.h" NotePad::NotePad(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this); //searchDialog = new SearchDialog(Q_NULLPTR, ui.textEdit);
lblStatus = new QLabel();
lblStatus->setAlignment(Qt::AlignRight);
statusBar()->addPermanentWidget(lblStatus); setActionState();
initTextEdifUI();
loadSettings();
} void NotePad::loadSettings()
{
//大小&位置
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Notepad", QSettings::NativeFormat);
int x = settings.value("iWindowPosX", ).toInt();
int y = settings.value("iWindowPosY", ).toInt();
int w = settings.value("iWindowPosDX", ).toInt();
int h = settings.value("iWindowPosDY", ).toInt();
this->setGeometry(x, y, w, h);
ui.actWordWrap->setChecked(settings.value("fWrap", true).toBool());
ui.actStatus->setChecked(settings.value("StatusBar", true).toBool());
ui.statusBar->setVisible(ui.actStatus->isChecked());
} void NotePad::saveSettings()
{
//大小&位置
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Notepad", QSettings::NativeFormat);
settings.setValue("iWindowPosX", this->x());
settings.setValue("iWindowPosY", this->y());
settings.setValue("iWindowPosDX", this->width());
settings.setValue("iWindowPosDY", this->height());
settings.setValue("fWrap", ui.actWordWrap->isChecked());
settings.setValue("StatusBar", ui.actStatus->isChecked());
} void NotePad::setFileName(QString fileName)
{
this->fileName = fileName;
ui.textEdit->document()->setModified(false); QString shownName;
if (fileName.isEmpty())
shownName = tr("未命名");
else
shownName = QFileInfo(fileName).fileName();
setWindowTitle(tr("%1[*] - %2").arg(shownName, tr("记事本")));
setWindowModified(false); lastDir = QFileInfo(fileName).absoluteDir().absolutePath();
} void NotePad::loadFromFile(QString fileName)
{
QFileInfo fileInfo(fileName);
if (!fileInfo.isFile())
return; QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
{
QMessageBox::warning(this, tr("提示"), tr("不能打开此文件!"), tr("确定"));
return;
} setFileName(fileName);
QTextStream in(&file);
ui.textEdit->setText(in.readAll());
} void NotePad::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("text/uri-list"))
event->acceptProposedAction();
} void NotePad::dropEvent(QDropEvent *event)
{
QList<QUrl> urls = event->mimeData()->urls();
if (urls.isEmpty())
return; QString fileName = urls.first().toLocalFile();
if (!fileName.isEmpty())
loadFromFile(fileName);
} void NotePad::closeEvent(QCloseEvent * event)
{
if (confirmSave())
{
saveSettings();
event->accept();
}
else
event->ignore();
} bool NotePad::confirmSave()
{
if (!ui.textEdit->document()->isModified())
return true; QMessageBox::StandardButtons sb = QMessageBox::question(this, tr("提示"), tr("是否将更改保存到 %1?").arg(this->windowTitle().replace(tr(" - 记事本"), "")),
tr("保存(&S)"), tr("不保存(&N)"), tr("取消")); switch (sb)
{
case :
return saveFile();
case :
return true;
case :
return false;
default:
return true;
}
} bool NotePad::saveFile()
{
if (this->fileName.isEmpty())
return saveFileAs(); return saveFile(this->fileName);
} bool NotePad::saveFile(QString fileName)
{
if (!ui.textEdit->document()->isModified())
return false; QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text))
{
QMessageBox::critical(this, tr("提示"), tr("不能写入文件!"), tr("确定"));
return false;
} QTextStream out(&file);
out << ui.textEdit->toPlainText();
setFileName(fileName);
return true;
} bool NotePad::saveFileAs()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("另存为"),
lastDir + tr("./未命名.txt"), tr("文本文档(*.txt);;所有文件(*.*)"));
if (fileName.isEmpty())
return false; return saveFile(fileName);
} void NotePad::setActionState()
{
ui.actUndo->setEnabled(false);
ui.actCopy->setEnabled(false);
ui.actCut->setEnabled(false);
ui.actDelete->setEnabled(false); ui.actFind->setEnabled(false);
ui.actFindNext->setEnabled(false);
ui.actGoto->setEnabled(false);
} void NotePad::initTextEdifUI()
{
QPalette palette = ui.textEdit->palette();
palette.setColor(QPalette::Highlight, Qt::darkGreen);
palette.setColor(QPalette::HighlightedText, Qt::white);
ui.textEdit->setPalette(palette); ui.textEdit->setAcceptDrops(false);
setAcceptDrops(true);
} //槽函数
void NotePad::slotNew()
{
if (!confirmSave())
return; ui.textEdit->clear();
setFileName("");
} void NotePad::slotOpen()
{
if (!confirmSave())
return; QString fileName = QFileDialog::getOpenFileName(this, tr("另存为"),
lastDir, tr("文本文档(*.txt);;所有文件(*.*)"));
if (!fileName.isEmpty())
loadFromFile(fileName);
} void NotePad::slotSave()
{
saveFile();
} void NotePad::slotSaveAs()
{
saveFileAs();
} void NotePad::slotPageSetup()
{
QPrinter printer;
QPageSetupDialog pageSetUpdlg(&printer, this);
if (pageSetUpdlg.exec() == QDialog::Accepted)
printer.setOrientation(QPrinter::Landscape);
else
printer.setOrientation(QPrinter::Portrait);
} void NotePad::slotPrint()
{
QPrinter printer;
QString printerName = printer.printerName();
if (printerName.size() == )
return; QPrintDialog dlg(&printer, this);
if (ui.textEdit->textCursor().hasSelection())
dlg.addEnabledOption(QAbstractPrintDialog::PrintSelection);
// 如果在对话框中按下了打印按钮,则执行打印操作
if (dlg.exec() == QDialog::Accepted)
ui.textEdit->print(&printer);
} void NotePad::slotExit()
{
this->close();
} void NotePad::slotUndo()
{
ui.textEdit->undo();
} void NotePad::slotCut()
{
ui.textEdit->cut();
} void NotePad::slotCopy()
{
ui.textEdit->copy();
} void NotePad::slotPaste()
{
ui.textEdit->paste();
} void NotePad::slotDelete()
{
ui.textEdit->textCursor().removeSelectedText();
} void NotePad::slotFind()
{
if (replaceDialog != Q_NULLPTR && replaceDialog->isVisible())
{
replaceDialog->activateWindow();
return;
} if (searchDialog == Q_NULLPTR)
searchDialog = new SearchDialog(this, ui.textEdit);
searchDialog->show();
searchDialog->activateWindow();
} void NotePad::slotFindNext()
{
if (searchDialog == Q_NULLPTR)
searchDialog = new SearchDialog(this, ui.textEdit);
searchDialog->search();
} void NotePad::slotReplace()
{
if (searchDialog != Q_NULLPTR && searchDialog->isVisible())
{
searchDialog->activateWindow();
return;
} if (replaceDialog == Q_NULLPTR)
replaceDialog = new ReplaceDialog(this, ui.textEdit);
replaceDialog->show();
replaceDialog->activateWindow();
} void NotePad::slotGoto()
{
//跳转...传this以此做为其窗主,Modal状态标题栏闪烁
GotoDialog gotoDialog(this);
gotoDialog.setLineNumber(ui.textEdit->textCursor().blockNumber() + , ui.textEdit->document()->lineCount());
if (gotoDialog.exec() == QDialog::Accepted)
{
int line = gotoDialog.gotoLine;
QTextCursor cursor = ui.textEdit->textCursor();
int position = ui.textEdit->document()->findBlockByNumber(line - ).position();
cursor.setPosition(position, QTextCursor::MoveAnchor);
ui.textEdit->setTextCursor(cursor);
}
} void NotePad::slotSelectAll()
{
ui.textEdit->selectAll();
} void NotePad::slotDatetime()
{
QString dateTime = QDateTime::currentDateTime().toString(Qt::SystemLocaleDate);
ui.textEdit->textCursor().insertText(dateTime);
} void NotePad::slotWordWrap()
{
if (ui.actWordWrap->isChecked())
ui.textEdit->setWordWrapMode(QTextOption::WordWrap);
else
ui.textEdit->setWordWrapMode(QTextOption::NoWrap); ui.actGoto->setEnabled(!ui.actWordWrap->isChecked());
ui.actStatus->setEnabled(ui.actWordWrap->isChecked());
if (!ui.actWordWrap->isChecked())
{
if (ui.actStatus->isChecked())
{
ui.actStatus->setChecked(false);
ui.statusBar->setVisible(ui.actStatus->isChecked());
}
}
else if (this->statusChecked)
ui.actStatus->trigger();
} void NotePad::slotFont()
{
bool ok;
QFont font = QFontDialog::getFont(&ok, ui.textEdit->font());
if (ok)
ui.textEdit->setFont(font);
} void NotePad::slotStatus()
{
this->statusChecked = ui.actStatus->isChecked();
ui.statusBar->setVisible(ui.actStatus->isChecked());
} void NotePad::slotHelp()
{
QMessageBox::warning(this, tr("提示"), tr("黔驴技穷,搞不定[IHxHelpPane->(\"mshelp://windows/?id=e725b43f-94e4-4410-98e7-cc87ab2739aa\")]"), tr("确定")); //HxHelpPane *helpPane = new HxHelpPane();
//CoInitialize(NULL);
//IHxHelpPane *helpPane = NULL;
//HRESULT hr = ::CoCreateInstance(CLSID_HxHelpPane, NULL, CLSCTX_ALL, IID_IHxHelpPane, reinterpret_cast<void**>(&helpPane));
//if (SUCCEEDED(hr))
// helpPane->DisplayTask(BSTR("mshelp://windows/?id=e725b43f-94e4-4410-98e7-cc87ab2739aa"));
} void NotePad::slotAbout()
{
QString appPath = QApplication::applicationFilePath();
HICON icon = ExtractIcon(NULL, appPath.toStdWString().c_str(), );
ShellAbout((HWND)this->winId(), tr("Qt记事本").toStdWString().c_str(), tr("作者:刘景威").toStdWString().c_str(), icon);
} void NotePad::slotCopyAvailable(bool enabled)
{
ui.actCopy->setEnabled(enabled);
} void NotePad::slotCursorPositionChanged()
{
QTextCursor tc = ui.textEdit->textCursor();
QString info = tr("第%1行,第%2列 ").arg(tc.blockNumber() + ).arg(tc.columnNumber());
lblStatus->setText(info);
} void NotePad::slotRedoAvailable(bool enabled)
{
} void NotePad::slotSelectionChanged()
{
QString selecdedText = ui.textEdit->textCursor().selectedText();
//ui.actUndo
ui.actCopy->setEnabled(!selecdedText.isEmpty());
ui.actCut->setEnabled(!selecdedText.isEmpty());
ui.actDelete->setEnabled(!selecdedText.isEmpty());
} void NotePad::slotTextChanged()
{
slotSelectionChanged(); QString text = ui.textEdit->toPlainText();
ui.actFind->setEnabled(text != "");
ui.actFindNext->setEnabled(text != "");
ui.actGoto->setEnabled(text != "" && !ui.actWordWrap->isChecked());
} void NotePad::slotUndoAvailable(bool enabled)
{
ui.actUndo->setEnabled(enabled);
}
3、跳转模块(goto.cpp)
#include "qmessagebox.h"
#include "goto.h" GotoDialog::GotoDialog(QWidget *parent) :
QDialog(parent)
{
ui.setupUi(this);
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint); ui.label->setBuddy(ui.lineEdit);
QRegExp regx("[0-9]+$");
QValidator *validator = new QRegExpValidator(regx, this);
ui.lineEdit->setValidator(validator);
} GotoDialog::~GotoDialog()
{
} void GotoDialog::setLineNumber(int currLine, int maxLineCount)
{
ui.lineEdit->setText(QString::number(currLine));
ui.lineEdit->selectAll();
this->maxLineCount = maxLineCount;
} void GotoDialog::accept()
{
QString value = ui.lineEdit->text().trimmed();
if (value.isEmpty())
{
this->showMessage(tr("请输入要跳到的行数。"));
return;
}
if (value.toInt() > this->maxLineCount)
{
this->showMessage(tr("行数超过了总行数。"));
return;
} this->gotoLine = value.toInt();
return QDialog::accept();
} void GotoDialog::showMessage(QString title)
{
QMessageBox::warning(this, tr("记事本 - 跳行"), title, tr("确定"));
}
4、查找功能(search.cpp):
#include "qmessagebox.h"
#include "qtextedit.h"
#include "qtextdocument.h"
#include "search.h" SearchDialog::SearchDialog(QWidget *parent, QTextEdit *textEdit) :
QDialog(parent),
textEdit(textEdit)
{
ui.setupUi(this);
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint); ui.label->setBuddy(ui.lineEdit);
ui.searchButton->setEnabled(false);
QObject::connect(ui.searchButton, SIGNAL(clicked()), this, SLOT(search()));
QObject::connect(ui.lineEdit, &QLineEdit::textChanged, [=]()
{
ui.searchButton->setEnabled(ui.lineEdit->text() != "");
});
} SearchDialog::~SearchDialog()
{
} void SearchDialog::activateWindow()
{
QDialog::activateWindow(); ui.lineEdit->setText(textEdit->textCursor().selectedText());
ui.lineEdit->selectAll();
} //此重载方法实现选择数据填充,置入上面函数中
//void SearchDialog::show(QString text)
//{
// QDialog::show();
// if (text != "")
// ui.lineEdit->setText(text);
//} void SearchDialog::search()
{
QString text = ui.lineEdit->text();
if (text.isEmpty())
return; QTextDocument::FindFlags findFlags;
if (ui.cbCaseSensitive->isChecked())
findFlags = QTextDocument::FindCaseSensitively;
if (ui.rbUp->isChecked())
findFlags |= QTextDocument::FindBackward;
bool found = textEdit->find(text, findFlags); if (!found)
QMessageBox::information(this, tr("记事本"), tr("找不到\"%1\"").arg(text), QMessageBox::Ok);
}
5、替换功能(replace.cpp):
#include "qmessagebox.h"
#include "qtextedit.h"
#include "qtextdocument.h"
#include "replace.h" ReplaceDialog::ReplaceDialog(QWidget *parent, QTextEdit *textEdit) :
QDialog(parent),
textEdit(textEdit)
{
ui.setupUi(this);
setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint | Qt::MSWindowsFixedSizeDialogHint); initDialog();
} ReplaceDialog::~ReplaceDialog()
{
} void ReplaceDialog::initDialog()
{
ui.label->setBuddy(ui.lineEdit);
ui.lblReplace->setBuddy(ui.replaceEdit);
ui.searchButton->setEnabled(false);
ui.replaceButton->setEnabled(false);
ui.replaceAllButton->setEnabled(false);
QObject::connect(ui.searchButton, SIGNAL(clicked()), this, SLOT(search()));
QObject::connect(ui.replaceButton, SIGNAL(clicked()), this, SLOT(replace()));
QObject::connect(ui.replaceAllButton, SIGNAL(clicked()), this, SLOT(replaceAll()));
QObject::connect(ui.lineEdit, &QLineEdit::textChanged, [=]()
{
ui.searchButton->setEnabled(ui.lineEdit->text() != "");
ui.replaceButton->setEnabled(ui.lineEdit->text() != "");
ui.replaceAllButton->setEnabled(ui.lineEdit->text() != "");
});
} void ReplaceDialog::activateWindow()
{
QDialog::activateWindow(); ui.lineEdit->setText(textEdit->textCursor().selectedText());
ui.lineEdit->selectAll();
} bool ReplaceDialog::search(bool showWarn)
{
QString text = ui.lineEdit->text();
if (text.isEmpty())
return false; QTextDocument::FindFlags findFlags;
if (ui.cbCaseSensitive->isChecked())
findFlags = QTextDocument::FindCaseSensitively;
bool found = textEdit->find(text, findFlags); if (!found && showWarn)
QMessageBox::information(this, tr("记事本"), tr("找不到\"%1\"").arg(text), QMessageBox::Ok);
return found;
} void ReplaceDialog::replace()
{
bool found = search();
if (!found)
return; QTextCursor textCursor = textEdit->textCursor();
QString replaceText = ui.replaceEdit->text();
textCursor.insertText(replaceText); //寻找下一个
search();
} void ReplaceDialog::replaceAll()
{
QString text = ui.lineEdit->text();
if (text.isEmpty())
return; QTextDocument::FindFlags findFlags;
if (ui.cbCaseSensitive->isChecked())
findFlags = QTextDocument::FindCaseSensitively;
QString replaceText = ui.replaceEdit->text();
while (textEdit->find(text, findFlags))
{
QTextCursor textCursor = textEdit->textCursor();
textCursor.insertText(replaceText);
}
//往回找
findFlags |= QTextDocument::FindBackward;
while (textEdit->find(text, findFlags))
{
QTextCursor textCursor = textEdit->textCursor();
textCursor.insertText(replaceText);
}
}
源码下载: