ComboBox of CheckBoxes? - qt

I am trying to make the items in a ComboBox checkable. I tried this:
http://programmingexamples.net/wiki/Qt/ModelView/ComboBoxOfCheckBoxes
where I subclassed QStandardItemModel and re-implemented the flags() function to make the items checkable. Then I added this model to the ComboBox. Unfortunately, a checkbox does not appear with the items. Can anyone see where I have gone wrong?

Have you set a check state as well as making them checkable?
In my example below, this line is critical:
item->setData(Qt::Unchecked, Qt::CheckStateRole);
If it is omitted the check boxes won't render as there isn't a valid check-state to render.
The example shows check boxes in a combobox, list and table, as I couldn't get it to work at first either, so I tried different views.
test.cpp
#include <QtGui>
int main(int argc, char** argv)
{
QApplication app(argc, argv);
QStandardItemModel model(3, 1); // 3 rows, 1 col
for (int r = 0; r < 3; ++r)
{
QStandardItem* item = new QStandardItem(QString("Item %0").arg(r));
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::Unchecked, Qt::CheckStateRole);
model.setItem(r, 0, item);
}
QComboBox* combo = new QComboBox();
combo->setModel(&model);
QListView* list = new QListView();
list->setModel(&model);
QTableView* table = new QTableView();
table->setModel(&model);
QWidget container;
QVBoxLayout* containerLayout = new QVBoxLayout();
container.setLayout(containerLayout);
containerLayout->addWidget(combo);
containerLayout->addWidget(list);
containerLayout->addWidget(table);
container.show();
return app.exec();
}
test.pro
QT=core gui
SOURCES=test.cpp

I have a little addition.
If one compiles the skyhisi's code then the combobox on Mac OS X
doesn't look as combobox with native checkboxes. You can see it
on the screenshot.
Tested with qt-4.8.5 and 5.1.1.
It seems like Qt draws these controls by itself. Our team has
found the following workaround by pure accident. You can subclass QStyledItemDelegate and reimplement paint() this way:
void SubclassOfQStyledItemDelegate::paint(QPainter * painter_, const QStyleOptionViewItem & option_, const QModelIndex & index_) const
{
QStyleOptionViewItem & refToNonConstOption = const_cast<QStyleOptionViewItem &>(option_);
refToNonConstOption.showDecorationSelected = false;
//refToNonConstOption.state &= ~QStyle::State_HasFocus & ~QStyle::State_MouseOver;
QStyledItemDelegate::paint(painter_, refToNonConstOption, index_);
}
You can then set this delegate to the combo box by adding the following lines to skyhisi's code:
SubclassOfQStyledItemDelegate *delegate = new SubclassOfQStyledItemDelegate(this);
combo->setItemDelegate(delegate);
The comboBox installed with this delegate looks the following way:
On Windows there may be a different issue: text of the checkBoxes has sticked background or dotted border around an item:
To change this appearance one can add the following line to the overridden paint just
before the line QStyledItemDelegate::paint(painter_, refToNonConstOption, index_)
(in the code sample this line was commented):
refToNonConstOption.state &= ~QStyle::State_HasFocus & ~QStyle::State_MouseOver;
Result:

You can try this with QListView:
QStringList values = QStringList << "check 1" << "check 2" << "check 3" << "check 4";
QStandardItemModel model = new QStandardItemModel;
for (int i = 0; i < values.count(); i++)
{
QStandardItem *item = new QStandardItem();
item->setText(values[i]);
item->setCheckable(true);
item->setCheckState(Qt::Unchecked);
model->setItem(i, item);
}
ui->list->setModel(model);

I tried to make this example on Linux Mint, but I can't make the checkboxes visible.
I had to implement the SubclassOfQStyledItemDelegate class and set the delegate to the checkbox as Neptilo and gshep advised.

Related

How to change the font of the qcombobox label/header only?

When I change the font of my QComboBox comboBox->setFont(whateverQFont); it is applied on the dropdown menu as well (all the items), and it overrides the Qt::FontRole data I have set on my items with comboBox->setItemData(index, itemSpecificFont, Qt::FontRole);
I'd like to set a font on the QComboBox label only and leave the dropdown displayed as it was. Or even better : to have directly the same font as the selected item.
Is there an easy way to do that ?
Edit: Solution of Jasonhan works fine for an editable QComboBox (-> setting the font on the QLineEdit) but is not applicable for a regular QComboBox, as the QLabel is private.
Before starting implement a custom model you could try with QListView.
It just applies to the drop-down menu and you can change its font with the usual setFont function; the you have to apply it to your QComboBox thorugh routine setView.
Something like this (it's not Qt C++ code, I've skipped all arguments in function calls):
QComboBox *combobox = new QComboBox();
combobox->setFont();
...
QListView *listview = new QListView();
listview->setFont();
combobox->setView(listview);
After 2 years, I saw this question. I don't know whether you have found a better method or not. If not, following code may give you a hint.
QComboBox label as you said is actually a QLineEdit, so you just need to set this component's font, and it will solve your problem.
QComboBox *box = new QComboBox();
//add some list items to box
if (box->lineEdit())
box->lineEdit()->setFont(font);//font is your desirable font
Something that works for a non-editable QComboBox is to install a QProxyStyle that sets the font when a CE_ComboBoxLabel control element is drawn.
Here's an example that sets the label font to italic:
#include <QApplication>
#include <QProxyStyle>
#include <QPainter>
#include <QComboBox>
class MyProxyStyle : public QProxyStyle
{
public:
void drawControl(QStyle::ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget = nullptr) const override
{
if (element == QStyle::CE_ComboBoxLabel)
{
auto fnt = painter->font();
fnt.setItalic(true);
painter->setFont(fnt);
}
QProxyStyle::drawControl(element, option, painter, widget);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.setStyle(new MyProxyStyle);
QComboBox cb;
cb.addItem("Option 1");
cb.addItem("Option 2");
cb.addItem("Option 3");
cb.show();
app.exec();
}

Adding Rows in a QTreeWidget in runtime messes up widgetItems visuals

I have a QDialog show a QTreeWidget that has the parameters of some objects.
I have added a button to add extra fields in the vectors of the objects visually like this.
void on_Create_Surface_Pressed()
{
QPushButton * const button = qobject_cast<QPushButton*>(sender());
QTreeWidgetItem* branch = treeButtons[button];
if(branch)
{
QTreeWidgetItem* leaf = new QTreeWidgetItem(branch);
QString str("Point ");
str+=QString::number(branch->childCount()-1);
QLabel* label = new QLabel(str);
QString surfType[3] = {QString("x") , QString("y") ,QString("z")};
QTableWidget* surfTable = sampleTree( surfType);<--(Create and add a
Table under the
qtreewidgetItem)
leaf->treeWidget()->setItemWidget(leaf , 0 , label);
leaf->treeWidget()->setItemWidget(leaf , 1 , surfTable);
update();
}
}
My problem is that the new rows are rendered above the next QTreeWidgetItem until the user collapses the tree(which is the gimmick i m using).
Is there a workaround for this? Or isn't QTreeWidget purposed for this use and a QTreeView would serve better?

QToolButton with text: Overwrite minimal height to minic regular button height

I am displaying QToolButtons with icon plus text (Qt::ToolButtonTextBesideIcon) outside of a tool bar. Each button has a QAction associated with it which determines the used icon and the displayed text. All those buttons are placed inside a QGridLayout. So far so good.
Unfortunately, it looks like that as soon as you add a QAction to a QToolButton, Qt automatically decides to shrink it down to the minimal size, which is not what I want given my QGridLayout. I used the following lines to correct that behavior in the horizontal dimension:
QToolButton* pButton = new QToolButton(0);
pButton->addDefaultAction(pAction);
pButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
pButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
However that still leaves my button with a smaller height than a regular (push) button. I already tried various other size policies without success.
How to best solve this issue? Is there a reliable way to determine the "normal" button height?
One idea I had was to create a regular dummy button, place it in the same layout and then read its size. This size could then be applied to my QToolButton and the dummy button would be destroyed again. Is there a more elegant / reliable way?
I do not understand what do you want to achieve.
Difference between QPushButton and QToolButton is that, QToolButton has implemented PopupMenu ( can be done easily for QPushButton also )
As I understand visual difference is only small arrow in lower right corner of QToolButton, when you use added QActions to QToolButton
This arrow is for me only difference between QToolButton and QPushButton. But maybe I am missing something.
From your examples ( QToolButton with icon + text: How to center both? )
it does not look like you want to use that popup feature. Thats why I do not understand, why to use QToolButton instead of QPushButtons.
In this example shows:
1) Same height of QToolButton and QPushButton
2) PopuMenu for QPushButton
As for me, I do not understand why to use QToolButton and try to make it look like QPushButton when it is simple to use QPushButton as QToolButton
#include <QtGui>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Prepare layout
QMainWindow *window = new QMainWindow;
QWidget *centralWidget = new QWidget(window);
QGridLayout *grid = new QGridLayout(centralWidget);
QTextEdit *textEdit = new QTextEdit();
window->setCentralWidget(centralWidget);
QAction *toolAction = new QAction(window->style()->standardIcon(QStyle::SP_MediaPlay), "ToolButton", window);
QObject::connect(toolAction, &QAction::triggered, [=]() {
qDebug() << "action";
});
QPushButton *pushButton = new QPushButton(toolAction->icon(), "PushButton1", window);
pushButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QPushButton *pushButton2 = new QPushButton(toolAction->icon(), "PushButton2", window);
pushButton2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QPushButton *pushButton3 = new QPushButton(toolAction->icon(), "PushButton2", window);
pushButton3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QObject::connect(pushButton3, &QPushButton::released, [window, pushButton3, toolAction](){
QMenu menu;
menu.addAction(toolAction);
QPoint pos = window->mapToGlobal(pushButton3->pos());
pos += QPoint(0, pushButton3->height());
menu.exec(pos);
});
QObject::connect(pushButton, SIGNAL(pressed()), toolAction, SLOT(trigger()));
QObject::connect(pushButton2, SIGNAL(pressed()), toolAction, SLOT(trigger()));
QToolButton *toolButton = new QToolButton(window);
toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton->setText("Popup action");
toolButton->addAction(toolAction);
toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QToolButton *toolButton2 = new QToolButton(window);
toolButton2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolButton2->setDefaultAction(toolAction);
toolButton2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
toolButton->setMaximumHeight(pushButton->sizeHint().height());
toolButton->setMinimumHeight(pushButton->sizeHint().height());
toolButton2->setMaximumHeight(pushButton->sizeHint().height());
toolButton2->setMinimumHeight(pushButton->sizeHint().height());
grid->addWidget(textEdit ,0,0,1,2);
grid->addWidget(toolButton ,1,0,1,1);
grid->addWidget(pushButton ,1,1,1,1);
grid->addWidget(toolButton2 ,2,0,1,1);
grid->addWidget(pushButton2 ,2,1,1,1);
grid->addWidget(pushButton3 ,3,0,1,2);
window->show();
return a.exec();
}

Drawing lines and checkbox on same layer in Qt

I want to design a GUI using Qt. That contains lines and check-boxs, which are connected to each other like this :
----------[ ]-----------
------[ ]---------[ ]-------------
(where dash represents line and [] is for check-box)
Lines are created dynamically. And selecting the check-box will disable the corresponding line. So basically the lines and check-box should be on same layer.
Any hint/link about the implementation is appreciated.
You'll need a combination of QFrame, QCheckBox, and QHBoxLayout. For something a little fancier, you could sub-class your own QWidget for each section and add them incrementally to a QVBoxLayout. Something like this...
class CheckLine : public QWidget
{
Q_OBJECT
public:
CheckLine(int numboxes = 1, QObject* parent = 0) :
QWidget(parent)
{
m_layout = new QHBoxLayout;
m_layout->setSpacing(0); //you can also set the margins to zero if need be
setLayout(m_layout);
QFrame* firstline = new QFrame();
firstline->setFrameShape(QFrame::HLine);
m_layout->addWidget(firstline);
m_lines.append(firstline);
for(int i = 0; i < numboxes; ++i)
addBox();
}
void addBox()
{
QCheckBox* newbox = new QCheckBox(""); //add text here - or leave it blank if you want no label
m_newbox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_layout->addWidget(newbox);
m_boxes.append(newbox);
QFrame* newline = new QFrame();
newline->setFrameShape(QFrame::HLine);
m_layout->addWidget(newline);
m_lines.append(newline);
/* link each checkbox to disable the line after it */
connect(newbox, SIGNAL(toggled(bool)), newline, SLOT(setEnabled(bool)));
// connect(newbox, SIGNAL(toggled(bool)), this, SLOT(setEnabled(bool))); //use this instead if you want it to disable the entire row
}
private:
QHBoxLayout* m_layout;
QList<QCheckBox*> m_boxes;
QList<QFrame*> m_lines;
};
Then, create a widget with a QVBoxLayout and new a CheckLine, incrementing numboxes by 1 each time. Tweak the code if you want any checkbox to disable the entire line.

How do I make a QVector of widgets?

How do I make a QVector (or some other container class) of a dynamic number of widgets, such as QPushButton or QComboBox in Qt 4?
I've used the following in my window class's constructor:
QVector<QComboBox*> foo; // Vector of pointers to QComboBox's
And now I want to fill it with some number of controls which can change dynamically:
for(int count = 0; count < getNumControls(); ++count) {
foo[count] = new QComboBox();
}
I've searched for hours trying to find the answer to this. The Qt forums mention making a QPtrList, but that class no longer exists in Qt4.
I'd later try to get the text value from each using array-style indexing or the .at() function.
I would really appreciate an example of declaring, initializing, and populating any data structure of any QWidgets (QComboBox, QPushButton, etc.)
here you go :)
#include <QWidget>
#include <QList>
#include <QLabel>
...
QList< QLabel* > list;
...
list << new QLabel( parent, "label 1" );
..
..
foreach( QLabel* label, list ) {
label->text();
label->setText( "my text" );
}
If you are trying just to get a simple example to work, its important that your widgets have a parent (for context / clean up) purposes.
Hope this helps.
foo[count] = new QComboBox();
This won't affect the size of foo. If there isn't already an item at index count, this will fail.
See push_back, or operator<<, which add an item to the end of the list.
QVector<QComboBox*> foo;
// or QList<QComboBox*> foo;
for(int count = 0; count < getNumControls(); ++count) {
foo.push_back(new QComboBox());
// or foo << (new QComboBox());
}
Later, to retrieve the values:
foreach (QComboBox box, foo)
{
// do something with box here
}

Resources