Category Archives: Qt 5 C++

Qt 5 C++ – Layout

Trong phần này các bạn sẽ được học cách làm việc với các loại layout có trong Qt 5, mình sẽ đề cập đến bốn loại layout là QHBoxLayout, QVBoxLayout, QFormLayout, và QGridLayout.

Nói về layout thì khi bạn viết một ứng dụng, bạn sẽ phải dùng đến các widget (hay control trong Visual Studio) như nút bấm, label… trong Qt 5 bạn có hai cách để thiết kế chúng, một là tự quy định vị trí, kích thước, cái này gọi là Absolute Positioning, hai là dùng các layout, các layout sẽ quản lý widget cho bạn.

Absolute Positioning

Như đã nói ở trên, bạn sẽ tự định vị vị trí cũng như kích thước của từng widget theo tọa độ pixel. Và đây là những vấn đề bạn sẽ gặp nếu thiết kế theo kiểu Absolute Positioning:

  • Kích thước của các widget sẽ không tự động thay đổi khi cửa sổ chính thay đổi.
  • Ứng dụng nhìn sẽ rất khác (thường là xấu hơn) trên từng hệ điều hành.
  • Thay đổi kiểu font cũng sẽ làm hỏng giao diện.
  • Nếu bạn muốn thay đổi giao diện, chẳng hạn thêm/bớt nút bấm, di chuyển textbox lên xuống… bạn sẽ phải làm lại từ đầu, như thế rất tốn thời gian và không chuyên nghiệp.

Tuy sẽ có những trường hợp mà bạn phải làm theo kiểu absolute. Còn lại thì hầu hết đều nên dùng đến layout.

#include <QApplication>
#include <QDesktopWidget>
#include <QTextEdit>

class Absolute : public QWidget {
    
 public:
     Absolute(QWidget *parent = 0);
};

Absolute::Absolute(QWidget *parent)
    : QWidget(parent) {
        
  QTextEdit *ledit = new QTextEdit(this);
  ledit->setGeometry(5, 5, 200, 150);
}

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Absolute window;

  window.setWindowTitle("Absolute");
  window.show();

  return app.exec();
}

Trong ví dụ trên chúng ta tạo ra widget QTextEdit và thiết kế nó bằng tay. Phương thức setGeometry() quy định vị trí và kích thước của widget.

Capture
Trước khi thay đổi kích thước window
Capture
Sau khi thay đổi kích thước window

QVBoxLayout

Lớp QVBoxLayout sẽ sắp xếp các widget theo hàng dọc. Để thêm widget vào layout thì ta dùng phương thức addWidget().

#pragma once

#include <QWidget>

class VerticalBox : public QWidget {

  public:
    VerticalBox(QWidget *parent = 0);
};
#include "verticalbox.h"
#include <QVBoxLayout>
#include <QPushButton>

VerticalBox::VerticalBox(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout(this);
  vbox->setSpacing(1);
  
  QPushButton *settings = new QPushButton("Settings", this);
  settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *accounts = new QPushButton("Accounts", this);
  accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *loans = new QPushButton("Loans", this);
  loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *cash = new QPushButton("Cash", this);
  cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  QPushButton *debts = new QPushButton("Debts", this);
  debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

  vbox->addWidget(settings);
  vbox->addWidget(accounts);
  vbox->addWidget(loans);
  vbox->addWidget(cash);
  vbox->addWidget(debts);

  setLayout(vbox);
}

Trong ví dụ trên, chúng ta có một layout và năm button. Mỗi button sẽ dãn ra theo cả hai chiều ngang và dọc.

QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setSpacing(1);

Tạo một đối tượng QVBoxLayout, phương thức setSpacing() quy định khoảng cách giữa các widget, trong trường hợp này là 1 pixel.

QPushButton *settings = new QPushButton("Settings", this);
settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

Với mỗi button được tạo ra, chúng ta quy định kích thước cho chúng bằng phương thức setSizePolicy(), tham số đầu tiên là kích thước chiều ngang, tham số thứ hai là kích thước chiều dọc. Mặc định trong Qt thì chiều ngang sẽ tự động dãn nở, còn chiều rộng thì không. Hằng số QSizePolicy::Expanding quy định kiểu co dãn là tự động co dãn.

vbox->addWidget(settings);
vbox->addWidget(accounts);
...

Sử dụng phương thức addWidget() để đưa widget vào layout.

setLayout(vbox);

Cuối cùng set layout cho window.

#include "verticalbox.h"
#include <QApplication>

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  VerticalBox window;

  window.resize(240, 230);
  window.setWindowTitle("VerticalBox");
  window.show();

  return app.exec();
}
Capture

Bạn có thể thử kéo dãn cửa sổ window để thấy các button cũng tự co dãn theo.

Buttons

Trong ví dụ dưới đây, chúng ta sẽ hiển thị hai button nằm ở góc dưới bên phải cửa sổ.

#pragma once

#include <QWidget>
#include <QPushButton>

class Buttons : public QWidget {
    
  public:
    Buttons(QWidget *parent = 0);

  private:
    QPushButton *okBtn;
    QPushButton *applyBtn;
};
#include "buttons.h"
#include <QVBoxLayout>
#include <QHBoxLayout>

Buttons::Buttons(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout(this);
  QHBoxLayout *hbox = new QHBoxLayout();

  okBtn = new QPushButton("OK", this);
  applyBtn = new QPushButton("Apply", this);

  hbox->addWidget(okBtn, 1, Qt::AlignRight);
  hbox->addWidget(applyBtn, 0);

  vbox->addStretch(1);
  vbox->addLayout(hbox);
}
QVBoxLayout *vbox = new QVBoxLayout(this);
QHBoxLayout *hbox = new QHBoxLayout();

Chúng ta tạo hai layout, QVBoxLayoutQHBoxLayout, trong đó QHBoxLayout cũng tương tự như QVBoxLayout nhưng sắp xếp các widget theo chiều ngang.

hbox->addWidget(okBtn, 1, Qt::AlignRight);
hbox->addWidget(applyBtn, 0);

Các button được đặt trong layout nằm ngang, và được canh lề về phía bên phải với phương thức addWidget().Ở đây chúng ta dùng ba tham số cho phương thức này (khác với các phần trước chỉ có một), tham số đầu tiên là biến widget được thêm vào, tham số thứ hai là chỉ số stretch (co dãn), tham số thứ ba là chỉ số align (canh lề).

Về tham số stretch, tham số stretch nhận giá trị là các số nguyên, quy định widget sẽ chiếm bao nhiêu phần trong tổng số chiều ngang (hoặc dọc) của layout. Mặc định tham số này là 0, khi đó chiều ngang (hoặc dọc) sẽ được chia đều cho các widget. Khi có một widget có chỉ số 1, các widget còn lại mang chỉ số 0, thì các widget 0 sẽ nhận chiều ngang vừa đủ bao bọc lấy tiêu đề của nó, còn widget 1 sẽ chiếm toàn bộ chiều ngang còn lại. Khi các widget này có chỉ số khác 0 với 1, thì chúng sẽ được chia đều theo đúng tỉ lệ đó. Ví dụ nút OK có stretch là 3, nút Apply có stretch là 4 chẳng hạn, thì layout sẽ chia chiều ngang theo đúng tỉ lệ 3:4 cho hai nút này.

Về tham số align, tham số này nhận bốn giá trị là Qt::AlignCenter, Qt::AlignRight, Qt::AlignLèt và no alignment (cái này là mặc định nếu bạn ko đưa giá trị nào vào), đối với no alignment thì widget sẽ tự động dãn chiều ngang/dọc của nó cho đủ tỉ lệ đã được layout phân chia. Còn lại ba loại kia thì widget sẽ tự thu nhỏ chiều ngang/dọc của nó cho vừa đủ với tiêu đề của nó rồi tự nằm đúng vị trí bên trái, phải hoặc giữa với khu vực đã được layout phân chia.

Nếu đoạn giải thích trên có vẻ hơi khó hiểu thì bạn có thể tự điều chỉnh các tham số để hiểu thêm về cách hoạt động của nó hoặc comment hỏi ở dưới bài, mình sẽ giải thích rõ hơn.

vbox->addStretch(1);
vbox->addLayout(hbox);

Ở đây mình sử dụng phương thức addStretch(), phương thức này sẽ đưa một khoảng trống vào layout, đối với QVBoxLayout (layout dọc) thì khoảng trống này sẽ được đưa từ trên xuống, với layout ngang là từ trái qua phải, khoảng trống được đưa vào sẽ lấp đầy phần khoảng trống dư thừa do các widget không đủ kích thước để lấp.

#include <QApplication>
#include "buttons.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  

  Buttons window;

  window.resize(290, 170);
  window.setWindowTitle("Buttons");
  window.show();
  
  return app.exec();
}
Capture

Kết hợp các layout

Đoạn code ví dụ dưới đây kết hợp các layout lại với nhau thay vì chỉ dùng một-hai layout. Việc kết hợp các layout lại sẽ làm cho giao diện trở nên tinh xảo hơn. Để kết hợp layout thì chúng ta dùng phương thức addLayout().

#pragma once

#include <QWidget>

class Layouts : public QWidget {

  public:
    Layouts(QWidget *parent = 0);
};
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include "nesting.h"

Layouts::Layouts(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout();
  QHBoxLayout *hbox = new QHBoxLayout(this);

  QListWidget *lw = new QListWidget(this);
  lw->addItem("The Omen"); 
  lw->addItem("The Exorcist");
  lw->addItem("Notes on a scandal");
  lw->addItem("Fargo");
  lw->addItem("Capote");

  QPushButton *add = new QPushButton("Add", this);
  QPushButton *rename = new QPushButton("Rename", this);
  QPushButton *remove = new QPushButton("Remove", this);
  QPushButton *removeall = new QPushButton("Remove All", this);

  vbox->setSpacing(3);
  vbox->addStretch(1);
  vbox->addWidget(add);
  vbox->addWidget(rename);
  vbox->addWidget(remove);
  vbox->addWidget(removeall);
  vbox->addStretch(1);

  hbox->addWidget(lw);
  hbox->addSpacing(15);
  hbox->addLayout(vbox);

  setLayout(hbox);
}

Chúng ta tạo ra một window có chứa bốn button và một list widget. Các button nằm trong vertical layout (layout chiều dọc), bản thân layout này được đặt trong một horizontal layout (layout ngang) và được đặt bên phải layout. Ngoài ra bên trái layout ngang này còn chứa list widget. Nếu chúng ta co dãn kích thước window thì kích thước của list widget cũng tự động co dãn theo.

QVBoxLayout *vbox = new QVBoxLayout();

QVBoxLayout để chứa các button.

QHBoxLayout *hbox = new QHBoxLayout(this);

QHBoxLayout là layout chính cho toàn bộ widget.

QListWidget *lw = new QListWidget(this);
lw->addItem("The Omen"); 
lw->addItem("The Exorcist");
lw->addItem("Notes on a scandal");
lw->addItem("Fargo");
lw->addItem("Capote");

Tạo QListWidget và thêm các item cho nó.

QPushButton *add = new QPushButton("Add", this);
QPushButton *rename = new QPushButton("Rename", this);
QPushButton *remove = new QPushButton("Remove", this);
QPushButton *removeall = new QPushButton("Remove All", this);

Tạo bốn button.

vbox->setSpacing(3);
vbox->addStretch(1);
vbox->addWidget(add);
vbox->addWidget(rename);
vbox->addWidget(remove);
vbox->addWidget(removeall);
vbox->addStretch(1);

Tạo vertical layout, thêm các tùy chỉnh như khoảng cách giữa các widget và chỉ số stretch cho nó.

hbox->addWidget(lw);
hbox->addSpacing(15);
hbox->addLayout(vbox);

Đặt list widget vào trong horizontal layout. Sau đó đưa vertical layout vào horizontal layout bằng phương thức addLayout().

#include <QApplication>
#include "nesting.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Layouts window;
  
  window.setWindowTitle("Layouts");
  window.show();

  return app.exec();
}
Capture

QFormLayout

QFormLayout là layout trợ giúp chúng ta trong việc tạo các form (biểu mẫu). Layout này chia thiết kế làm hai cột, một bên là tên các trường, một bên là các trường để điền, có thể là các text box hay spin box. Xem ví dụ.

#pragma once

#include <QWidget>

class FormEx : public QWidget {
    
  public:
    FormEx(QWidget *parent = 0);
};
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include "form.h"

FormEx::FormEx(QWidget *parent)
    : QWidget(parent) {
        
  QLineEdit *nameEdit = new QLineEdit(this);
  QLineEdit *addrEdit = new QLineEdit(this);
  QLineEdit *occpEdit = new QLineEdit(this);
  
  QFormLayout *formLayout = new QFormLayout;
  formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
  formLayout->addRow("Name:", nameEdit);
  formLayout->addRow("Email:", addrEdit);
  formLayout->addRow("Age:", occpEdit);
  
  setLayout(formLayout);
}

Đoạn code trên tạo ra ba label và ba line edit (hay textbox trong visual studio).

QFormLayout *formLayout = new QFormLayout;

Tạo một đối tượng QFormLayout.

formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);

Căn chỉnh lề cho cột label bằng phương thức setLabelAlignment(), các bạn có thể dùng các tham số khác nhau để thử nghiệm.

formLayout->addRow("Name:", nameEdit);

Phương thức  addRow() sẽ thêm một dòng vào trong form, tham số đầu tiên là label sẽ được hiển thị, tham số thứ hai là biến widget.

#include <QApplication>
#include "form.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  

  FormEx window;

  window.setWindowTitle("Form example");
  window.show();

  return app.exec();
}

.Capture

QGridLayout

Lớp QGridLayout thiết kế các widget theo dạng lưới (hay bảng).

#pragma once

#include <QWidget>

class Calculator : public QWidget {

  public:
    Calculator(QWidget *parent = 0);

};
#include <QGridLayout>
#include <QPushButton>
#include "calculator.h"

Calculator::Calculator(QWidget *parent)
    : QWidget(parent) {

  QGridLayout *grid = new QGridLayout(this);
  grid->setSpacing(2);

  QList<QString> values({ "7", "8", "9", "/", 
    "4", "5", "6", "*",
    "1", "2", "3", "-",
    "0", ".", "=", "+"
  });
  
  int pos = 0;
  
  for (int i=0; i<4; i++) {
   for (int j=0; j<4; j++) { QPushButton *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40);
     grid->addWidget(btn, i, j);
     pos++;
   }
  }  
  
  setLayout(grid);
}

Chúng ta sẽ thiết kế giao diện của một chương trình máy tính bỏ túi.

QGridLayout *grid = new QGridLayout(this);
grid->setSpacing(2);

Tạo đối tượng QGridLayout và quy định khoảng cách giữa các widget là 2 pixel.

QList<QString> values({ "7", "8", "9", "/", 
  "4", "5", "6", "*",
  "1", "2", "3", "-",
  "0", ".", "=", "+"
});

Tạo danh sách các tên các nút bấm cần đưa vào.

for (int i=0; i<4; i++) {
  for (int j=0; j<4; j++) { QPushButton *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40);
      grid->addWidget(btn, i, j);
      pos++;
  }
} 

Cuối cùng đưa 16 widget nút bấm vô layout. Các nút bấm sẽ có kích thước cố định như nhau.

#include <QApplication>
#include "calculator.h"

int main(int argc, char *argv[]) {

  QApplication app(argc, argv); 

  Calculator window;

  window.move(300, 300);
  window.setWindowTitle("Calculator");
  window.show();

  return app.exec();
}
Capture

Tổng kết

Để kết thúc bài chúng ta sẽ tạo ra một giao diện phức tạp một tí bằng QGridLayout :).

#pragma once

#include <QWidget>

class Review : public QWidget {
    
  public:
    Review(QWidget *parent = 0);
};
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>
#include "review.h"

Review::Review(QWidget *parent)
    : QWidget(parent) {

  QGridLayout *grid = new QGridLayout(this);
  grid->setVerticalSpacing(15);
  grid->setHorizontalSpacing(10);

  QLabel *title = new QLabel("Title:", this);
  grid->addWidget(title, 0, 0, 1, 1);
  title->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

  QLineEdit *edt1 = new QLineEdit(this);
  grid->addWidget(edt1, 0, 1, 1, 1);

  QLabel *author = new QLabel("Author:", this);
  grid->addWidget(author, 1, 0, 1, 1);
  author->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

  QLineEdit *edt2 = new QLineEdit(this);
  grid->addWidget(edt2, 1, 1, 1, 1);

  QLabel *review = new QLabel("Review:", this);
  grid->addWidget(review, 2, 0, 1, 1);
  review->setAlignment(Qt::AlignRight | Qt::AlignTop);

  QTextEdit *te = new QTextEdit(this);
  grid->addWidget(te, 2, 1, 3, 1);

  setLayout(grid);
}

Trong ví dụ trên, ta tạo ra một form thường dùng để đánh giá sách, các trường gồm có tên tác giả, tiêu đề, nội dung đánh giá.

#include <QApplication>
#include "review.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  

  Review window;

  window.setWindowTitle("Review");
  window.show();

  return app.exec();
}
Capture

Qt 5 C++ – Menu và toolbar

Trong bài này chúng ta sẽ học về cách làm menu và toolbar trong một ứng dụng Qt 5.

Ví dụ

Đoạn code dưới đây sẽ hiển thị một menu đơn giản.

#pragma once

#include <QMainWindow>
#include <QApplication>

class SimpleMenu : public QMainWindow {

  public:
    SimpleMenu(QWidget *parent = 0);
};
#include "simplemenu.h"
#include <QMenu>
#include <QMenuBar>

SimpleMenu::SimpleMenu(QWidget *parent)
    : QMainWindow(parent) {
    
  QAction *quit = new QAction("&Quit", this);

  QMenu *file;
  file = menuBar()->addMenu("&File");
  file->addAction(quit);

  connect(quit, &QAction::triggered, qApp, QApplication::quit);
}

Đoạn code trên sẽ tạo ra menu File. Một lớp muốn có menu thì lớp này phải kế thừa từ QMainWindow.

QAction *quit = new QAction("&Quit", this);

Dòng code trên tạo một đối tượng QAction. Một action là một lớp chuyên để thực hiện một lệnh nào đó. Lý do tại sao nên dùng action thì bạn để ý trong một ứng dụng GUI thông thường có rất nhiều cách để thi hành một công việc nào đó, chẳng hạn muốn thoát Qt Creator thì bạn có thể bấm vào dấu “X” trên góc phải của Qt Creator, hoặc vào menu File→Quit, hoặc bấm Ctrl+Q, tất cả ba cách trên đều làm chung một việc, nên tốt nhất là ta connect cả ba cách đó vào một action. Một QMenu có thể có một hoặc nhiều action.

QMenu *file;
file = menuBar()->addMenu("&File");

Tạo một đối tượng QMenu.

file->addAction(quit);

Dùng phương thức addAction() để thêm action.

connect(quit, &QAction::triggered, qApp, QApplication::quit);

Sau đó connect action này với phương thức quit() của qApp. Nếu chưa biết qApp là gì thì bạn có thể tìm phần trước của bài này để đọc.

#include "simplemenu.h"

int main(int argc, char *argv[]) {

  QApplication app(argc, argv);  
    
  SimpleMenu window;

  window.resize(250, 150);
  window.setWindowTitle("Simple menu");
  window.show();

  return app.exec();
}

Untitled

Icon, shortcut, separator

Trong phần này chúng ta sẽ tùy biến menu bằng cách thêm icon, phím tắt (shortcut) và các dòng phân cách – separator.

#pragma once

#include <QMainWindow>
#include <QApplication>

class AnotherMenu : public QMainWindow {
    
  public:
    AnotherMenu(QWidget *parent = 0);
};
#include "anothermenu.h"
#include <QMenu>
#include <QMenuBar>

AnotherMenu::AnotherMenu(QWidget *parent)
    : QMainWindow(parent) {
          
  QPixmap newpix("new.png");
  QPixmap openpix("open.png");
  QPixmap quitpix("quit.png");

  QAction *newa = new QAction(newpix, "&New", this);
  QAction *open = new QAction(openpix, "&Open", this);
  QAction *quit = new QAction(quitpix, "&Quit", this);
  quit->setShortcut(tr("CTRL+Q"));

  QMenu *file;
  file = menuBar()->addMenu("&File");
  file->addAction(newa);
  file->addAction(open);
  file->addSeparator();
  file->addAction(quit);
  
  qApp->setAttribute(Qt::AA_DontShowIconsInMenus, false);

  connect(quit, &QAction::triggered, qApp, &QApplication::quit);
}

Trong ví dụ này chúng ta tạo ra một menu với ba action, nhưng chỉ dùng tới action quit thôi. Ngoài ra ta sẽ thêm một dòng phân cách và phím tắt Ctrl+Q. 

QPixmap newpix("new.png");
QPixmap openpix("open.png");
QPixmap quitpix("quit.png");

Đây là hình icon cho menu. Lưu ý có một số môi trường không cho phép hiển thị icon trên menu.

QAction *newa = new QAction(newpix, "&New", this);
QAction *open = new QAction(openpix, "&Open", this);
QAction *quit = new QAction(quitpix, "&Quit", this);

Đoạn code trên tạo QAction với tham số đầu tiên là icon mà nó sẽ dùng.

quit->setShortcut(tr("CTRL+Q"));

Tạo phím tắt, cứ mỗi lần bấm tổ hợp đúng tổ hợp phím thì chương trình sẽ thoát.

file->addSeparator();

Thêm dòng phân cách. Dòng phân cách chẳng qua chỉ là một đường kẻ ngang để gộp nhóm một số action có chung đặc điểm lại với nhau.

qApp->setAttribute(Qt::AA_DontShowIconsInMenus, false);

Như đã nói ở trên, trong một số môi trường icon sẽ không được hiển thị, nhưng chúng ta có thể bắt nó hiển thị bằng cách đưa thuộc tính Qt::AA_DontShowIconsInMenus thành false.

#include "anothermenu.h"

int main(int argc, char *argv[]) {

  QApplication app(argc, argv);  
    
  AnotherMenu window;

  window.resize(350, 200);
  window.setWindowTitle("Simple menu");
  window.show();

  return app.exec();
}

File main.

Untitled

Check menu

Ví dụ dưới đây sẽ tạo ra menu có thể check được. Chúng ta sẽ tạo ra một thanh trạng thái (statusbar), cứ mỗi lần check hoặc bỏ check menu này thì statusbar sẽ hiện hoặc ẩn.

#pragma once

#include <QMainWindow>
#include <QApplication>

class Checkable : public QMainWindow {
    
  Q_OBJECT  

  public:
    Checkable(QWidget *parent = 0);
 
  private slots:
    void toggleStatusbar();

  private:
    QAction *viewst;
};
#include "checkable.h"
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>

Checkable::Checkable(QWidget *parent)
    : QMainWindow(parent) {

  viewst = new QAction("&View statusbar", this);
  viewst->setCheckable(true);
  viewst->setChecked(true);

  QMenu *file;
  file = menuBar()->addMenu("&File");
  file->addAction(viewst);

  statusBar();

  connect(viewst, &QAction::triggered, this, &Checkable::toggleStatusbar);
}

void Checkable::toggleStatusbar() {
    
  if (viewst->isChecked()) {
      
      statusBar()->show();
  } else {
      
      statusBar()->hide();
  }
}

Check menu sẽ làm ẩn hoặc hiện statusbar.

viewst = new QAction("&View statusbar", this);
viewst->setCheckable(true);
viewst->setChecked(true);

Phương thức setCheckable() để chuyển menu thành dạng có thể check được. Rồi dùng phương thức setChecked() để làm cho menu đó đã check rồi nếu thích.

if (viewst->isChecked())     
    statusBar()->show();
else     
    statusBar()->hide();

Phương thức toggleStatusbar() sẽ làm cho thanh statusbar ẩn hoặc hiện.

#include "checkable.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Checkable window;

  window.resize(250, 150);
  window.setWindowTitle("Checkable menu");
  window.show();

  return app.exec();
}

Untitled

QToolBar

Lớp QToolBar sẽ hiển thị một thanh ngang chứa các nút lệnh cho ứng dụng, ngoài ra bạn có thể dùng chuột kéo thanh này sang trái, phải hoặc nằm dưới.

#pragma once

#include <QMainWindow>
#include <QApplication>

class Toolbar : public QMainWindow {
    
  Q_OBJECT  

  public:
    Toolbar(QWidget *parent = 0);
};
#include "toolbar.h"
#include <QToolBar>
#include <QIcon>
#include <QAction>

Toolbar::Toolbar(QWidget *parent)
    : QMainWindow(parent) {
    
  QPixmap newpix("new.png");
  QPixmap openpix("open.png");
  QPixmap quitpix("quit.png");
  
  QToolBar *toolbar = addToolBar("main toolbar");
  toolbar->addAction(QIcon(newpix), "New File");
  toolbar->addAction(QIcon(openpix), "Open File");
  toolbar->addSeparator();
  QAction *quit = toolbar->addAction(QIcon(quitpix), 
      "Quit Application");
  
  connect(quit, &QAction::triggered, qApp, &QApplication::quit);
}

Lưu ý là luôn phải kế thừa từ lớp QMainWindow nếu muốn tạo menu hoặc toolbar.

QToolBar *toolbar = addToolBar("main toolbar");

Phương thức addToolBar() sẽ tạo một toolbar.

toolbar->addAction(QIcon(newpix), "New File");
toolbar->addAction(QIcon(openpix), "Open File");
toolbar->addSeparator();

Tạo hai action và một separator.

#include "toolbar.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Toolbar window;

  window.resize(300, 200);
  window.setWindowTitle("QToolBar");
  window.show();

  return app.exec();
}

Capture

Khung chương trình mẫu

Để kết thúc bài này, chúng ta sẽ tạo ra một khung chương trình mẫu.

#pragma once

#include <QMainWindow>
#include <QApplication>

class Skeleton : public QMainWindow {
    
  Q_OBJECT  

  public:
    Skeleton(QWidget *parent = 0);
};
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QTextEdit>
#include <QToolBar>
Skeleton::Skeleton(QWidget *parent)
    : QMainWindow(parent) {
        
  QPixmap newpix("new.png");
  QPixmap openpix("open.png");
  QPixmap quitpix("quit.png");

  QAction *quit = new QAction("&Quit", this);

  QMenu *file;
  file = menuBar()->addMenu("&File");
  file->addAction(quit);

  connect(quit, &QAction::triggered, qApp, &QApplication::quit);
  
  QToolBar *toolbar = addToolBar("main toolbar");
  toolbar->addAction(QIcon(newpix), "New File");
  toolbar->addAction(QIcon(openpix), "Open File");
  toolbar->addSeparator();
  
  QAction *quit2 = toolbar->addAction(QIcon(quitpix), 
      "Quit Application");
  connect(quit2, &QAction::triggered, qApp, &QApplication::quit);

  QTextEdit *edit = new QTextEdit(this);  

  setCentralWidget(edit);

  statusBar()->showMessage("Ready");
}

Trong ví dụ này  chúng ta tạo cả toolbar và statusbar.

QTextEdit *edit = new QTextEdit(this);  

setCentralWidget(edit);

Tạo một đối tượng QTextEdit và đặt nó ngay giữa QMainWindow.

#include "skeleton.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Skeleton window;

  window.resize(350, 250);
  window.setWindowTitle("Application skeleton");
  window.show();

  return app.exec();
}

Capture

Qt 5 C++ – GUI

Những bài trước chủ yếu ta làm việc qua console. Từ bài này chúng ta sẽ bắt đầu làm việc với GUI, widgets.

Ở đây chúng ta sẽ cho hiển thị icon, tooltip. Hiển thị cửa sổ window ở giữa màn hình. Sau đó tìm hiểu qua về cơ chế Signal và Slot.

Ví dụ

#include <QApplication>
#include <QWidget>

int main(int argc, char *argv[]) {

    QApplication app(argc, argv);

    QWidget window;

    window.resize(250, 150);
    window.setWindowTitle("Simple example");
    window.show();

    return app.exec();
}

Ví dụ trên sẽ hiển thị một cửa sổ lên màn hình.

QApplication app(argc, argv);

Đây là đối tượng QApplication. Bất cứ project nào trong Qt 5 đều phải có dòng này. Trừ project console thì dùng QCoreApplication.

QWidget window;

Bạn có thể hiểu lớp QWidget là một lớp window, chuyên để tạo window :).

window.resize(250, 150);
window.setWindowTitle("Simple example");
window.show();

Thay đổi kích thước của widget, thêm tiêu đề cho widget rồi hiển thị lên màn hình.

return app.exec();

Cuối cùng app.exec() sẽ chạy chương trình.

Capture

Tooltip

Tooltip chính là các câu gợi ý khi bạn di chuyển chuột vào vị trí nào đó. Xem ví dụ.

#include <QApplication>
#include <QWidget>

int main(int argc, char *argv[]) {

  QApplication app(argc, argv);  

  QWidget window;

  window.resize(250, 150);
  window.move(300, 300);
  window.setWindowTitle("Simple Example");
  window.setToolTip("This is a tooltip");
  window.show();

  return app.exec();
}

Ví dụ này sẽ show tooltip khi bạn di chuyển chuột trên widget.

window.setWindowTitle("ToolTip");

Phương thức setToolTip() sẽ làm việc này.

Untitled

Icon

Chắc bạn cũng chẳng còn lạ lẫm gì với Icon. Xem ví dụ.

#include <QApplication>
#include <QWidget>
#include <QIcon>

int main(int argc, char *argv[]) {

  QApplication app(argc, argv);  
    
  QWidget window;

  window.resize(250, 150);
  window.setWindowTitle("Icon");
  window.setWindowIcon(QIcon("web.png"));
  window.show();

  return app.exec();
}

Icon sẽ hiện lên trên góc trái của widget.

window.setWindowIcon(QIcon("web.png"));

Để hiện icon thì bạn dùng phương thức setWindowIcon() và đưa vào đường dẫn đến một file ảnh bất kỳ.

Capture

Con trỏ chuột

#include <QApplication>
#include <QWidget>
#include <QFrame>
#include <QGridLayout>

class Cursors : public QWidget {

 public:
     Cursors(QWidget *parent = 0);
};

Cursors::Cursors(QWidget *parent)
    : QWidget(parent) {
    
  QFrame *frame1 = new QFrame(this);
  frame1->setFrameStyle(QFrame::Box);
  frame1->setCursor(Qt::SizeAllCursor);

  QFrame *frame2 = new QFrame(this);
  frame2->setFrameStyle(QFrame::Box);
  frame2->setCursor(Qt::WaitCursor);

  QFrame *frame3 = new QFrame(this);
  frame3->setFrameStyle(QFrame::Box);
  frame3->setCursor(Qt::PointingHandCursor);

  QGridLayout *grid = new QGridLayout(this);
  grid->addWidget(frame1, 0, 0);
  grid->addWidget(frame2, 0, 1);
  grid->addWidget(frame3, 0, 2);

  setLayout(grid);
}

int main(int argc, char *argv[]) {

  QApplication app(argc, argv);  
    
  Cursors window;

  window.resize(350, 150);
  window.setWindowTitle("Cursors");
  window.show();

  return app.exec();
}

Code ví dụ trên sẽ hiển thị 3 hình vuông trên 1 cửa sổ, mỗi lần bạn di chuột vào hình vuông khác thì con trỏ chuột sẽ hiển thị kiểu khác.

QFrame *frame1 = new QFrame(this);

Tạo một đối tượng QFrame, mình sẽ nói về QFrame ở bài viết khác.

frame1->setFrameStyle(QFrame::Box);

Phương thức setFrameStyle() để set style cho nó là Box, như thế sẽ thấy được đường viền của nó.

frame1->setCursor(Qt::SizeAllCursor);

Phương thức setCursor() để set kiểu con trỏ chuột, dùng các kiểu có sẵn trong Qt.

QGridLayout *grid = new QGridLayout(this);
grid->addWidget(frame1, 0, 0);
grid->addWidget(frame2, 0, 1);
grid->addWidget(frame3, 0, 2);
setLayout(grid);

QGridLayout sẽ tự động điều chỉnh vị trí các frame theo kiểu bảng. Chúng ta sẽ học về các layout này trong các bài viết sau.

QPushButton

QPushButton là lớp widget dùng để hiển thị nút bấm. Ví dụ dưới đây sẽ hiển thị một button, khi click vào nút thì thoát chương trình.

#include <QApplication>
#include <QWidget>
#include <QPushButton>

class MyButton : public QWidget {
    
 public:
     MyButton(QWidget *parent = 0);
};

MyButton::MyButton(QWidget *parent)
    : QWidget(parent) {
           
  QPushButton *quitBtn = new QPushButton("Quit", this);
  quitBtn->setGeometry(50, 40, 75, 30);

  connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);
}

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  MyButton window;

  window.resize(250, 150);  
  window.setWindowTitle("QPushButton");
  window.show();

  return app.exec();
}

Ví dụ trên có sử dụng cơ chế Signal Slot.

QPushButton *quitBtn = new QPushButton("Quit", this);
quitBtn->setGeometry(50, 40, 75, 30);

Tạo đối tượng QPushButton. Điều chỉnh kích thước và đưa nó lên cửa sổ bằng phương thức setGeometry().

connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);

Khi click vào nút bấm, một signal sẽ được phát ra. Slot là một phương thức của một đối tượng nào đó được dùng để nhận signal và xử lý signal đó. Trong trường hợp này là đối tượng qApp, đây là một biến toàn cục tham chiếu đến chương trình của chúng ta, bạn muốn tìm hiểu về nó có thể tìm đọc thêm về QAplication.

Capture

Signal và Slot

Chúng ta sẽ tìm hiểu thêm về Signal và slot bằng cách viết một chương trình tăng-giảm số đếm, signal và slot là cách mà các đối tượng giao tiếp với nhau trong Qt.

#pragma once

#include <QWidget>
#include <QApplication>
#include <QPushButton>
#include <QLabel>

class PlusMinus : public QWidget {
    
  Q_OBJECT

  public:
    PlusMinus(QWidget *parent = 0);

  private slots:
    void OnPlus();
    void OnMinus();

  private:
    QLabel *lbl;

};
class PlusMinus : public QWidget {
    
  Q_OBJECT
...  

Bạn phải thêm Q_OBJECT vào đầu định nghĩa lớp nếu muốn sử dụng signal và slot.

#include "plusminus.h"
#include <QGridLayout>

PlusMinus::PlusMinus(QWidget *parent)
    : QWidget(parent) {
        
  QPushButton *plsBtn = new QPushButton("+", this);
  QPushButton *minBtn = new QPushButton("-", this);
  lbl = new QLabel("0", this);
  
  QGridLayout *grid = new QGridLayout(this);
  grid->addWidget(plsBtn, 0, 0);
  grid->addWidget(minBtn, 0, 1);
  grid->addWidget(lbl, 1, 1);

  setLayout(grid);  

  connect(plsBtn, &QPushButton::clicked, this, &PlusMinus::OnPlus);
  connect(minBtn, &QPushButton::clicked, this, &PlusMinus::OnMinus);
}

void PlusMinus::OnPlus() {
    
  int val = lbl->text().toInt();
  val++;
  lbl->setText(QString::number(val));
}

void PlusMinus::OnMinus() {
    
  int val = lbl->text().toInt();
  val--;
  lbl->setText(QString::number(val));
}

Chúng ta có hai button và một label. Hai button này sẽ tăng hoặc giảm biến đếm và hiển thị biến đếm lên label.

connect(plsBtn, &QPushButton::clicked, this, &PlusMinus::OnPlus);
connect(minBtn, &QPushButton::clicked, this, &PlusMinus::OnMinus);

Ở đây chúng ta định nghĩa từng slot cho signal clicked.

void PlusMinus::OnPlus() {
    
  int val = lbl->text().toInt();
  val++;
  lbl->setText(QString::number(val));
}

Trong phương thức OnPlus() chúng ta lấy biến đếm từ label. Label này chỉ trả về 1 string nên chúng ta phải chuyển sang kiểu số. Sau đó chúng ta tăng con số đó lên rồi chuyển ngược lại string để hiển thị lên label.

#include "plusminus.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  PlusMinus window;

  window.resize(300, 190);
  window.setWindowTitle("Plus minus");
  window.show();

  return app.exec();
}

File main.cpp.
Capture

Qt 5 C++ – File và thư mục

Trong phần này chúng ta sẽ học về cách làm việc với file và thư mục.

QFile, QDir, và QFileInfo là các lớp chuyên làm việc với file trong Qt5. QFile cho phép bạn đọc và ghi file, QDir cho phép bạn truy cập và xem nội dung thư mục. QFileInfo cung cấp các thông tin về file độc lập với hệ thống, bao gồm tên file, đường dẫn thư mục, thời gian chỉnh sửa lần cuối, quyền đọc ghi hay quyền sở hữu.

Kích thước file

Ví dụ dưới đây lấy kích thước của một file

#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
    qWarning("Usage: file_size file");
    return 1;
  }
  
  QString filename = argv[1];
  
  if (!QFile(filename).exists()) {
  
    qWarning("The file does not exist");
    return 1;
  }  
  
  QFileInfo fileinfo(filename);
  
  qint64 size = fileinfo.size();
  
  QString str = "The size is: %1 bytes";
  
  out << str.arg(size) << endl;
}

Kích thước file có thể truy xuất thông qua phương thức size().

QString filename = argv[1];

Chúng ta lấy tên file qua tham số truyền vào chương trình.

if (!QFile(filename).exists()) {
  qWarning("The file does not exist");
  return 1;
}

Kiểm tra xem file đã tồn tại hay chưa bằng phương thức exists(), nếu chưa tồn tại thì cho in thông báo và thoát chương trình.

QString str = "The size is: %1 bytes";

out << str.arg(size) << endl;

In kết quả ra màn hình.

The size is: 28029 bytes

Đọc file trong Qt

Muốn đọc file thì trước tiên phải mở file đó ra đã. Các phương thức mở file sẽ trả về một stream để đọc file, nếu đã từng lập trình với Java hay C# thì có lẽ bạn đã từng được học về khái niệm stream, còn nếu chưa thì bạn cứ hiểu stream giống như std::cin hay std::cout trong thư viện <iostream> của C++ ấy, chỉ khác là trong C++ thường thì cincout mặc định làm việc với màn hình và bàn phím, còn trong Qt thì làm việc với file.

#include <QTextStream>
#include <QFile>

int main(void) {
    
  QTextStream out(stdout);

  QFile file("colours");
  
  if (!file.open(QIODevice::ReadOnly)) {
      
    qWarning("Cannot open file for reading");
    return 1;
  }

  QTextStream in(&file);

  while (!in.atEnd()) {
      
    QString line = in.readLine();    
    out << line << endl;
  }

  file.close();
}

Ví dụ trên đọc dữ liệu từ file colours. Trong đó chứa tám string.

if (!file.open(QIODevice::ReadOnly)) {
  qWarning("Cannot open file for reading");
  return 1;
}

Phương thức open() sẽ mở file ở chế độc read-only. Nếu giá trị trả về false thì ta báo không mở file được và cho thoát chương trình.

QTextStream in(&file);

Tạo một đối tượng thuộc lớp QTextStream nhận vào đối tượng QFile. Đây chính là một stream và ta sẽ dùng nó để đọc file.

while (!in.atEnd()) {
    
  QString line = in.readLine();    
  out << line << endl;
}

Trong vòng lặp while, chúng ta sẽ đọc file theo từng dòng cho đến hết file bằng phương thức readLine(), mỗi dòng trong file sẽ được lưu vô một biến QString rồi in ra màn hình. Phương thức atEnd() kiểm tra xem đã hết file hay chưa.

file.close();

Khi đã đọc file xong ta dùng phương thức close() để đóng file.

red
green
blue
yellow
brown
white
black
violet

Ghi file trong Qt

Để ghi file thì cũng tương tự như đọc file, phải mở được file đó trước. Và cũng như đọc file, chúng ta sẽ có một stream riêng để ghi file.

#include <QTextStream>
#include <QFile>

int main(void) {
    
  QTextStream out(stdout);
    
  QString filename = "distros";
  QFile file(filename);
  
  if (file.open(QIODevice::WriteOnly)) {
      
    QTextStream out(&file);
    out << "Xubuntu" << endl;
    out << "Arch" << endl;
    out << "Debian" << endl;
    out << "Redhat" << endl;
    out << "Slackware" << endl;
    
  } else {
      
    qWarning("Could not open file");
  }  
  
  file.close(); 
}

Ví dụ trên ghi tên 5 phiên bản của hệ điều hành họ Linux vào một file tên là distros.

QString filename = "distros";
QFile file(filename);

Tạo một đối tượng QFile với tên file.

if (file.open(QIODevice::WriteOnly))

Mở file ở chế độ WriteOnly với phương thức open().

QTextStream out(&file);

Dòng code trên tạo ra một đối tượng QTextStream để ghi file.

out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;

Dùng toán tử << để ghi dữ liệu.

file.close(); 

Cuối cùng đóng file lại.

Xubuntu
Arch
Debian
Redhat
Slackware

Copy một file trong Qt

Xem ví dụ để biết cách copy file.

#include <QTextStream>
#include <QFile>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);
  
  if (argc != 3) {
      
      qWarning("Usage: copyfile source destination");
      return 1;
  }
  
  QString source = argv[1];
  
  if (!QFile(source).exists()) {
      qWarning("The source file does not exist");
      return 1;
  }
  
  QString destin(argv[2]);

  QFile::copy(source, destin);
}

Qt cung cấp phương thức QFile::copy() để copy một file, tham số gồm file gốc và file đích sẽ được tạo ra.

if (argc != 3) {
    
    qWarning("Usage: copyfile source destination");
    return 1;
}

Ở đây mình truyền vào chương trình 2 tham số là tên file gốc và tên file đích. Nếu không đủ cả 2 thì ta báo lỗi và thoát chương trình.

QString source = argv[1];

Lấy tên file gốc từ tham số đầu tiên.

if (!QFile(source).exists()) {
    qWarning("The source file does not exist");
    return 1;
}

Kiểm tra xem file gốc có tồn tại hay không, nếu không thì in ra thông báo lỗi và thoát chương trình.

QString destin(argv[2]);

Lấy file đích.

QFile::copy(source, destin);

Dùng phương thức QFile::copy() để copy file gốc sang file đích.

Làm việc với thư mục trong Qt

Lớp QDir được sinh ra để làm việc này. Xem ví dụ.

#include <QTextStream>
#include <QDir>

int main(void) {
    
  QTextStream out(stdout);
  QDir dir;
  
  if (dir.mkdir("mydir")) {
    out << "mydir successfully created" << endl;
  }
  
  dir.mkdir("mydir2");
  
  if (dir.exists("mydir2")) {
    dir.rename("mydir2", "newdir");    
  }
  
  dir.mkpath("temp/newdir");
}

Ở trên mình trình bày bốn phương thức thường dùng của lớp này.

if (dir.mkdir("mydir")) {
  out << "mydir successfully created" << endl;
}

Phương thức mkdir() tạo ra một thư mục. Trả về true nếu tạo thành công.

if (dir.exists("mydir2")) {
  dir.rename("mydir2", "newdir");    
}

Phương thức exists() kiểm tra xem một thư mục đã tồn tại hay chưa. Phương thức rename() đổi tên một thư mục.

dir.mkpath("temp/newdir");

Phương thức mkpath() tạo ra một thư mục và tạo cả các thư mục cha của nó nếu chưa có.

Một số hằng số về các thư mục đặc biệt

Trong hệ thống có một số thư mục đặc biệt, chẳng hạn như thư mục home hay thư mục gốc. Xem ví dụ.

#include <QTextStream>
#include <QDir>

int main(void) {
    
  QTextStream out(stdout);
   
  out << "Current path:" << QDir::currentPath() << endl;
  out << "Home path:" << QDir::homePath() << endl;
  out << "Temporary path:" << QDir::tempPath() << endl;
  out << "Rooth path:" << QDir::rootPath() << endl;
}

Ví dụ trên in ra bốn thư mục đặc biệt

out << "Current path:" << QDir::currentPath() << endl;

Phương thức QDir::currentPath() trả về thư mục mà mình đang làm việc chẳng hạn ở trên mình đang làm việc với file special_paths.cpp thì phương thức này sẽ in ra đường dẫn đến file này.

out << "Home path:" << QDir::homePath() << endl;

Phương thức QDir::homePath() trả về đường dẫn đến thư mục user của bạn.

out << "Temporary path:" << QDir::tempPath() << endl;

Phương thức QDir::tempPath() trả về đường dẫn đến thư mục temp của user của bạn.

out << "Rooth path:" << QDir::rootPath() << endl;

Phương thức QDir::rootPath() trả về đường dẫn đến thư mục gốc của bạn, nếu bạn dùng Windows thì thường nó là thư mục cài win, nếu dùng linux thì là dấu /.

Đường dẫn file

Xem ví dụ.

#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
      out << "Usage: file_times file" << endl;
      return 1;
  }
  
  QString filename = argv[1];
  
  QFileInfo fileinfo(filename);
  
  QString absPath = fileinfo.absoluteFilePath();
  QString baseName = fileinfo.baseName();
  QString compBaseName = fileinfo.completeBaseName();
  QString fileName = fileinfo.fileName();
  QString suffix = fileinfo.suffix();
  QString compSuffix = fileinfo.completeSuffix();
  
  out << "Absolute file path: " << absPath << endl;
  out << "Base name: " << baseName << endl;
  out << "Complete base name: " << compBaseName << endl;
  out << "File name: " << fileName << endl;
  out << "Suffix: " << suffix << endl;
  out << "Whole suffix: " << compSuffix << endl;
}

Trong ví dụ trên ta in ra đường dẫn của một file và trính xuất một vài thành phần trong đường dẫn đó.

QFileInfo fileinfo(filename);

Dùng lớp QFileInfo để làm việc này.

QString absPath = fileinfo.absoluteFilePath();

Phương thức absoluteFilePath() trả về đường dẫn đầy đủ của file.

QString baseName = fileinfo.baseName();

Phương thức baseName() trả về tên file, không lấy phần mở rộng của file, không lấy đường dẫn thư mục.

QString compBaseName = fileinfo.completeBaseName();

Phương thức completeBaseName() cũng giống với phương thức baseName().

QString fileName = fileinfo.fileName();

Phương thức fileName() trả về tên file và cả phần mở rộng của file.

QString suffix = fileinfo.suffix();

Phương thức suffix() trả về phần mở rộng của file.

QString compSuffix = fileinfo.completeSuffix();

Phương thức completeSuffix() cũng giống với phương thức suffix().

Liệt kê nội dung thư mục trong Qt

Ví dụ dưới đây liệt kê nội dung của một thư mục.

#include <QTextStream>
#include <QFileInfo>
#include <QDir>

int main(int argc, char *argv[]) {
    
  QTextStream out(stdout);

  if (argc != 2) {
      
      qWarning("Usage: list_dir directory");
      return 1;
  }
  
  QString directory = argv[1];
  
  QDir dir(directory);
  
  if (!dir.exists()) {
      qWarning("The directory does not exist");
      return 1;
  }  
  
  dir.setFilter(QDir::Files | QDir::AllDirs);
  dir.setSorting(QDir::Size | QDir::Reversed);

  QFileInfoList list = dir.entryInfoList();
  
  int max_size = 0;
  
  foreach (QFileInfo finfo, list) {
      
      QString name = finfo.fileName();
      int size = name.size();
      
      if (size > max_size) {
          
          max_size = size;
      }
  }
  
  int len = max_size + 2;
  
  out << QString("Filename").leftJustified(len).append("Bytes") << endl;
  
  for (int i = 0; i < list.size(); ++i) {
      
    QFileInfo fileInfo = list.at(i);
    QString str = fileInfo.fileName().leftJustified(len);
    str.append(QString("%1").arg(fileInfo.size()));
    out << str << endl;
  }
    
  return 0;
}

Dùng phương thức entryInfoList() trong lớp QDir để làm việc này. Trong code ví dụ trên chúng ta in ra danh sách đã được sắp xếp theo kích thước của file/thư mục. Chúng ta in ra hai cột, cột đầu tiên là tên file/thư mục, cột thứ hai là kích thước file/thư mục.

QDir dir(directory);

Tạo ra một đối tượng QDir.

dir.setFilter(QDir::Files | QDir::AllDirs);

Phương thức setFilter() có tác dụng lọc các thành phần mà mình muốn liệt kê, trong ví dụ mình lọc ra cả file và thư mục.

dir.setSorting(QDir::Size | QDir::Reversed);

Phương thức setSorting() sẽ tự động sắp xếp các thành phần trong thư mục dựa theo tham số truyền vào mà ở đây là sắp xếp đảo ngược theo kích thước.

QFileInfoList list = dir.entryInfoList();

Phương thức entryInfoList() sẽ trả về một đối tượng thuộc lớp QFileInfoList, lớp này là một danh sách các đối tượng QFileInfo đã được sắp xếp. Lớp này tương tự với QList<QFileInfo>.

foreach (QFileInfo finfo, list) {
    
    QString name = finfo.fileName();
    int size = name.size();
    
    if (size > max_size) {
        
        max_size = size;
    }
}

Duyệt qua danh sách và tìm ra file có độ dài lớn nhất. Mình lấy thông tin này ra để lát sau in kết quả ra màn hình thì có thể ngăn cách hai cột cho đẹp.

int len = max_size + 2;

Chúng ta cũng không dùng chính xác độ dài lớn nhất mà cộng thêm 2 vào nữa.

for (int i = 0; i < list.size(); ++i) {
  QFileInfo fileInfo = list.at(i);
  QString str = fileInfo.fileName().leftJustified(len);
  str.append(QString("%1").arg(fileInfo.size()));
  out << str << endl;
}

Cuối cùng ta duyệt qua toàn bộ danh sách và in kết quả ra màn hình.

Filename      Bytes
list_dir.pro  291
list_dir.cpp  1092
..            4096
.             4096
list_dir.o    10440
list_dir      19075
Makefile      28369

Trong bài này, bạn đã được học cách làm việc với file và thư mục trong Qt.

Qt 5 C++ – Một số lớp hữu ích

Trong phần này chúng ta sẽ học về một số lớp dùng để lưu trữ dữ liệu trong Qt 5. Đó là các lớp QVector, QList, QStringList, QSet, và QMap.

Trong thư viện chuẩn của C++ là Standard Template Library (STL) cũng có một số lớp có chức năng tương tự. Bạn có thể dùng các lớp đó hoặc các lớp mình sắp nói dưới đây, cái nào cũng tốt cả.

Các lớp này được chia làm hai loại, loại thứ nhất lưu dữ liệu theo kiểu danh sách, loại thứ hai lưu theo kiểu các cặp khóa-giá trị (key-value). QList, QVector, QLinkedList là các lớp lưu theo kiểu danh sách; còn QMap và QHash lưu theo kiểu cặp khóa-giá trị.

QVector

QVector lưu trữ dữ liệu theo kiểu danh sách. Các phần tử  trong QVector nằm kế tiếp nhau trong bộ nhớ và có thể truy cập thông qua chỉ số nên có tốc độ truy xuất dữ liệu rất nhanh nếu số lượng phần tử không nhiều. Nếu dữ liệu cần lưu trữ quá lớn thì tốt nhất là bạn nên dùng QList thay thế.

#include <QVector>
#include <QTextStream>

int main(void) {

    QTextStream out(stdout);

    QVector<int> vals = {1, 2, 3, 4, 5};
    
    out << "The size of the vector is: " << vals.size() << endl;
       
    out << "The first item is: " << vals.first() << endl;
    out << "The last item is: " << vals.last() << endl;
    
    vals.append(6);
    vals.prepend(0);   
    
    out << "Elements: "; 
    
    for (int val : vals) {
        out << val << " ";
    }    
    
    out << endl;
   
    return 0;
}

Ví dụ trên tạo ra một vector lưu trữ các số nguyên.

QVector<int> vals = {1, 2, 3, 4, 5};

Khởi tạo vector

out << "The size of the vector is: " << vals.size() << endl;

Phương thức size() trả về kích thước của vector – số lượng các phần tử có trong vector.

out << "The first item is: " << vals.first() << endl;

Truy xuất phần tử đầu tiên với phương thức first().

out << "The last item is: " << vals.last() << endl;

Truy xuất phần tử cuối cùng với phương thức last().

vals.append(6);

Phương thức append() chèn một phần tử vào sau vector.

vals.prepend(0);

Phương thức prepend() chèn một phần tử vào đầu vector.

for (int val : vals) {
    out << val << " ";
}    

Chúng ta duyệt qua vector và in các phần tử ra màn hình.

The size of the vector is: 5
The first item is: 1
The last item is: 5
Elements: 0 1 2 3 4 5 6 

QList

QList cũng có chức năng tương tự như QVector. Nó cũng lưu trữ dữ liệu theo kiểu danh sách và truy xuất các phần tử thông qua chỉ số, nhưng tốc độ chèn và xóa dữ liệu nhanh hơn QVector. Đây là một trong những lớp hay dùng nhất trong Qt

#include <QTextStream>
#include <QList>
#include <algorithm>

int main(void) {

    QTextStream out(stdout);

    QList<QString> authors = {"Balzac", "Tolstoy", "Gulbranssen", "London"};
  
    for (int i=0; i < authors.size(); ++i) {
        out << authors.at(i) << endl;
    }  
  
    authors << "Galsworthy" << "Sienkiewicz";
    
    out << "***********************" << endl;

    std::sort(authors.begin(), authors.end());

    out << "Sorted:" << endl;
    for (QString author : authors) {  
        out << author << endl;
    }  
}

Ví dụ cách sử dụng QList.

QList<QString> authors = {"Balzac", "Tolstoy", "Gulbranssen", "London"};

Khởi tạo QList là danh sách tên các nhà văn.

for (int i=0; i < authors.size(); ++i) {
    out << authors.at(i) << endl;
}  

Ta duyệt qua QList bằng vòng lặp for và in dữ liệu ra màn hình bằng phương thức at(). Phương thức này nhận tham số đầu vào là chỉ số phần tử.

authors << "Galsworthy" << "Sienkiewicz";

Dùng toán tử << để chèn một phần tử mới vào sau QList.

std::sort(authors.begin(), authors.end());

Phương thức std::sort() sắp xếp các phần tử trong QList.

out << "Sorted:" << endl;
for (QString author : authors) {
    out << author << endl;
} 

In ra danh sách đã được sắp xếp.

Balzac
Tolstoy
Gulbranssen
London
***********************
Sorted:
Balzac
Galsworthy
Gulbranssen
London
Sienkiewicz
Tolstoy

QStringList

QStringList cũng giống như QList nhưng chuyên để làm việc với string.

#include <QTextStream>
#include <QList>

int main(void) {

    QTextStream out(stdout);

    QString string = "coin, book, cup, pencil, clock, bookmark";
    QStringList items = string.split(",");
    QStringListIterator it(items);
    
    while (it.hasNext()) {
        out << it.next().trimmed() << endl;    
    }
}

Ví dụ trên tạo ra một danh sách các string và in các phần tử trong danh sách ra màn hình.

QString string = "coin, book, cup, pencil, clock, bookmark";
QStringList items = string.split(",");

Phương thức split() tách một chuỗi thành nhiều chuỗi con dựa vào dấu phân tách mà ta đưa vào.

QStringListIterator it(items);

QStringListIterator là một lớp chuyên dùng để thao tác với QStringList.

while (it.hasNext()) {
    out << it.next().trimmed() << endl;    
}

Sau khi đã khởi tạo iterator, chúng ta in từng phần tử ra màn hình. Phương thức trimmed() sẽ xóa các khoảng trống thừa trong chuỗi.

coin
book
cup
pencil
clock
bookmark

QSet

QSet lưu trữ các phần tử dữ liệu đơn lẻ. Các phần tử trong QSet không được lưu theo một thứ tự nào cả.

#include <QSet>
#include <QList>
#include <QTextStream>
#include <algorithm>

int main(void) {
    
    QTextStream out(stdout);

    QSet<QString> cols1 = {"yellow", "red", "blue"};
    QSet<QString> cols2 = {"blue", "pink", "orange"};
      
    out << "There are " << cols1.size() << " values in the set" << endl;
   
    cols1.insert("brown");
   
    out << "There are " << cols1.size() << " values in the set" << endl;
    
    cols1.unite(cols2);
    
    out << "There are " << cols1.size() << " values in the set" << endl;
    
    for (QString val : cols1) {
        out << val << endl;
    }
    
    QList<QString> lcols = cols1.values();
    std::sort(lcols.begin(), lcols.end());
    
    out << "*********************" << endl;
    out << "Sorted:" << endl;
    
    for (QString val : lcols) {
        out << val << endl;
    }    
   
   return 0;
}

Ví dụ trên tạo ra các QSet lưu tên của các màu. Không có phần tử nào xuất hiện hai lần trở lên – tức là giá trị của mỗi phần tử là khác nhau.

QSet<QString> cols1 = {"yellow", "red", "blue"};
QSet<QString> cols2 = {"blue", "pink", "orange"};

Chúng ta có hai tập dữ liệu, hai tập này chứa một phần tử chung là “blue”.

out << "There are " << cols1.size() << " values in the set" << endl;

Phương thức size() trả về kích thước của tập.

cols1.insert("brown");

Chúng ta thêm một phần tử mới vào tập bằng phương thức insert().

cols1.unite(cols2);

Phương thức unite() thực hiện phép hợp hai tập này. Tập cols1 sẽ thêm vào các phần tử có trong tập cols2 mà chưa có trong tập cols1, mà ở đây chính là phần tử “blue”.

for (QString val : cols1) {
    out << val << endl;
}

Ta dùng vòng lặp for để in các phần tử ra của tập cols1 màn hình.

QList<QString> lcols = cols1.values();
std::sort(lcols.begin(), lcols.end());

Chúng ta không thể sắp xếp một tập hợp. Nếu cần bạn có thể tạo ra một QList rồi sắp xếp, phương thức values() sẽ trả về một QList chứa các phần tử trong tập, nhưng thứ tự của các phần tử trong tập là ngẫu nhiên.

There are 3 values in the set
There are 4 values in the set
There are 6 values in the set
pink
orange
brown
blue
yellow
red
*********************
Sorted:
blue
brown
orange
pink
red
yellow

QMap

QMap lưu dữ liệu theo kiểu các cặp khóa-giá trị (key-value). Chúng ta có thể truy xuất phần tử thông qua key.

#include <QTextStream>
#include <QMap>

int main(void) {

    QTextStream out(stdout);

    QMap<QString, int> items = { {"coins", 5}, {"books", 3} };
    
    items.insert("bottles", 7);
    
    QList<int> values = items.values();
    
    out << "Values:" << endl;

    for (int val : values) {
        out << val << endl;
    }
    
    QList<QString> keys = items.keys();

    out << "Keys:" << endl;
    for (QString key : keys) {
        out << key << endl;
    }    
    
    QMapIterator<QString, int> it(items);
    
    out << "Pairs:" << endl;
    
    while (it.hasNext()) {
        it.next();
        out << it.key() << ": " << it.value() << endl;    
    }
}

Trong ví dụ trên, chúng ta có các cặp key, mỗi key có một value.

QMap<QString, int> items = { {"coins", 5}, {"books", 3} };

Ta tạo ra một QMap chứa hai cặp key-value.

items.insert("bottles", 7);

Ta thêm một cặp key-value bằng phương thức insert().

QList<int> values = items.values();

out << "Values:" << endl;

for (int val : values) {
    out << val << endl;
}

Chúng ta in toàn bộ các cặp ra màn hình. Ngoài ra ta có thể dùng phương thức values() để lấy về một QList chứa các value.

QList<QString> keys = items.keys();

out << "Keys:" << endl;
for (QString key : keys) {
    out << key << endl;
} 

Ta in các key ra màn hình. Cũng giống như trên, ta có thể dùng phương thức keys() để lấy về một QList các key.

QMapIterator<QString, int> it(items);

QMapIterator là biến lặp iterator cho QMap, dùng để thao tác với QMap.

while (it.hasNext()) {
    it.next();
    out << it.key() << ": " << it.value() << endl;    
}

Ta dùng iterator này để duyệt qua QMap. Phương thức key() sẽ trả về khóa và phương thức value() sẽ trả về giá trị.

Values:
3
7
5
Keys:
books
bottles
coins
Pairs:
books: 3
bottles: 7
coins: 5

Sắp xếp các lớp do người dùng định nghĩa

Trong ví dụ dưới đây, chúng ta sẽ sắp xếp một QList chứa các đối tượng thuộc lớp Book do chúng ta tự định nghĩa.

class Book {
    
    public:
        Book(QString, QString);
        QString getAuthor() const;
        QString getTitle() const;
        
    private:            
        QString author;
        QString title;    
};

Đây là file header của lớp Book.

#include <QString>
#include "book.h"

Book::Book(QString auth, QString tit) {
    
    author = auth;
    title = tit;
}

QString Book::getAuthor() const {
    
    return author;
}

QString Book::getTitle() const {
    
    return title;
}

Đây là file cài dặt các phương thức của lớp Book, trong lớp này ta cài đặt hai phương thức truy xuất dữ liệu.

#include <QTextStream>
#include <QList>
#include <algorithm> 
#include "book.h"

bool compareByTitle(const Book &b1, const Book &b2) {
    
  return b1.getTitle() < b2.getTitle();
}

int main(void) {

    QTextStream out(stdout);

    QList<Book> books = {
        Book("Jack London", "The Call of the Wild"),
        Book("Honoré de Balzac", "Father Goriot"),
        Book("Leo Tolstoy", "War and Peace"),
        Book("Gustave Flaubert", "Sentimental education"),
        Book("Guy de Maupassant", "Une vie"),
        Book("William Shakespeare", "Hamlet")
    };
    
    std::sort(books.begin(), books.end(), compareByTitle);
    
    for (Book book : books) {
        out << book.getAuthor() << ": " << book.getTitle() << endl;
    }
}

Ta tạo ra vài đối tượng Book, đưa chúng vô QList và sắp xếp với hàm std::sort.

bool compareByTitle(const Book &b1, const Book &b2) {    
  return b1.getTitle() < b2.getTitle();
}

Phương thức compareByTitle() là phương thức so sánh sẽ được sử dụng bởi hàm sort.

std::sort(oks.begin(), books.end(), compareByTitle);

Hàm std::sort sẽ sắp xếp các book dựa theo tựa book.

Honoré de Balzac: Father Goriot
William Shakespeare: Hamlet
Gustave Flaubert: Sentimental education
Jack London: The Call of the Wild
Guy de Maupassant: Une vie
Leo Tolstoy: War and Peace

Trong bài này, chúng ta đã học về một số lớp lưu trữ dữ liệu thường dùng trong Qt 5.

Qt 5 C++ – Ngày và giờ

Trong phần này chúng ta sẽ bàn về hệ thống ngày giờ trong Qt 5

Qt5 có các lớp QDate, QTimeQDateTime chuyên dùng để làm việc với ngày giờ. Lớp QDate là lớp dùng cho ngày tháng trong dương lịch, lớp này có các phương thức dùng để xác định ngày, so sánh hay chỉnh sửa ngày. Lớp QTime dùng cho giờ, có các phương thức dùng để so sánh giờ, xác định giờ và các phương thức khác để làm việc với giờ. Lớp QDateTime là lớp vừa làm việc được với ngày, vừa làm việc với giờ, lớp này là gộp chung của lớp QDateQTime.

Khởi tạo các đối tượng ngày và giờ

Chúng ta có thể khởi tạo bằng cách dùng các constructor để tạo dữ liệu hoặc tạo đối tượng rỗng rồi đưa dữ liệu vào sau cũng được.

#include <QTextStream>
#include <QDate>
#include <QTime>

int main(void) {
    
   QTextStream out(stdout);

   QDate dt1(2015, 4, 12);
   out << "The date is " << dt1.toString() << endl;
   
   QDate dt2;
   dt2.setDate(2015, 3, 3);
   out << "The date is " << dt2.toString() << endl;

   QTime tm1(17, 30, 12, 55);
   out << "The time is " << tm1.toString("hh:mm:ss.zzz") << endl;
   
   QTime tm2;
   tm2.setHMS(13, 52, 45, 155);
   out << "The time is " << tm2.toString("hh:mm:ss.zzz") << endl;   
}

Ở trên chúng ta khởi tạo các đối tượng bằng cả hai cách.

QDate dt1(2015, 4, 12);

Hàm khởi tạo đối tượng QDate nhận 3 tham số đầu vào: year, month và day.

out << "The date is " << dt1.toString() << endl;

Ta in ngày tháng ra màn hình, sử dụng phương thức toString() để chuyển một đối tượng thành một chuỗi.

QTime tm2;
tm2.setHMS(13, 52, 45, 155);

Ở đây ta tạo ra một đối tượng rỗng, sau đó đưa dữ liệu vào thông qua phương thức setHMS(). Tham số gồm có hour, minute, second và milisecond.

out << "The time is " << tm2.toString("hh:mm:ss.zzz") << endl;

Sau đó ta lại in giờ ra màn hình. Bên trong phương thức toString() bạn có thể dịnh dạng cho output cần hiển thị theo ý muốn của bạn. Ở trên mình dùng định dạng hh:mm:ss.zz, đây cũng là định dạng mặc định của hệ thống.

The date is Sun Apr 12 2015
The date is Tue Mar 3 2015
The time is 17:30:12.055
The time is 13:52:45.155

Ngày giờ hiện tại

Trong ví dụ dưới đây, chúng ta sẽ in ra ngày giờ hiện tại của hệ thống.

#include <QTextStream>
#include <QTime>
#include <QDate>

int main(void) {
    
   QTextStream out(stdout);

   QDate cd = QDate::currentDate();
   QTime ct = QTime::currentTime();
   
   out << "Current date is: " << cd.toString() << endl;
   out << "Current time is: " << ct.toString() << endl;      
}
QDate cd = QDate::currentDate();

Phương thức tĩnh QDate::currentDate() sẽ trả về ngày hiện tại.

QTime ct = QTime::currentTime();

Phương thức tĩnh QTime::currentTime() trả về giờ hiện tại.

Current date is: Fri Oct 30 2015
Current time is: 20:55:27

So sánh ngày

Ta có thể dùng các toán tử so sánh để so sánh ngày. Xem ví dụ.

#include <QTextStream>
#include <QDate>

int main(void) {
    
   QTextStream out(stdout);

   QDate dt1(2015, 4, 5);
   QDate dt2(2014, 4, 5);
   
   if (dt1 < dt2) {
       out << dt1.toString() << " comes before " << dt2.toString() << endl;
   } else {
       out << dt1.toString() << " comes after " << dt2.toString() << endl;
   }   
}

Đoạn code ví dụ trên so sánh 2 ngày.

if (dt1 < dt2) {
    out << dt1.toString() << " comes before " << dt2.toString() << endl;
} else {
    out << dt1.toString() << " comes after " << dt2.toString() << endl;
}   

Ta dùng toán tử bé hơn “<” để biết ngày nào xuất hiện trước trong lịch.

Sun Apr 5 2015 comes after Sat Apr 5 2014

Ta có thể dùng các toán tử so sánh này với cả lớp QTimeQDateTime.

Tìm năm nhuận

Một năm có 365 ngày, năm nhuận là năm có 366 ngày. Lý do tại sao có năm nhuận là vì một năm hay một vòng quay của Trái đất quanh mặt trời không phải là mất chính xác 365 ngày, mà là 365.25 ngày (hay 365 ngày và 6 giờ). Do vậy, để lịch của chúng ta trùng khớp với tự nhiên, cứ 4 năm ta lại thêm 1 ngày vào tháng 2. Tức là năm thường tháng 2 có 28 ngày, năm nhuận tháng 2 có 29 ngày.

Thường thì trong các ngôn ngữ thường dùng như C++, Java không có sẵn hàm tính năm nhuận, bạn phải code lại từ đầu. Qt có phương thức QDate::isLeapYear() dùng để xác định xem một năm có phải là năm nhuận hay không.

#include <QTextStream>
#include <QDate>

int main() {
    
   QTextStream out(stdout);
         
   QList<int> years({2010, 2011, 2012, 2013, 2014, 2015, 2016});       
                  
   foreach (int year, years) {
       if (QDate::isLeapYear(year)) {
           out << year << " is a leap year" << endl;
       } else {
           out << year << " is not a leap year" << endl;
       }
   }     
}

Trong ví dụ trên chúng ta có một danh sách các năm. Chúng ta sẽ xem thử có năm nào là năm nhuận không.

QList<int> years({2010, 2011, 2012, 2013, 2014, 2015, 2016});  

Dòng trên chúng ta khởi tạo một list các năm. Đây là cách khởi tạo trong C++11. Vì thế nên bạn cần khai báo C++11 trong file project bằng cách thêm một trong các dòng sau:

  • CONFIG += c++11
  • QMAKE_CXXFLAGS += -std=c++11
  • QMAKE_CXXFLAGS += -std=c++0x.
foreach (int year, years) {
    if (QDate::isLeapYear(year)) {
        out << year << " is a leap year" << endl;
    } else {
        out << year << " is not a leap year" << endl;
    }
}    

Chúng ta duyệt qua hết các năm trong list và dùng phương thức QDate::isLeapYear() để biết năm nào là năm nhuận, phương thức này trả về kiểu bool.

TEMPLATE = app
TARGET = leapyear
INCLUDEPATH += .
CONFIG += c++11

# Input
SOURCES += leapyear.cpp

QT -= gui

Đây là file project. Chúng ta thêm dòng khai báo CONFIG += c++11 để kích hoạt C++11. Ngoài ra chúng ta cũng tắt module gui đi bằng dòng QT -= gui vì ở đây chúng ta đang viết ứng dụng dòng lệnh nên gui cũng không cần thiết lắm.

2010 is not a leap year
2011 is not a leap year
2012 is a leap year
2013 is not a leap year
2014 is not a leap year
2015 is not a leap year
2016 is a leap year

Các định dạng ngày tháng có sẵn

Qt5 có một số định dạng ngày tháng có sắn. Ở trên mình đã nói trong phương thức toString() bạn có thể thêm một chuỗi định dạng vô để in ra kết quả như ý muốn. Ngoài ra bạn có thể sử dụng một số hằng có sẵn trong Qt5. Mặc định nếu bạn không dùng thì hệ thống sẽ sử dụng định dạng Qt::TextDate. Xem ví dụ.

#include <QTextStream>
#include <QDate>
int main(void) {

   QTextStream out(stdout);

   QDate cd = QDate::currentDate();
   
   out << "Today is " << cd.toString(Qt::TextDate) << endl;
   out << "Today is " << cd.toString(Qt::ISODate) << endl;
   out << "Today is " << cd.toString(Qt::SystemLocaleShortDate) << endl;
   out << "Today is " << cd.toString(Qt::SystemLocaleLongDate) << endl;
   out << "Today is " << cd.toString(Qt::DefaultLocaleShortDate) << endl;
   out << "Today is " << cd.toString(Qt::DefaultLocaleLongDate) << endl;
   out << "Today is " << cd.toString(Qt::SystemLocaleDate) << endl;
   out << "Today is " << cd.toString(Qt::LocaleDate) << endl;   
}

Trong ví dụ trên, mình in ra 8 định dạng ngày tháng khác nhau.

out << "Today is " << cd.toString(Qt::ISODate) << endl;

Định dạng Qt::ISODate là định dạng tiêu chuẩn quốc tế.

Today is Sat Oct 31 2015
Today is 2015-10-31
Today is 10/31/15
Today is Saturday, October 31, 2015
Today is 10/31/15
Today is Saturday, October 31, 2015
Today is 10/31/15
Today is 10/31/15

Tùy chỉnh định dạng ngày tháng

Như đã nói ở trên, ngoài các định có sẵn trong Qt thì các bạn có thể tự tùy chỉnh định dạng ngày tháng cho hợp với ý mình. Bảng dưới đây mô tả các đặc tả về định dạng ngày tháng:

ĐẶC TẢ KẾT QUẢ
d In ra ngày mà không có số 0 dẫn đầu (1 →  31)
dd In ra ngày có một số 0 dẫn đầu (01 →  31)
ddd In ra tên viết tắt của ngày trong tuần (vd Mon →  Sun). Sử dụng QDate::shortDayName().
dddd In ra tên đầy đủ của ngày trong tuần (vd Monday →   Sunday). Sử dụng QDate::longDayName().
M In ra tháng không có số 0 dẫn đầu (1 →  12)
MM In ra tháng có một số 0 dẫn đầu (01 → 12)
MMM In ra tên viết tắt của tháng (vd Jan →  Dec). Sử dụng QDate::shortMonthName().
MMMM In ra tên đầy đủ của tháng (vd January →  December). Sử dụng QDate::longMonthName().
yy In ra hai số cuối của năm (00 →  99)
yyyy In ra đầy đủ bốn số của năm.
#include <QTextStream>
#include <QDate>
int main(void) {

   QTextStream out(stdout);

   QDate cd = QDate::currentDate();
   
   out << "Today is " << cd.toString("yyyy-MM-dd") << endl;
   out << "Today is " << cd.toString("yy/M/dd") << endl;
   out << "Today is " << cd.toString("d. M. yyyy") << endl;
   out << "Today is " << cd.toString("d-MMMM-yyyy") << endl; 
}

Trong code ví dụ trên mình in ra bốn kiểu định dạng khác nhau.

out << "Today is " << cd.toString("yyyy-MM-dd") << endl;

Đây là định dạng tiêu chuẩn quốc tế. Năm được ghi ra đủ 4 chữ số, ngày tháng ngăn cách nhau bởi dấu gạch ngang “-“. Tháng và ngày được in ra 2 chữ số, bao gồm cả chữ số 0 dẫn đầu (nếu có).

out << "Today is " << cd.toString("yy/M/dd") << endl;

Đây cũng là một định dạng phổ biến. Ngày tháng được ngăn cách bởi dấu “/”. Tháng được in một chữ số (không có số 0 dẫn đầu).

Today is 2015-10-31
Today is 15/10/31
Today is 31. 10. 2015
Today is 31-October-2015

Lấy thứ trong tuần

Phương thức dayOfWeek() trả về một con số từ 1 đến 7 đại diện cho Thứ Hai (Monday) đến Chủ Nhật (Sunday).

#include <QTextStream>
#include <QDate>

int main(void) {

   QTextStream out(stdout);

   QDate cd = QDate::currentDate();
   int wd = cd.dayOfWeek();
   
   out << "Today is " << QDate::shortDayName(wd) << endl;
   out << "Today is " << QDate::longDayName(wd) << endl;          
}

Trong ví dụ trên ta in ra thứ trong tuần theo hai dạng viết tắt và viết đầy đủ.

QDate cd = QDate::currentDate();

Lấy ngày hiện tại

int wd = cd.dayOfWeek();

Lấy thứ trong tuần.

out << "Today is " << QDate::shortDayName(wd) << endl;

Lấy tên viết tắt của thứ bằng phương thức QDate::shortDayName().

out << "Today is " << QDate::longDayName(wd) << endl;    

Lấy tên đầy đủ của thứ bằng phương thức QDate::longDayName().

Today is Sat
Today is Saturday

Lấy số ngày trong tháng và năm

Ta có thể biết trong tháng hay trong năm có bao nhiêu ngày bằng phương thức daysInMonth() và phương thức daysInYear().

#include <QTextStream>
#include <QDate>

int main(void) {
    
   QTextStream out(stdout);
   QList<QString>months;
   
   months.append("January");
   months.append("February");
   months.append("March");
   months.append("April");
   months.append("May");
   months.append("June");
   months.append("July");
   months.append("August");
   months.append("September");
   months.append("October");
   months.append("November");
   months.append("December");

   QDate dt1(2015, 9, 18);
   QDate dt2(2015, 2, 11);
   QDate dt3(2015, 5, 1);
   QDate dt4(2015, 12, 11);
   QDate dt5(2015, 1, 21);
   
   out << "There are " << dt1.daysInMonth() << " days in " 
       << months.at(dt1.month()-1) << endl;      
   out << "There are " << dt2.daysInMonth() << " days in " 
       << months.at(dt2.month()-1) << endl;
   out << "There are " << dt3.daysInMonth() << " days in " 
       << months.at(dt3.month()-1) << endl;
   out << "There are " << dt4.daysInMonth() << " days in " 
       << months.at(dt4.month()-1) << endl;
   out << "There are " << dt5.daysInMonth() << " days in " 
       << months.at(dt5.month()-1) << endl;
   
   out << "There are " << dt1.daysInYear() << " days in year " 
       << QString::number(dt1.year()) << endl;         
}

Chúng ta tạo năm đối tượng QDate và tính số ngày trong tháng và trong năm.

out << "There are " << dt1.daysInMonth() << " days in " << months.at(dt1.month()-1) << endl;   

Chúng ta sử dụng phương thức daysInMonth() để lấy số ngày trong đối tượng này.

out << "There are " << dt1.daysInYear() << " days in year " << QString::number(dt1.year()) << endl;      

Ở đây chúng ta lấy số ngày trong năm bằng phương thức daysInYear().

There are 30 days in September
There are 28 days in February
There are 31 days in May
There are 31 days in December
There are 31 days in January
There are 365 days in year 2015

Kiểm tra tính hợp lệ của ngày tháng

Phương thức isValid() được dùng để làm việc này. Xem ví dụ

#include <QTextStream>
#include <QDate>

int main(void) {
    
   QTextStream out(stdout);
   
   QList<QDate> dates({QDate(2015, 5, 11), QDate(2015, 8, 1), QDate(2015, 2, 30)});
   
   for (int i=0; i < dates.size(); i++) {
   
       if (dates.at(i).isValid()) {
           out << "Date " << i+1 << " is a valid date" << endl;
       } else {
           out << "Date " << i+1 << " is not a valid date" << endl;
       }
    }
}

Chúng ta kiểm tra sự hợp lệ của ba đối tượng ngày tháng.

QList<QDate> dates({QDate(2015, 5, 11), QDate(2015, 8, 1), QDate(2015, 2, 30)});

Hai ngày đầu tiên là hợp lệ. Cái cuối cùng không hợp lệ vì tháng hai chỉ có 28 hoặc 29 ngày.

Date 1 is a valid date
Date 2 is a valid date
Date 3 is not a valid date

Tính số ngày từ một ngày cụ thể

Chúng ta có thể tính số ngày từ một ngày cụ thể sử dụng phương thức addDays()  daysTo().

#include <QTextStream>
#include <QDate>

int main(void) {
    
   QTextStream out(stdout);

   QDate dt(2015, 5, 11);  
   QDate nd = dt.addDays(55);
   
   QDate xmas(2015, 12, 24);
   
   out << "55 days from " << dt.toString() << " is " << nd.toString() << endl;   
   out << "There are " << QDate::currentDate().daysTo(xmas) << " days till Christmas" << endl;       
}

Chúng ta cộng thêm 55 ngày từ ngày 11/5/2015 và tính số ngày từ ngày hiện tại đến ngày giáng sinh năm 2015.

QDate dt(2015, 5, 11);  
QDate nd = dt.addDays(55);

Phương thức addDays() trả về một QDate.

QDate xmas(2015, 12, 24);
...
out << "There are " << QDate::currentDate().daysTo(xmas) << " days till Christmas" << endl; 

Dùng phương thúc daysTo() để tính số ngày từ đây đến giáng sinh.

55 days from Mon May 11 2015 is Sun Jul 5 2015
There are 54 days till Christmas

Lớp QDateTime

Lớp QDateTime chứa cả lớp QDate và lớp QTime. Các phương thức của lớp QDateTime cũng giống như của hai lớp kia. Xem ví dụ.

#include <QTextStream>
#include <QDateTime>

int main(void) {

   QTextStream out(stdout);
   QDateTime cdt = QDateTime::currentDateTime();   
   
   out << "The current datetime is " << cdt.toString() << endl;
   out << "The current date is " << cdt.date().toString() << endl;
   out << "The current time is " << cdt.time().toString() << endl;   
}

Ví dụ trên lấy ngày giờ của ngày hiện tại.

The current datetime is Sat Oct 31 17:10:50 2015
The current date is Sat Oct 31 2015
The current time is 17:10:50

Qt 5 C++ – Kiểu String

Ở bài này chúng ta sẽ làm việc với chuỗi. Qt5 có một lớp tên là QString chuyên dùng để làm việc với chuỗi. Lớp này chứa rất nhiều phương thức hữu ích.

Lớp QString lưu trữ các ký tự bằng lớp QChar 16 bit. Mỗi QChar tương ứng với một ký tự Unicode.

Trong ví dụ dưới đây, chúng ta sẽ không dùng đến module GUI, dùng dòng lệnh là được rồi. Khi bạn tạo một project Qt Console Application thì mặc nhiên trong file *.pro đã loại bỏ module gui bằng dòng QT -= gui. Nhưng nếu bạn tạo project Widget Application thì module GUI được Qt Creator tự động thêm vào nên bạn có thể bỏ module này ra bằng cách thêm dòng QT -= gui trong file *.pro.

Ví dụ

Trong ví dụ dưới đây, chúng ta sẽ sử dụng một số phương thức cơ bản của lớp QString

#include <QTextStream>

int main()
{
     QTextStream out(stdout);

     QString a = "Pho";

     a.append(" Code");
     a.prepend("Hello ");

     out << a << endl;
     out << "Chuoi a co " << a.count() << " ky tu"<< endl;

     out << a.toUpper() << endl;
     out << a.toLower() << endl;
     
     return 0;
}

Trong đoạn code trên, chúng ta khởi tạo một QString. Sau đó gắn thêm vài từ vào trước và sau chuỗi. In ra chiều dài của chuỗi. Cuối cùng, ta chuyển chuỗi nãy thành dạng viết HOA và viết thường.

QString a = "Pho";

Khởi tạo một QString.

a.append(" Code");
a.prepend("Hello ");

Nối thêm chuỗi mới vào trước và sau chuỗi ban đầu.

out << a << endl;

In dòng “Hello Pho Code” ra màn hình.

out << "Chuoi a co " << a.count() << " ky tu" << endl;

Phương thức count() trả về số ký tự trong chuỗi. Ngoài ra lớp QString còn cung cấp 2 phương thức có chức năng tương tự là length()size().

out << a.toUpper() << endl;
out << a.toLower() << endl;

Hai phương thức trên trả về chuỗi gốc đã được chuyển thành in HOA và in thường. Hai phương thức này không thay đổi nội dung của chuỗi gốc mà chỉ là copy chuỗi gốc ra một nơi khác rồi thay đổi nội dung của chuỗi mới và trả về cho chúng ta chuỗi mới.

Hello Pho Code
Chuoi a co 14 ky tu
HELLO PHO CODE
hello pho code

Khởi tạo chuỗi

Có rất nhiều cách để khởi tạo một QString.

#include <QTextStream>
int main()
{
     QTextStream out(stdout);

     QString str1 = "The night train";
     out << str1 << endl;

     QString str2("A yellow rose");
     out << str2 << endl;

     std::string s1 = "A blue sky";
     QString str3 = s1.c_str();
     out << str3 << endl;

     std::string s2 = "A thick fog";
     QString str4 = QString::fromLatin1(s2.data(), s2.size());
     out << str4 << endl;

     char s3[] = "A deep forest";
     QString str5(s3);
     out << str5 << endl;

     return 0;
}

Đoạn code trên khởi tạo QString bằng 5 cách khác nhau.

QString str1 = "The night train";

 

Cách khởi tạo này giống với hầu hết các ngôn ngữ lập trình thông thường.

QString str2("A yellow rose");

Khởi tạo theo kiểu đối tượng

std::string s1 = "A blue sky";
QString str3 = s1.c_str();

Ở đây chúng ta có một đối tượng string trong thư viện chuẩn của C++. Chúng ta sử dụng phương thức c_str() để lấy một mảng các ký tự kiểu char. Đây là cách lưu trữ chuỗi trong ngôn ngữ C, và ta có thể dùng nó để gán vào QString.

std::string s2 = "A thick fog";
QString str4 = QString::fromLatin1(s2.data(), s2.size());

Đoạn code trên chuyển đổi chuỗi kiểu string trong C++ thành QString bằng phương thức fromLatin1(). Tham số đầu vào là con trỏ chỉ đến một mảng ký tự. Con trỏ này có thể lấy từ phương thức data(). Tham số thứ hai là kích thước của std::string.

char s3[] = "A deep forest";
QString str5(s3);

Đây là một mảng ký tự trong ngôn ngữ C. Các phương thức khởi tạo của QString có thể nhận bất kỳ một mảng ký tự bất kỳ để khởi tạo.

The night train
A yellow rose
A blue sky
A thick fog
A deep forest

Truy xuất phần tử chuỗi

Như đã nói, QString là một dãy các QChar. Mỗi phần tử của QString có thể được truy xuất bằng toán tử [] hoặc phương thức at().

#include <QTextStream>

int main()
{
     QTextStream out(stdout);

     QString a = "Eagle";

     out << a[0] << endl;
     out << a[4] << endl;

     out << a.at(0) << endl;

     if(a.at(5).isNull() 
     {
          out << "Outside the range of the string" << endl;
     }
}

Đoạn code trên in ra một số ký tự tại một số vị trí cụ thể trong QString.

out << a[0] << endl;
out << a[4] << endl;

Chúng ta in ra phần tử thứ nhất và thứ 4 trong chuỗi

out << a.at(0) << endl;

Phương thức at() lấy ký tự đầu tiên của chuỗi.

if(a.at(5).isNull())
{
     out << "Outside the range of the string" << endl;
}

Phương thức at() sẽ trả về NULL nếu chúng ta truy xuất phần tử nằm ngoài phạm vi mảng.

E
e
E
Outside the range of the string

Lấy độ dài chuỗi

Có 3 phương thức để làm được việc này. Đó là size(), count()length(). Cả 3 phương thức này đều cho ra kết quả giống nhau là số ký tự có trong chuỗi hay độ dài của chuỗi.

#include <QTextStream>

int main()
{
     QTextStream out(stdout);

     QString s1 = "Eagle";
     QString s2 = "Eagle\n";
     QString s3 = "Eagle ";

     out << s1.length() << endl;
     out << s2.size() << endl;
     out << s3.count() << endl;
}

Đoạn code trên lấy kích thước của 3 chuỗi.

QString s2 = "Eagle\n;
QString s3 = "Eagle ";

Cả hai chuỗi trên đều chứa ký tự trắng – dấu cách.

5
6
6

Định dạng chuỗi

Chúng ta có thể dùng các ký tự điều khiển để làm tham số và chúng sẽ được thay thế bằng các giá trị thực. Xem ví dụ để hiểu thêm.

#include <QTextStream>

int main()
{
     QTextStream out(stdout);

     QString s1 = "There are %1 white roses";
     int n = 12;

     out << s1.arg(n) << endl;

     QString s2 = "The tree is %1 m high";
     double h = 5.65;
    
     out << s2.arg(h) << endl;

     QString s3 = "We have %1 lemons and %2 oranges";
     int ln = 12;
     int on = 4;

     out << s3.arg(ln).arg(on) << endl;

     return 0;
}

Tham số ở đây là %, theo sau là số thứ tự. Trong một chuỗi có thể có nhiều tham số như vậy. Phương thức arg() có thể nhận kiểu integer, long, char hay cả QChar.

QString s1 = "There are %1 white roses";
int n = 12;

%1 là tham số sẽ được thay thế.

out << s1.arg(n) << endl;

Phương thức arg() nhận vào 1 số nguyên. %1 sẽ được thay thế bởi biến n.

QString s2 = "The tree is %1 m high";
double h = 5.65;

out << s2.arg(h) << endl;

 

3 dòng trên cũng làm việc tương tự, chỉ khác là thay số nguyên bằng số thực.

QString s3 = "We jave %1 lemons and %2 oranges";
int ln = 12;
int on = 4;

out << s3.arg(ln).arg(on) << endl;

Ở đây chúng ta có 2 tham số. Tham số %1 lấy giá trị từ biến ln, %2 lấy giá trị từ biến on.

There are 12 white roses
The tree is 5.65 m high
We have 12 lemons and 4 oranges

Lấy chuỗi con

Các phương thức left(), mid() và right() có thể được dùng để lấy chuỗi con. Ví dụ:

#include <QTextStream>
int main() 
{

   QTextStream out(stdout);
   
   QString str = "The night train";
   
   out << str.right(5) << endl;
   out << str.left(9) << endl;
   out << str.mid(4, 5) << endl;
   
   QString str2("The big apple");
   QStringRef sub(str2, 0, 7);
   
   out << sub.toString() << endl;
   
   return 0;
}

Ta dùng cả 3 phương thức để lấy chuỗi con.

out << str.right(5) << endl;

Phương thức right(5) lấy 5 ký tự cuối cùng của chuôi

out << str.left(9) << endl;

Phương thức left(9) lấy 9 ký tự đầu tiên của chuỗi.

out << str.mid(4, 5) << endl;

mid(4, 5) lấy 5 ký tự từ vị trí số 4 trong chuỗi.

 
QString str2("The big apple");
QStringRef sub(str2, 0, 7);

Lớp QStringRef là một lớp tương tự như lớp QString, nhưng QStringRef là một lớp read-only, tức là khi đã khởi tạo ra rồi thì không thể thay đổi nội dung chuỗi được nữa. Tham số của phương thức khởi tạo lớp QStringRef nhận đầu vào là một chuỗi, tham số thứ 2 là vị trí bắt đầu, và tham số thứ 3 là độ dài chuỗi được lấy.

train
The night
night
The big

Duyệt chuỗi

Bản thân QString là một kiểu dữ liệu dạng mảng/danh sách nên ta có thể duyệt qua từng phần tử của chuỗi.

#include <QTextStream>

int main()
{
     QTextStream out(stdout);

     QString str = "There are many stars.";
     
     foreach(QChar qc, str)      
          out << qc << " ";     

     out << endl;

     for(QChar *it = str.begin() ; it != str.end() ; ++it)
          out << *it << " ";

     out << endl;

     for(int i = 0 ; i < str.size() ; ++i)
          out << str.at(i) << " ";

     out << endl;
     return 0;
}

Ở đây mình dùng 3 dạng vòng lặp. Mỗi lần lặp qua một ký tự, ta in ký tự đó ra màn hình kèm theo dấu cách ” “.

T h e r e   a r e   m a n y   s t a r s . 
T h e r e   a r e   m a n y   s t a r s . 
T h e r e   a r e   m a n y   s t a r s . 

So sánh chuỗi

Trong QString có một phương thức tĩnh là QString::compare() dùng để so sánh hai chuỗi. Phương thức này trả về một số nguyên.

Giá trị trả về:

  • < 0: chuỗi 1 bé hơn chuỗi 2
  • = 0: 2 chuỗi bằng nhau
  • > 0: chuỗi 1 lớn hơn chuỗi 2

Một ký tự được gọi là bé hơn một ký tự khác khi vị trí của nó bé hơn ký tự kia trong bảng chữ cái. Chẳng hạn A < D, E < Z… Nếu hai ký tự giống nhau nhưng khác chữ HOA và thường, thì chữ hoa bé hơn, vd R < r. Để so sánh hai chuỗi, ký tự đầu tiên của từng chuỗi sẽ được so sánh với nhau, nếu chúng bằng nhau thì tiếp tục so sánh ký tự tiếp theo cho đến khi có 2 ký tự khác nhau, hoặc đã hết ký tự để so sánh – tức là 2 chuỗi bằng nhau.

#include <QTextStream>

#define STR_EQUAL 0

int main() {

   QTextStream out(stdout);
   
   QString a = "Rain";
   QString b = "rain";
   QString c = "rain\n";
   
   if (QString::compare(a, b) == STR_EQUAL) {
     out << "a, b are equal" << endl;
   } else {
     out << "a, b are not equal" << endl;
   }
   
   out << "In case insensitive comparison:" << endl;
   
   if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
     out << "a, b are equal" << endl;
   } else {
     out << "a, b are not equal" << endl;
   }     
   
   if (QString::compare(b, c) == STR_EQUAL) {
     out << "b, c are equal" << endl;
   } else {
     out << "b, c are not equal" << endl;
   }   

   c.chop(1);
   
   out << "After removing the new line character" << endl;
   
   if (QString::compare(b, c) == STR_EQUAL) {
     out << "b, c are equal" << endl;
   } else {
     out << "b, c are not equal" << endl;
   }            
   
   return 0;
}

Đoạn code trên so sánh 2 chuỗi trong cả hai trường hợp có phân biệt và không phân biệt chữ HOA và thường. Tham số thứ 3 trong phương thức compare() chỉ định có sử dụng phân biệt HOA thường hay không, mặc định là có.

#define STR_EQUAL 0

Định nghĩa hằng STR_EQUAL để cho dễ nhìn 🙂

if (QString::compare(a, b) == STR_EQUAL) {
    out << "a, b are equal" << endl;
} else {
    out << "a, b are not equal" << endl;
}

Hai chuỗi và không bằng nhau. Vì chúng khác nhau ký tự đầu.

if (QString::compare(a, b, Qt::CaseInsensitive) == STR_EQUAL) {
    out << "a, b are equal" << endl;
} else {
    out << "a, b are not equal" << endl;
}    

Trường hợp trên là không quan tâm chữ HOA và thường. Nên hai chuỗi ab bằng nhau.

c.chop(1);

Phương thức chop() bỏ một ký tự sau cùng của chuỗi. Nên bây giờ chuỗi cb bằng nhau.

a, b are not equal
In case insensitive comparison:
a, b are equal
b, c are not equal
After removing the new line character
b, c are equal

Chuyển đổi chuỗi

Nhiều khi chúng ta cần chuyển đổi qua lại giữa các kiểu dữ liệu. Các phương thức toInt(), toFloat(), toLong() là 3 phương thức chuyển một chuỗi thành một số nguyên, số thực và số nguyên dài (ngoài ra còn nhiều phương thức nữa tương tự). Phương thức setNum() chuyển một kiểu số bất kỳ thành một chuỗi.

#include <QTextStream>

int main(void) {

  QTextStream out(stdout);
   
  QString s1 = "12";
  QString s2 = "15";
  QString s3, s4;
  
  out << s1.toInt() + s2.toInt() << endl;
   
  int n1 = 30;
  int n2 = 40;
  
  out << s3.setNum(n1) + s4.setNum(n2) << endl;
   
  return 0;
}

Đoạn code trên chuyển 2 chuỗi thành 2 số rồi thực hiện phép toán cộng. Rồi chuyển 2 số khác thành hai chuỗi và thực hiện phép nối chuỗi.

27
3040

Dạng ký tự

Ký tự cũng có nhiểu dạng khác nhau như ký số, chữ cái, dấu cách, dấu chấm câu… Lớp QChar có các phương thức kiểm tra xem ký tự thuộc dạng nào như isDigit(), isLetter(), isSpace(), isPunct().

#include <QTextStream>

int main(void) {

  QTextStream out(stdout);
   
  int digits  = 0;
  int letters = 0;
  int spaces  = 0;
  int puncts  = 0;
  
  QString str = "7 white, 3 red roses.";
  
  foreach(QChar s, str) {
  
    if (s.isDigit()) {
      digits++;
    } else if (s.isLetter()) {
      letters++;
    } else if (s.isSpace()) {
      spaces++;
    } else if (s.isPunct()) {
      puncts++;
    }    
  }  
  
  out << QString("There are %1 characters").arg(str.count()) << endl;
  out << QString("There are %1 letters").arg(letters) << endl;
  out << QString("There are %1 digits").arg(digits) << endl;
  out << QString("There are %1 spaces").arg(spaces) << endl;
  out << QString("There are %1 punctuation characters").arg(puncts) << endl;
    
  return 0;
}

Trong đoạn code trên ta có một chuỗi, sau đó ta đếm số lượng các ký số, chữ cái, dấu cách và dấu chấm câu trong chuỗi.

int digits  = 0;
int letters = 0;
int spaces  = 0;
int puncts  = 0;

Ta định nghĩa các biến số nguyên để lưu số lượng mỗi loại.

foreach(QChar s, str) {

  if (s.isDigit()) {
    digits++;
  } else if (s.isLetter()) {
    letters++;
  } else if (s.isSpace()) {
    spaces++;
  } else if (s.isPunct()) {
    puncts++;
  }    
} 

Vòng lặp foreach sẽ duyệt qua chuỗi, tại mỗi ký tự, ta kiểm tra xem ký tự đó là dạng gì và tăng biến đếm lên.

There are 21 characters
There are 13 letters
There are 2 digits
There are 4 spaces
There are 2 punctuation characters

Sửa đổi chuỗi

Một số phương thức (chẳng hạn như toLower()) trả về một chuỗi được sửa đổi từ chuỗi gốc. Nhưng có những phương thức thay đổi chính chuỗi gốc.

#include <QTextStream>

int main(void) {

   QTextStream out(stdout);
   
   QString str = "Lovely";   
   str.append(" season");
   
   out << str << endl;
   
   str.remove(10, 3);
   out << str << endl;

   str.replace(7, 3, "girl");
   out << str << endl;
   
   str.clear();
   
   if (str.isEmpty()) {
     out << "The string is empty" << endl;
   }
   
   return 0;
}

Có 4 phương thức được giới thiệu ở đây:

str.append(" season");

Phương thức append() thêm nội dung vào sau chuỗi.

str.remove(10, 3);

Phương thức remove() loại bỏ 3 ký tự từ vị trí số 10.

str.replace(7, 3, "girl");

Phương thức replace() thay thế 3 ký tự từ vị trí số 7 bằng chuỗi “girl”.

str.clear();

Phương thức clear() xóa hẳn nội dung chuỗi.

Lovely season
Lovely sea
Lovely girl
The string is empty

Căn chỉnh chuỗi

Đôi khi chúng ta cần căn chỉnh chuỗi vì mục đích thẩm mỹ. Các phương thức leftJusttified()rightJusttified() được dùng để làm việc này.

#include <QTextStream>

int main(void) {
    
   QTextStream out(stdout);
   
   QString field1 = "Name: ";
   QString field2 = "Occupation: ";
   QString field3 = "Residence: ";
   QString field4 = "Marital status: ";

   int width = field4.size();
   
   out << field1.rightJustified(width, ' ') << "Robert\n";
   out << field2.rightJustified(width, ' ') << "programmer\n";
   out << field3.rightJustified(width, ' ') << "New York\n";
   out << field4.rightJustified(width, ' ') << "single\n";
    
   return 0;
}

Đoạn code trên canh lề chuỗi về bên phải.

out << field1.rightJustified(width, ' ') << "Robert\n";

Phương thức rightJustified() trả về một chuỗi có chiều dài là width. Nếu độ dài của chuỗi không đủ dài thì chuỗi sẽ được tự động lấp đầy bằng các ký tự trong tham số thứ 2. Trong ví dụ của mình là dấu cách ” “.

          Name: Robert
    Occupation: programmer
     Residence: New York
Marital status: single

Trong bài này, chúng ta đã làm việc với chuỗi trong Qt 5.

Qt 5 C++ – Giới thiệu về Qt 5

Trong bài này mình giới thiệu sơ lược về lịch sử của Qt, sau đó cài đặt bộ IDE Qt Creator và viết một chương trình nhỏ.

Qt được phát triển bởi Trolltech, một công ty phần mềm của Nauy. Năm 2008 công ty này được mua lại bởi Nokia. Tháng 8 năm 2012 một công ty phần mềm của Phần Lan là Digia đã mua lại công nghệ Qt từ Nokia. Từ đó Qt được phát triển thêm một phiên bản mã nguồn mở. Trang web mã nguồn mở của Qt là http://www.qt.io. Hiện tại Qt đang được phát triển bởi 2 công ty, một là công ty Qt Company – một nhánh con của Digia, nhắm tới các ứng dụng dành cho doanh nghiệp, hai là Qt Project dành cho các dự án mã nguồn mở.

Qt

Qt là một framework đa nền tảng. Một số ứng dụng phổ biến được viết từ Qt có thể kể đến như KDE, Opera, Google Earth, và Skype. Qt lần đầu tiên được giới thiệu vào tháng 5 năm 1995. Qt có thể được dùng để phát triển ứng dụng mã nguồn mở lẫn các ứng dụng dành cho doanh nghiệp. Bộ công cụ phát triển Qt rất mạnh mẽ vì nó được cả một cộng đồng mã nguồn mở hỗ trợ. Có đến hàng ngàn các nhà phát triển mã nguồn mở sử dụng Qt trên toàn thế giới.

Tải Qt Creator

Các bạn vào http://www.qt.io/download-open-source/ để tải về bộ IDE của Qt là Qt Creator. Phiên bản Qt mà mình sử dụng là phiên bản 5.5.1.

Ở trên trang chủ của qt có 2 bộ cài là bộ cài online và bộ cài offline (ấn vào dòng View All Downloads để hiện ra bộ cài offline). Bộ cài online rất nhẹ chỉ vài MB, bạn download về xong cài lên thì nó sẽ phải tải từng file về như thế rất chậm, nên theo kinh nghiệm của mình thì nên bạn tải bộ cài offline cho nhanh và nên chọn bộ cài dùng trình biên dịch MinGW 4.9.2 nhé vì ở đây mình cũng dùng MinGW.

Kiểm tra phiên bản

Các bạn thử tạo một project Qt Console application rồi dùng đoạn code dưới đây để in phiên bản Qt đang sử dụng ra màn hình.

#include <QtCore>
#include <iostream>

int main()
{
     std::cout << "Qt version:  " << qVersion() << endl;
     return 0;
}

Hàm qVersion() trả về phiên bản Qt mà bạn đang dùng.

Chạy thử một chương trình GUI

Bây giờ chúng ta sẽ viết một chương trình nhỏ. Chương trình này chỉ làm một việc là hiển thị một cửa sổ.

#include <QApplication>
#include <QtWidgets/QWidget>

int main(int argc, char *argv[]
{
     QApplication app(argc, argv);

     QWidget window;
     window.resize(250, 150);
     window.setWindowTitle("Simple example");
     window.show();

     return app.exec();
}

Khi tạo một project Qt bất kỳ, sẽ có một file có đuôi là *.pro trong project. File này chứa những thông tin về các module mà project sẽ sử dụng.

Để chỉ ra cho project biết mình sẽ sử dụng module nào các bạn thêm tên của module vào sau QT += . Một số module mà chúng ta hay dùng là core, gui, widgets, network, multimedia…v.v.

Mặc định khi tạo 1 project Console Application thì module widgets không được thêm vào nên chúng ta phải tự thêm vào.

QT += core
QT -= gui

TARGET = simple
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp

Qt += widgets

Xong các bạn nhấn Ctrl-R hoặc vào Build->Run để IDE tự dịch và chạy chương trình.

Capturea