QT SIMPLEPAD 2.1
Не буду углубляться в процесс пояснения как пользоваться программой QT Creator, а просто приведу здесь текст исходных файлов и поясню как это работает. Честно говоря, я сам с удовльствием пользуюсь этим приложением, поскольку лично для меня оно имеет довольно интересную функцию, а именно автоматическое сохранение текста при любом изменении файла, что позволяет открыть его в том виде, каким он был во время работы даже в том случае, если у вас внезапоно отрубили свет. Примечательно, что исходный файл при этом не переписывается, то есть... Другими словами, во время редактирования исходный сайт остается неизменным, но программа помнит внесенные изменения и их можно сохранить в исходный файл в любой момент. Правда, если после открытия программы вы все же решитесь открыть другой файл, то все ваши изменения будут забыты. В остальном почти все то же, что и везде: "открыть, сохранить, сохранить как, закрыть, отменить, повторить, выровнять текст", есть справка о программе и об авторе, а так же отдельное поле для записи баг-репортов.
Обнаруженные на данный момент проблемы:
1. При отмене действия сочетанием клавиш Сtrl+z статус-бар не получает сообщения об отмене действия. В то же время, соответствующий триггер в меню "файл" это сообщение выводит. Аналогичная функция повтора действия сочетанием клавиш Ctrl+R отлично работает. Возможно, проблема заключается в сочетании клавиш - во втором случае сочетание нестандартное. Возможно, стандартные сочетания перехватываются другим обработчиком.
2. Замечена какая-то фигня с кодировкой русских символов при сохранении файла. Вместо исходного файла программа создает другой файл, имя которого состоит из квадратиков. Примечательно, что это наблюдается в Windows 10, в Windows 7, кажется, ничего подобного не было. Так или иначе, проблема устранена, подробности на https://ru.stackoverflow.com/.
К сожалению, я пока не разобрался с диплоем QT программ под Linux, поэтому для корректной работы релизной версии программы необходимо установить QT Creator с комплектом Desktop QT 5.15.1 GCC 64Bit. Буду рад, если кто-нибудь поможет разобраться с диплоем Linux, если вы разбираетесь в этом, можно связаться где-нибудь в Discorde и организовать небольшую учебную сессию через AnyDesk, я за это заплачу.
SimplePad.pro
QT += widgets QT += printsupport QT += core gui TRANSLATIONS = SimplePad_ru_RU.ts_ru.ts greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 LIBS += -L"./lib" # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ about.cpp \ main.cpp \ mainwindow.cpp \ service.cpp HEADERS += \ about.h \ global.h \ mainwindow.h \ service.h FORMS += \ about.ui \ mainwindow.ui \ service.ui TRANSLATIONS += \ SimplePad_ru_RU.ts # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target RESOURCES += res.qrc RC_FILE = ico.rc //единственный рабочий способ задать иконку программы в Windows. Требует создания соответствующего файла со следующим содержимым:
//IDI_ICON1 ICON DISCARDABLE "SimplePad.ico"
about.h
#ifndef ABOUT_H #define ABOUT_H #include "QDialog" namespace Ui { class About; } class About : public QDialog { Q_OBJECT public: explicit About(QWidget *parent = nullptr); ~About(); private slots: void on_pushButton_clicked(); private: Ui::About *ui; }; #endif // ABOUT_H
global.h
#ifndef GLOBAL_H #define GLOBAL_H #endif // GLOBAL_H #include "QString" QString FN = "Данная кнопка выводит на экран информацию, записанную в глобальной переменной FN, отвечающей за хранение пути и имени читаемого файла. Если вы видите данное сообщение, значит файл еще не открыт."; //Это сообщение выводится для скрытой кнопки FN, которую обычно не видно и которая, может быть, даже не нужна. QString CR=0; QString TP = "temp";
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include "QMainWindow" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_act1Open_triggered(); void on_act3Saveas_triggered(); void on_act4Close_triggered(); void on_About1_triggered(); void on_About2_triggered(); void on_Btn1_clicked(); void on_Btn2_clicked(); void on_Btn3_clicked(); void on_Btn4_clicked(); void on_act2Save_triggered(); void on_act5Quit_triggered(); void on_textEdit_textChanged(); //Вот эти новым методом обеспечивают функции "Отмена", "повторение" void undo(); void redo(); // void on_Justify_triggered(); void on_Left_triggered(); void on_Right_triggered(); void on_Center_triggered(); void on_BR_triggered(); void on_Undo_triggered(); void on_Redo_triggered(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
service.h
#ifndef SERVICE_H #define SERVICE_H #include "QDialog" namespace Ui { class service; } class service : public QDialog { Q_OBJECT public: explicit service(QWidget *parent = nullptr); ~service(); private slots: void on_tE2_textChanged(); private: Ui::service *ui; }; #endif // SERVICE_H
about.cpp
#include "about.h" #include "ui_about.h" #include "QPixmap" #include "QLabel" About::About(QWidget *parent) : QDialog(parent), ui(new Ui::About) { ui->setupUi(this); QPixmap image("madmentat.jpg"); } About::~About() { delete ui; } void About::on_pushButton_clicked() { close(); }
main.cpp
#include "mainwindow.h" #include "QApplication" int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow w; // Объявляем новое окошко w.setWindowTitle(("SimplePad v 2.1")); // Задаем имя окошка. Такое, в полосочке наверху. w.show(); // Выводим новое окошко на экран. return app.exec(); }
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "QFile" #include "QTextStream" #include "QMessageBox" #include "QTextEdit" #include "QPushButton" #include "QFileDialog" #include "QDialog" #include "QDebug" #include "QString" #include "QPrinter" #include "QPrintDialog" #include "QTextCodec" #include "about.h" #include "global.h" #include "service.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->setCentralWidget(ui->textEdit); //вот эта строчка растягивает textEdit на все поле программы. //Далее билеберда которая обеспечивает функции "Отменить, повторить" connect(ui->Undo, &QAction::triggered, this, &MainWindow::undo); connect(ui->Redo, &QAction::triggered, this, &MainWindow::redo); //Следующая конструкция читает из temp текст, который был открыт в предыдущий раз когда открывалась программа и выводит его в текстовое поле. QFile tempFile("temp"); if (!tempFile.open(QIODevice::ReadOnly | QIODevice::Text)) qDebug() << "Ошибка при открытии файла"; QTextStream stream(&tempFile); ui->textEdit->setText(stream.readAll()); //Вывод в текстовое поле содержимое читаемого файла //ui->textEdit->setText(fileName); //Вывод в текстовое поле пути файла и его названия //Следующая констукция считывает путь и имя файла из fn QFile name("fn"); //значит, о том, что тут происходит, по-порядку. Тут мы вызываем метод QFile по имени name, который читает содежимое файла "fn" if (!name.open(QIODevice::ReadOnly | QIODevice::Text)) return; QString str; //объявляем текстовую переменную для хранения пути и имени файла, которые мы собираемся получить в следующей строке //Следующий кусок кода необходим для того, чтобы на Винде не вылезали крокозябры, так как по умолчанию Виндоз использует кодировку CP1251 #ifdef Q_OS_WIN64 //задаем условия компиляции для Win64 QTextCodec *codec = QTextCodec::codecForName("CP1251"); assert(codec); str = codec->toUnicode(name.readAll()); #endif #ifdef Q_OS_LINUX //задаем условия компиляции для Linux str = name.readAll(); #endif FN=str; // Глобальная переменная FN теперь содержит тоже, что и переменная str statusBar()->showMessage("Открыт файл " + FN); //Отладочное окошко где показано содержание FN //QTextEdit *editor = new QTextEdit; //editor->setText(FN); //editor->show(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_act1Open_triggered() // Открываем файл { //далее новый вариант открытия файла где можно выбрать тип файла QString fileName = QFileDialog::getOpenFileName(this, QString("Открыть файл"), QDir::currentPath(), "Текстовые файлы (*.txt);;Все файлы (*.*)"); FN=fileName; //Запись имени файла в глобальную переменную FN QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; qDebug() << "Ошибка при открытии файла"; QTextStream in(&file); ui->textEdit->setText(in.readAll()); //Вывод в текстовое поле содержимое читаемого файла //ui->textEdit->setText(fileName); //Вывод в текстовое поле пути файла и его названия //ui->textEdit->setText(FN); //Вывод в текстовое поле содержимое глобальной переменной FN //Следующая конструкция пишет путь и имя файла в файл fn QFile fn("fn"); if (!fn.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream writeStream(&fn); writeStream << FN; statusBar()->showMessage("Открыт файл "+FN); } void MainWindow::on_act3Saveas_triggered() { QString fileName = QFileDialog::getSaveFileName(this, QString("Открыть файл"), QDir::currentPath(), "Текстовые файлы (*.txt);;Все файлы (*.*)"); QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream out(&file); //поток для записи текста out <textEdit->toPlainText(); statusBar()->showMessage("Файл сохранен "+FN); // Эта функция выводит соответствующее сообщение в статусбар. Цифра в конце - время отображения. } void MainWindow::on_Btn1_clicked() { QTextEdit textEdit; // Объявляем наше текстовое поле ui->textEdit->clear(); // Очищаем его } void MainWindow::on_Btn2_clicked() { qApp->closeAllWindows(); // вот эта строчка, по-видимому, закрывает все окна приложения qApp->quit(); // а эта закрывает само приложение } void MainWindow::on_About1_triggered() { QMessageBox::information(NULL,QObject::tr("О программе"),tr("SimplePad 2.1 - Данное приложение создано для быстрой записи текстовой информации. Тут имеются функции Undo и Redo, позволяющие отменять и повторять ваши действия. Программа может открывать файлы, имеет два способа сохранения, закрывает файлы и выводит соответствующие сообщения в статус-бар. Кроме того, она помнит изменения в файле, который вы редактировали в предыдущий раз, но не сохраняет их, пока вы не сделаете это определенным сочетанием клавиш или кликом по соответствующему пункту меню."));
} void MainWindow::on_About2_triggered() { About *frm = new About(); frm->show(); } void MainWindow::on_act2Save_triggered() { QFile file(FN); //Запись в файл, имя которого взято из переменной FN if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream out(&file);//поток для записи текста out <textEdit->toPlainText(); statusBar()->showMessage("Файл сохранен в "+FN); } void MainWindow::on_Btn3_clicked() { ui->textEdit->setText(FN); } void MainWindow::on_act4Close_triggered() { //Здесь наша задача заставить программу при закрытии файла изменить путь сохранения на файл временного хранения temp //чтобы все дальнейшие изменения в текстовом поле сохранились после закрытия и повторного открытия программы //FN=TP; //Глобальная переменная FN получает значение глобальной переменной TP //забал зачем... Или же... Вот, в чем дело. Действительно, хотел сделать чтобы QTextEdit textEdit; // Объявляем наше текстовое поле ui->textEdit->clear(); // Очищаем его //Далее записываем в файл fn путь и имя открытого файла QFile fn("fn"); if (!fn.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream writeStream(&fn); writeStream << FN; statusBar()->showMessage("Файл закрыт. Во избежание потери данных, все дальнейшие изменения следует сохранить при помощи команды Alt+S"); } void MainWindow::on_act5Quit_triggered() { QApplication::exit(); } void MainWindow::on_textEdit_textChanged() //при изменении текста происходит автоматическая запись в файл temp { QFile file("temp"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; QTextStream out(&file);//поток для записи текста out <textEdit->toPlainText(); statusBar()->showMessage("Сохранено в "+FN, 10000); } void MainWindow::on_Btn4_clicked() //кнопка показывает содержимое файла temp { QFile file("temp"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; qDebug() << "Ошибка при открытии файла"; QTextStream in(&file); ui->textEdit->setText(in.readAll()); } void MainWindow::on_BR_triggered() { service *frm = new service(); frm->show(); } //Функции "отменить", "повторить" void MainWindow::undo() { ui->textEdit->undo(); statusBar()->showMessage("Действие отменено "+FN); } void MainWindow::redo() { ui->textEdit->redo(); statusBar()->showMessage("Действие возвращено "+FN); } void MainWindow::on_Justify_triggered() //выровнять по горизонтали { QTextCursor cursor = ui->textEdit->textCursor(); QTextBlockFormat textBlockFormat = cursor.blockFormat(); textBlockFormat.setAlignment(Qt::AlignJustify); cursor.mergeBlockFormat(textBlockFormat); ui->textEdit->setTextCursor(cursor); } void MainWindow::on_Left_triggered() //выровнять по левому краю { QTextCursor cursor = ui->textEdit->textCursor(); QTextBlockFormat textBlockFormat = cursor.blockFormat(); textBlockFormat.setAlignment(Qt::AlignLeft); cursor.mergeBlockFormat(textBlockFormat); ui->textEdit->setTextCursor(cursor); } void MainWindow::on_Right_triggered() //выровнять по правому краю { QTextCursor cursor = ui->textEdit->textCursor(); QTextBlockFormat textBlockFormat = cursor.blockFormat(); textBlockFormat.setAlignment(Qt::AlignRight); cursor.mergeBlockFormat(textBlockFormat); ui->textEdit->setTextCursor(cursor); } void MainWindow::on_Center_triggered() { QTextCursor cursor = ui->textEdit->textCursor(); QTextBlockFormat textBlockFormat = cursor.blockFormat(); textBlockFormat.setAlignment(Qt::AlignCenter); cursor.mergeBlockFormat(textBlockFormat); ui->textEdit->setTextCursor(cursor); } void MainWindow::on_Undo_triggered() { ui->textEdit->undo(); statusBar()->showMessage("Действие отменено "+FN); } void MainWindow::on_Redo_triggered() { ui->textEdit->redo(); statusBar()->showMessage("Действие возвращено "+FN); } void MainWindow::on_Print_triggered() { QTextEdit textEdit; #ifndef QT_NO_PRINTER //интересная команда #ifndef - как бы инверсированная версия #ifdef - в данном случае вроде как "если принтер не неопределен, то"... QPrinter printer(QPrinter::HighResolution); QPrintDialog *dlg = new QPrintDialog(&printer, this); if (ui->textEdit->textCursor().hasSelection()) dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection); dlg->setWindowTitle(tr("Print Document")); if (dlg->exec() == QDialog::Accepted) { ui->textEdit->print(&printer); } delete dlg; #endif }
res.qrc
<RCC>
<qresource prefix="/text">
<file>temp</file>
<file>fn</file>
</qresource>
<qresource prefix="/images">
<file>simplePad.png</file>
<file>madmentat.jpg</file>
</qresource>
</RCC>