Виджет таблицы в Qt представляет собой довольный гибкий и удобный компонент для вывода структурированных данных. Используя QTableView
вы можете полностью контролировать способ отображения данных модели. Одной из типичных задач является отрисовка в ячейке таблицы индикатора прогресса QProgressBar
. Этим мы сейчас и займемся.
Не будем усложнять демонстрационный пример и определим следующий простой виджет:
#include <QWidget>
#include <QTimer>
class QTableWidget;
class QPushButton;
class ProgressBarDemo : public QWidget {
Q_OBJECT
public:
ProgressBarDemo( QWidget* parent = 0 );
~ProgressBarDemo();
private slots:
void addRow();
void onProgress();
private:
QTableWidget* m_table;
QPushButton* m_btnAdd;
QTimer m_timer;
int m_currentRow;
int m_currentProgress;
};
За основу мы взяли не сам QTableView
, а его наследника QTableWidget
, для которого не требуется создавать дополнительный класс модели. Однако все, о чем мы будем говорить, будет работать и для QTableView
. Более того, описанный подход также будет работать для QTreeView
и QListView
.
На форме виджета мы разместим таблицу с единственным столбцом Progress
. А внизу прикрепим кнопку Add
. После нажатия на кнопку в таблицу будет добавлена строка и запустится таймер. Этим таймером мы будем моделировать некую активность и наращивать значение прогресса от нуля до сотни. Соответствующая реализация не намного сложнее, чем звучит ее описание:
#include <QTableWidget>
#include <QHeaderView>
#include <QVBoxLayout>
#include <QPushButton>
#include <QApplication>
static const QStringList TABLE_COLUMN_LABELS = QStringList() << "Progress";
static const int PROGRESS_STEP = 5;
static const int PROGRESS_DELAY_MSEC = 100;
static const int MAX_PROGRESS_VALUE = 100;
ProgressBarDemo::ProgressBarDemo( QWidget* parent ) :
QWidget( parent ),
m_currentRow( 0 ),
m_currentProgress( 0 ) {
QVBoxLayout* mainLayout = new QVBoxLayout;
setLayout( mainLayout );
m_table = new QTableWidget;
m_table->setColumnCount( TABLE_COLUMN_LABELS.count() );
m_table->setHorizontalHeaderLabels( TABLE_COLUMN_LABELS );
m_table->horizontalHeader()->setResizeMode( QHeaderView::Stretch );
mainLayout->addWidget( m_table );
QHBoxLayout* panelLayout = new QHBoxLayout;
mainLayout->addLayout( panelLayout );
panelLayout->addStretch( 1 );
m_btnAdd = new QPushButton( "Add" );
connect( m_btnAdd, SIGNAL( clicked() ), SLOT( addRow() ) );
panelLayout->addWidget( m_btnAdd );
resize( 640, 480 );
m_timer.setInterval( PROGRESS_DELAY_MSEC );
connect( &m_timer, SIGNAL( timeout() ), SLOT( onProgress() ) );
}
ProgressBarDemo::~ProgressBarDemo() {
}
void ProgressBarDemo::addRow() {
m_currentProgress = 0;
m_currentRow = m_table->rowCount();
m_table->insertRow( m_currentRow );
if( QTableWidgetItem* item = new QTableWidgetItem( "0" ) ) {
item->setFlags( item->flags() ^ Qt::ItemIsEditable );
m_table->setItem( m_currentRow, 0, item );
}
m_btnAdd->setDisabled( true );
m_timer.start();
}
void ProgressBarDemo::onProgress() {
m_currentProgress += PROGRESS_STEP;
if( QTableWidgetItem* item = m_table->item( m_currentRow, 0 ) ) {
item->setData( Qt::DisplayRole, m_currentProgress );
}
if( m_currentProgress == MAX_PROGRESS_VALUE ) {
m_timer.stop();
m_btnAdd->setEnabled( true );
}
}
Здесь лишь обратим внимание на то, что новая строка в таблицу добавляется с помощью insertRow()
. После чего создается элемент QTableWidgetItem
, который мы прикрепим к новой строке. Для него мы убираем возможность редактирования исключая флаг Qt::ItemIsEditable
. Установка нового значения происходит в слоте onProgress()
с помощью функции-члена setData()
для роли Qt::DisplayRole
.
Приложение уже можно запустить, и оно будет отображать возрастающий прогресс, но никакого специального индикатора вы не увидите. Это будет обычное текстовое поле с цифрами:
Но это не проблема. Сейчас мы легко все поправим.
А для этого нам понадобится не так уж много. Достаточно реализовать объект-делегат, который мы установим для всего столбца нашей таблицы с помощью setItemDelegateForColumn()
. Таким образом, отображение ячеек этого столбца будет осуществляться не стандартными методами, а так, как хотим мы, то есть в виде индикатора прогресса. Для определения делегата унаследуем класс QStyledItemDelegate
:
class ProgressBarDelegate : public QStyledItemDelegate {
public:
ProgressBarDelegate( QObject* parent = 0 );
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
};
В функцию paint()
нашему делегату будут переданы:
QPainter
, которым мы и будем рисовать индикатор;QStyleOptionViewItem
;QModelIndex
, с помощью которого мы можем получить всю необходимую информацию о ячейке таблицы, которую нам предстоит нарисовать.А вот и реализация делегата:
static const int PROGRESS_BAR_HEIGHT_PX = 20;
ProgressBarDelegate::ProgressBarDelegate( QObject* parent ) : QStyledItemDelegate( parent ) {
}
void ProgressBarDelegate::paint(
QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index
) const {
int progress = index.data().toInt();
QStyleOptionProgressBar progressBarOption;
QRect r = option.rect;
r.setHeight( PROGRESS_BAR_HEIGHT_PX );
r.moveCenter( option.rect.center() );
progressBarOption.rect = r;
progressBarOption.textAlignment = Qt::AlignCenter;
progressBarOption.minimum = 0;
progressBarOption.maximum = MAX_PROGRESS_VALUE;
progressBarOption.progress = progress;
progressBarOption.text = QString::number( progress ) + "%";
progressBarOption.textVisible = true;
QStyledItemDelegate::paint( painter, option, QModelIndex() );
QApplication::style()->drawControl( QStyle::CE_ProgressBar, &progressBarOption, painter );
}
В представленной реализации мы создаем набор опций QStyleOptionProgressBar
, в котором определяем стандартные параметры отображения индикатора прогресса. Обратите внимание, что размер индикатора мы определяем с помощью QRect
. Причем за основу взят размер из переданных нам опций option
. Но этот размер будет соответствовать всей области ячейки, а она может оказаться слишком высокой, из-за чего индикатор окажется слишком растянутым, поэтому для высоты мы выбрали свое значение.
В конце функции paint()
мы сначала вызываем реализацию, предусмотренную в базовом классе, но передали в последнем аргументе пустой QModelIndex
, чтобы не выводить никаких данных модели. Это нужно, чтобы наша ячейка могла отображать некоторые базовые состояния, типа рамки фокуса и подсветки в случае выбора (можете убрать эту строку и убедиться, что ничего не происходит, когда вы щелкаете по ячейкам). А сам индикатор прогресса с заготовленными параметрами рисуется вызовом QApplication::style()->drawControl()
.
Вот и все. Осталось заменить стандартный делегат в таблице на наш. Это нужно сделать в конструкторе виджета. В том месте, где мы создавали m_table
. В результате должен получиться примерно такой фрагмент:
m_table = new QTableWidget;
m_table->setColumnCount( TABLE_COLUMN_LABELS.count() );
m_table->setHorizontalHeaderLabels( TABLE_COLUMN_LABELS );
m_table->horizontalHeader()->setResizeMode( QHeaderView::Stretch );
m_table->setItemDelegateForColumn( 0, new ProgressBarDelegate ); // устанавливаем наш делегат
mainLayout->addWidget( m_table );
После запуска и пары нажатий на кнопку Add
я получил следующее:
Все работает!
Вот мы и познакомились со способом отображения индикатора прогресса в виджете таблицы. Таким же образом вы можете рисовать в своем делегате вообще все, что угодно. Кроме того, поскольку базовые классы у QTableView
, QTreeView
и QListView
общие, то одни и те же делегаты будут работать в любом из них.
Скачать пример использования QProgressBar в таблице QTableView
Как это правильно сделать, через QT Creator?
Здравствуйте, Илья. Добавил в конец статьи ссылку на загрузку исходных кодов проекта.
Anonymous
Как это правильно создать?