I would like to personalize the 3rd columns to contain QCheckBox widgets. I would like those QCheckBox widgets to be customized with three icons: 1.png (Default state) | 2.png (Selected state) | 3.png (Disabled state). So I could do this using a custom delegate with the following implementation:
#include "mydelegate.h"
#include <QCheckBox>
#include <QPainter>
#include <QKeyEvent>
#include <QtDebug>
#include <QApplication>
#include <QStyleOptionViewItem>
MyDelegate::MyDelegate(QObject* parent) :
QStyledItemDelegate(parent)
{
// 1.png
_icon.addPixmap(QPixmap("1.png"), QIcon::Active, QIcon::On);
// 2.png
_icon.addPixmap(QPixmap("2.png"), QIcon::Selected, QIcon::On);
// 3.png
_icon.addPixmap(QPixmap("3.png"), QIcon::Disabled, QIcon::On);
}
void MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem&
option, const QModelIndex& index) const
{
if (index.column() != 2)
QStyledItemDelegate::paint(painter, option, index);
else
{
bool value = index.model()->data(index,
Qt::UserRole).toBool();
QStyleOptionButton buttonVis;
buttonVis.rect = option.rect;
buttonVis.iconSize = QSize(50, 50);
buttonVis.icon = _icon;
buttonVis.features |= QStyleOptionButton::Flat;
buttonVis.state |= value ? QStyle::State_Enabled : QStyle::State_None;
QApplication::style()->drawControl(QStyle::CE_PushButton, &buttonVis, painter);
}
}
bool MyDelegate::editorEvent(QEvent* event, QAbstractItemModel* model,
const QStyleOptionViewItem& option, const QModelIndex& index)
{
if (event->type() == QEvent::MouseButtonRelease)
{
bool value = model->data(index, Qt::UserRole).toBool();
model->setData(index, !value, Qt::UserRole);
}
return true;
}
The problem that it's work for 1.png and 3.png and not for the 2.png. i want that it will work for the three icons
It is not necessary to implement the paint(...) or use editorEvent(...) methods, it is only necessary to override the initStyleOption(...) method by enabling the option of the icon.
In the following example the item (2, 2) is selected, the item (4, 2) is disabled and the other items are enabled but not selected showing 3 types of icons:
#include <QtWidgets>
class MyDelegate: public QStyledItemDelegate
{
public:
MyDelegate(QObject *parent=nullptr):
QStyledItemDelegate(parent)
{
m_icon.addPixmap(QPixmap("1.png"), QIcon::Active, QIcon::On);
m_icon.addPixmap(QPixmap("2.png"), QIcon::Selected, QIcon::On);
m_icon.addPixmap(QPixmap("3.png"), QIcon::Disabled, QIcon::On);
}
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{
QStyledItemDelegate::initStyleOption(option, index);
if (index.column() == 2){
option->features |= QStyleOptionViewItem::HasDecoration;
option->icon = m_icon;
option->decorationSize = QSize(50, 50);
}
}
private:
QIcon m_icon;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTableWidget w(10, 4);
MyDelegate *delegate = new MyDelegate(&w);
w.setItemDelegate(delegate);
QTableWidgetItem *item = new QTableWidgetItem;
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
w.setItem(4, 2, item);
w.show();
return app.exec();
}
Update: The OP is using an inadequate terminology since it refers to the status of a widget (normal, selected, disabled), instead it must indicate the states unchecked, partially checked and checked.
#include <QtWidgets>
class MyDelegate: public QStyledItemDelegate
{
public:
MyDelegate(QObject *parent=nullptr):
QStyledItemDelegate(parent)
{
uncheckedIcon = QIcon("1.png");
partiallyCheckedIcon = QIcon("2.png");
checkedIcon = QIcon("3.png");
}
void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override{
QStyledItemDelegate::initStyleOption(option, index);
if (index.column() == 2){
QIcon m_icon;
QVariant value = index.data(Qt::UserRole);
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
if(state == Qt::Unchecked)
option->icon = uncheckedIcon;
else if (state == Qt::PartiallyChecked)
option->icon = partiallyCheckedIcon;
else
option->icon = checkedIcon;
option->features |= QStyleOptionViewItem::HasDecoration;
option->decorationSize = QSize(50, 50);
}
}
bool editorEvent(QEvent* event, QAbstractItemModel* model,
const QStyleOptionViewItem& option, const QModelIndex& index) override
{
bool r = QStyledItemDelegate::editorEvent(event, model, option, index);
if(index.column() != 2)
return r;
if ((event->type() == QEvent::MouseButtonRelease)
|| (event->type() == QEvent::MouseButtonDblClick)
|| (event->type() == QEvent::MouseButtonPress)) {
if ((event->type() == QEvent::MouseButtonPress)
|| (event->type() == QEvent::MouseButtonDblClick))
return true;
QVariant value = index.data(Qt::UserRole);
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
state = static_cast<Qt::CheckState>((state + 1) % 3);
return model->setData(index, state, Qt::UserRole);
}
return r;
}
private:
QIcon uncheckedIcon;
QIcon partiallyCheckedIcon;
QIcon checkedIcon;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTableWidget w(10, 4);
MyDelegate *delegate = new MyDelegate(&w);
w.setItemDelegate(delegate);
w.resize(640, 480);
w.show();
return app.exec();
}
Related
I'm trying to create an effect similar to this gif:
While you are hovering over the tab button it shows a text and also resizes the icon.
I was reading the docs about the property icon-size it says it supports QTabBar, but adding the property into the tab stylesheet, has no effect when the button is being hovered.
I'm doing something wrong or it's a 'bug'?
QTabBar::tab:hover {
font-size: 12pt;
icon-size: 64px 64px;
color: rgb(255, 255, 255);
background: transparent;
}
As I couldn't get it working with a stylesheet, I tried using an event filter:
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::HoverEnter)
{
QTabBar *tabbar = static_cast<QTabBar*>(obj);
tabbar->setIconSize(QSize(40, 40));
}
else if (event->type() == QEvent::HoverLeave)
{
QTabBar *tabbar = static_cast<QTabBar*>(obj);
tabbar->setIconSize(QSize(32,32));
}
// pass the event on to the parent class
return QMainWindow::eventFilter(obj, event);
}
Result:
Whenever any tab button has hovered, it resizes all icons.
How I could resize just the tab button under the mouse instead of the entire "QTabBar"?
Tab stylesheet options from the gif above:
QTabWidget::pane {
background: transparent;
}
QTabBar::tab {
font-size: 12pt;
color: rgba(255, 255, 255, 0);
background: transparent;
}
QTabBar::tab:hover {
font-size: 12pt;
color: rgb(255, 255, 255);
background: transparent;
}
QTabBar::tab:selected {
background: transparent;
}
I don't understand what's going on with the text, the tab text is "Tab" but it's cropping the "T".
Why the tab pane background is not getting transparent?
The recommendation in my comment of yesterday:
The last resort might be to make a custom widget for the tab bar, and to use a QStackedWidget as container for the tab pages.
There are probably samples around. Instead of googling I didn't hesitate to add one on my own:
qIconBar.h:
#ifndef Q_ICON_BAR
#define Q_ICON_BAR
#include <vector>
#include <QIcon>
#include <QWidget>
class QIconBar: public QWidget {
Q_OBJECT
private:
Qt::Orientation _ori;
QSize _qSizeIcon = QSize(64, 64); // icon size
int _spc = 4; // size between
double _scl = 0.6; // scale factor for icons not under mouse
struct Tab {
QIcon qIcon;
QString qLabel;
};
std::vector<Tab> _tabs;
int _iTab = 0; // current tab
signals:
void currentChanged(int i);
void tabClicked(int i);
public:
/// constructor.
explicit QIconBar(
Qt::Orientation ori = Qt::Horizontal, QWidget* pQParent = nullptr);
/// destructor.
virtual ~QIconBar() = default;
// disabled:
QIconBar(const QIconBar&) = delete;
QIconBar& operator=(const QIconBar&) = delete;
public:
virtual QSize sizeHint() const;
QSize iconSize() const { return _qSizeIcon; }
void setIconSize(const QSize& qSizeIcon);
int addTab(const QIcon& qIcon, const QString& qText)
{
return insertTab(-1, qIcon, qText);
}
int insertTab(int i, const QIcon& qIcon, const QString& qText);
void removeTab(int i);
int tabAt(const QPoint& qPos) const;
QIcon tabIcon(int i) const
{
return (size_t)i < _tabs.size() ? _tabs[i].qIcon : QIcon();
}
void setTabIcon(int i, const QIcon& qIcon);
QString tabText(int i) const
{
return (size_t)i < _tabs.size() ? _tabs[i].qLabel : QString();
}
void setTabText(int i, const QString& qLabel);
int count() const { return (int)_tabs.size(); }
int currentIndex() const { return _iTab; }
void setCurrentIndex(int i);
protected:
virtual void paintEvent(QPaintEvent* pQEvent) override;
virtual void mouseMoveEvent(QMouseEvent* pQEvent) override;
virtual void mousePressEvent(QMouseEvent* pQEvent) override;
virtual void leaveEvent(QEvent* pQEvent) override;
};
#endif // Q_ICON_BAR
Concerning the API, I (shamelessly) borrowed from QTabBar.
qIconBar.cc:
#include <QMouseEvent>
#include <QPainter>
#include "qIconBar.h"
QIconBar::QIconBar(Qt::Orientation ori, QWidget* pQParent):
QWidget(pQParent),
_ori(ori)
{
setMouseTracking(true);
}
QSize QIconBar::sizeHint() const
{
const int n = (int)_tabs.size();
const int spc = std::max(n - 1, 0) * _spc;
return _ori == Qt::Vertical
? QSize(_qSizeIcon.width(), _qSizeIcon.height() * n + spc)
: QSize(_qSizeIcon.width() * n + spc, _qSizeIcon.height());
}
int QIconBar::insertTab(int i, const QIcon& qIcon, const QString& qText)
{
if ((size_t)i > _tabs.size()) i = (int)_tabs.size();
_tabs.insert(_tabs.begin() + i, { qIcon, qText });
update();
if (i <= _iTab && _iTab + 1 < (int)_tabs.size()) {
setCurrentIndex(_iTab + 1);
}
return i;
}
void QIconBar::removeTab(int i)
{
if ((size_t)i >= _tabs.size()) return;
_tabs.erase(_tabs.begin() + i);
update();
if ((size_t)_iTab >= _tabs.size()) {
setCurrentIndex(0);
}
}
int QIconBar::tabAt(const QPoint& qPos) const
{
if (!QRect(QPoint(), size()).contains(qPos)) return -1;
return _ori == Qt::Vertical
? qPos.y() % (_qSizeIcon.height() + _spc) < _qSizeIcon.height()
? qPos.y() / (_qSizeIcon.height() + _spc)
: -1
: qPos.x() % (_qSizeIcon.width() + _spc) < _qSizeIcon.width()
? qPos.x() / (_qSizeIcon.width() + _spc)
: -1;
}
void QIconBar::setCurrentIndex(int i)
{
_iTab = i;
emit currentChanged(_iTab);
update();
}
void QIconBar::paintEvent(QPaintEvent* pQEvent)
{
const QPoint qPosMouse = mapFromGlobal(QCursor::pos());
const int iTabMouse = tabAt(qPosMouse);
QPainter qPainter(this);
for (int i = 0, n = (int)_tabs.size(); i < n; ++i) {
const Tab& tab = _tabs[i];
const QPoint qPos(
(_ori == Qt::Horizontal) * i * (_qSizeIcon.width() + _spc)
+ _qSizeIcon.width() / 2,
(_ori == Qt::Vertical) * i *(_qSizeIcon.height() + _spc)
+ _qSizeIcon.height() / 2);
if (i == _iTab) {
QRect qRect(QPoint(), _qSizeIcon);
qRect.moveCenter(qPos);
qPainter.fillRect(qRect, palette().color(QPalette::Base));
}
if (!tab.qIcon.isNull()) {
const double scale = iTabMouse == i ? 1.0 : _scl;
const QSize qSizeIcon = scale * _qSizeIcon;
const QPixmap qPixmap = tab.qIcon.pixmap(qSizeIcon);
QRect qRect(QPoint(), qSizeIcon);
qRect.moveCenter(qPos);
qPainter.drawPixmap(qRect, qPixmap);
}
if (i == iTabMouse) {
QFont qFont = font();
qFont.setBold(true);
qPainter.setFont(qFont);
QRect qRect(QPoint(), _qSizeIcon);
qRect.moveCenter(qPos);
qPainter.drawText(qRect, Qt::AlignHCenter | Qt::AlignBottom,
tab.qLabel);
}
}
}
void QIconBar::mouseMoveEvent(QMouseEvent* pQEvent)
{
update();
QWidget::mouseMoveEvent(pQEvent);
}
void QIconBar::mousePressEvent(QMouseEvent* pQEvent)
{
if (pQEvent->button() == Qt::LeftButton) {
const int iTab = tabAt(pQEvent->pos());
if ((size_t)iTab < _tabs.size()) {
setCurrentIndex(iTab);
emit tabClicked(iTab);
return;
}
}
QWidget::mousePressEvent(pQEvent);
}
void QIconBar::leaveEvent(QEvent* pQEvent)
{
update();
QWidget::leaveEvent(pQEvent);
}
A sample application testQIconBar.cc:
#include <QtWidgets>
#include "qIconBar.h"
const struct Item {
const char* filePathIcon;
const char* label;
} items[] = {
{ "icons/document-open.svg", "File" },
{ "icons/document-properties.svg", "Edit" },
{ "icons/edit-find.svg", "Find" },
{ "icons/help-help.svg", "Help" }
};
const size_t nItems = std::size(items);
int main(int argc, char** argv)
{
QApplication app(argc, argv);
// setup GUI
QWidget qWinMain;
qWinMain.setWindowTitle("QIconBar Test");
qWinMain.resize(640, 480);
QHBoxLayout qHBox;
QIconBar qIconBar(Qt::Vertical);
qHBox.addWidget(&qIconBar);
QStackedLayout qStack;
qHBox.addLayout(&qStack, 1);
qWinMain.setLayout(&qHBox);
qWinMain.show();
// populate icon bar and stack
QFont qFont = qWinMain.font();
qFont.setPixelSize(30);
for (const Item& item : items) {
qIconBar.addTab(
QIcon(item.filePathIcon), QString::fromUtf8(item.label));
const QString qText
= QString("This is the page for item %1.").arg(item.label);
QLabel* pQLbl = new QLabel(qText);
pQLbl->setFrameStyle(QFrame::StyledPanel | QFrame::Plain);
pQLbl->setFont(qFont);
pQLbl->setAlignment(Qt::AlignCenter);
qStack.addWidget(pQLbl);
}
qStack.setCurrentIndex(qIconBar.currentIndex());
// install signal handlers
QObject::connect(&qIconBar, &QIconBar::currentChanged,
&qStack, &QStackedLayout::setCurrentIndex);
// runtime loop
return app.exec();
}
Additionally, I used the following CMakeLists.txt:
project(QIconBar)
cmake_minimum_required(VERSION 3.10.0)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt5Widgets CONFIG REQUIRED)
include_directories("${CMAKE_SOURCE_DIR}")
qt5_wrap_cpp(moc_sources QIconBar.h
TARGET testQIconBar)
add_executable(testQIconBar
testQIconBar.cc qIconBar.cc qIconBar.h
${moc_sources})
target_link_libraries(testQIconBar Qt5::Widgets)
…and compiled this in Visual Studio 2019.
Output:
According to feedback, I tested my MCVE concerning transparency of the widgets — i.e. with a background image in the main window:
The QIconBar doesn't clear it's background — the contents is just drawn over the background of the parent window. (It's the same with the QLabels I used in the QStackedLayout.)
I try to make popup label which shows the name of vertical header item because name is large and I want header width to be just size of its number.
I made event filter and made popup dialog and popuplabel qlabel.
but size of popup is larger than size of header item.
if I make size equal to rect size ,the text disappear.
if i adjust the size of popup ,it makes greater rect showing text with offset downwards corresponding to visual index of header item.
this is the code:
#ifndef TABLEVIEW_H
#define TABLEVIEW_H
#include <QDialog>
#include <QEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QTimer>
class TableView : public QTableView {
Q_OBJECT
QDialog* popup;
QLabel* popupLabel;
int editor_index ;
public:
TableView(QWidget* parent = Q_NULLPTR) :QTableView(parent) {
viewport()->installEventFilter(this);
horizontalHeader()->viewport()->installEventFilter(this);
verticalHeader()->viewport()->installEventFilter(this);
setMouseTracking(true);
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
}
bool eventFilter(QObject* watched, QEvent* event) {
//if (viewport() == watched)
if ((watched == horizontalHeader()->viewport() ||
watched == verticalHeader()->viewport()))
{
if (event->type() == QEvent::MouseMove)
{
/*/
if (popup) { //delete previous popup just in case
popup->contentsRect().setRect(0,0,0,0) ;
}
if (popupLabel) { //delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0,0,0,0);
}
*/
/*
////////////////////////////////////////////////////////
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
////////////////////////////////////////////////////////
*/
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
int mouse_pos = header->orientation() == Qt::Horizontal ? mouseEvent->x() : mouseEvent->y();
int logical_index = header->logicalIndex(header->visualIndexAt(mouse_pos));
if (logical_index >= 0)
{ // if mouse is over an item
showPopup(logical_index, watched);
}
else
{
popup->hide();
}
}
else if (event->type() == QEvent::Leave) {
popup->hide();
}
}
else if (popup == watched)
{
if (event->type() == QEvent::Leave)
{
popup->hide();
}
}
return QTableView::eventFilter(watched, event);
}
private:
void showPopup(const int & logical_index, QObject* watched) const
{
if (logical_index >= 0)
{
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
QFont font("times", 12);
QFontMetrics fm(font);
int pixelsWide = fm.width("xxxxxx");
int pixelsHigh = fm.height();
QRect rect; // line edit rect in header's viewport's coordinates
if (header->orientation() == Qt::Horizontal) {
//rect.setLeft(header->sectionPosition(logical_index));
rect.setLeft(header->sectionViewportPosition(logical_index));
rect.setWidth(header->sectionSize(logical_index));
rect.setTop(0);
rect.setHeight(header->height());
}
else {
//rect.setTop((header->sectionPosition(logical_index)));
rect.setTop((header->sectionViewportPosition(logical_index)));
//QPoint point(0,header->sectionPosition(logical_index));
//rect.setTop((mapToGlobal(point)).y() );
//rect.topLeft())
int cy = header->sectionSize(logical_index);
rect.setHeight(header->sectionSize(logical_index));
rect.setLeft(0);
rect.setWidth(header->width());
//rect.setCoords(rect.left(),rect.top(),rect.left()+rect.width(),rect.top()+rect.height());
}
//rect.adjust(1, 1, 1, 1);
popupLabel->move(rect.bottomLeft());
//popupLabel->move(rect.topLeft());
//popupLabel->move(viewport()->mapToGlobal(rect.topLeft()));
popupLabel->resize(rect.size());
//popupLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
//popupLabel->show();
//popupLabel->setFixedHeight(rect.height());
//popupLabel->setFixedWidth(rect.width());
//popupLabel->setFrame(false);
//get current item text
QString text = header->model()->
headerData(logical_index, header->orientation()).toString();
//int z=text.size();
popupLabel->setText(text);
//popupLabel->setFocus();
///////////////////////////////////////////////////////////////////////////
//editor_index = logical_index; //save for future use
popupLabel->installEventFilter(this->parent()); //catch focus out event
//if user presses Enter it should close editor
//connect(header_editor, SIGNAL(returnPressed()),
// ui->tableWidget, SLOT(setFocus()));
//popupLabel->setGeometry(rect);
//popupLabel->adjustSize();
//popupLabel->setFixedSize(100, 100);
popupLabel->show();
////////////////////////////////////////////////////////////////////////////
//popup->move(viewport()->mapToGlobal(rect.bottomLeft()));
popup->move(viewport()->mapToGlobal(rect.topLeft()));
//popup->move(rect.topLeft());
//popup->resize(rect.size());
//popup->move(rect.bottomLeft());
//popup->move(rect.topLeft());
//popup->setFixedSize(100, popup->heightForWidth(100));
//popup->setFixedSize(100, 100);
//popupLabel->setText(text);
//popupLabel->show();
// popupLabel->setText(logical_index.data(Qt::DisplayRole).toString());
//popup->adjustSize();
//popup->setGeometry(rect);
popup->show();
//sleep 10 ms;
return;// true; // filter out event
}
else {
popup->hide();
//delete previous popup just in case
popup->contentsRect().setRect(0, 0, 0, 0);
//popup = nullptr;
//delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0, 0, 0, 0);
//popupLabel = nullptr;
return; //false;
}
}
};
#endif // TABLEVIEW_H
I use beyond compare to find out what I changed:
1.I move popupLabel to point 0,0
2.I resize popup to rect.size()
If not enuf space for display text,I will resize popupLabel and popup to a larger size.
code:
#include <QApplication>
#include <QTableWidget>
#include <QStyledItemDelegate>
#include <QDialog>
#include <QEvent>
#include <QLabel>
#include <QMouseEvent>
#include <QTableView>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QTimer>
#include <QDebug>
class TableModel :public QAbstractTableModel
{
Q_OBJECT
public:
int rowCount(const QModelIndex& parent = QModelIndex()) const
{
return 4;
}
int columnCount(const QModelIndex& parent = QModelIndex()) const
{
return 2;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
{
if (role == Qt::DisplayRole)
return QString::number(index.row()) + QString::number(index.column());
return QVariant();
}
};
class TableView : public QTableView {
Q_OBJECT
QDialog* popup;
QLabel* popupLabel;
int editor_index;
public:
TableView(QWidget* parent = Q_NULLPTR) :QTableView(parent) {
viewport()->installEventFilter(this);
horizontalHeader()->viewport()->installEventFilter(this);
verticalHeader()->viewport()->installEventFilter(this);
setMouseTracking(true);
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
}
bool eventFilter(QObject* watched, QEvent* event) {
//if (viewport() == watched)
if ((watched == horizontalHeader()->viewport() ||
watched == verticalHeader()->viewport()))
{
if (event->type() == QEvent::MouseMove)
{
/*/
if (popup) { //delete previous popup just in case
popup->contentsRect().setRect(0,0,0,0) ;
}
if (popupLabel) { //delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0,0,0,0);
}
*/
/*
////////////////////////////////////////////////////////
popup = new QDialog(this, Qt::Popup | Qt::ToolTip);
//popup = new QDialog(viewport(), Qt::Popup | Qt::ToolTip);
//QVBoxLayout* layout = new QVBoxLayout;
popupLabel = new QLabel(popup);
popupLabel->setWordWrap(false);
//layout->addWidget(popupLabel);
popupLabel->setTextFormat(Qt::RichText);
//popup->setLayout(layout);
popup->installEventFilter(this);
QTimer::singleShot(0, popup, &QWidget::hide);
////////////////////////////////////////////////////////
*/
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
int mouse_pos = header->orientation() == Qt::Horizontal ? mouseEvent->x() : mouseEvent->y();
int logical_index = header->logicalIndex(header->visualIndexAt(mouse_pos));
if (logical_index >= 0)
{ // if mouse is over an item
showPopup(logical_index, watched);
}
else
{
popup->hide();
}
}
else if (event->type() == QEvent::Leave) {
popup->hide();
}
}
else if (popup == watched)
{
if (event->type() == QEvent::Leave)
{
popup->hide();
}
}
return QTableView::eventFilter(watched, event);
}
private:
void showPopup(const int& logical_index, QObject* watched) const
{
if (logical_index >= 0)
{
QHeaderView* header = static_cast<QHeaderView*>(watched->parent());
QFont font("times", 12);
QFontMetrics fm(font);
int pixelsWide = fm.width("xxxxxx");
int pixelsHigh = fm.height();
QRect rect; // line edit rect in header's viewport's coordinates
if (header->orientation() == Qt::Horizontal) {
//rect.setLeft(header->sectionPosition(logical_index));
rect.setLeft(header->sectionViewportPosition(logical_index));
rect.setWidth(header->sectionSize(logical_index));
rect.setTop(0);
rect.setHeight(header->height());
}
else {
//rect.setTop((header->sectionPosition(logical_index)));
rect.setTop((header->sectionViewportPosition(logical_index)));
//QPoint point(0,header->sectionPosition(logical_index));
//rect.setTop((mapToGlobal(point)).y() );
//rect.topLeft())
int cy = header->sectionSize(logical_index);
rect.setHeight(header->sectionSize(logical_index));
rect.setLeft(0);
rect.setWidth(header->width());
//rect.setCoords(rect.left(),rect.top(),rect.left()+rect.width(),rect.top()+rect.height());
}
//rect.adjust(1, 1, 1, 1);
//popupLabel->move(rect.bottomLeft());
//popupLabel->move(rect.topLeft());
popupLabel->move(QPoint(0,0));
popupLabel->resize(rect.size());
//popupLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
//popupLabel->show();
//popupLabel->setFixedHeight(rect.height());
//popupLabel->setFixedWidth(rect.width());
//popupLabel->setFrame(false);
//get current item text
QString text = header->model()->
headerData(logical_index, header->orientation()).toString();
//int z=text.size();
popupLabel->setText(text);
qDebug() << rect << text;
//popupLabel->setFocus();
///////////////////////////////////////////////////////////////////////////
//editor_index = logical_index; //save for future use
popupLabel->installEventFilter(this->parent()); //catch focus out event
//if user presses Enter it should close editor
//connect(header_editor, SIGNAL(returnPressed()),
// ui->tableWidget, SLOT(setFocus()));
//popupLabel->setGeometry(rect);
//popupLabel->adjustSize();
//popupLabel->setFixedSize(100, 100);
popupLabel->show();
////////////////////////////////////////////////////////////////////////////
//popup->move(viewport()->mapToGlobal(rect.bottomLeft()));
popup->move(viewport()->mapToGlobal(rect.topLeft()));
//popup->move(rect.topLeft());
popup->resize(rect.size());
//popup->move(rect.bottomLeft());
//popup->move(rect.topLeft());
//popup->setFixedSize(100, popup->heightForWidth(100));
//popup->setFixedSize(100, 100);
//popupLabel->setText(text);
//popupLabel->show();
// popupLabel->setText(logical_index.data(Qt::DisplayRole).toString());
//popup->adjustSize();
//popup->setGeometry(rect);
popup->show();
//sleep 10 ms;
return;// true; // filter out event
}
else {
popup->hide();
//delete previous popup just in case
popup->contentsRect().setRect(0, 0, 0, 0);
//popup = nullptr;
//delete previous popupLabel just in case
popupLabel->contentsRect().setRect(0, 0, 0, 0);
//popupLabel = nullptr;
return; //false;
}
}
};
#include"main.moc"
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
TableView view;
view.setModel(new TableModel);
view.show();
return a.exec();
}
I am trying to allow users to add new "widgets" (images, text, perhaps other custom data too. Image is good enough for now) to a kind of design area. And then I would like them to be able to resize/move those conveniently. The best way for the moving part seems to be to use QGraphicsView. Can be done nicely with 4 lines of code:
auto const scene = new QGraphicsScene{this};
auto const item = scene->addPixmap(QPixmap{":/img/example.png"});
item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
ui->graphicsView->setScene(scene);
This results in something like this:
It's nice, but cannot be resized with the mouse. I've seen (on this site) multiple ways to make this, sort of, resizable with a mouse, but they are all kind of hacky.
I've seen this example on Qt website which takes a different approach of creating a custom container for a moveable-and-resizeable container. It seems like it can be made work with a bit more tweaking, but it's again, not a nice solution. The widget doesn't really look like it's resizable. When selected, the borders don't have the nice the clue that it's a dynamically placed/sized thing.
Having ms-paint like moveable widgets must be a common use case, so I reckon there has got to be a nice way to get this happen. Is this the case? QGraphicsScene seems like a good candidate honestly. Perhaps I am missing something?
Ok, so I had this problem and my solution was to link the creation of the handlers with the selection of the item:
mainwindow.h
#pragma once
#include <QMainWindow>
#include <QGraphicsItem>
#include <QPainter>
class Handler: public QGraphicsItem
{
public:
enum Mode
{
Top = 0x1,
Bottom = 0x2,
Left = 0x4,
TopLeft = Top | Left,
BottomLeft = Bottom | Left,
Right = 0x8,
TopRight = Top | Right,
BottomRight = Bottom | Right,
Rotate = 0x10
};
Handler(QGraphicsItem *parent, Mode mode);
~Handler(){}
void updatePosition();
QRectF boundingRect() const override;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QPointF iniPos;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
private:
Mode mode;
bool isMoving = false;
};
class ObjectResizerGrip: public QGraphicsItem
{
public:
ObjectResizerGrip(QGraphicsItem *parent): QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemHasNoContents, true);
setFlag(QGraphicsItem::ItemIsSelectable, false);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
void updateHandlerPositions();
virtual QRectF boundingRect() const override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override{Q_UNUSED(painter) Q_UNUSED(option) Q_UNUSED(widget)}
protected:
QList<Handler*> handlers;
};
class Object4SidesResizerGrip: public ObjectResizerGrip
{
public:
Object4SidesResizerGrip(QGraphicsItem *parent);
};
class Item:public QGraphicsItem
{
public:
Item(QGraphicsItem *parent=nullptr): QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
setAcceptHoverEvents(true);
}
QRectF boundingRect() const override
{
return boundingBox;
}
void setWidth(qreal value)
{
auto width = boundingBox.width();
if(width == value) return;
width = qMax(value, 100.0);
setDimensions(width, boundingBox.height());
}
void setHeight(qreal value)
{
auto height = boundingBox.height();
if(height == value) return;
height = qMax(value, 100.0);
setDimensions(boundingBox.width(), height);
}
void setDimensions(qreal w, qreal h)
{
prepareGeometryChange();
boundingBox = QRectF(-w/2.0, -h/2.0, w, h);
if(resizerGrip) resizerGrip->updateHandlerPositions();
update();
}
private:
ObjectResizerGrip* resizerGrip = nullptr;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override
{
if(change == ItemSelectedHasChanged && scene())
{
if(value.toBool())
{
if(!resizerGrip)
resizerGrip = newSelectionGrip();
}
else
{
if(resizerGrip)
{
delete resizerGrip;
resizerGrip = nullptr;
}
}
}
return QGraphicsItem::itemChange(change, value);
}
QRectF boundingBox;
virtual ObjectResizerGrip *newSelectionGrip() =0;
};
class CrossItem:public Item
{
public:
CrossItem(QGraphicsItem *parent=nullptr): Item(parent){};
private:
virtual ObjectResizerGrip *newSelectionGrip() override
{
return new Object4SidesResizerGrip(this);
}
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
painter->drawLine(boundingRect().topLeft(), boundingRect().bottomRight());
painter->drawLine(boundingRect().topRight(), boundingRect().bottomLeft());
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
};
mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QHBoxLayout>
// Return nearest point along the line to a given point
// http://stackoverflow.com/questions/1459368/snap-point-to-a-line
QPointF getClosestPoint(const QPointF &vertexA, const QPointF &vertexB, const QPointF &point, const bool segmentClamp)
{
QPointF AP = point - vertexA;
QPointF AB = vertexB - vertexA;
qreal ab2 = AB.x()*AB.x() + AB.y()*AB.y();
if(ab2 == 0) // Line lenth == 0
return vertexA;
qreal ap_ab = AP.x()*AB.x() + AP.y()*AB.y();
qreal t = ap_ab / ab2;
if (segmentClamp)
{
if (t < 0.0f) t = 0.0f;
else if (t > 1.0f) t = 1.0f;
}
return vertexA + AB * t;
}
Object4SidesResizerGrip::Object4SidesResizerGrip(QGraphicsItem* parent) : ObjectResizerGrip(parent)
{
handlers.append(new Handler(this, Handler::Left));
handlers.append(new Handler(this, Handler::BottomLeft));
handlers.append(new Handler(this, Handler::Bottom));
handlers.append(new Handler(this, Handler::BottomRight));
handlers.append(new Handler(this, Handler::Right));
handlers.append(new Handler(this, Handler::TopRight));
handlers.append(new Handler(this, Handler::Top));
handlers.append(new Handler(this, Handler::TopLeft));
handlers.append(new Handler(this, Handler::Rotate));
updateHandlerPositions();
}
QRectF ObjectResizerGrip::boundingRect() const
{
return QRectF();
}
void ObjectResizerGrip::updateHandlerPositions()
{
foreach (Handler* item, handlers)
item->updatePosition();
}
Handler::Handler(QGraphicsItem *parent, Mode mode): QGraphicsItem(parent), mode(mode)
{
QPen pen(Qt::white);
pen.setWidth(0);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, false);
setAcceptHoverEvents(true);
setZValue(100);
setCursor(Qt::UpArrowCursor);
updatePosition();
}
void Handler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen pen(isMoving ? QColor(250,214,36) : QColor(100,100,100));
pen.setWidth(0);
pen.setBrush(pen.color());
painter->setPen(pen);
painter->setBrush(QColor(100,100,100,150));
if(mode & Rotate)
{
auto rect_ = ((Item*) parentItem()->parentItem())->boundingRect();
auto topPos = QPointF(rect_.left() + rect_.width() / 2 - 1, rect_.top());
painter->drawLine(mapFromParent(topPos), mapFromParent(topPos - QPointF(0, 175)));
painter->drawEllipse(boundingRect());
}
else
painter->drawRect(boundingRect());
}
QRectF Handler::boundingRect() const
{
return QRectF(-25, -25, 50, 50);
}
void Handler::updatePosition()
{
auto rect_ = ((Item*) parentItem()->parentItem())->boundingRect();
switch (mode)
{
case TopLeft:
setPos(rect_.topLeft());
break;
case Top:
setPos(rect_.left() + rect_.width() / 2 - 1,rect_.top());
break;
case TopRight:
setPos(rect_.topRight());
break;
case Right:
setPos(rect_.right(),rect_.top() + rect_.height() / 2 - 1);
break;
case BottomRight:
setPos(rect_.bottomRight());
break;
case Bottom:
setPos(rect_.left() + rect_.width() / 2 - 1,rect_.bottom());
break;
case BottomLeft:
setPos(rect_.bottomLeft());
break;
case Left:
setPos(rect_.left(), rect_.top() + rect_.height() / 2 - 1);
break;
case Rotate:
setPos(0, rect_.top() - 200);
break;
}
}
void Handler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(mode & Rotate)
{
Item* item = (Item*) parentItem()->parentItem();
auto angle = QLineF(item->mapToScene(QPoint()), event->scenePos()).angle();
if(!(QApplication::keyboardModifiers() & Qt::AltModifier)) // snap to 45deg
{
auto modAngle = fmod(angle+180, 45);
if(modAngle < 10 || modAngle > 35)
angle = round(angle/45)*45;
}
item->setRotation(0);
angle = QLineF(item->mapFromScene(QPoint()), item->mapFromScene(QLineF::fromPolar(10, angle).p2())).angle();
item->setRotation(90 - angle);
item->update();
}
else
{
Item* item = (Item*) parentItem()->parentItem();
auto diff = mapToItem(item, event->pos()) - mapToItem(item, event->lastPos());
auto bRect = item->boundingRect();
if(mode == TopLeft || mode == BottomRight)
diff = getClosestPoint(bRect.topLeft(), QPoint(0,0), diff, false);
else if(mode == TopRight || mode == BottomLeft)
diff = getClosestPoint(bRect.bottomLeft(), QPoint(0,0), diff, false);
if(mode & Left || mode & Right)
{
item->setPos(item->mapToScene(QPointF(diff.x()/2.0, 0)));
if(mode & Left)
item->setWidth(item->boundingRect().width() - diff.x());
else
item->setWidth(item->boundingRect().width() + diff.x());
}
if(mode & Top || mode & Bottom)
{
item->setPos(item->mapToScene(QPointF(0, diff.y()/2.0)));
if(mode & Top)
item->setHeight(item->boundingRect().height() - diff.y());
else
item->setHeight(item->boundingRect().height() + diff.y());
}
item->update();
}
((ObjectResizerGrip*) parentItem())->updateHandlerPositions();
}
void Handler::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
isMoving = true;
}
void Handler::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
isMoving = false;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
auto const graphicsView = new QGraphicsView(this);
graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
auto const scene = new QGraphicsScene(this);
auto const item = new CrossItem();
item->setWidth(100);
item->setHeight(100);
scene->addItem(item);
item->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
graphicsView->setScene(scene);
graphicsView-> fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
setCentralWidget(graphicsView);
}
MainWindow::~MainWindow()
{
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
This solution is far from perfect but it works and can be a good start for improvements.
Known issues:
rotation grip requires FullViewportUpdate because I was too lazy to implement it in a separate child item and it is drawing outside the bounding box.
there are probably better architectures like using proxies or signals/event.
When it comes to using mouse, keyboard and in general capturing operating system events, you have to rely on the event system. The base class is QEvent, which in your specific case allows you to "QResizeEvent, QMouseEvent, QScrollEvent, ..." and many more fun things.
In most examples, customizing the Qt slider is done like this (with a stylesheet):
mySlider = new QSlider(centralWidget);
mySlider->setObjectName(QStringLiteral("mySlider"));
mySlider->setGeometry(QRect(645, 678, 110, 21));
mySlider->setOrientation(Qt::Horizontal);
mySlider->setStyleSheet("QSlider::groove:horizontal {background-image:url(:/main/graphics/mySliderBackround.png);}"
"QSlider::handle:horizontal {background-image:url(:/main/graphics/mySliderHandle.png); height:21px; width: 21px;}");
This works fine for me as well.
I have a situation where I need to programmatically set the background using a dyamically created pixmap. Using the code below, this is how I accomplished it. The problem is that when I am on Fedora Linux, this slider works fine. When I'm on OSX or Windows, the slider handle goes off the goove.
Here's what it looks like on OSX. Notice how the handle is off the groove. The left side is customized with a stylesheet, the right is customized with the Style object below.
Create the slider and assign the style:
mySlider = new QSlider(centralWidget);
mySlider->setObjectName(QStringLiteral("mySlider"));
mySlider->setGeometry(QRect(645, 678, 110, 21));
mySlider->setOrientation(Qt::Horizontal);
mySlider->setStyle(new MySliderStyle(mySlider->style()));
The custom slider style code:
Header
#ifndef MYSTYLE_H
#define MYSTYLE_H
#include <QObject>
#include <QWidget>
#include <QProxyStyle>
#include <QPainter>
#include <QStyleOption>
#include <QtWidgets/QCommonStyle>
class MySliderStyle : public QProxyStyle
{
private:
QPixmap groovePixmap;
QPixmap handlePixmap;
public:
LightingSliderStyle(QStyle *style)
: QProxyStyle(style)
{
setColor(QColor::fromRgba(0));
this->handlePixmap = <snip initialize the pixmap>;
this->grooveMaskPixmap = <snip initialize the pixmap>;
}
void drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const;
QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const;
void setColor(QColor color);
};
#endif // MYSTYLE_H
Implementation*
#include "MySliderStyle.h"
QRect MySliderStyle::subControlRect(ComplexControl control,
const QStyleOptionComplex *option,
SubControl subControl,
const QWidget *widget) const
{
QRect rect;
rect = QCommonStyle::subControlRect(control, option, subControl, widget);
if (control == CC_Slider && subControl == SC_SliderHandle)
{
// this is the exact pixel dimensions of the handle png files
rect.setWidth(21);
rect.setHeight(21);
}
else if (control == CC_Slider && subControl == SC_SliderGroove)
{
// this is the exact pixel dimensions of the slider png files
rect.setWidth(widget->width());
rect.setHeight(widget->height());
}
return rect;
}
void MySliderStyle::drawComplexControl(QStyle::ComplexControl control,
const QStyleOptionComplex *option,
QPainter *painter,
const QWidget *widget) const
{
if (control == CC_Slider)
{
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
{
QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);
if ((slider->subControls & SC_SliderGroove) && groove.isValid())
{
Qt::BGMode oldMode = painter->backgroundMode();
painter->setBackgroundMode(Qt::TransparentMode);
painter->drawPixmap(groove, groovePixmap);
painter->setBackgroundMode(oldMode);
}
if ((slider->subControls & SC_SliderHandle) && handle.isValid())
{
Qt::BGMode oldMode = painter->backgroundMode();
painter->setBackgroundMode(Qt::TransparentMode);
painter->drawPixmap(handle, handlePixmap);
painter->setBackgroundMode(oldMode);
}
}
}
else
{
QProxyStyle::drawComplexControl(control, option, painter, widget);
}
}
void MySliderStyle::setColor(QColor color)
{
QImage myGrooveImage;
// <snip>
// Code to create the custom pixmap
// <snip>
groovePixmap = QPixmap::fromImage(myGrooveImage);
}
UPDATE
The code for this project is open source and available here
A call to QCommonStyle::subControlRect and adjusting the width/height is not enough. You must also recalculate the x/y position.
You can therfore use the QCommonStyle::subControlRect function as reference to calculate the proper rectangle:
QRect LightingSliderStyle::subControlRect(ComplexControl control,
const QStyleOptionComplex *option,
SubControl subControl,
const QWidget *widget) const
{
if (control == CC_Slider)
{
if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
QRect ret;
int tickOffset = 0;
int thickness = 21; // height
int len = 21; // width
switch (subControl) {
case SC_SliderHandle: {
int sliderPos = 0;
bool horizontal = slider->orientation == Qt::Horizontal;
sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum+1,
slider->sliderPosition,
(horizontal ? slider->rect.width()
: slider->rect.height()) - len,
slider->upsideDown);
if (horizontal)
ret.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness);
else
ret.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len);
break; }
case SC_SliderGroove:
if (slider->orientation == Qt::Horizontal)
ret.setRect(slider->rect.x(), slider->rect.y() + tickOffset,
slider->rect.width(), thickness);
else
ret.setRect(slider->rect.x() + tickOffset, slider->rect.y(),
thickness, slider->rect.height());
break;
default:
break;
}
return visualRect(slider->direction, slider->rect, ret);
}
}
return QCommonStyle::subControlRect(control, option, subControl, widget);
}
I'm using QTableView and QStandardItemModel to show some data.
For each row, there is a column which has a check Box, this check box is inserted by setItem, the code is as follows:
int rowNum;
QStandardItemModel *tableModel;
QStandardItem* __tmpItem = new QStandardItem();
__tmpItem->setCheckable(true);
__tmpItem->setCheckState(Qt::Unchecked);
tableModel->setItem(rowNum,0,__tmpItem);
Now I want to interact with the check box. If a check box changes its state by user (from checked to unchecked or vice versa), I want to do something on the corresponding data row.
I know I can use signal-slot to catch the change of checkbox, but since there are lots of data row, I don't want to connect each row one by one.
Is there anyway to interact with the check action more effectively? Thanks :)
I don't deal with QTableView+QStandardItemModel, but may be example below will help you:
1). table.h file:
#ifndef TABLE__H
#define TABLE__H
#include <QtGui>
class ItemDelegate : public QItemDelegate
{
public:
ItemDelegate(QObject *parent = 0)
: QItemDelegate(parent)
{
}
virtual void drawCheck(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &, Qt::CheckState state) const
{
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
check(option, option.rect, Qt::Checked).size(),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (textMargin * 2), option.rect.height()));
QItemDelegate::drawCheck(painter, option, checkRect, state);
}
virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
const QModelIndex &index)
{
Q_ASSERT(event);
Q_ASSERT(model);
// make sure that the item is checkable
Qt::ItemFlags flags = model->flags(index);
if (!(flags & Qt::ItemIsUserCheckable) || !(flags & Qt::ItemIsEnabled))
return false;
// make sure that we have a check state
QVariant value = index.data(Qt::CheckStateRole);
if (!value.isValid())
return false;
// make sure that we have the right event type
if (event->type() == QEvent::MouseButtonRelease) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
check(option, option.rect, Qt::Checked).size(),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (2 * textMargin), option.rect.height()));
if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
return false;
} else if (event->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
&& static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
return false;
} else {
return false;
}
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);
QMessageBox::information(0,
QString((state == Qt::Checked) ? "Qt::Checked" : "Qt::Unchecked"),
QString("[%1/%2]").arg(index.row()).arg(index.column()));
return model->setData(index, state, Qt::CheckStateRole);
}
virtual void drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const
{
QItemDelegate::drawFocus(painter, option, option.rect);
}
};
static int ROWS = 3;
static int COLS = 1;
class Table : public QTableWidget
{
Q_OBJECT
public:
Table(QWidget *parent = 0)
: QTableWidget(ROWS, COLS, parent)
{
setItemDelegate(new ItemDelegate(this));
QTableWidgetItem *item = 0;
for (int i=0; i<rowCount(); ++i) {
for (int j=0; j<columnCount(); ++j) {
setItem(i, j, item = new QTableWidgetItem);
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsUserCheckable);
item->setCheckState((i+j) % 2 == 0 ? Qt::Checked : Qt::Unchecked);
}
}
}
};
#endif
2). main.cpp file:
#include <QApplication>
#include "table.h"
int main(int argc, char **argv)
{
QApplication a(argc, argv);
Table w;
w.show();
return a.exec();
}
Good luck.
PS: here is the original text.
handle the click event, there you will get the modelindex, get the data and modify the same
if you are going to insert more than one text or icon, then you need to set the delegate for your listview
Here is another similar idea of the example suggested by mosg using a QStyleItemDelegate instead. http://developer.qt.nokia.com/faq/answer/how_can_i_align_the_checkboxes_in_a_view
Use on clicked slot and index.data(Qt::CheckStateRole) :
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{
if(index.column() == 2){
if(index.data(Qt::CheckStateRole) == Qt::Checked){
//your code
}else if(index.data(Qt::CheckStateRole) == Qt::Unchecked){
//your code
}
}
//get other infos
QVariant value = index.sibling(index.row(),0).data();
QString selectedMessageName = value.toString();
}