Qt Child Frame passing KeyEvents to Parent Frame - qt

I have implemented an OSGQt based model viewer. I am currently attempting to reposition submodels by changing their properties in a child QFrame to the main MainWindow based viewer frame. The child frame is non-modal and the key events are being passed to the OSG Viewer based handler in the MainWindow instead of the child window.
What would be the appropriate attributes for the child window to keep the keyboard focus there and allow typing new values in the QLineEdit based modifier for the QTreeWidgetItem column.

I have implemented a working solution by using a custom ItemDelegate as follows.
class DoublePositionEditDelegate: public QStyledItemDelegate {
public:
DoublePositionEditDelegate(QObject* parent=0): QStyledItemDelegate(parent) {}
virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QDoubleSpinBox* dspinbox = new QDoubleSpinBox(parent);
dspinbox->setMaximum(1000000.0);
dspinbox->setMinimum(-1000000.0);
dspinbox->grabKeyboard();
return dspinbox;
}
};
A more elegant/better solution is still welcome.

Related

QCombobox not using custom delegate to render current item

I am using a custom delegate to render rich text in a QComboBox. It is used to allow users to select a color to plot a variable on a graph. It works for items in the drop down menu, but not the selected item. Any help would be appreciated.
Here is the code for the delegate I am using:
class CustomDelegate : public QStyledItemDelegate
{
public:
CustomDelegate();
protected:
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};
void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption(&optionV4, index);
QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style();
QTextDocument doc;
doc.setHtml(optionV4.text);
/// Painting item without text
optionV4.text = QString();
style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter);
QAbstractTextDocumentLayout::PaintContext ctx;
// Highlighting text if item is selected
if (optionV4.state & QStyle::State_Selected)
ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText));
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4);
painter->save();
painter->translate(textRect.topLeft());
painter->setClipRect(textRect.translated(-textRect.topLeft()));
doc.documentLayout()->draw(painter, ctx);
painter->restore();
}
QSize CustomDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption(&optionV4, index);
QTextDocument doc;
doc.setHtml(optionV4.text);
doc.setTextWidth(optionV4.rect.width());
return QSize(doc.idealWidth(), doc.size().height());
}
I set the options and delegate for the combo box using code similar, I removed some of the options to reduce the size of the code:
ui->SelectColor->clear();
ui->SelectColor->addItem("Select Color");
ui->SelectColor->addItem("<font color='blue'>Blue</font>");
ui->SelectColor->addItem("<font color='darkBlue'>Dark Blue</font>");
ui->SelectColor->addItem("<font color='red'>Red</font>");
ui->SelectColor->addItem("Dark Yellow");
ui->SelectColor->addItem("<font color='magenta'>Dark Magenta</font>");
ui->SelectColor->addItem("White");
ui->SelectColor->setItemDelegate(new CustomDelegate);
Some of the options are just the names because they don't look good when rendered.
Your custom styled item delegate is applied to items that are part of QComboBox (i.e. ones in QComboBox popup), not for the current item which is represented. The easiest way to achieve what you want would be to introduce your custom class inheriting QComboBox, and then override void QWidget::paintEvent(QPaintEvent *event) method by applying changes you would like to introduce, e.g. set some colored text. Another way (in case you can not introduce another class inheriting QComboBox for some reason) would be to introduce an event filter that does something just after the QPaintEvent. However, usage of event filters might be tricky, and I would advise you to just introduce some another class, and then override paint event.
Now, in case you want to show current item in the same way as an option in the combo box list popup, you could do the following (code is not complete, and you should apply it to your needs yourself):
virtual void paintEvent(QPaintEvent* e) override
{
// QComboBox::paintEvent(e); - this will leave just a rectangle in which you can perform your custom drawings.
// Will make your option colored at least.
QPainter p(this);
QTextDocument doc;
doc.setHtml(this->currentText());
doc.drawContents(&p, rect());
}
Finally, it is worth to mention that QComboBox painting is pretty complex, and you might still need to rewrite pretty everything that Qt already has done for you, so it would be possible to apply other styles you might want not to lose. In order to do that, you should take a look into the source code of this class. You can do this in the Code Browser by Woboq for C & C++ (QComboBox). Hopefully, this clarifies the problem for you more, and now you know what to do in order to achieve your goal.

QTableView with QStandardItemModel: How to perform live updates during editing a cell?

Recently, I made the switch to QT. It has taken some time, but I am starting to find my way around. However, one issue remains:
I want to port a program, that responds to every key press while editing a cell in a table view (QTableView with QStandardItemModel). The idea is to show and update a list of possibilities on a separate form while the user is entering a text in a table view's cell. After every key stroke, the list needs to be updated according to the current text in the edit field of some cell.
Using QTableView::installEventFilter and QEvent::KeyPress, I can get every key press while the table view is in focus, but the cell text / model is only updated after editing, which prohibits live updates of the list.
The model's dataChanged signal is only emitted after editing has finished and not during the user's input.
Any ideas on how to solve this?
Should I use a QItemDelegate?
Or should a QLineEdit be connected to a cell somehow and can this be done without it visually being apparent, so the user still appears to be working directly inside a cell?
Thank you for any help
It works (that is, until the main window is resized...)
Perhaps not the best solution, but at least i found a way to make it work.
I put the slot in the source file that contains the main window, because that is what I have always been used to (C++ Builder)...
GLiveEdit.H - Subclass QStyledItemDelegate
#ifndef GLIVEEDIT_H
#define GLIVEEDIT_H
#include <QStyledItemDelegate>
class GLiveEdit : public QStyledItemDelegate {
public:
GLiveEdit (QObject *_ParentWindow = 0, const char *_Slot = 0);
protected:
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
protected:
mutable QLineEdit *Editor;
const char *Slot;
QWidget *ParentWindow;
};
#endif // GLIVEEDIT_H
GLiveEdit.CPP
#include "gliveedit.h"
#include <QLineEdit>
GLiveEdit::GLiveEdit (QObject *_ParentWindow, const char *_Slot)
: QStyledItemDelegate (_ParentWindow)
{
Editor = 0;
Slot = _Slot;
ParentWindow = (QWidget *) _ParentWindow;
}
QWidget *GLiveEdit::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Editor = (QLineEdit *) QStyledItemDelegate::createEditor (parent, option, index);
connect (Editor, SIGNAL (textChanged (const QString &)), ParentWindow, Slot);
return Editor;
}
MainWindow.CPP - Install Subclassed QStyledItemDelegate ("GLiveEdit")
void MainWindow::SetUp()
{
// Instantiate subclassed QStyledItemDelegate "GLiveEdit" as "Live"
// Also pass the slot "OnEditChanged" that is to be called during editing
Live = new GLiveEdit (this, SLOT (OnEditChanged (const QString &)));
// Tell the table about the instantiated subclassed delegate "Live"
ui->tvOverview->setItemDelegate (Live);
}
void MainWindow::OnEditChanged (const QString &NewText)
{
// NewText contains the up to date text that is currently being edited
}
Anyone any ideas on having it also work after the window has been resized?
Calling the SetUp function from within QMainWindow::resizeEvent does not seem to work, unfortunately.
Also, I suppose QTableView deletes the item delegate from memory itself?
Edit: The item delegate only stops working if the window is resized during the editing... It keeps functioning if the edit is finished first.
It works! Problems after resizing were due to a bug of mine :-) (located elsewhere in the source code)
Any suggestions for improvement still welcome!

Catch item creation moment in QTreeView to set custom widget

I have custom model inherited from QAbstractItemModel and custom view inherited from QAbstractItemView. Model is a wrap on data organized as a tree. When model is changed it emits neccessary signals to notify view about changes. View has default item delegate.
And now I want to craete a custom widget for every item in a view and set it with QAbstractItemView::setIndexWidget(). How can I catch and handle every item creation in the view to make that?
you are better off using an itemdelegate.
class MyItemDelegate: public QAbstractItemDelegate
{
Q_OBJECT
QWidget *widget;
public:
MyItemDelegate(QObject *p):QAbstractItemDelegate(p)
{
//create widget
}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
//initialize painting widget
widget->render(painter);
}
}

Qt: Signal while a QTableView item data is being edited instead of after edit is done?

I have a QTableView which has some QString based items in its model. I implemented setData in my table model, so editing is working (I can change the data in the cell, setData is called to update the model, and the table is properly updated).
Currently setData is only called when the user is done with editing, e.g. after they hit Enter or click out of the text entry box to finalize the text entry. I want to update other other parts of the table while the user is typing/editing into the text edit control instead of after they're done and the edited contents are finalized.
A simple example of what I want to have is for the next table cell to display a count of how many characters have been entered into the cell being edited, but to do this as the user is typing/editing the cell contents, not just after the edit is finalized and setData is called.
Any pointers to what I should be looking for? Thanks!
You can subclass QStyledItemDelegate and commit the data whenever something changes, and then set that delegate for the view with QAbstractItemView::setItemDelegate.
class MyDelegate : public QStyledItemDelegate {
QSignalMapper *mapper;
public:
MyDelegate(QObject*parent = 0)
: QStyledItemDelegate(parent)
, mapper(new QSignalMapper(this))
{
connect(mapper, SIGNAL(mapped(QWidget*)), SIGNAL(commitData(QWidget*)));
}
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(qobject_cast<QLineEdit*>(editor)) {
connect(editor, SIGNAL(textChanged(QString)), mapper, SLOT(map()));
mapper->setMapping(editor, editor);
}
return editor;
}
};
The answer offered by #alexisdm did not work for me when I needed a persistent
editor enabled by QAbstractTableModel::setPersistentEditor(QModelIndex()).
The following solves this problem:
class Delegate : public QStyledItemDelegate
{
Q_OBJECT
public:
// ... omitted for brevity
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
auto *editor = static_cast<QLineEdit*>(
QStyledItemDelegate::createEditor(parent, option, index));
if (editor) {
connect(editor,
&QLineEdit::textChanged,
[=] (const QString &)
{
const_cast<Delegate*>(this)->commitData(editor);
});
}
return editor;
}
// ... omitted for brevity
};
We simply cast the constness from this and make it commit data for the editor.
Note that in the lambda we capture the editor variable by value [=] because otherwise, capturing with reference would make the value of editor undefined when the function runs out of scope.

cannot select QAbstractItemView item, when it's disabled

When I set flags of QAbstractItemModel selectable but not enabled, I can't select items by mouse click. However internally select() function selects objects.
Is this qt bug, or I do something wrong?
From what I understood, you want to "Disable" the item, but at the same time, be able to select it. it's fairly easy to fake that on the model.
if ( role == Qt::BackgroundRole ){
return QVariant(QApplication::palette()->color(QPalette::Inactive, QPalette::Window );
}
This will paint your item as grayed out, and you will still be able to select it.
You're doing something wrong. If you disable a widget it is greyed out and it doesn't receive user mouse clicks and keyboard input.
I just had similar problem (I need to copy disabled items). Here is solution that sets correct style for disabled items (without ignoring any style sheets).
Create custom item delegate for your model.
/// Returns false only if item needs to be rendered as disabled.
bool isIndexEnabled(const QModelIndex &index)
{
// Implement this function.
}
class ItemDelegate : public QStyledItemDelegate {
public:
explicit ItemDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent) {}
protected:
void initStyleOption(
QStyleOptionItemView *option, const QModelIndex &index) const override
{
QStyledItemDelegate::initStyleOption(option, index);
if (!isIndexEnabled(index))
option->state &= ~QStyle::State_Enabled;
}
};
Set the new item delegate to your model.
auto itemDelegate = new ItemDelegate(model)
model->setItemDelegate(itemDelegate);

Resources