Mục tiêu thiết kế
- Giao diện gồm ba ô nhập liệu (QLineEdit) và một nhãn hiển thị kết quả (QLabel).
- Nhãn cập nhật tự động tổng giá trị số từ ba ô nhập — ngay khi người dùng gõ.
- Khi nội dung ô nhập không phải số hợp lệ, giá trị được coi là
0. - Giá trị khởi tạo cho ba ô lần lượt là
"1","2","3".
Cài đặt thư viện cần thiết
Tải mã nguồn RxCpp và phần mở rộng RxQt bằng lệnh Git:
$ git clone --recursive https://github.com/ReactiveX/RxCpp.git
$ git clone --recursive https://github.com/tetsurom/rxqt.git
Tạo dự án Qt Widgets
- Mở Qt Creator → File > New File or Project...
- Chọn Application > Qt Widgets Application.
- Đặt tên dự án là
RxSumApp, chọn thư mục lưu trữ phù hợp. - Chọn bộ công cụ Desktop Qt và hoàn tất.
Cấu hình tệp dự án (.pro)
Mở tệp RxSumApp.pro và cập nhật các dòng sau:
CONFIG += c++17
INCLUDEPATH += $$PWD/../RxCpp/Rx/v2/src \
$$PWD/../rxqt/include
Lưu ý: Thay $$PWD/../ bằng đường dẫn tuyệt đối hoặc tương đối chính xác tới vị trí bạn đã clone hai kho lưu trữ.
Thiết kế giao diện trong Qt Designer
- Mở
mainwindow.ui, kéo một Form Layout vào trung tâm. - Thêm ba Line Edit vào layout, đặt tên lần lượt là:
inputA→ thiết lập thuộc tínhtext="1",alignment=AlignRightinputB→text="2",alignment=AlignRightinputC→text="3",alignment=AlignRight
- Thêm một Label, đặt tên là
sumLabel, căn lề phải.
Triển khai không dùng Reactive Extensions
Để so sánh, ta có thể xử lý thủ công qua signal-slot truyền thống:
- Trong Qt Designer, nhấp chuột phải từng QLineEdit → Go to slot... → chọn
textChanged(const QString&). - Qt Creator sẽ sinh ba hàm thành viên trong
MainWindow:
// mainwindow.h
private slots:
void on_inputA_textChanged(const QString &text);
void on_inputB_textChanged(const QString &text);
void on_inputC_textChanged(const QString &text);
// mainwindow.cpp
void MainWindow::on_inputA_textChanged(const QString &text) { updateSum(); }
void MainWindow::on_inputB_textChanged(const QString &text) { updateSum(); }
void MainWindow::on_inputC_textChanged(const QString &text) { updateSum(); }
void MainWindow::updateSum() {
const int a = ui->inputA->text().toInt(&_); // _ bỏ qua lỗi chuyển đổi
const int b = ui->inputB->text().toInt(&_);
const int c = ui->inputC->text().toInt(&_);
ui->sumLabel->setNum(a + b + c);
}
Và gọi updateSum() trong constructor để khởi tạo nhãn.
Triển khai theo phong cách Reactive
Thay vì gắn từng slot riêng lẻ, ta sử dụng luồng quan sát (Observable) để kết hợp dữ liệu theo thời gian thực:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <rxqt.hpp>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// Tạo luồng quan sát từ mỗi QLineEdit, phát giá trị ban đầu trước khi thay đổi
auto makeStream = [](QLineEdit* edit) {
return rxqt::from_signal(edit, &QLineEdit::textChanged)
.start_with(edit->text());
};
// Kết hợp ba luồng: lấy giá trị mới nhất từ mỗi luồng, tính tổng, cập nhật giao diện
rxcpp::observable<>::combine_latest(
makeStream(ui->inputA),
makeStream(ui->inputB),
makeStream(ui->inputC),
[](const QString& a, const QString& b, const QString& c) {
return a.toInt() + b.toInt() + c.toInt();
}
)
.subscribe([this](int total) {
ui->sumLabel->setNum(total);
});
}
MainWindow::~MainWindow() {
delete ui;
}
Giải thích ngắn gọn:
from_signalbiến tín hiệu Qt thành Observable.start_withđảm bảo luồng luôn bắt đầu bằng giá trị hiện tại của ô nhập.combine_latestphát ra kết quả mỗi khi bất kỳ luồng nào cập nhật — phù hợp với yêu cầu "cập nhật tức thì".subscribenhận kết quả và cập nhật QLabel mà không cần quản lý trạng thái thủ công.