Qt 5 C++ – Widget phần 2

5/5 - (1 vote)

Trong phần này chúng ta tiếp tục tìm hiểu về một số widget hay dùng trong Qt 5. Gồm có QCheckBox, QListWidget, QProgressBar, QPixmap, QSplitter, và QTableWidget.

QCheckBox

CheckBox thi chắc các bạn cũng thấy nhiều rồi, còn nếu chưa thì làm theo ví dụ dưới đây rồi thấy 🙂

Trong ví dụ này, chúng ta cho hiển thị một checkbox, nếu tick vào checkbox thì thay đổi tiêu đề cửa sổ.

#pragma once

#include <QWidget>

class CheckBox : public QWidget {
    
  Q_OBJECT

  public:
    CheckBox(QWidget *parent = 0);

  private slots:
    void showTitle(int);
};
#include <QCheckBox>
#include <QHBoxLayout>
#include "checkbox.h"

CheckBox::CheckBox(QWidget *parent)
    : QWidget(parent) {

  QHBoxLayout *hbox = new QHBoxLayout(this);
  
  QCheckBox *cb = new QCheckBox("Show Title", this);
  cb->setCheckState(Qt::Checked);
  hbox->addWidget(cb, 0, Qt::AlignLeft | Qt::AlignTop);

  connect(cb, &QCheckBox::stateChanged, this, &CheckBox::showTitle);
}

void CheckBox::showTitle(int state) {
    
  if (state == Qt::Checked) {
    setWindowTitle("QCheckBox");
  } else {
    setWindowTitle(" ");
  }
}

Chúng ta cho hiển thị checkbox rồi kết nối signal stateChanged() của nó với slot showTitle().

cb->setCheckState(Qt::Checked);

Mặc định checkbox sẽ được tick khi chương trình chạy.

void CheckBox::showTitle(int state) {

  if (state == Qt::Checked) {
    setWindowTitle("QCheckBox");
  } else {
    setWindowTitle(" ");
  }
}

Chúng ta có thể kiểm tra checkbox đã được tick hay chưa bằng cách so sánh nó với macro Qt::Checked hoặc có thể dùng phương thức QCheckBox::isChecked().

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

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

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

  return app.exec();
}
Capture

QListWidget

QListWidget dùng để hiển thị một danh sách các item, trong ví dụ dưới đây chúng ta sẽ cài đặt các phương thức add, rename, remove cho listwidget.

#pragma once

#include <QWidget>
#include <QPushButton>
#include <QListWidget>

class ListWidget : public QWidget {
    
  Q_OBJECT

  public:
    ListWidget(QWidget *parent = 0);

  private slots:
    void addItem();
    void renameItem();
    void removeItem();
    void clearItems();

  private:
    QListWidget *lw;
    QPushButton *add;
    QPushButton *rename;
    QPushButton *remove;
    QPushButton *removeAll; 
};
#include "listwidget.h"
#include <QVBoxLayout>
#include <QInputDialog>

ListWidget::ListWidget(QWidget *parent)
    : QWidget(parent) {

  QVBoxLayout *vbox = new QVBoxLayout();
  vbox->setSpacing(10);

  QHBoxLayout *hbox = new QHBoxLayout(this);

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

  add = new QPushButton("Add", this);
  rename = new QPushButton("Rename", this);
  remove = new QPushButton("Remove", this);
  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);
  
  connect(add, &QPushButton::clicked, this, &ListWidget::addItem);
  connect(rename, &QPushButton::clicked, this, &ListWidget::renameItem);
  connect(remove, &QPushButton::clicked, this, &ListWidget::removeItem);
  connect(removeAll, &QPushButton::clicked, this, &ListWidget::clearItems);
  
  setLayout(hbox);
}

void ListWidget::addItem() {
    
  QString c_text = QInputDialog::getText(this, "Item", "Enter new item");
  QString s_text = c_text.simplified();
  
  if (!s_text.isEmpty()) {
      
    lw->addItem(s_text);
    int r = lw->count() - 1;
    lw->setCurrentRow(r);
  }
}

void ListWidget::renameItem() {
    
  QListWidgetItem *curitem = lw->currentItem();
  
  QString c_text = curitem->text();
  QString r_text = QInputDialog::getText(this, "Item", "Enter new item", 
                                         QLineEdit::Normal, c_text);
  
  QString s_text = r_text.simplified();
  curitem.setText(s_text);
}

void ListWidget::removeItem() {
    
  int r = lw->currentRow();

  if (r != -1) {
      
    QListWidgetItem *item = lw->takeItem(r);
    lw->removeItemWidget(item);
  }
}

void ListWidget::clearItems(){
    
  if (lw->count() != 0) {
    lw->clear();
  }
}

Chúng ta cho hiển thị listwidget và bốn button. Những button này sẽ được dùng để add, rename, remove item trong list.

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à đưa 5 item vào.

void ListWidget::addItem() {
    
  QString c_text = QInputDialog::getText(this, "Item", "Enter new item");
  QString s_text = c_text.simplified();
  
  if (!s_text.isEmpty()) {
      
    lw->addItem(s_text);
    int r = lw->count() - 1;
    lw->setCurrentRow(r);
  }
}

Phương thức addItem() sẽ thêm một item vào list widget bằng cách hiển thị một dialog (hộp thoại) để người dùng nhập tên của item mới vào. Dialog này sẽ trả về một string là tên của item mới, sau đó chúng ta dùng phương thức simplified() để bỏ các khoảng trắng thừa rồi kiểm tra nếu tên item không phải là chuỗi rỗng thì chúng ta thêm item này vào list. Cuối cùng dùng phương thức setCurrentRow() để làm nổi bật item đó trên list.

void ListWidget::renameItem() {
    
  QListWidgetItem *curitem = lw->currentItem();  
  QString c_text = curitem->text();
  QString r_text = QInputDialog::getText(this, "Item", "Enter new item", 
                                         QLineEdit::Normal, c_text);
  
  QString s_text = r_text.simplified();
  curitem.setText(s_text);  
}

Để đổi tên một item trong list thì đầu tiên chúng ta lấy biến tham chiếu đến item đang được chọn trong list bằng phương thức currentItem(). Tiếp theo chúng ta hiển thị một dialog để người dùng nhập tên mới của item, trước đó chúng ta cũng lấy tên cũ của item để đưa vào dialog đề phòng trường hợp người dùng đổi ý không muốn đổi tên item nữa. Dialog này sẽ trả về một string và chúng ta cũng dùng phương thức simplified() để bỏ các khoảng trắng thừa. Sau đó dùng phương thức setText() để đổi tên item.

void ListWidget::removeItem() {
    
  int r = lw->currentRow();

  if (r != -1) {
      
    QListWidgetItem *item = lw->takeItem(r);
    lw->removeItemWidget(item);
  }
}

Để xóa một item thì chúng ta lấy số hàng đang được chọn trong list bằng phương thức currentRow(), sau đó lấy biến tham chiếu đến item bằng phương thức takeItem()phương thức này sẽ trả về một đối tượng QListWidgetItem, cuối cùng chúng ta dùng phương thức removeItemWidget() để xóa item được chọn ra.

void ListWidget::clearItems(){
    
  if (lw->count() != 0) {
    lw->clear();
  }
}

Phương thức clear() dùng để xóa toàn bộ item ra khỏi list.

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

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

  window.setWindowTitle("QListWidget");
  window.show();
  
  return app.exec();
}

Capture

QProgressBar

QProgressBar dùng để theo dõi tiến trình hoàn thành của một công việc nào đó.
#pragma once

#include <QWidget>
#include <QProgressBar>
#include <QPushButton>

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

  private:
    int progress;    
    QTimer *timer;
    QProgressBar *pbar; 
    QPushButton *startBtn;
    QPushButton *stopBtn;
    static const int DELAY = 200;
    static const int MAX_VALUE = 100;
    
    void updateBar();
    void startMyTimer();
    void stopMyTimer();
};
#include <QProgressBar>
#include <QTimer>
#include <QGridLayout>
#include "progressbar.h"

ProgressBarEx::ProgressBarEx(QWidget *parent)
    : QWidget(parent) {
        
  progress = 0;      
  timer = new QTimer(this);
  connect(timer, &QTimer::timeout, this, &ProgressBarEx::updateBar);

  QGridLayout *grid = new QGridLayout(this);
  grid->setColumnStretch(2, 1);
         
  pbar = new QProgressBar();
  grid->addWidget(pbar, 0, 0, 1, 3);

  startBtn = new QPushButton("Start", this);
  connect(startBtn, &QPushButton::clicked, this, &ProgressBarEx::startMyTimer);
  grid->addWidget(startBtn, 1, 0, 1, 1);
  
  stopBtn = new QPushButton("Stop", this);
  connect(stopBtn, &QPushButton::clicked, this, &ProgressBarEx::stopMyTimer);
  grid->addWidget(stopBtn, 1, 1);
}

void ProgressBarEx::startMyTimer() {
  
  if (progress >= MAX_VALUE) {
      
      progress = 0;
      pbar->setValue(0);
  }
    
  if (!timer->isActive()) {
      
    startBtn->setEnabled(false); 
    stopBtn->setEnabled(true); 
    timer->start(DELAY);
  }
}

void ProgressBarEx::stopMyTimer() {
    
  if (timer->isActive()) {
      
    startBtn->setEnabled(true);  
    stopBtn->setEnabled(false);
    timer->stop();
  }
}

void ProgressBarEx::updateBar() {
    
  progress++;
  
  if (progress <= MAX_VALUE) { pbar->setValue(progress);
  } else {
      
    timer->stop();
    startBtn->setEnabled(true);  
    stopBtn->setEnabled(false);      
  }
}

Trong ví dụ trên, chúng ta có một  QProgressBar và hai button. Một button để chạy timer, khi timer chạy, nó sẽ update progress bar. Button còn lại dùng để tắt timer.

pbar = new QProgressBar();

Khi khởi tạo QProgressBar, phạm vi giá trị mặc định là từ 0 đến 100.

void ProgressBarEx::updateBar() {
  progress++;
  
  if (progress <= MAX_VALUE) { pbar->setValue(progress);
  } else {
      
    timer->stop();
    startBtn->setEnabled(true);  
    stopBtn->setEnabled(false);      
  }
}

Thông tin về tiến trình được lưu trong biến progress. Phương thức setValue() sẽ cập nhật giá trị cho progress bar.

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

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

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

  return app.exec();
}
Capture

QPixmap

QPixmap là lớp dùng để làm việc với ảnh.

#pragma once

#include <QWidget>

class Pixmap : public QWidget {
    
  public:
    Pixmap(QWidget *parent = 0);
};
#include <QApplication>
#include <QPixmap>
#include <QLabel>
#include <QHBoxLayout>
#include "pixmap.h"

Pixmap::Pixmap(QWidget *parent)
    : QWidget(parent) {

  QHBoxLayout *hbox = new QHBoxLayout(this);
  
  QPixmap pixmap(qApp->applicationDirPath() + "/qt.png");
  
  QLabel *label = new QLabel(this);
  label->setPixmap(pixmap);

  hbox->addWidget(label, 0, Qt::AlignTop);
}

Trong ví dụ trên chúng ta cho hiển thị logo của Qt lên pixmap.

QPixmap pixmap(qApp->applicationDirPath() + "/qt.jpg");

QLabel *label = new QLabel(this);
label->setPixmap(pixmap);

Chúng ta không thể đưa pixmap trực tiếp lên window mà phải đưa vào một QLabel rồi hiển thị từ label này.

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

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

  window.setWindowTitle("QPixmap");
  window.show();
  
  return app.exec();
}

Capture

QSplitter

QSplitter cho phép chúng ta thay đổi kích thước của các widget con bằng cách kéo đường viền của chúng.

#pragma once

#include <QWidget>

class Splitter : public QWidget {
    
  public:
    Splitter(QWidget *parent = 0);
};
#include <QFrame>
#include <QSplitter>
#include <QHBoxLayout>
#include "splitter.h"

Splitter::Splitter(QWidget *parent)
    : QWidget(parent) {
        
  QHBoxLayout *hbox = new QHBoxLayout(this);

  QFrame *topleft = new QFrame(this);
  topleft->setFrameShape(QFrame::StyledPanel);

  QFrame *topright = new QFrame(this);
  topright->setFrameShape(QFrame::StyledPanel);

  QSplitter *splitter1 = new QSplitter(Qt::Horizontal, this);
  splitter1->addWidget(topleft);
  splitter1->addWidget(topright);

  QFrame *bottom = new QFrame(this);
  bottom->setFrameShape(QFrame::StyledPanel);

  QSplitter *splitter2 = new QSplitter(Qt::Vertical, this);
  splitter2->addWidget(splitter1);
  splitter2->addWidget(bottom);
  
  QList<int> sizes({50, 100});
  splitter2->setSizes(sizes);

  hbox->addWidget(splitter2);
}

Trong ví dụ này chúng ta dùng ba frame widget và hai splitter widget.

QSplitter *splitter1 = new QSplitter(Qt::Horizontal, this);
splitter1->addWidget(topleft);
splitter1->addWidget(topright);

Tạo splitter và thêm hai frame vào.

QSplitter *splitter2 = new QSplitter(Qt::Vertical, this);
splitter2->addWidget(splitter1);

Chúng ta có thể chồng các splitter lên nhau.

QList<int> sizes({50, 100});
splitter2->setSizes(sizes);

Phương thức setSizes() điều chỉnh kích thước của các widget con trong splitter.

#include <QDesktopWidget>
#include <QApplication>
#include "splitter.h"

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

  window.resize(350, 300);
  window.setWindowTitle("QSplitter");
  window.show();
  
  return app.exec();
}
Untitled

QTableWidget

QTableWidget là widget tạo bảng tính giống như Microsoft Excel. Widget này sử dụng khá phức tạp, các bạn có thể tự tìm hiểu thêm.

#pragma once

#include <QWidget>

class Table : public QWidget {
    
  public:
    Table(QWidget *parent = 0);
};
#include <QHBoxLayout>
#include <QTableWidget>
#include "table.h"

Table::Table(QWidget *parent)
    : QWidget(parent) {
        
  QHBoxLayout *hbox = new QHBoxLayout(this);

  QTableWidget *table = new QTableWidget(25, 25, this);

  hbox->addWidget(table);
}

Ví dụ trên sẽ hiển thị QTableWidget lên window.

QTableWidget *table = new QTableWidget(25, 25, this);

Chúng ta tạo bảng có 25 dòng và 25 cột.

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

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

  window.resize(400, 250);
  window.setWindowTitle("QTableWidget");
  window.show();

  return app.exec();
}
Capture
0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments