So what I have here is my delegate code for a QAbstractTableModel. The 2nd column (index = 1), is always a comboBox. That comboBox has a list of actions that a given Unit in my simulation can perform. Upon clicking certain actions like Build or Train, I want a Dialog to pop up giving the user a set of choices. After making there choice, the delegate is supposed to update the model's data and move on.
Here is the snag I've run into. If I try to call a dialog from setModelData(), setEditorData(), those functions are const so I can't assign the results of the dialog to anything.
What I have tried to do is to connect the signal indexChanged(QString) from the QComboBox created in createEditor(). This at least allowed me to assign the result and call the dialog only when the appropriate choice was made. However I keep getting a seggy fault and the trace in QTCreator will not land on C code (33 calls of assembly). I'm unsure as to why I got that seggy.
When I tried that approach, the table no longer had the combobox once the dialog appeared. It reverted to its previous state and as soon as the program left the slot I made for QComboBox::indexChanged(QString), the seggy happened.
DelegateAction.h
#ifndef DELEGATEACTION_H
#define DELEGATEACTION_H
#include <QVariant>
#include <QStyledItemDelegate>
#include <QString>
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
#include <QMouseEvent>
#include <JECMessageTable.h>
#include <UnitBase.h>
#include <ModelListUnit.h>
class DelegateAction : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DelegateAction(QObject *parent = 0);
~DelegateAction();
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
public slots:
void setUnits(QList<Unit*>* units);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
//bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
private slots:
void comboChanged(QString string);
void comboDestroyed();
private:
bool comboUpdated;
const UnitBase* selectedUnit;
ModelListUnit *model; // The model for the JECMessageTable.
QList<Unit*>* units; // The current list of units.
};
#endif // DELEGATEACTION_H
DelegateAction.cpp
#include "DelegateAction.h"
DelegateAction::DelegateAction(QObject *parent) :
QStyledItemDelegate(parent),
selectedUnit(0),
model(0)
{
}
DelegateAction::~DelegateAction()
{
delete model;
}
QWidget * DelegateAction::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* editor = 0;
switch (index.column())
{
case 0:
default:
{
editor = new QLabel();
break;
}
case 1:
{
QComboBox* combo = new QComboBox(parent);
combo->addItem("Idle");
combo->addItem("Gather");
combo->addItem("Train");
combo->addItem("Build");
combo->addItem("Upgrade");
editor = combo;
// connect(combo, SIGNAL(currentIndexChanged(QString)), this, SLOT(comboChanged(QString)));
// connect(combo, SIGNAL(destroyed()), this, SLOT(comboDestroyed()));
break;
}
case 4:
{
editor = new QProgressBar(parent);
break;
}
}
editor->installEventFilter(const_cast<DelegateAction*>(this));
return editor;
}
void DelegateAction::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::DisplayRole);
switch (index.column())
{
case 0:
default:
{
QLabel* label = static_cast<QLabel*>(editor);
label->setText(value.toString());
break;
}
case 1:
{
QComboBox* combo = static_cast<QComboBox*>(editor);
combo->setCurrentIndex(combo->findText(value.toString()));
break;
}
case 4:
{
QProgressBar* progress = static_cast<QProgressBar*>(editor);
progress->setValue(value.toInt());
break;
}
}
}
void DelegateAction::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QVariant value;
switch (index.column())
{
case 0:
default:
{
value = static_cast<QLabel*>(editor)->text();
break;
}
case 1:
{
QString string = static_cast<QComboBox*>(editor)->currentText();
switch (Action::getType(string))
{
case Action::INVALID:
{
return;
}
case Action::IDLE:
{
return;
}
case Action::GATHER:
{
return;
}
case Action::BUILD:
{
return;
}
case Action::TRAIN:
{
// Summon the build choice dialog.
if (units == 0)
{
return;
}
JECMessageTable messageBox(this->model, "Test");
messageBox.exec();
if (messageBox.result() == QDialog::Accepted)
{
//selectedUnit = this->model->getSelectedUnit();
}
else
{
//selectedUnit = 0;
}
return;
}
case Action::MORPH:
{
return;
}
case Action::UPGRADE:
{
}
}
break;
}
case 4:
{
value = static_cast<QProgressBar*>(editor)->value();
break;
}
}
model->setData(index, value);
}
void DelegateAction::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
QSize DelegateAction::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == 4)
{
return QSize(option.rect.width(), option.rect.height());
}
else
{
return QStyledItemDelegate::sizeHint(option, index);
}
}
void DelegateAction::paint(QPainter *painter, const QStyleOptionViewItem &item, const QModelIndex &index) const
{
if (index.column() == 4 && index.isValid() == true)
{
QStyleOptionProgressBarV2 progress;
progress.minimum = 0;
progress.maximum = 100;
progress.progress = index.data().toInt();
progress.rect = QRect(item.rect.x(), item.rect.y(), item.rect.width(), item.rect.height());
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progress, painter);
}
else
{
// Painting the cells normally.
QStyledItemDelegate::paint(painter, item, index);
}
}
void DelegateAction::setUnits(QList<Unit*> *units)
{
this->units = units;
if (units != 0)
{
if (units->length() > 0)
{
model = new ModelListUnit(&units->at(0)->getUnitBase()->getOptionUnit());
}
}
}
void DelegateAction::comboChanged(QString string)
{
// comboUpdated = true;
// switch (Action::getType(string))
// {
// case Action::INVALID:
// {
// return;
// }
// case Action::IDLE:
// {
// return;
// }
// case Action::GATHER:
// {
// return;
// }
// case Action::BUILD:
// {
// return;
// }
// case Action::TRAIN:
// {
// // Summon the build choice dialog.
// if (units == 0)
// {
// return;
// }
// JECMessageTable messageBox(model, "Test");
// messageBox.exec();
// if (messageBox.result() == QDialog::Accepted)
// {
// selectedUnit = model->getSelectedUnit();
// }
// else
// {
// selectedUnit = 0;
// }
// return;
// }
// case Action::MORPH:
// {
// return;
// }
// case Action::UPGRADE:
// {
// }
// }
}
void DelegateAction::comboDestroyed()
{
disconnect(this, SLOT(comboChanged(QString)));
disconnect(this, SLOT(comboDestroyed()));
}
//bool DelegateAction::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
//{
// QStyledItemDelegate::editorEvent(event, model, option, index);
// if (comboUpdated == true && index.isValid() == true)
// {
// comboUpdated = false;
// if (selectedUnit != 0)
// {
// units->at(index.row());
// }
// }
//}
I figured this out. If anyone is interested in something similar, I sub classed QTableView.
Inside QTableView i used QTableView::setIndexWidget() to use QComboBoxes and QProgressBars. Basically my subclass had a QList of both types of widgets and they corresponded to a particular row in the table.
derive ComboBoxEditor from QComboBox
keep a Action object in ComboBoxEditor (data stored in model index)
when delegate's comboChanged() is called, you can access the Action via sender(), that is ComboBoxEditor.
Please refer to the official example, StarDelegate.
http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html
Related
I'm trying to create a widget representing a table and update some data.
In order to do this I am following the Qt Model/View tutorial.
I've created classes (that you can find at the end of the post)
EmittersTableModel that inherits from QAbstractTableModel
EmittersTableWidget that inherits from QTableView
I havethe EmittersTableModel object as private member of EmittersTableWidget. In its constructor I instantiate the model and use the setModel() method.
Then, when I try to update data, I call the EmittersTableWidget::setRadioData() method, and I emit the datachanged() signal.
I've verified with the debugger that:
emit dataChanged(topLeft, bottomRight) is called
EmittersTableModel::rowCount() is called
EmittersTableModel::columnCount() is called
EmittersTableModel::flags() is never called
EmittersTableModel::data() is never called.
It seems for me that I'm doing all that tutorial says (use setModel(), implement needed virtual functions, emit the signal).
What I'm missing?
EmittersTableModel.h
#include <QAbstractTableModel>
#include <QVector>
#include "Radio.h"
typedef QMultiMap<QString, MapScenario::Core::RadioPtr> PlayerRadioMap;
class EmittersTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
EmittersTableModel(QObject *parent);
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const ;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
void setRadioData(const PlayerRadioMap &xRadioMap);
private:
typedef struct
{
QString xName;
MapScenario::Core::RadioPtr pxRadio;
} TableRowData;
PlayerRadioMap m_xRadioMap;
QVector<TableRowData> m_xDataVector;
};
EmittersTableModel.cpp
#include "EmittersTableModel.h"
EmittersTableModel::EmittersTableModel(QObject *parent)
:QAbstractTableModel(parent)
{
}
int EmittersTableModel::rowCount(const QModelIndex & /*parent*/) const
{
return m_xDataVector.size() - 1;
}
int EmittersTableModel::columnCount(const QModelIndex & /*parent*/) const
{
return 8;
}
QVariant EmittersTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case 0 :
{
return m_xDataVector.at(index.row()).xName;
} break;
case 1 :
{
return m_xDataVector.at(index.row()).pxRadio->getName();
} break;
}
return QString("Row%1, Column%2")
.arg(index.row() + 1)
.arg(index.column() +1);
}
return QVariant();
}
Qt::ItemFlags EmittersTableModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsEditable;
}
QVariant EmittersTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole)
{
if (orientation == Qt::Horizontal) {
switch (section)
{
case 0:
return QString("Player");
case 1:
return QString("Emitter");
case 2:
return QString("Freq.");
case 3:
return QString("Power");
case 4:
return QString("Modulation");
case 5:
return QString("Freq. Hopp.");
case 6:
return QString("Silent");
case 7:
return QString("Rec. Power");
}
}
}
return QVariant();
}
void EmittersTableModel::setRadioData(const PlayerRadioMap &xRadioMap)
{
m_xDataVector.clear();
PlayerRadioMap::const_iterator xIt;
for (xIt = xRadioMap.begin(); xIt != xRadioMap.end(); ++xIt)
{
TableRowData xData;
xData.xName = xIt.key();
xData.pxRadio = xIt.value();
m_xDataVector.append(xData);
}
if (false == m_xDataVector.empty())
{
QModelIndex topLeft = createIndex(0, 0);
QModelIndex bottomRight = createIndex(m_xDataVector.size() - 1, 7);
emit dataChanged(topLeft, bottomRight);
}
}
EmittersTableWidget.h
#include <QTableView>
#include <QHeaderView>
#include <QMultiMap>
#include <boost/smart_ptr.hpp>
#include "EmittersTableModel.h"
#include "Scenario.h"
#include "Radio.h"
namespace MapScenario
{
namespace Core
{
class Player;
}
}
/** Class for the player properties table model window */
class EmittersTableWidget : public QTableView
{
Q_OBJECT
public:
EmittersTableWidget(QWidget *xParent = 0);
~EmittersTableWidget();
public slots:
void refreshScenarioDataSlot(const MapScenario::Core::ScenarioPtr pxScenario);
private:
EmittersTableModel *m_pxModel;
void getTransmitterMap(const MapScenario::Core::ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const;
void sendDataToTableModel(const PlayerRadioMap &xRadioMap);
};
EmittersTableWidget.cpp
#include "EmittersTableWidget.h"
#include "Player.h"
#include "CoreException.h"
using MapScenario::Core::ScenarioPtr;
using MapScenario::Core::Radio;
using MapScenario::Core::PlayerPtr;
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION //
///////////////////////////////////////////////////////////////////////////////
EmittersTableWidget::EmittersTableWidget(QWidget *xParent)
: QTableView(xParent)
{
m_pxModel = new EmittersTableModel(0);
setModel(m_pxModel);
horizontalHeader()->setVisible(true);
verticalHeader()->setVisible(false);
setShowGrid(true);
setGridStyle(Qt::NoPen);
setCornerButtonEnabled(false);
setWordWrap(true);
setAlternatingRowColors(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSortingEnabled(true);
}
EmittersTableWidget::~EmittersTableWidget()
{
delete m_pxModel;
}
///////////////////////////////////////////////////////////////////////////////
// PUBLIC SLOTS SECTION //
///////////////////////////////////////////////////////////////////////////////
void EmittersTableWidget::refreshScenarioDataSlot(const ScenarioPtr pxScenario)
{
PlayerRadioMap xRadioMap;
getTransmitterMap(pxScenario, &xRadioMap);
sendDataToTableModel(xRadioMap);
}
void EmittersTableWidget::getTransmitterMap(const ScenarioPtr pxScenario, PlayerRadioMap *pxRadioMap) const
{
QVector<QString> xNameList;
QVector<QString>::const_iterator xNameIt;
QStringList::const_iterator xRadioIt;
pxScenario->getPlayersNameList(xNameList);
for (xNameIt = xNameList.begin(); xNameIt != xNameList.end(); ++xNameIt)
{
QStringList xRadioList;
PlayerPtr pxPlayer = pxScenario->getPlayer(*xNameIt);
pxPlayer->getRadioNameList(xRadioList);
for (xRadioIt = xRadioList.begin(); xRadioIt != xRadioList.end(); ++xRadioIt)
{
pxRadioMap->insert(pxPlayer->getName(), pxPlayer->getRadio(*xRadioIt));
}
}
}
void EmittersTableWidget::sendDataToTableModel(const PlayerRadioMap &xRadioMap)
{
m_pxModel->setRadioData(xRadioMap);
}
I've found my problem.
In the tutorial that I've seen it was supposed that row and column numbers are Always constant. Instead I start with zero rows, and then I add or remove them when I need. In order to do this, I need to re-implement following methods:
virtual bool insertRows(int row, int count, const QModelIndex &parent)
virtual bool insertColumns(int column, int count, const QModelIndex &parent)
virtual bool removeRows(int row, int count, const QModelIndex &parent)
virtual bool removeColumns(int column, int count, const QModelIndex &parent)
as explained in QAbstractItemModel page in subclassing section. Now I insert and remove rows when needed and table is updated correctly.
I have ComboBox filled with CheckBoxes when the ComboBox is opened the first item's delegate createEditor is not called but when you move to second item it is called and delegate is properly created. After this when you move back to first item then delegate is working. The problem is only with selecting first item for first time. If you have got only one item in comboBox this item is unselectable.
This is my delegate's code:
#include "checkboxlistdelegate.h"
CheckBoxListDelegate::CheckBoxListDelegate(QObject *parent)
: QItemDelegate(parent) {
}
void CheckBoxListDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const {
//Get item data
bool value = index.data(Qt::UserRole).toBool();
QString text = index.data(Qt::DisplayRole).toString();
// fill style options with item data
const QStyle *style = QApplication::style();
QStyleOptionButton opt;
opt.state |= value ? QStyle::State_On : QStyle::State_Off;
opt.state |= QStyle::State_Enabled;
opt.text = text;
opt.rect = option.rect;
opt.palette = QPalette(Qt::white);
// draw item data as CheckBox
style->drawControl(QStyle::CE_CheckBox,&opt,painter);
}
QWidget* CheckBoxListDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex & index ) const {
QCheckBox *editor = new QCheckBox(parent);
editor->setStyleSheet("QCheckBox {background-color: #aaaaaa; color: white;}");
return editor;
}
void CheckBoxListDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const {
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
myEditor->setText(index.data(Qt::DisplayRole).toString());
myEditor->setChecked(index.data(Qt::UserRole).toBool());
}
void CheckBoxListDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const {
//get the value from the editor (CheckBox)
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
bool value = myEditor->isChecked();
//set model data
QMap<int,QVariant> data;
data.insert(Qt::DisplayRole,myEditor->text());
data.insert(Qt::UserRole,value);
model->setItemData(index,data);
emit indexChanged(index);
}
void CheckBoxListDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const {
Q_UNUSED(index);
editor->setGeometry(option.rect);
}
And this is code of my QComboBox subclass:
CheckBoxList::CheckBoxList(QWidget *widget)
: QComboBox(widget),title(""),selection() {
// set delegate items view
view()->setItemDelegate(new CheckBoxListDelegate(this));
view()->setStyleSheet("QAbstractItemView {background-color:white;}");
// Enable editing on items view
view()->setEditTriggers(QAbstractItemView::AllEditTriggers);
connect(view()->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(onItemClicked(QModelIndex)));
}
void CheckBoxList::paintEvent(QPaintEvent *) {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
QStyleOptionComboBox opt;
initStyleOption(&opt);
opt.currentText = title;
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
void CheckBoxList::setDisplayText(QString text) {
title = text;
}
QString CheckBoxList::getDisplayText() const {
return title;
}
bool CheckBoxList::isChecked(const QModelIndex& index) const {
return view()->model()->itemData(index).value(Qt::UserRole).toBool();
}
Thank you.
I'm doing an implementation of a CheckBox that inherits from QitemDelegate, to put it into a QTableView.
the problem is that I get when inserted flush left and I need it centered.
As I understand the method that is responsible for the Paint. I have it written as follows:
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
bool checkValue;
QStyleOptionButton BtnStyle;
BtnStyle.state = QStyle::State_Enabled;
if(index.model()->data(index, Qt::DisplayRole).toBool() == true)
{
BtnStyle.state |= QStyle::State_On;
checkValue = true;
}else{
BtnStyle.state |= QStyle::State_Off;
checkValue = false;
}
BtnStyle.direction = QApplication::layoutDirection();
BtnStyle.rect = option.rect;
QApplication::style()->drawControl(QStyle::CE_CheckBox,&BtnStyle,painter );
QApplication::style()->drawControl(QStyle::CE_CheckBox,&BtnStyle,painter );
}
what is missing to appear centered?
so I have the delegated:
.h
class BooleanWidget : public QWidget
{
Q_OBJECT
QCheckBox * checkBox;
public:
BooleanWidget(QWidget * parent = 0)
{
checkBox = new QCheckBox(this);
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(checkBox,0, Qt::AlignCenter);
}
bool isChecked(){return checkBox->isChecked();}
void setChecked(bool value){checkBox->setChecked(value);}
};
class CheckBoxDelegate : public QItemDelegate
{
Q_OBJECT
private:
BooleanWidget *checkbox;
public:
CheckBoxDelegate(QObject *parent);
~CheckBoxDelegate();
void setEditorData( QWidget *editor,const QModelIndex &index )const;
void setModelData( QWidget *editor,QAbstractItemModel *model,const QModelIndex &index )const;
QWidget *createEditor( QWidget *parent,const QStyleOptionViewItem &/* option */,const QModelIndex &/* index */ )const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
public slots:
void changed( bool );
};
.cpp
void CheckBoxDelegate::changed( bool value )
{
BooleanWidget *checkbox = static_cast<BooleanWidget*>( sender() );
emit commitData( checkbox );
emit closeEditor( checkbox );
}
QWidget *CheckBoxDelegate::createEditor( QWidget *parent,const QStyleOptionViewItem &/* option */,const QModelIndex &/* index */ ) const
{
BooleanWidget *editor = new BooleanWidget( parent );
connect( editor, SIGNAL( toggled ( bool ) ), this, SLOT( changed( bool ) ) );
return editor;
}
void CheckBoxDelegate::setEditorData( QWidget *editor,const QModelIndex &index ) const
{
int value = index.model()->data(index, Qt::DisplayRole).toInt();
BooleanWidget *checkbox = static_cast<BooleanWidget*>(editor);
if(value == 1)
{
checkbox->setChecked(true);
}
else
{
checkbox->setChecked(false);
}
}
void CheckBoxDelegate::setModelData( QWidget *editor,QAbstractItemModel *model,const QModelIndex &index ) const
{
BooleanWidget *checkBox = qobject_cast<BooleanWidget*>( editor );
Qt::CheckState value;
if(checkBox->isChecked())
value = Qt::Checked;
else
value = Qt::Unchecked;
model->setData( index, value, Qt::DisplayRole);
}
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
If you are extending the QItemDelegate class, it has a drawCheck() function, what will draw you a nince, centered checkbox. You can use it in the paint() function.
Edit:
Here is an example, assuming you have a class called BooleanEditor, what inherits from QItemDelegate:
void BooleanEditor::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
For keeping the checkbox centered when entering the edit mode, you can do something like this:
class BooleanWidget : public QWidget
{
Q_OBJECT
QCheckBox * checkBox;
public:
BooleanWidget(QWidget * parent = 0)
{
checkBox = new QCheckBox(this);
QHBoxLayout * layout = new QHBoxLayout(this);
layout->addWidget(checkBox,0, Qt::AlignCenter);
}
bool isChecked(){return checkBox->isChecked();}
void setChecked(bool value){checkBox->setChecked(value);}
};
And in your ItemDelegates createEditor() method return an instance of this BooleanWidget class. In your setModelData() and setEditorData() you can now cast your input widget to this BooleanWidget:
BooleanWidget * widget = qobject_cast<BooleanWidget*>(editor);
and then use the is/setChecked method.
I solve this problem with the following item delegate:
booleanitemdelegate.h
#ifndef BOOLEANITEMDELEGATE_H
#define BOOLEANITEMDELEGATE_H
#include <QItemDelegate>
class BooleanItemDelegate : public QItemDelegate
{
public:
BooleanItemDelegate(QObject *parent);
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;public:
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
};
#endif // BOOLEANITEMDELEGATE_H
booleanitemdelegate.cpp
#include "booleanitemdelegate.h"
BooleanItemDelegate::BooleanItemDelegate(QObject *parent):
QItemDelegate(parent)
{
}
void BooleanItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
drawCheck(painter, option, option.rect, index.data().toBool() ? Qt::Checked : Qt::Unchecked);
drawFocus(painter, option, option.rect);
}
bool BooleanItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
if(event->type() == QEvent::MouseButtonRelease){
model->setData(index, !model->data(index).toBool());
event->accept();
}
return QItemDelegate::editorEvent(event, model, option, index);
}
I hope, I can help.
that's what I did to center-allign the editor control:
QWidget *checkBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QCheckBox *editor = new QCheckBox(parent);
editor->setTristate(allowTriState); //my class' variable
// this does the trick :)
editor->setStyleSheet("QCheckBox {margin-left: 43%; margin-right: 57%;}");
// this should do it better
// editor->setStyleSheet("QCheckBox {margin-left: auto; margin-right: auto;}");
// but in my case the checkbox is slightly to the left of original
return editor;
}
solved the problem as follows:
believes that inherits from the itemDelegate QStyledItemDelegate and reimplemented methods "paint" and "editorEvent".
In the method "editorEvent" emit a signal that indicates which row was selected.
Here is the code
class ItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
signals:
void clickSignal(int);
public:
ItemDelegate(QObject *parent = 0)
: QStyledItemDelegate(parent)
{
}
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QStyleOptionViewItemV4 viewItemOption(option);
if (index.column() == 0) {
const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
QRect newRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
QSize(option.decorationSize.width() + 5,option.decorationSize.height()),
QRect(option.rect.x() + textMargin, option.rect.y(),
option.rect.width() - (2 * textMargin), option.rect.height()));
viewItemOption.rect = newRect;
}
QStyledItemDelegate::paint(painter, viewItemOption, index);
}
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,
option.decorationSize,
QRect(option.rect.x() + (2 * textMargin), option.rect.y(),
option.rect.width() - (2 * textMargin),
option.rect.height()));
} else {
return false;
}
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
? Qt::Unchecked : Qt::Checked);
emit(clickSignal(index.row()));
return model->setData(index, state, Qt::CheckStateRole);
}
};
the class where I have a connect QTableView do this:
connect(check,SIGNAL(clickSignal(int)),this,SLOT(CheckMark(int))); //check the itemDelegate
performed the operation with the check that were marked
Without too much fussing instead of directly setting rect:
BtnStyle.rect = option.rect;
Construct a new QRect and move x coordinate to the right as much as you like:
QRect r = option.rect;
QRect r1(r.x() + 34, r.y(), r.width(), r.height());
BtnStyle.rect = r1;
The solution in Python to center checkbox and allow user check/uncheck here
I have a QAbstractItemModel and a QItemDelegate and here is my problem. The Delegate does nothing. Its subroutines are being called but nothing happens.
Here is what I would like to see in my table.
Text : QComboBox : Text : Text : QProgressBar
where : is a column seperator.
Delegate.
#ifndef DELEGATEACTION_H
#define DELEGATEACTION_H
#include <QVariant>
#include <QItemDelegate>
#include <QWidget>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
class DelegateAction : public QItemDelegate
{
Q_OBJECT
public:
explicit DelegateAction(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
#endif // DELEGATEACTION_H
#include "DelegateAction.h"
DelegateAction::DelegateAction(QObject *parent) :
QItemDelegate(parent)
{
}
QWidget * DelegateAction::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QWidget* editor = 0;
switch (index.column())
{
case 0:
default:
{
editor = new QLabel();
break;
}
case 1:
{
QComboBox* combo = new QComboBox(parent);
combo->addItem("Test");
combo->addItem("Test 2");
editor = combo;
break;
}
case 4:
{
editor = new QProgressBar(parent);
break;
}
}
editor->installEventFilter(const_cast<DelegateAction*>(this));
return editor;
}
void DelegateAction::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::DisplayRole);
switch (index.column())
{
case 0:
default:
{
QLabel* label = static_cast<QLabel*>(editor);
label->setText(value.toString());
break;
}
case 1:
{
QComboBox* combo = static_cast<QComboBox*>(editor);
combo->setCurrentIndex(combo->findText(value.toString()));
break;
}
case 4:
{
QProgressBar* progress = static_cast<QProgressBar*>(editor);
progress->setValue(value.toInt());
break;
}
}
}
void DelegateAction::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QVariant value;
switch (index.column())
{
case 0:
default:
{
value = static_cast<QLabel*>(editor)->text();
break;
}
case 1:
{
value = static_cast<QComboBox*>(editor)->currentText();
break;
}
case 4:
{
value = static_cast<QProgressBar*>(editor)->value();
break;
}
}
model->setData(index, value);
}
void DelegateAction::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
Model.
#ifndef MODELACTIONS_H
#define MODELACTIONS_H
#include <QAbstractTableModel>
#include <Unit.h>
class ModelAction : public QAbstractTableModel
{
Q_OBJECT
public:
explicit ModelAction(QObject *parent = 0);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
void sort(int column, Qt::SortOrder order);
void setUnits(const QList<Unit*>* units);
private:
const QList<Unit*>* units;
bool ascending[5];
};
#endif // MODELACTIONS_H
#include "ModelAction.h"
ModelAction::ModelAction(QObject *parent) :
QAbstractTableModel(parent),
units(0)
{
}
int ModelAction::rowCount(const QModelIndex &parent) const
{
if (units == 0)
{
return 0;
}
else
{
return units->length();
}
}
int ModelAction::columnCount(const QModelIndex &parent) const
{
return 5;
}
QVariant ModelAction::data(const QModelIndex &index, int role) const
{
if (index.isValid() == false)
{
return QVariant();
}
if (role == Qt::TextAlignmentRole)
{
if (index.column() == 0 || index.column() == 2)
{
return int(Qt::AlignLeft | Qt::AlignVCenter);
}
else
{
return int(Qt::AlignRight | Qt::AlignVCenter);
}
}
else if (role == Qt::DisplayRole)
{
if (index.column() == 0)
{
// Unit's id.
return index.row() + 1;
}
else if (index.column() == 1)
{
return "bob";
// Unit's Action.
//return mechs.at(index.row())->getWeight();
}
else if (index.column() == 2)
{
// Unit's Action start.
//return mechs.at(index.row())->getTechnology();
}
else if (index.column() == 3)
{
// Unit's Action end.
//return Currency::numberToCurrency(mechs.at(index.row())->getPurchaseValue());
}
else if (index.column() == 4)
{
// Unit's Action progress.
//return Currency::numberToCurrency(mechs.at(index.row())->getSellValue());
}
}
return QVariant();
}
QVariant ModelAction::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
{
return QVariant();
}
if (orientation == Qt::Horizontal)
{
if (section == 0)
{
return "Id";
}
else if (section == 1)
{
return "Action";
}
else if (section == 2)
{
return "Begin Time";
}
else if (section == 3)
{
return "End Time";
}
else if (section == 4)
{
return "Progress";
}
}
return QVariant();
}
void ModelAction::sort(int column, Qt::SortOrder order)
{
// MechCompare compare;
// compare.column = column;
// ascending[column] = !ascending[column];
// compare.ascending = ascending[column];
// qSort(mechs.begin(), mechs.end(), compare);
// reset();
}
void ModelAction::setUnits(const QList<Unit *> *units)
{
this->units = units;
reset();
}
Qt::ItemFlags ModelAction::flags(const QModelIndex &index) const
{
switch (index.column())
{
case 0:
default:
{
return Qt::NoItemFlags;
break;
}
case 1:
{
return Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
}
}
bool ModelAction::setData(const QModelIndex &index, const QVariant &value, int role)
{
switch (index.column())
{
case 1:
{
}
}
}
The only issue I'm aware of is the ModelAction::setData() function is incomplete. I have to go back and edit the data classes that this model displays before I can complete that subroutine. Still the comboboxes and progressbars should still display, just not do anything.
At this point I only see the id numbers and my test text "bob" for each row in the table. The QComboBox and QProgressBar are not rendered at all.
Any help will be appreciated.
Jec
The delegate functions you implemented are for editors. They are not displayed when you are not editing an item. It seems you may want QAbstractItemView::setIndexWidget instead of the delegate.
The createEditor method is only called after certain events. For example, when you double-click a cell,... As buck pointed out, you've got to use setIndexWidget.
How to make QComboBox as MultiSelect in QT ?
There is no option of MultiSelect in Combox in QT ?
OR
Anybody can suggest me some diffrent control but look & feel should be like QCombobox only.
//This is the file named as CheckBoxList.h
#ifndef CHECKBOXLIST_H
#define CHECKBOXLIST_H
#include <QtGui>
class CheckBoxList: public QComboBox
{
Q_OBJECT;
public:
CheckBoxList(QWidget *widget = 0);
virtual ~CheckBoxList();
bool eventFilter(QObject *object, QEvent *event);
virtual void paintEvent(QPaintEvent *);
void SetDisplayText(QString text);
QString GetDisplayText() const;
private:
QString m_DisplayText;
};
#endif // CHECKBOXLIST_H
//This is the file named as CheckBoxList.cpp
#include "CheckBoxList.h"
#include <QtGui>
// internal private delegate
class CheckBoxListDelegate : public QItemDelegate
{
public:
CheckBoxListDelegate(QObject *parent)
: QItemDelegate(parent)
{
;
}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
//Get item data
bool value = index.data(Qt::UserRole).toBool();
QString text = index.data(Qt::DisplayRole).toString();
// fill style options with item data
const QStyle *style = QApplication::style();
QStyleOptionButton opt;
opt.state |= value ? QStyle::State_On : QStyle::State_Off;
opt.state |= QStyle::State_Enabled;
opt.text = text;
opt.rect = option.rect;
// draw item data as CheckBox
style->drawControl(QStyle::CE_CheckBox,&opt,painter);
//QMessageBox::information(0,"Info",text);
}
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem & option ,
const QModelIndex & index ) const
{
// create check box as our editor
QCheckBox *editor = new QCheckBox(parent);
return editor;
}
void setEditorData(QWidget *editor,
const QModelIndex &index) const
{
//set editor data
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
myEditor->setText(index.data(Qt::DisplayRole).toString());
myEditor->setChecked(index.data(Qt::UserRole).toBool());
//
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
//get the value from the editor (CheckBox)
QCheckBox *myEditor = static_cast<QCheckBox*>(editor);
bool value = myEditor->isChecked();
//set model data
QMap<int,QVariant> data;
data.insert(Qt::DisplayRole,myEditor->text());
data.insert(Qt::UserRole,value);
model->setItemData(index,data);
}
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
editor->setGeometry(option.rect);
}
};
//min-width:10em;
CheckBoxList::CheckBoxList(QWidget *widget )
:QComboBox(widget),m_DisplayText(0)
{
// set delegate items view
view()->setItemDelegate(new CheckBoxListDelegate(this));
//view()->setStyleSheet(" padding: 15px; ");
// Enable editing on items view
view()->setEditTriggers(QAbstractItemView::CurrentChanged);
// set "CheckBoxList::eventFilter" as event filter for items view
view()->viewport()->installEventFilter(this);
// it just cool to have it as defualt ;)
view()->setAlternatingRowColors(true);
}
CheckBoxList::~CheckBoxList()
{
;
}
bool CheckBoxList::eventFilter(QObject *object, QEvent *event)
{
// don't close items view after we release the mouse button
// by simple eating MouseButtonRelease in viewport of items view
if(event->type() == QEvent::MouseButtonRelease && object==view()->viewport())
{
return true;
}
return QComboBox::eventFilter(object,event);
}
void CheckBoxList::paintEvent(QPaintEvent *)
{
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
// if no display text been set , use "..." as default
if(m_DisplayText.isNull())
opt.currentText = "......";
else
opt.currentText = m_DisplayText;
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
void CheckBoxList::SetDisplayText(QString text)
{
m_DisplayText = text;
}
QString CheckBoxList::GetDisplayText() const
{
return m_DisplayText;
}
One alternative is to set menu with checkable actions to a button, as I've shown here.
Or you can change the selection model of the combo and control the hiding and showing of the pop up window as shown here.