界面编程之实例学习,系统记事本是个极好的参考,初学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 >= 2) { QString s = QString::fromLocal8Bit(argv[1]); 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", 0).toInt(); int y = settings.value("iWindowPosY", 0).toInt(); int w = settings.value("iWindowPosDX", 800).toInt(); int h = settings.value("iWindowPosDY", 600).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 0: return saveFile(); case 1: return true; case 2: 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() == 0) 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() + 1, 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 - 1).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(), 0); 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() + 1).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); } }
源码下载: