different items in one cell QTableWidget - qt

I am learning Qt and create a small example like this.
table
I have read some suggested questions which relate to my problem here but right now they are not easy for me to understand.
This is my code, above function is calendar interaction, below function is for showing items:
SmallExample::SmallExample(QWidget *parent)
: QWidget(parent)
{
.........
connect(ui.tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(calendar_clicked(QTableWidgetItem*)));
}
void SmallExample::calendar_clicked(QTableWidgetItem* tableitem)
{
int column = tableitem->column();
SmallExample::row = tableitem->row();
if (column == 2) {
if (!calendar) {
calendar = new QCalendarWidget();
}
calendar->setWindowTitle("Calendar");
calendar->setWindowModality(Qt::WindowModal);
calendar->show();
connect(calendar, SIGNAL(activated(const QDate&)), this, SLOT(date_selected(const QDate&)));
}
}
void SmallExample::date_selected(const QDate&)
{
QTableWidgetItem *itemcalendar = new QTableWidgetItem;
QIcon icon(":/icon/calendar.jpg");
itemcalendar->setIcon(icon);
SmallExample::ui.tableWidget->setItem(SmallExample::row, 0, itemcalendar);
QString text= SmallExample::calendar->selectedDate().toString("dd.MM.yyyy");
QTableWidgetItem *datetext = new QTableWidgetItem;
datetext->setText(text);
SmallExample::ui.tableWidget->setItem(SmallExample::row, 0, datetext);
SmallExample::calendar->close();
}
I know when datetext is added, the itemcalendar will be overwritten, so it does not appear any more. I want both of which will appear, but I don't know how to solve this. Thanks in advance!
update code:
void SmallExample::date_selected(const QDate&)
{
QTableWidgetItem *itemcalendar = SmallExample::ui.tableWidget->item(SmallExample::row, 2);
QIcon icon(":/icon/calendar.jpg");
itemcalendar->setIcon(icon);
QString date = SmallExample::calendar->selectedDate().toString("dd.MM.yyyy")
itemcalendar->setText(date);
}

Maybe you should consider using a QStyledItemDelegate for the second column of your table.
See this post and the star example from Qt documentation.
Here is a code sample:
class CalendarDelegate : public QStyledItemDelegate
{
public:
CalendarDelegate (QObject *parent = 0) :
QStyledItemDelegate(parent)
{
}
void CalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
painter->save();
QIcon icon(":/icon/calendar.jpg");
QSize iconsize = option.decorationSize;
painter->drawPixmap(0.0, 0.0, icon.pixmap(iconsize.width(), iconsize.height()));
painter->restore();
}
}
And then in the SmallExample class constructor:
SmallExample::ui.tableWidget->setItemDelegateForColumn(2, new CalendarDelegate(this));

If you set one time only icon and another time text, you can try such code, with item method:
QTableWidgetItem *itemcalendar = new QTableWidgetItem;
QIcon icon(":/icon/calendar.jpg");
itemcalendar->setIcon(icon);
SmallExample::ui.tableWidget->setItem(SmallExample::row, 2, itemcalendar);
...
QTableWidgetItem* itemcalendar = SmallExample::ui.tableWidget->item(SmallExample::row, 2);
itemcalendar->setText(date);

This is how your code should look like:
QTableWidgetItem *itemcalendar = new QTableWidgetItem;
QIcon icon(":/icon/calendar.jpg");
itemcalendar->setIcon(icon);
itemcalendar->setText(SmallExample::calendar->selectedDate().toString("dd.MM.yyyy"));
SmallExample::ui.tableWidget->setItem(SmallExample::row, 2, itemcalendar);
I have just tried this example and it works as expected (both text and icon appears in item) no matter in which order I set icon and text.
You don't have to create new item evety time you choose date in date_selected, use item as Kirill suggested:
QTableWidgetItem *itemcalendar = SmallExample::ui.tableWidget->item(SmallExample::row, 2);
QString date = SmallExample::calendar->selectedDate().toString("dd.MM.yyyy");
itemcalendar->setText(date);

Related

Subclassing QTreeWidgetItem

I am trying to subclass QTreeWidgetItem so I can fill-in class with data I need.
Went through all the code and couldn't find one complete example on how to do it right.
In my implementation something is wrong because application won't start up, but if I do it with QTreeWidgetItem then it works without any problems.
So I have created subclass as H file:
#ifndef XITEM_H
#define XITEM_H
#include <QTreeWidget>
class XItem : public QObject,public QTreeWidgetItem
{
Q_OBJECT
public:
XItem ();
void setText(int column, const QString &atext);
void addChild(QTreeWidgetItem *child);
};
#endif
And C file:
#include "X.h"
XItem ::XItem (): QTreeWidgetItem(UserType)
{
}
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
And code that runs it all:
QTreeWidget * tree = ui->treeWidget;
QTreeWidgetItem * topLevel = new QTreeWidgetItem();
topLevel->setText(0, "This is top level");
for(int i=0; i<5; i++)
{
//QTreeWidgetItem * item = new QTreeWidgetItem();
XItem *item = new XItem();
item->setText(0,"item " + QString::number(i+1));
topLevel->addChild(item);
}
tree->addTopLevelItem(topLevel);
If I run it like this application hangs and if I comment:
XItem *item = new XItem();
and un-comment:
QTreeWidgetItem * item = new QTreeWidgetItem();
it works.
Could anyone help out on this please.
Kind regards!
Anyway... after all years still simple mistake as this one can come along long hours coding.
ERROR:
void XItem::setText(int column, const QString &atext){
setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
addChild(child);
}
Should be:
void XItem::setText(int column, const QString &atext){
QTreeWidgetItem::setText(column,atext);
}
void XItem::addChild(QTreeWidgetItem *child){
QTreeWidgetItem::addChild(child);
}

Drawing a check mark in Qt

I am working with simple QTreeView. Each row of tree is a specific class inherited from one class EditorRow.
EditorRow has these functions:
virtual QVariant data(ColumnIndexEnum index, int role = Qt::DisplayRole) const = 0;
virtual void setData(const QVariant& data, ColumnIndexEnum index, int role = Qt::UserRole);
virtual QWidget* getEditor(QWidget* parent) const;
Each row has its specific widget, which is shown in the right column when selecting that row. When the row is not selected data function returns appropriate value for each row(f.e. the value which was chosen in the QComboBox).
But in case of row, which's widget is QCheckBox, I need to draw a checked(or unchecked) checkbox when the row is not selected.
I have tried to use Decoration role like this:
if(Qt::DecorationRole == role)
{
if(ValueColumn == index)
{
QStyle* style = QApplication::style();
QStyleOptionButton opt;
opt.state |= QStyle::State_Enabled;
if(isChecked())
opt.state = QStyle::State_On;
else
opt.state = QStyle::State_Off;
const int indicatorWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &opt);
const int indicatorHeight = style->pixelMetric(QStyle::PM_IndicatorHeight, &opt);
const int listViewIconSize = indicatorWidth;
const int pixmapWidth = indicatorWidth;
const int pixmapHeight = qMax(indicatorHeight, listViewIconSize);
opt.rect = QRect(0, 0, indicatorWidth, indicatorHeight);
QPixmap pixmap = QPixmap(pixmapWidth, pixmapHeight);
pixmap.fill(Qt::transparent);
{
QPainter painter(&pixmap);
QCheckBox cb;
cb.setLayoutDirection(Qt::RightToLeft);
style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &painter, &cb);
}
return QIcon(pixmap);
}
}
It actually works, but the icon is shown always, even when the row is selected.
I think it is because of DecorationRole.
Do You have any ideas how to handle this problem ?
Thank You.

QStandardItemModel header with widget and text

i need to use checkbox with text, like this "Check all":
in header of QStanndardItemModel. I tried like this
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
_model->setHorizontalHeaderItem(1, item0);
This way only works for items not for header, i mean for items if i use
_model->setItem(new QStandardItem(some_item);
I heard about writing my own class which inherit QHeaderView but i dont know if this can help in my problem. I would ask if there is a easy way to achieve this?
Regards
i did something like this:
QStandardItemModel *model = new QStandardItemModel(this);
ui->tableView->setModel(model);
QtCheckHeaderView *header = new QtCheckHeaderView(Qt::Horizontal, ui->tableView);
QtCheckHeaderView *vheader = new QtCheckHeaderView(Qt::Vertical, ui->tableView);
ui->tableView->setHorizontalHeader(header);
ui->tableView->setVerticalHeader(vheader);
QStandardItem *root = model->invisibleRootItem();
QList<QList<QStandardItem *> > items;
for (int i = 0; i < 10; ++i)
{
QList<QStandardItem *> res;
for (int j = 0; j < 1000; ++j)
{
res.append(new QStandardItem(QString("%1;%2").arg(j).arg(i)));
vheader->addCheckable(j);
}
items.append(res);
root->appendColumn(res);
header->addCheckable(i);
}
this works great. Draws checkbox with text in vertical and horizontal header.
But this works great only if i put this code to MainWindow constructor. If i put this code to for example pushbutton function this wont work. Datas are fine but headers are invisible.
ui->tableView->repaint();
wont work. Someone maybe knows the answer why this is happening and/or how to solve this?
thanks for answer
Qt models don't support item flags for headers. Setting item flags on items that are used as headers has no effect. QHeaderView doesn't support drawing checkboxes. I'm afraid subclassing QHeaderView is the simpliest way.
I wrote an example of how it can be implemented based on this FAQ page. This class assumes that you use QStandardItemModel and uses flags of its header items. If someone would want to use other model class, subclassing QAbstractItemModel and implementing missing interface and changing headerview implementation would be needed.
class MyHeader : public QHeaderView
{
public:
MyHeader(Qt::Orientation orientation, QWidget * parent = 0);
protected:
QStandardItemModel* standard_model;
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
virtual void mousePressEvent(QMouseEvent *event);
virtual void setModel(QAbstractItemModel* model);
};
MyHeader::MyHeader(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent)
{
standard_model = 0;
}
void MyHeader::paintSection(QPainter *painter, const QRect &rect, int logical_index) const {
painter->save();
QHeaderView::paintSection(painter, rect, logical_index);
painter->restore();
if (standard_model && orientation() == Qt::Horizontal) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
int pos = sectionViewportPosition(logical_index);
QStyleOptionButton option;
option.rect = QRect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (item->checkState() == Qt::Checked) {
option.state = QStyle::State_On;
} else {
option.state = QStyle::State_Off;
}
option.state |= QStyle::State_Enabled;
style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter);
}
}
}
void MyHeader::mousePressEvent(QMouseEvent *event) {
int offset = (height() - style()->pixelMetric(QStyle::PM_IndicatorHeight))/2;
if (standard_model && orientation() == Qt::Horizontal) {
for(int logical_index = 0; logical_index < count(); logical_index++) {
int pos = sectionViewportPosition(logical_index);
QRect rect(offset + pos, offset,
style()->pixelMetric(QStyle::PM_IndicatorWidth),
style()->pixelMetric(QStyle::PM_IndicatorHeight));
if (rect.contains(event->pos())) {
QStandardItem* item = standard_model->horizontalHeaderItem(logical_index);
if (item && item->isCheckable()) {
item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
return;
}
}
}
}
QHeaderView::mousePressEvent(event);
}
void MyHeader::setModel(QAbstractItemModel *model) {
QHeaderView::setModel(model);
standard_model = qobject_cast<QStandardItemModel*>(model);
}
//usage
QTableView view;
QStandardItemModel model;
model.setColumnCount(5);
QStandardItem* item0 = new QStandardItem("some text");
item0->setCheckable(true);
item0->setCheckState(Qt::Checked);
item0->setText("some text");
model.setHorizontalHeaderItem(0, item0);
view.setModel(&model);
MyHeader *myHeader = new MyHeader(Qt::Horizontal, &view);
view.setHorizontalHeader(myHeader);
view.show();

QListView and delegate display unintended item

I've a problem with my QListView, it paint an unintended item on the top left of the QListView :
http://s4.postimage.org/64orbk5kd/Screen_Shot_2013_02_14_at_20_23_14.png
I use a QStyledItemDelegate in my QListView :
m_stringList.push_back("FIRST");
m_stringList.push_back("SECOND");
m_stringList.push_back("THIRD");
m_model.setStringList(m_stringList);
ui->processesListView->setFlow(QListView::LeftToRight);
ui->processesListView->setModel(&m_model);
ui->processesListView->setItemDelegate(new ProcessItemDelegate(this, ui->processesListView));
The delegate (ProcessItemDelegate) paint method use a custom QWidget to display the information :
void ProcessItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &inIndex ) const
{
_listItem->setContent(_listView->model()->data(inIndex).toString());
painter->save();
painter->translate(option.rect.center());
_listItem->render(painter);
painter->restore();
}
The setContent method of the QWidget is very simple :
void ProcessItem::setContent(const QString &s)
{
ui->processId->setText(s);
}
I have another way to add a widget to some list using a QListWidget.
For example knowing that ui->historyView is a QListWidget element and HistoryElementView a subclass of QWidget.
void View::onHistoryChanged(const QList<HistoryElement> &history)
{
clearHistory();
foreach(HistoryElement elt, history)
{
HistoryElementView *historyViewElement = new HistoryElementView(elt.getDateTime("dd/MM/yyyy - hh:mm"), elt.getFilename());
QListWidgetItem *item = new QListWidgetItem();
ui->historyView->addItem(item);
ui->historyView->setItemWidget(item, historyViewElement);
}
}
void View::clearHistory()
{
QListWidgetItem *item;
while (ui->historyView->count() != 0)
{
item = ui->historyView->takeItem(0);
delete item;
}
}
You do not need to delete the widgets inside your QListWidgetItem, it will be handle by Qt.
Once your widgets are inside the list, you can retrieve them using :
// Using index
QListWidgetItem *item = ui->historyView->item(0);
HistoryElementView *elt = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
// Using position
QListWidgetItem *item = ui->historyView->itemAt(pos);
HistoryElementView *historyElement = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
Hope it helps.

QComboBox inside QTreeWidgetItem

Is there something similar to the (PyQT)
QTreeWidgetItem.setCheckState(0, Qt.Checked) but for the combo box?
I can't see anything in the reference, so how can I insert a custom QComboBox as one of the elements within QTreeWidgetItem?
Use QTreeWidget::setItemWidget ( QTreeWidgetItem * item, int column, QWidget * widget ) to put the combo box into the cells.
For example, let's make all rows of the second column of a 2-column QTreeWidget to all be combo boxes:
QTreeWidgetItemIterator it(ui->treeWidget);
while (*it) {
QComboBox *comboBox = new QComboBox(this);
comboBox->addItems(QStringList() << "item1" << "item2");
ui->treeWidget->setItemWidget(*it, 1, comboBox);
++it;
}
Our example widget now looks like this:
I know this is an old question but I think I have a more thorough answer. To get any functionality out of the QComboBox, you'll probably need to subclass it. Here's the solution that I came up with:
#ifndef COMBOBOXITEM_H
#define COMBOBOXITEM_H
#include
class ComboBoxItem : public QComboBox
{
Q_OBJECT
private:
QTreeWidgetItem *item;
int column;
public:
ComboBoxItem(QTreeWidgetItem*, int);
public slots:
void changeItem(int);
};
ComboBoxItem::ComboBoxItem(QTreeWidgetItem *item, int column)
{
this->item = item;
this->column = column;
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(changeItem(int)));
}
void ComboBoxItem::changeItem(int index)
{
if(index >=0)
{
item->setData(this->column, Qt::UserRole, this->itemText(index));
qDebug() item->data(this->column, Qt::UserRole).toString();
}
}
#include "moc_ComboBoxItem.cpp"
#endif // COMBOBOXITEM_H
////// Sample implementation..
lst = new QTreeWidget;
// Snip
QTreeWidgetItem *itm = new QTreeWidgetItem;
// Snip
ComboBoxItem *cmb = new ComboBoxItem(itm, 1);
cmb->addItem("One");
cmb->addItem("Two");
cmb->addItem("Three");
cmb->addItem("Four");
lst->setItemWidget(itm, 1, cmb);
I hope that helps someone in need of a QComboBox inside of a QTreeWidgetItem!
Use
setItemWidget(QTreeWidgetItem( ), column, QWidget( ) )
.Just add your QComboBox() as a parameter, as it inherits QWidget() so it is compatible.
tree = QTreeWidget()
cmb = QComboBox()
cmb.addItem("Item1", 'value1')
cmb.addItem("Item2", 'value2')
cmb.addItem("Item3", 'value3')
item = QTreeWidgetItem(tree.invisibleRootItem())
column = 0
item.setData(column, Qt.EditRole, 'NameYouWant')
column += 1
tree.setItemWidget(item, column , cmb)
Here is small fix to the another posters method. I found that is uses Data to update the box How ever I made small change to setText updater for the method.
#ifndef COMBOBOXITEM_H
#define COMBOBOXITEM_H
#include <QtGui>
class ComboBoxItem : public QComboBox
{
Q_OBJECT
private:
QTreeWidgetItem *item;
int column;
public:
ComboBoxItem(QTreeWidgetItem*, int);
public slots:
void changeItem(int);
};
ComboBoxItem::ComboBoxItem(QTreeWidgetItem *item, int column)
{
this->item = item;
this->column = column;
connect(this, SIGNAL(currentIndexChanged(int)), SLOT(changeItem(int)));
}
void ComboBoxItem::changeItem(int index)
{
if(index >=0)
{
this->item->setText(this->column, this->currentText());
}
}
#include "moc_ComboBoxItem.cpp"
#endif // COMBOBOXITEM_H
////// Sample implementation..
lst = new QTreeWidget;
// Snip
QTreeWidgetItem *itm = new QTreeWidgetItem;
// Snip
ComboBoxItem *cmb = new ComboBoxItem(itm, 1);
cmb->addItem("One");
cmb->addItem("Two");
cmb->addItem("Three");
cmb->addItem("Four");
lst->setItemWidget(itm, 1, cmb);
This is easiest method:
QComboBox *cb = new QComboBox(this);
QStringList cbTexts;
cbTexts << tr("First") << tr("Second") << tr("Third");
cb->addItems(cbTexts);
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget);
ui->treeWidget->addTopLevelItem(item);
ui->treeWidget->setItemWidget(item, [colum here], cb);
for (int col = 0; col < [num colums]; ++col) ui->treeWidget->resizeColumnToContents(col);

Resources