QT SIMPLEPAD 2.1

SimplePad2.1

Готовая программа под Windows

Исходники

Не буду углубляться в процесс пояснения как пользоваться программой 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>