Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing a Basic Notepad Application in Qt: UI Layout, Signals and Slots, and File Operations

Tech 1

Implementing a Basic Notepad Application in Qt: UI Layout, Signals and Slots, and File Operations

UI Interface Configuration

UI design and styling requires familiarity with Qt's styling system. Practice by creating a calculator interface, which can be further enhanced with additional functionality.

Style Sheet Configuration

font-size: 40px;            /* Font size */
font: 10pt "Microsoft YaHei"; /* Font size and style */
border: 0px solid white;     /* Border width and color */
border-radius: 0px;          /* Border radius */
/* Text color settings */
color: red;                  
color: #FF00FE;             
color: rgba(255, 0, 255, 255); /* 'a' represents transparency */
/* Background color settings */
background-color: blue
background-color: #00FFEE
background-color: rgba(255, 255, 0, 255); 

Button Three-State Styling

/* Normal button state */
QPushButton {
    background-color: rgba(220, 250, 220, 255);
    border: 0px solid white;
    border-radius: 0px;
    font-size: 60px;
}
/* Hover button state */
QPushButton:hover { 
    background-color: rgba(255, 255, 0, 255);
    border: 0px solid white;
    border-radius: 0px;
    color: #FF00FE;
    font-size: 60px;
}
/* Pressed button state */
QPushButton:pressed {
    background-color: rgba(255, 255, 0, 255);
    border: 0px solid white;
    border-radius: 0px;
    color: #FF00FE;
    font-size: 60px;
} 

Using Images as Button Backgrounds

Resource files are essential for managing application assets like icons, translations, and images. Placing these resources in a dedicated file ensures they remain accessible and prevents loss.

To add image resources:

  1. Open the style sheet editor
  2. Add a resource
  3. Use border-image property
  4. Apply to button three-state styles

Button Response - Signals and Slots

Basic Concepts of Signals and Slots

In Qt, signals and slots form a powerful event communication mechanism. Understanding them is crucial for developing Qt applications.

  • Signals: Messages emitted by objects when specific events occur. For example, QPushButton has a clicked() signal emitted when the button is clicked.
  • Slots: Methods that respond to signals. A slot can be any function that gets called when its associated signal is emitted.
  • Connecting signals and slots: Using QObject::connect() method links signals to slots. When the signal is emitted, the connected slot function is automatically executed.

Setting Up QPushButton Signals and Slots

Connection Method Description Example
Auto-connection (using UI file) In Qt Designer, signals and slots can be automatically connected through naming conventions. Slots named on_objectName_signalName are automatically connected to corresponding signals when the UI file is loaded. In Qt Designer, name a button as pushButton, then define on_pushButton_clicked() in code.
Using QObject::connect The most common approach, directly connecting signals to slots using the QObject::connect function. QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
Using C++11 Lambda expressions Leverages C++11 lambda expressions for connecting signals and slots, allowing anonymous functions at the connection point for more concise code. QObject::connect(sender, &Sender::signal, [=] { /* lambda body */ });
Using function pointers Introduced in Qt5, allows type-safe signal-slot connections using function pointers, with IDE autocompletion and error detection support. QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot);

Auto-connection

In the UI file, right-click a button and select Go to Slot, then choose the triggering signal to automatically connect to the slot function.

Using QObject::connect

// Header file slot function declaration
private slots:
    void actionButton5_clicked();
    
// Constructor signal connection
QObject::connect(ui->actionButton5, SIGNAL(clicked()), this, SLOT(actionButton5_clicked()));

// Slot function implementation
void MainWindow::actionButton5_clicked()
{
    qDebug() << "Button 5 clicked()" << endl;
}

Using Lambda Expressions

// Lambda expression approach
QObject::connect(ui->actionButton6, &QPushButton::clicked, [=]() {
    qDebug() << "Button 6 clicked()" << endl;
});

Using Function Pointers

// Header file slot function declaration
private slots:
    void on_actionButton7_clicked();
    
// Constructor signal connection
QObject::connect(ui->actionButton7, &QPushButton::clicked, this, &MainWindow::on_actionButton7_clicked);

// Slot function implementation
void MainWindow::on_actionButton7_clicked()
{
    qDebug() << "Button 7 clicked()" << endl;
}

Custom Signals and Slots

Custom signals and slots enable communication between objects in Qt. They are a core feature of Qt's object communication system, allowing objects to notify others when specific events occur.

Defining Signals

Signals are declared using the signals keyword in a class. They don't require implementation, only declaration.

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    
signals:
    void customSignal();            // Custom signal
    void customSignalWithValue(int value); // Custom signal with parameter
};

Defining Slots

Slot functions can be any regular member functions, typically marked with the slots keyword. They can have return types and accept parameters, but their parameter types must match the signals they're connected to.

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void customSlot();                  // Custom slot
    void customSlotWithValue(int value); // Custom slot with parameter

private:
    Ui::MainWindow *ui;
};

Connecting Signals and Slots

Use QObject::connect to link signals to slots. This is typically done in the constructor.

// Connect signals to slots
connect(this, SIGNAL(customSignal()), this, SLOT(customSlot()));
connect(this, SIGNAL(customSignalWithValue(int)), this, SLOT(customSlotWithValue(int)));

Emitting Signals

Use the emit keyword to trigger signals. When emitted, all connected slots are called.

emit customSignal();    // Emit signal
emit customSignalWithValue(123);    // Emit signal with parameter

Practical Application - Page Navigation

For navigating from page A to page B, clicking a button on page A hides page A and shows page B. To return from page B to page A, use custom signals and slots.

When a button on page B is clicked, it emits a signal that page A monitors. When page A detects this signal, it hides page B and shows page A.

Switching to New Window

// Page switching
// Create new window
SecondaryWindow *secondaryWindow = new SecondaryWindow();
connect(ui->navigateButton, &QPushButton::clicked, this, [=]() {
    secondaryWindow->show();
    this->hide();
});

Returning to Original Window

secondarywindow.h:

signals:
    void returnButtonClicked();         // Custom signal for return button

secondarywindow.cpp:

// Connect return button to emit signal
connect(ui->returnButton, &QPushButton::clicked, [=]() {
    // Emit return signal
    emit returnButtonClicked();
});

firstwindow.cpp:

// Receive signal from secondary window
connect(secondaryWindow, &SecondaryWindow::returnButtonClicked, [=]() {
    secondaryWindow->hide();
    this->show();
});

File Operations with QFile

Main Features

  • File Reading: QFile supports opening files for reading or writing operations
  • File Information: Can retrieve file information such as size, modification date, etc.
  • File Operations: Provides capabilities for renaming, moving, deleting files, etc.
  • Error Handling: QFile includes error handling mechanisms for file operations

Direct File Reading and Writing

Common Method Functionality
open(flags) Opens a file, specifying mode (read-only, write-only, read-write, etc.)
close() Closes the file
read(char *data, qint64 maxlen) and write(char *data, qint64 maxlen) Used for reading and writing data
exists() Checks if the file exists
remove() Deletes the file
copy() Copies the file
size() File size in characters

File Reading

void MainWindow::on_openButton_clicked()
{
    // 1. Open file
    // Use absolute path
    QFile file("C:/projects/notepad/data.txt");
    // Open file in read-only text mode
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))    
        qDebug() << "File open error" << endl;

    // 2. Read file
    qint64 fileSize = file.size();
    char* buffer = new char[fileSize];
    qint64 bytesRead = file.read(buffer, fileSize);
    if(bytesRead == -1){
        qDebug() << "File read error" << endl;
        return;
    }

    // 3. Output file content and close
    qDebug() << buffer << endl;
    file.close();
    delete[] buffer;
}

File Writing

// Save file button
void MainWindow::on_saveButton_clicked()
{
    // 1. Open file
    // Use absolute path
    QFile file("C:/projects/notepad/data.txt");  
    // Open file in read-write text mode, truncate on open
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))      
        qDebug() << "File open error" << endl;

    // 2. Write to file
    QByteArray data = "Hello Qt";
    qint64 bytesWritten = file.write(data);
    if(bytesWritten == -1){
        qDebug() << "File write error" << endl;
        return;
    }
    
    // 3. Close file
    file.close();
}

File Stream Operations

File Reading

void MainWindow::on_openButton_clicked()
{
    // Load file using absolute path
    QFile file("C:/projects/notepad/data.txt");
    // Open file in read-only text mode
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))    
        qDebug() << "File open error" << endl;

    // Read file data stream
    QTextStream in(&file);  // Bind file to stream
    in.setCodec("UTF-8");
    while(!in.atEnd())
    {
        QString line = in.readLine();
        qDebug() << line << endl;
    }

    file.close();   // Close file
}

File Writing

// Save file button
void MainWindow::on_saveButton_clicked()
{
    // 1. Open file
    // Use absolute path
    QFile file("C:/projects/notepad/data.txt");  
    // Open file in read-write text mode, truncate on open
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))      
        qDebug() << "File open error" << endl;

    // Write data using stream
    QTextStream out(&file);  // Bind file to stream
    out.setCodec("UTF-8");
    out << "Sample text" << "\n";

    file.close();   // Close file
}

QFileDialog File Selection Dialog

Opening Files

The basic workflow for using QFileDialog typically follows these steps:

  1. Create an instance: First, create a QFileDialog object
  2. Set mode: Set the dialog mode as needed (open file, save file, etc.)
  3. Set filters: Optionally set file type filters to restrict selectable file types
  4. Show dialog: Display the dialog using exec() and handle user selections

Basic Implementation

// Step-by-step file opening
// 1. Create instance
QFileDialog dialog;

// Set mode
// QFileDialog::AnyFile - Any file
// QFileDialog::ExistingFile - Existing file
// QFileDialog::Directory - Directory
// QFileDialog::ExistingFiles - Multiple existing files
dialog.setFileMode(QFileDialog::ExistingFile);

// Set file type filter
dialog.setNameFilter(tr("Text files (*.txt);;Images (*.png *.jpg *.bmp);;All files (*.*)"));

// Show dialog
dialog.exec();
// Get selected files
QStringList selectedFiles = dialog.selectedFiles(); 
for(const QString &filePath : selectedFiles)
{
    qDebug() << filePath << endl;
}

Alternative Approach

For simpler file opening, you can use the static getOpenFileName method:

QString fileName;
fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                      "C:/projects/notepad",
                                      tr("Text files (*.txt)"));

Creating/Saving Files

Creating or saving files is simpler than opening them, using the getSaveFileName method:

void MainWindow::on_saveButton_clicked()
{
    // Create a file using file dialog
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                "C:/projects/notepad/data.txt",
                                                tr("Text (*.txt)"));
    
    // Open and write to the file
    QFile file(fileName);
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))
        qDebug() << "File open error" << endl;

    // Write data to the file
    QTextStream out(&file);
    out.setCodec("UTF-8");
    out << "Data from QFileDialog" << "\n";

    file.close();   // Close file
}

Integrating Basic Notepad Functionality

Since file operations involve opening, saving, and closing files, the file object and filename should be set as class member variables for access by member functions.

Line Editor and Text Editor

Line Editor

Common Method Functionality
setText(QString text) Sets line editor content by overwriting existing text
clear() Clears line editor content
QString text() Gets line editor content

Text Editor

Unlike line editors, text editors support appending data but don't have a text() method. Use toPlainText() to retrieve the content.

Common Method Functionality
setText(QString text) Sets text editor content by overwriting existing text
append(QString text) Appends data to text editor as new lines (not available in line editor)
clear() Clears text editor content
QString toPlainText() Gets text editor content

File Opening

To open a file, combine three key steps: using QFileDialog to select the file, reading its content, and displaying it in the text editor.

void MainWindow::on_openButton_clicked()
{
    /****** 1. Select text file using QFileDialog *****/
    QString selectedFile = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                      "C:/projects/notepad",
                                                      tr("Text files (*.txt)"));

    /***** 2. Read text file content using stream *****/
    // Set file name
    file.setFileName(selectedFile);
    
    // Open file in read-write mode
    if(!file.open(QIODevice::ReadWrite | QIODevice::Text))
        qDebug() << "File open error" << endl;

    // Read file data stream
    QTextStream in(&file);
    in.setCodec("UTF-8");
    
    // Clear text editor before displaying content
    ui->textEdit->clear();
    
    while(!in.atEnd())
    {
        QString line = in.readLine();
        /****** 3. Display file content in text editor ******/
        ui->textEdit->append(line);
    }

    // Note: File is kept open for potential editing
    // Will be closed when application closes or user explicitly closes it
}

Note: While step-by-step file selection code is verbose, it's necessary for opening multiple files. Using file streams is more reliable than direct reading methods for handling encoding issues.

File Saving

Similarly, saving a file involves three main steps: retrieving content from the text editor, using a file dialog to create a file, and writing the data to the new file.

// Save file button
void MainWindow::on_saveButton_clicked()
{
    // If no file is currently open, create a new one
    if(!file.isOpen())
    {
        // Create file using dialog
        QString newFileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                        "C:/projects/notepad/data.txt",
                                                        tr("Text (*.txt)"));      
        
        // Open the new file
        file.setFileName(newFileName);
        
        // Open file in read-write mode, truncate on open
        if(!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))    
            qDebug() << "File open error" << endl;
    }

    // Clear existing content and save text editor content
    file.resize(0);
    
    // Write text editor content to file
    QTextStream out(&file);
    out.setCodec("UTF-8");
    
    // Get text editor content
    QString content = ui->textEdit->toPlainText();
    out << content;
   
    // Note: File is kept open for further editing
    // Will be closed when application closes or user explicitly closes it
}

Close Button Implementation

When the close button is clicked, use the isOpen() method to check if a file is open.

// Close button implementation
void MainWindow::on_closeButton_clicked()
{
    ui->textEdit->clear();
    
    if(file.isOpen())
    {
        file.close();
    }
}
Tags: qtC++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.