qtableview text edit - qt

I'm writing a program with PyQt. I use QTableView to display data.
The problem is when I trigger the edit(for example press F2) of a cell, the text in the cell is all selected(highlighted) by default. It's not convenience because I want to modify the text but not rewrite them all.
So I want to know if there is any function to change the behavior?
Thank you

Not sure if there is a simpler way, but you can write your own item delegate which creates a QLineEdit. When updating the editor with model's data you deselect the text and possibly move the cursor to the beginning. The delegate would be something like this (I don't have a Qt installation available right now so I can't test it, but the idea should work):
QWidget * MyDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem & option,
const QModelIndex & index) const
{
// Just creates a plain line edit.
QLineEdit *editor = new QLineEdit(parent);
return editor;
}
void MyDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
// Fetch current data from model.
QString value = index.model()->data(index, Qt::EditRole).toString();
// Set line edit text to current data.
QLineEdit * lineEdit = static_cast<QLineEdit*>(editor);
lineEdit->setText(value);
// Deselect text.
lineEdit->deselect();
// Move the cursor to the beginning.
lineEdit->setCursorPosition(0);
}
void MyDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
// Set the model data with the text in line edit.
QLineEdit * lineEdit = static_cast<QLineEdit*>(editor);
QString value = lineEdit.text();
model->setData(index, value, Qt::EditRole);
}
If you haven't used delegates before in Qt documentation there is a useful example.

You'll need to implement a delegate so you can override the widget used for edits to that field to use a custom editor widget.
The QTableView will use a QTextEdit by default and you could try sub-classing that and altering it's behaviour. My best guess would be that you need to manipulate the focus policy on the editor widget, possibly focusInEvent[1], to change it's behaviour when it receives focus.
[1] http://doc.qt.nokia.com/4.7/qwidget.html#focusInEvent

Related

How would I use a QStyledItemDelegate to make only part of a view editable?

I'm trying to teach myself how to use the QStyledItemDelegate class correctly. Qt's got a fantastic example that I read here: Qt Spin Box Delegate Example.
But here's a question I can't seem to answer. Let's take a look at their example screenshot.
How would I write SpinBoxDelegate in their example such that I could only edit part of the data, say, only items in column 2?
I'm assuming you're already re-implementing QAbstractItemDelegate::createEditor()
The simplest way to indicate that a certain index in your table shouldn't be editable is to return NULL from this function, for example:
QWidget *QAbstractItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index )
{
if( index.column() == 0 )
return NULL;
return new QSpinBox( parent );
}
You can get fancier by stuffing additional information in your model and retrieving it with QModelIndex::data()

How to customize QCombobox with multiple comlumns

I am using QCombobox, i want to every item in QCombobox displays three icons. But currently, every item in QCombobox only displays one icon!
Every icon should be changed dynamically.
You should create new custom QAbstractItemDelegate and set it to QComboBox using void QComboBox::setItemDelegate ( QAbstractItemDelegate * delegate ) api.
In delegate, you need to implement
virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const = 0
as you required.
You will also need to use following API to provide different icon to combo box in userData, that you can use in deletegate's paint method to retrieve icon and draw it.
void QComboBox::addItem ( const QString & text, const QVariant & userData = QVariant() )
Summary:
When I implement as above, there icons only show as drop down list clicked. In normal situation, the text only show. So, for three icons and text show in normal situation we must reimplement paintEvent of QCombobox in case subclass QCombobox or using eventFilter to catch paintEvent of QCombobox without subclass QComboBox!
Thank for your all response!
Reimplement paintEvent, or use big icon image with all 3 icons on it.

Render QWidget in paint() method of QWidgetDelegate for a QListView

i'm having difficulties implementing custom widget rendering in a QListView.
I currently have a QListView displaying my custom model called PlayQueue based on QAbstractListModel.
This is working fine with simple text, but now I would like to display a custom widget for each element.
So I subclassed a QStyledItemDelegate to implement the paint method like this:
void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
QWidget *widget = new QPushButton("bonjour");
widget->render(painter);
}
The selection background is properly rendered but no widget is displayed. I tried with simple QPainter commands like in Qt examples, and this is working fine:
void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
if (option.state & QStyle::State_Selected)
painter->setPen(option.palette.highlightedText().color());
painter->setFont(QFont("Arial", 10));
painter->drawText(option.rect, Qt::AlignCenter, "Custom drawing");
}
So I tried some changes like:
Changing QStyledItemDelegate to QItemDelegate
Adding painter->save() and painter->restore() around rendering
Setting the widget geometry to the available size
But i'm a bit stuck now, i searched for a while on the internet, but can't find any example doing what i want, they all talk about editing widget (which is a lot easier) or custom drawn control (predefined ones, like progress bars).
But here I really need a custom widget I created, containing some layout, labels & pixmaps.
Thanks for your help!
I'm using Qt 4.7.3 for GCC on Ubuntu 11.04.
Just to complete the whole picture: further one can find the code to manage the QWidget as QListView item using delegates.
I finally found out how to make it work within the subclass of QStyledItemDelegate using its paint(...) method.
It seems more effective for performance than previous solution, but this statement one needs to verify =) by investigation what does setIndexWidget() do with created QWidget.
So finally, here is the code:
class PackageListItemWidget: public QWidget
.....
class PackageListItemDelegate: public QStyledItemDelegate
.....
void PackageListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
// here we have active painter provided by caller
// by the way - we can't use painter->save() and painter->restore()
// methods cause we have to call painter->end() method before painting
// the QWidget, and painter->end() method deletes
// the saved parameters of painter
// we have to save paint device of the provided painter to restore the painter
// after drawing QWidget
QPaintDevice* original_pdev_ptr = painter->device();
// example of simple drawing (selection) before widget
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
// creating local QWidget (that's why i think it should be fasted, cause we
// don't touch the heap and don't deal with a QWidget except painting)
PackageListItemWidget item_widget;
// Setting some parameters for widget for example
// spec. params
item_widget.SetPackageName(index.data(Qt::DisplayRole).toString());
// geometry
item_widget.setGeometry(option.rect);
// here we have to finish the painting of provided painter, cause
// 1) QWidget::render(QPainter *,...) doesn't work with provided external painter
// and we have to use QWidget::render(QPaintDevice *,...)
// which creates its own painter
// 2) two painters can't work with the same QPaintDevice at the same time
painter->end();
// rendering of QWidget itself
item_widget.render(painter->device(), QPoint(option.rect.x(), option.rect.y()), QRegion(0, 0, option.rect.width(), option.rect.height()), QWidget::RenderFlag::DrawChildren);
// starting (in fact just continuing) painting with external painter, provided
// by caller
painter->begin(original_pdev_ptr);
// example of simple painting after widget
painter->drawEllipse(0,0, 10,10);
};
Ok I finally figured out how to do what I wanted. Here is what I did:
Drop the delegate class
Call QListView::setIndexWidget() in the data() method of my model to set the widget
Ensure no widget is already present when setting by checking QListView::indexWidget()
Handle the Qt::SizeHintRole role to return the widget's size hint
Return a blank QVariant for the Qt::DisplayRole role
This way I have my custom widgets displayed in the QListView, and they are properly lazy-loaded (that's why i used the model/view pattern). But I don't see how can I unload them when not beeing displayed, well that's another problem.
Here is an example for you.
It seems that you need to use QStylePainter but this is just for drawing as far as I understand it does not act like a real button.

Qt model-view: how to suppress model::data DecorationRole when the QTableView cell editor is active

I have a delegate to create a QLineEdit control when you want to edit a cell in my subclass of QTableView. In the data function of my model, I recently added a case to return an icon for Qt::DecorationRole for some items.
As the user edits a cell that has an icon, the value they enter may cause the icon to disappear. That is all working correctly.
The problem is, if the icon disappears as the user is still typing in the cell, my QLineEdit control is still sized as if there were an icon in the cell, but since there is no longer an icon, part of the text that the user is typing is displayed where the icon used to be.
So, I'd like to have my delegate size the QLineEdit editor to fill the whole cell even when an icon is present (so the icon would be invisible when editing), or even better I think, have the delegate suppress anything returned for Qt::DecorationRole when editing.
Currently, my delegate has these functions:
QWidget *MapTextDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem & /*option*/,
const QModelIndex & /*index*/) const {
QLineEdit *line_editor;
line_editor = new QLineEdit(parent);
connect(line_editor, SIGNAL(textChanged(QString)),
this, SLOT(MapTextChanged()));
return line_editor;
}
QSize MapTextDelegate::sizeHint(const QStyleOptionViewItem &/*option*/,
const QModelIndex &/*index*/) const
{
QLineEdit *line_editor = new QLineEdit();
return line_editor->sizeHint();
}
I don't see anything else in the delegate that might have to do with the size of the editor. I'm not too familiar with how to use delegates - actually, I'm pretty new to C++ and Qt.
Any ideas? I'm using Qt 4.7.
Turns out to be very easy. I reimplemented updateEditorGeometry in the delegate as follows, and that took care of it!
void MapTextDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
editor->setGeometry(option.rect);
}

How to insert QPushButton into TableView?

I am implementing QAbstractTableModel and I would like to insert a QPushButton in the last column of each row. When users click on this button, a new window is shown with more information about this row.
Do you have any idea how to insert the button? I know about delegating system but all examples are only about "how to edit color with the combo box"...
You can use
QPushButton* viewButton = new QPushButton("View");
tableView->setIndexWidget(model->index(counter,2), viewButton);
The model-view architecture isn't made to insert widgets into different cells, but you can draw the push button within the cell.
The differences are:
It will only be a drawing of a pushbutton
Without extra work (perhaps quite a bit of extra work) the button won't be highlighted on mouseover
In consequence of #1 above, you can't use signals and slots
That said, here's how to do it:
Subclass QAbstractItemDelegate (or QStyledItemDelegate) and implement the paint() method. To draw the pushbutton control (or any other control for that matter) you'll need to use a style or the QStylePainter::drawControl() method:
class PushButtonDelegate : public QAbstractItemDelegate
{
// TODO: handle public, private, etc.
QAbstractItemView *view;
public PushButtonDelegate(QAbstractItemView* view)
{
this->view = view;
}
void PushButtonDelegate::paint(
QPainter* painter,
const QStyleOptionViewItem & option,
const QModelIndex & index
) const
{
// assuming this delegate is only registered for the correct column/row
QStylePainter stylePainter(view);
// OR: stylePainter(painter->device)
stylePainter->drawControl(QStyle::CE_PushButton, option);
// OR: view->style()->drawControl(QStyle::CE_PushButton, option, painter, view);
// OR: QApplication::style()->drawControl(/* params as above */);
}
}
Since the delegate keeps you within the model-view realm, use the views signals about selection and edits to popup your information window.
You can use setCellWidget(row,column,QWidget*) to set a widget in a specific cell.

Resources