Disable specific items in QComboBox - qt

In my application, I want to disable some items (i.e. not selectable, no highlights when mouse hovering above, and the texts are greyed out) in the QComboBox when certain conditions are met.
I indeed found someone did ask the same question here: Disable Item in Qt Combobox
But none of these solutions in the answers seem to actually work (including the trick).
Is there a decent and 'correct' way to implement this?
EDIT:
I found out why setting the flags wouldn't disable the items in my application: for some reasons, I had to set the style QStyle::SH_ComboBox_UseNativePopup(see https://codereview.qt-project.org/#/c/82718/). And this setting for some reasons blocked the flag setting. Does anyone have an idea why, and how to work around? A minimum test example is included (modified from the answer of #Mike):
#include <QApplication>
#include <QComboBox>
#include <QStandardItemModel>
#include <QProxyStyle>
class ComboBoxStyle : public QProxyStyle
{
public:
int styleHint ( StyleHint hint, const QStyleOption * option = 0, const QWidget * widget = 0, QStyleHintReturn * returnData = 0 ) const override
{
if ( hint == QStyle::SH_ComboBox_UseNativePopup )
{
return 1;
}
return QProxyStyle::styleHint( hint, option, widget, returnData );
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QComboBox comboBox;
// Setting this style would block the flag settings later on.
comboBox.setStyle( new ComboBoxStyle() );
comboBox.insertItem(0, QObject::tr("item1"));
comboBox.insertItem(1, QObject::tr("item2"));
QStandardItemModel* model = qobject_cast<QStandardItemModel*>(comboBox.model());
QStandardItem* item= model->item(1);
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
comboBox.show();
return a.exec();
}

The answer linked in my comment above seems to be talking about an old version of Qt. I have tested on Qt5.4 and Qt5.6 and there is no need set the color yourself here, you just need to set and/or clear the Qt::ItemIsEnabled flag, here is an example:
#include <QtWidgets>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QComboBox comboBox;
comboBox.addItem(QObject::tr("item1"));
comboBox.addItem(QObject::tr("item2"));
comboBox.addItem(QObject::tr("item3"));
QStandardItemModel *model =
qobject_cast<QStandardItemModel *>(comboBox.model());
Q_ASSERT(model != nullptr);
bool disabled = true;
QStandardItem *item = model->item(2);
item->setFlags(disabled ? item->flags() & ~Qt::ItemIsEnabled
: item->flags() | Qt::ItemIsEnabled);
comboBox.show();
return a.exec();
}

Here is the technique #Mike describes, wrapped up in a convenient utility function:
void SetComboBoxItemEnabled(QComboBox * comboBox, int index, bool enabled)
{
auto * model = qobject_cast<QStandardItemModel*>(comboBox->model());
assert(model);
if(!model) return;
auto * item = model->item(index);
assert(item);
if(!item) return;
item->setEnabled(enabled);
}

Related

Is it possible to change default behavior of a checkable QGroupBox?

The question is pretty straight forward: is it possible to change the default behavior of a checkable QGroupBox object? I designed a user interface with many QLineEdit objects inside a checkable QGroupBox, the desired behavior is: when QGroupBox is not checked all of its children are enable and when it is checked all of its children are disable.
As you can see at the oficial QGroupBox documentation, it says:
If the check box is checked, the group box's children are enabled; otherwise, the children are disabled and are inaccessible to the user.
One trick is to modify the painting so that when it is checked the check is not shown and vice versa:
#include <QtWidgets>
class GroupBox: public QGroupBox{
public:
using QGroupBox::QGroupBox;
protected:
void paintEvent(QPaintEvent *){
QStylePainter paint(this);
QStyleOptionGroupBox option;
initStyleOption(&option);
if(isCheckable()){
option.state &= ~(isChecked() ? QStyle::State_On : QStyle::State_Off);
option.state |= (isChecked() ? QStyle::State_Off : QStyle::State_On);
}
paint.drawComplexControl(QStyle::CC_GroupBox, option);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GroupBox groupBox;
groupBox.setCheckable(true);
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(new QLineEdit);
vbox->addWidget(new QLineEdit);
vbox->addWidget(new QLineEdit);
vbox->addStretch(1);
groupBox.setLayout(vbox);
groupBox.show();
return a.exec();
}

How to redefine a node expanding in a QTreeWidget?

The way I understand the qtreeview.cpp the expand method in the QTreeView is responsible for expanding nodes. For example it is used in the expandOrCollapseItemAtPos method. I try to redefine a node expansion in the QTreeWidget:
#include <QApplication>
#include <QWidget>
#include <QTreeWidget>
#include <QMessageBox>
class MyTree : public QTreeWidget
{
public:
MyTree(QWidget *parent) : QTreeWidget(parent) {}
expandItem(const QTreeWidgetItem *item) {
QMessageBox msg;
msg.setText("EXPAND ITEM!!");
msg.exec();
QTreeWidget::expandItem(item);
}
expand(const QModelIndex &index) {
QMessageBox msg;
msg.setText("EXPAND!!");
msg.exec();
QTreeWidget::expand(index);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *mainWidget = new QWidget();
mainWidget->resize(200,100);
MyTree *myTree = new MyTree(mainWidget);
myTree->resize(200,100);
QTreeWidgetItem *node, *leaf;
node = new QTreeWidgetItem(myTree);
node->setText(0,"node");
leaf = new QTreeWidgetItem(node);
leaf->setText(0,"leaf");
mainWidget->show();
return a.exec();
}
But there is no any message box when I expand a node. I tried to comment QTreeWidget::expandItem(item); and QTreeWidget::expand(index); but expanding is still working.
How do I redefine a node expanding in a QTreeWidget?
QTreeWidget::expand and QTreeWidget::expandItem are non-virtual methods. So redefinition is not useful. I will use slot-signal mechanism with QTreeWidget::expanded/collapsed signals.
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(myExpand(QModelIndex)));
This should solve your problem -
connect(QTreeWidgetObject, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));

Prevent QApplication app from closing if a service is running

I have a QML app in which I have subclassed QApplication to create my main screen with QML. The issue i have is on clicking Close button the application closes as intended, but I want to handle a situation where if some services are running I want to override close button behaviour.
I tried overriding closeEvent() without any luck. Can anyone point me to some ways I can handle this?
UPDATE : This is the code snippet I tried
class SingleApplication : public QApplication {
Q_OBJECT
public:
SingleApplication(int &argc, char **argv);
void closeEvent ( QCloseEvent * event )
{
event->ignore();
}
}
MAIN.cpp
#include "view.h"
#include <QDebug>
#include <QDesktopWidget>
#include "SingleApplication.h"
int main(int argc, char *argv[])
{
SingleApplication app(argc, argv);
if(!app.isRunning()) {
app.processEvents();
View view(QUrl("qrc:/qml/main.qml"));
#ifdef Q_OS_LINUX
view.setFlags(Qt::WindowMinimizeButtonHint|Qt::WindowCloseButtonHint);
#endif
view.setMaximumSize(QSize(1280,700));
view.setMinimumSize(QSize(1280,700));
// Centering the App to the middle of the screen
int width = view.frameGeometry().width();
int height = view.frameGeometry().height();
QDesktopWidget wid;
int screenWidth = wid.screen()->width();
int screenHeight = wid.screen()->height();
view.setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
view.show();
return app.exec();
}
return 0;
}
There is no QApplication::closeEvent. Such virtual function belongs to QWidget.
Use of QApplication indicated that you have normal QWidget container for your QML UI (as you say UI is based on QML though). You should rather override that widget closeEvent e.g.:
class MyMainWidget : public QWidget // or is it QMainWindow?
{
// snip
private:
void closeEvent(QCloseEvent*);
}
void MyMainWidget::closeEvent(QCloseEvent* event)
{
// decide whether or not the event accepted
if (condition())
event->accept();
}
And if your container widget is not overridden yet (simply QWidget?), well, now you have to do so.
And you did not say whether or not you want to keep app window running. I assume you want that as well.

Disable validation in QSpinBox

Hence I have a QSpinBox, and want to unset validation for writing not only int values, but also string in it.
Please help me to fix this.
I have tried this, but it does not work:
class Spinbox:public QSpinBox
{
public:
Spinbox(QWidget* parent=0)
:QSpinBox(parent){}
void setLineEdit(QLineEdit *l)
{
QSpinBox::setLineEdit(l);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Spinbox de;
QLineEdit le;
le.setValidator(0);
le.setText("text");
de.setLineEdit(&le);
de.show();
return a.exec();
}
Qt docs say that:
If QLineEdit::validator() for the lineEdit returns 0, the internal
validator of the spinbox will be set on the line edit.
Thus, in order to disable the QSpinBox's built in validator, you need to set your own (dummy?). I.e.
class Validator : public QValidator
{
public:
State validate(QString &input, int &pos ) const
{
return QValidator::Acceptable;
}
};
[..]
Spinbox de;
QLineEdit le;
le.setValidator(new Validator());
le.setText("text");
de.setLineEdit(&le);
de.show();

Capture QML drawing buffer, without displaying

I need to grab each QML (QtQuick 2) drawing frame and sent it over the network.
At the moment I have used method listed below, but this method has two big disadvantage
1) Due to Qt5 documentation grabWindow() function has performance issues
2) It can't work with hidden QML window
Is it possible to get OpenGL render buffer right after QQuickWindow::afterRendering ?
Using FBOs ? Shared opengl context ?
class Grab: public QObject
{
public:
Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
public slots:
void Grabme()
{
QImage image = wnd_->grabWindow();
}
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
&grab, &Grab::Grabme, Qt::DirectConnection );
return app.exec();
}
Example bellow can grab any qml content to FBO and then sent it as Image via signal.
Only one problem of this approach is visibility, grab window must be visible for successful grabbing. If anybody knows how to prevent this you can help me and provide more advanced approach.
// main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GrabWindow grab;
grab.setResizeMode( QQuickView::SizeViewToRootObject );
grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
grab.setFlags( Qt::Popup );
grab.show();
return app.exec();
}
// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
Q_OBJECT
signals:
void changeImage( const QImage &image );
public:
GrabWindow( QWindow * parent = 0 );
private slots:
void afterRendering();
void beforeRendering();
private:
QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
QQuickView( parent )
{
setClearBeforeRendering( false );
setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
connect( this, SIGNAL( afterRendering() ), SLOT( afterRendering() ), Qt::DirectConnection );
connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
if( !fbo_.isNull() )
{
emit changeImage( fbo_->toImage() );
}
}
void GrabWindow::beforeRendering()
{
if (!fbo_)
{
fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
setRenderTarget(fbo_.data());
}
}
I managed to find a trick to make grabWindow() work when the Window is "not visible". The trick is to set the window's visibility: Window.Minimized and the flags: Qt.Tool. The window is not displayed to the user, but to the Qt's internals it appears to be visible and the grabWindow() method call works as expected. Remember to call that method only once the scene has been initialised.
The only problem with this solution (that I have come across) is that if the window's color property is set to transparent, the captured content has black background.
With later versions of Qt 5.X you can also use the software render backend.
The following renders any scene in the background without any visible window or OpenGL tricks:
// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
const char *source = "qrc:/main.qml";
if (argc > 1) source = argv[1];
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app{argc, argv};
QQuickRenderControl renderControl;
QQuickWindow window{&renderControl};
QQmlEngine engine;
QQmlComponent component{
&engine,
QUrl{QString::fromUtf8(source)}
};
QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
window.contentItem()->setSize(rootItem->size());
rootItem->setParentItem(window.contentItem());
window.resize(rootItem->size().width(), rootItem->size().height());
QImage image = renderControl.grab();
image.save("output.png");
return 0;
}

Resources