How to draw a custom control in QTableView? - c++

I have to draw a custom control in QTableView. This control must looks like FileChooser.
FileChooser http://www.vision.ee.ethz.ch/computing/sepp-irix/qt-3.0-mo/filechooser.png
QStyleOptionButton button_option;
button_option.state |= QStyle::State_Enabled | QStyle::State_Off;
button_option.rect = PushButtonRect(option); //calculate button rect
button_option.text = "...";
QApplication::style()->drawControl(
QStyle::CE_PushButton,
&button_option,
painter);
The code above draws QStyle::CE_PushButton - that looks like QButton, - but there is no QStyle::CE_LineEdit in Qt library. How can I draw QLineEdit?

In order to draw custom widgets in a Table View, you need to create a custom QItemDelegate subclass and override at least the createEditor method, where you can create any kind of widget which is displayed when double-clicking into the table cell. This item delegate can be assigned to the respective column in your table view.
You would then need to create a separate class e.g. CustomFileChooser which inherits from QWidget and consists of a Line Edit and Button.
Your createEditor method would then return such an object.
You may also have to override setEditorData (which shall assign the current model value to the editor widget which was created) and setModelData (which is called when the changes are committed).
This way, the line edit and button would only be visible after double-clicking into the table cell. If you want it to be always visible, you will have to override drawDisplay() as well.

I found an answer by myself. You may display a custom editor (ordinary widget) permanently using:
void QAbstractItemView::openPersistentEditor ( const QModelIndex & index )

First you need to understand that a button is a control Element and thus you can find it under CE but when you need a lineEdit it is not a control element.
In order to paint a lineEdit, I shall quote from the qt documentation,
"QStyleOptionFrameV2 inherits QStyleOptionFrame which is used for drawing several built-in Qt widgets, including QFrame, QGroupBox, QLineEdit, and QMenu."
Yes, only a sample code that might work will help you understand it clearly!
The code should somehow look like this
QStyleOptionFrameV2 *panelFrame = new QStyleOptionFrameV2;
QLineEdit *search = new QLineEdit;
panelFrame->initFrom(search);
panelFrame->rect = QRect(x,y,w,h);//Indeed the location and the size
panelFrame->lineWidth = QApplication::style->pixelMetric(QStyle::PM_DefaultFrameWidth, panelFrame, search);
panelFrame->state |= QStyle::State_Sunken;
QApplication::style()->drawPrimitive(QStyle::PE_PanelLineEdit, panelFrame, painter);

Related

Qt Desktop on Mac: QFormLayout sizing with subpanels

TL;DR: I'm having a grow/shrink probably using embedded forms inside a MainWindow. I'm unsure what to try next.
Okay, I have another sizing problem.
This is a sample app of what I'm trying to do:
When I click on the various toolbar options, I intend to change the central widget contents accordingly. Maybe I should just use a tab widget, but I wanted to do it this way.
In the simplest form, with a widget layout like this:
I set the central widget's layout to Horizontal, and the Inner Widget to FormLayout then set the inner widget's expand rules to expand any expandable fields. As I resize the window, the simple line edit expands and contracts as desired.
When I click the bus icon in the toolbar, I swap out the contents of the central widget with a separate panel. That panel has a widget with a form layout, and is also set to expand and collapse. Here are the layout rules for the second panel:
My trigger code does this:
currentCenter = ui->innerWidget; // In the constructor
currentCenter->hide();
if (v1Form == nullptr) {
v1Form = new V1Form(ui->centralWidget);
}
v1Form->show();
currentCenter = v1Form;
I have tried various orders to this, and I tried using setCentralWidget(). In all cases, the new central area remains a fixed size, even though the original one expands and collapses.
What is working: I can readily change the inner contains for different forms. That's working great. (It took a while to figure it out.)
-or- I can make simple popup forms that grow and shrink properly.
What is not working is grow/shrink when I embed my form inside my central widget or if I use setCentralWidget.
I'm not sure what else to try.
Maybe I should just use a tab widget, but I wanted to do it this way.
You should definitely use a QTabWidget as your central widget. It is designed specifically for your use case, and it will greatly simplify your code.
My trigger code does this:
currentCenter = ui->innerWidget; // In the constructor
currentCenter->hide();
if (v1Form == nullptr) {
v1Form = new V1Form(ui->centralWidget);
}
v1Form->show();
currentCenter = v1Form;
With a QTabWidget, your trigger code can be simplified to:
ui->innerTabWidget->setIndex(1).
You don't need to dynamically construct a V1Form. Simply use Qt Designer to create multiple pages in your QTabWidget and implement all your subpanel widgets within your MainWindow.ui.
(Nonetheless, if you want to implement each subpanel in its own separate *.ui file, you can still promote each page in your QTabWidget to your custom widget.)
What is not working is grow/shrink when I embed my form inside my central widget or if I use setCentralWidget.
To address your original symptoms: Your widgets don't grow/shrink because you didn't put them inside a layout that is part of your main window.
I found a solution doing it the way I started. I had to add one line of code:
void MainWindow::switchForm(QWidget *widget) {
if (centralForm != widget) {
if (centralForm != nullptr) {
centralForm->hide();
centralForm = nullptr;
}
if (widget != nullptr) {
centralForm = widget;
centralForm->show();
ui->centralwidget->layout()->addWidget(centralForm);
}
}
}
void MainWindow::on_actionSetup_triggered()
{
if (setupForm == nullptr) {
setupForm = new SetupForm(ui->centralwidget);
}
switchForm(setupForm);
}
The missing line -- adding my new form to the layout:
ui->centralwidget->layout()->addWidget(centralForm);

How to resize a QLabel displayed by a QWidgetAction after changing it's text

I use a QWidgetAction to add a header to a context menu (that will also show up on Windows, no matter what style is used, in contrast to addSection(), which does not always actually display the title).
The action's widget is a QLabel. It's text is changed by each invocation of the context menu. The menu is setup in the constructor of my class, and the QWidgetAction is added like so (all m_ variables are member variables declared in the header):
m_contextMenu = new QMenu(this);
m_menuTitle = new QLabel;
m_menuTitle->setAlignment(Qt::AlignCenter);
m_menuTitle->setMargin(4);
QWidgetAction *titleAction = new QWidgetAction(m_contextMenu);
titleAction->setDefaultWidget(m_menuTitle);
m_contextMenu->addAction(titleAction);
m_contextMenu->addSeparator();
When the menu is requested, the text of the label is changed and the menu is displayed like so:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
m_contextMenu->exec(place_to_display);
When the label's text is set for the first time (with a short text the label's text is set to), everything is fine:
but when it's set to some longer text, the size remains the same and the text is cropped:
I tried to fix this, but the only working solution I found was to define the QActions displayed in the menu in the constructor, owned by this, setting the label's text, clearing the menu and adding the actions again, like so:
m_contextMenu->clear();
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
m_contextMenu->addAction(m_menuTitleAction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(m_editAction);
m_contextMenu->addAction(m_deleteAction);
m_contextMenu->exec(place_to_display);
Is there a way to resize the title without rebuilding the menu each time?
The solution is to send a resize event instead:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
QResizeEvent re(new_size, m_contextMenu->size());
qApp->sendEvent(m_contextMenu, &re);
This will set the QMenu's internal itemsDirty flag and will force geometry recalculation when the menu is shown. Note that the new size in the event does not matter, as the menu will aways resize based on its sizeHint()!
The QResizeEvent solution didn't really work for me (with a more complex widget), I found the generic solution by reading the QMenu and QAction source code.
m_widget->installEventFilter(this);
// and in my case m_widget->layout()->setSizeConstraint(QLayout::SetFixedSize);
bool SomeClass::eventFilter(QObject* watched, QEvent* event)
{
if (watched == m_widget && event->type() == QEvent::Resize) {
// Force QMenu to recalculate the geometry of this item
QActionEvent e(QEvent::ActionChanged, this);
qApp->sendEvent(m_contextMenu, &e);
}
...
}
QActionEvent triggers everything we need in QMenu: recalculating geometries, resizing the menu to its new size (if it's visible), etc.
This answer extends user362515's answer.
There is little more effort required if you change the size of a hidden widget action (e.g., because of its menu is currently collapsed).
Create a new class ActionWidget which derives publicly from QWidget.
Then override the showEvent method and implement it like this:
void ActionWidget::showEvent(QShowEvent* event)
{
QResizeEvent resize_event(QSize(), parentWidget()->size());
parentWidget()->adjustSize();
qApp->sendEvent(parentWidget(), &resize_event);
QWidget::showEvent(event);
}
Notice that adjustSize must be called on the parent widget of the action widget and the event must be sent to the parent widget.
Of course, you must also reimplement QWidgetAction::createWidget such that it returns an instance of the ActionWidget-class and make sure that ActionWidget reports a proper (updated) size hint.

How to get the name of the widget holding the currently selected text? (QTextCharFormat)

A text editor app has multiple widgets, of multiple types: QTextEdit, QTableView, QTreeView. All of them need to have rich text editing functions.
This code enables the strikethrough function to work on just ONE specific widget. How to make it work on any widget that holds the currently selected text?
ui->toDoListTextEdit needs to be replaced with a call to the widget that holds the selection fmt, or the cursor?. How to do this, considering that there are three different types of widgets?
void MainWindow::on_actionStrike_triggered()
{
//put in strikethrough
QTextCharFormat fmt;
fmt.setFontStrikeOut(true);
//blend selection
QTextCursor cursor = ui->toDoListTextEdit->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(fmt);
ui->toDoListTextEdit->mergeCurrentCharFormat(fmt);
}
Take a look at MVC pattern, create own model which will store text to display, subclass from QTextView and emit signals to change model's data(text), same goes to model, implement signals to change custom QTextView class.

Setting the Height of a QPlainTextEdit Delegate in a QTableView

I'm working here on a project and currently I'm stuck on the following problem.
It is about a QTableView which has a column called "Description", the cells of this column contain a QPlainTextEditDelegate. I'm failing on setting the Height of the QPlainTextEdit everytime it is entered. Right now it behaves like a QLineEdit until I drag the row ( in which I'm active at that time ) of the QTableView larger.
What I want to do is to change the Height of the QPlainTextEdit once I entered it.
What are your suggestions? How can I proceed to get this thing done?
Thank you all in advance!
BTW Sorry for my poor english :/
edit:
Ok I solved it, but without sizeHint, I used updateEditorGeometry :
void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
And inside this Method, you can set the width or height like you want
editor->setGeometry(option.rect.x(),option.rect.y(),<your_width>,<your_height>);
But thank you anyway!
You should reimplement QAbstractItemDelegate::sizeHint method to return expected height when you create your editor. I don't think that it's necesary to emit QAbstractItemDelegate::sizeHintChanged signal after creating editor, but documentation doesn't say anything. If it doesn't work without it, you should emit sizeHintChanged after returning created editor widget to notify view of need to change row height.

Qt - QPushButtons in place of QTreeView Items

Is it possible to add QPushButtons for every item in a QTreeView? For instance, when you click on a TreeItem (that is a button), it's children get displayed as buttons as well? I just have a standard QTreeView.
_layout = new QVBoxLayout(this);
treeView = new QTreeView(this);
QStandardItemModel* standardModel = new QStandardItemModel();
QStandardItem* rootMenu = standardModel->invisibleRootItem();
//populate TreeView
treeView->setModel(standardModel);
treeView->setWordWrap(true);
treeView->setHeaderHidden(true);
//treeView->expandAll();
_layout->addWidget(treeView);
this->setLayout(_layout);
I have not personally done this (yet), but you could try using QAbstractItemView::setIndexWidget(). The widgets won't aren't connected in any way to the data model, so it is up to your code to update them if necessary. Also, you need to call it for each QModelIndex separately.
Here is the answer. You must create your own delegate and applay it for your QTreeView.
To create delegate you must subclass QStyledItemDelegate and re-implement its QStyledItemDelegate::paint(...) method in that way what you want, also, don't forget about re-implementing QStyledItemDelegate::sizeHint(...) method if needed, of course.
Also, you may need to re-implement QStyledItemDelegate::createEditor(...) method.
To apply created delegate to your view (QTreeView) you must create delegate and call QTreeView's method setItemDelegate (or setItemDelegateForColumn, or setItemDelegateForRow).
Good luck!