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>
