I have got a QListWidgetItem, which has a QWidget and some QLabels. The height of the labels (imageLabel, titleLabel and descriptionLabel) varies depending on the text length. So does the height of the QWidget, which leds to different sizes in QListWidgetItem. So far the parameters for setSizeHint are static:
QListWidgetItem* listWidgetItem = new QListWidgetItem();
listWidgetItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
listWidgetItem->setSizeHint(200, 180));
QWidget* widget = new QWidget();
QVBoxLayout* rootLayout = new QVBoxLayout();
rootLayout->setAlignment(Qt::AlignTop);
QHBoxLayout* contentLayout = new QHBoxLayout();
contentLayout->setAlignment(Qt::AlignLeft);
QLabel* imageLabel = new QLabel();
imageLabel->setPixmap(pixmap);
contentLayout->addWidget(imageLabel, 0, Qt::AlignTop);
QVBoxLayout* informationLayout = new QVBoxLayout();
informationLayout->setAlignment(Qt::AlignTop);
QLabel* titleLabel = new QLabel("<b>" + title + "</b>");
titleLabel->setWordWrap(true);
informationLayout->addWidget(titleLabel);
QLabel* descriptionLabel = new QLabel(description);
descriptionLabel->setWordWrap(true);
informationLayout->addWidget(descriptionLabel);
QLabel* dateLabel = new QLabel(date.toString());
informationLayout->addWidget(dateLabel);
contentLayout->addLayout(informationLayout);
rootLayout->addLayout(contentLayout);
QHBoxLayout* buttonLayout = new QHBoxLayout();
QPushButton* buttonOne = new QPushButton(tr("Button 1"));
QObject::connect(buttonOne, SIGNAL(clicked()), mButtonOneSignalMapper, SLOT(map()));
mButtonOneSignalMapper->setMapping(buttonOne, index);
buttonLayout->addWidget(buttonOne);
QPushButton* buttonTwo = new QPushButton(tr("Button 2"));
QObject::connect(buttonTwo, SIGNAL(clicked()), mButtonTwoSignalMapper, SLOT(map()));
mButtonTwoSignalMapper->setMapping(buttonTwo, index);
buttonLayout->addWidget(buttonTwo);
rootLayout->addLayout(buttonLayout);
widget->setLayout(rootLayout);
mListWidget->addItem(listWidgetItem);
mListWidget->setItemWidget(listWidgetItem, widget);
Is there any way to properly set the sizeHint regarding the width and height of the displayed content used in the QLabels of the QWidget?
For instance the first QListWidgetItem may have a descriptionLabel with a text length of 300 characters and the second QListWidgetItem may have a descriptionLabel with a text length of 1000 characters. So far both QListWidgetItems will have the same size (200px width and 180px height). While it may fit on the first QListWidgetItem, because it has only 300 characters, it may not fit on the second QListWidgetItem, because of the 1000 characters. Therefore I would like to somehow dynamically adjust the size of the QListWidgetItem regarding the needed space (first one will need less than the second one).
The way I see it, you won't be able to get a correct bounding rect for the label, unless you know it's future width, so that you can calculate the number of lines required to display the content. And you won't get the width before the layout with the other widgets is calculated.
An alternate approach might be to use an item delegate. Its sizeHint method has an option parameter with a preliminary rect, from which you can use the width and calculate the height with font metrics.
Concerning your other widgets, you could switch to a QTableWidget and put them in other columns..
The following code is not a working example .. just some clues to get you started..
class ItemDelegate : public QStyledItemDelegate
{
public:
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
painter->save();
QStyledItemDelegate::paint(painter,option,index);
QString title = index.data(Qt::UserRole).toString();
QFont f = option.font;
painter->setFont(f);
QFontMetrics fm(f);
QRect r = option.rect;
// r = r.adjusted(0, fm.lineSpacing(), 0, 0);
painter->drawText(r.left(), r.top(), r.width(), r.height(), Qt::AlignTop|Qt::AlignLeft|Qt::TextWordWrap, title, &r);
painter->restore();
}
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QFont f = option.font;
QRect r = option.rect;
QFontMetrics fm(f);
QString title = index.data(Qt::UserRole).toString();
QRect br = fm.boundingRect(r,Qt::AlignTop|Qt::AlignLeft | Qt::TextWordWrap,title);
return QSize(option.rect.width(),br.height());
}
};
Hope it helps,
Johannes
Related
I have created a custom widget that includes a vertical layout with a qlabel(that holds an icon) and a qcombobox
and I use them as listwidget items.
They are created based on a file that have 100s of icons.
I am trying to create a slider that scales the pixmap sizes when the app is running in real time.
I can't figure out how to code this properly so I can access this property.
This is the code in the mainwindow.cpp
/*load icons*/
QDir dir (....);
QFileInfoList list = dir.entryInfoList(QDir::AllEntries |
QDir::Dirs|QDir::NoDotAndDotDot);
for(int i=0 ; i < list.length() ; i++)
{
QIcon icon;
icon.addFile(list.at(i).absoluteFilePath(), QSize(), QIcon::Normal,
QIcon::Off);
mypix = icon.pixmap(QSize(128,128));
/*Custom Widget*/
widget.push_back(new QWidget(ui->listWidget));
widget[i]->setMinimumSize(QSize(0, 150));
/*the VB with of label-combo*/
layout.push_back(new QVBoxLayout(widget[i]));
/*Qlabel that holds the icon*/
pic.push_back(new QLabel (widget[i]));
pic[i]->setPixmap(mypix);
layout[i]->addWidget(pic[i]);
box.push_back(new QComboBox(widget[i]));
box[i]->addItem(list.at(i).baseName());
layout[i]->addWidget(box[i]);
QListWidgetItem * qlistwidgetitem = new QListWidgetItem;
ui->listWidget->addItem(qlistwidgetitem);
ui->listWidget->setItemWidget(ui->listWidget->item(i),widget[i]);
}
}
QListWidgetItem has a default size that does not take into account the size of the widget, the solution is to pass the sizeHint() of the widget to QListWidgetItem, also you must not set a height of 0 to the widget, only the minimum width.
QDir dir (...);
const QFileInfoList &infolist = dir.entryInfoList(QDir::AllEntries| QDir::Dirs| QDir::NoDotAndDotDot);
for(const QFileInfo &info: infolist){
QIcon icon;
icon.addFile(info.absoluteFilePath(), QSize(), QIcon::Normal, QIcon::Off);
QPixmap pix = icon.pixmap(QSize(128,128));
QWidget *w = new QWidget(ui->listWidget);
w->setMinimumWidth(150);
QVBoxLayout *lay = new QVBoxLayout(w);
QLabel *lbl = new QLabel(w);
lbl->setPixmap(pix);
QComboBox *combo = new QComboBox(w);
combo->addItem(info.baseName());
lay->addWidget(lbl);
lay->addWidget(combo);
widget << w;
layout << lay;
box << combo;
pic << lbl;
QListWidgetItem *qlistwidgetitem = new QListWidgetItem;
qlistwidgetitem->setSizeHint(w->sizeHint());
ui->listWidget->addItem(qlistwidgetitem);
ui->listWidget->setItemWidget(qlistwidgetitem, w);
}
Update:
If you want to change the size of the icon with a QSlider it is advisable to save the icon so we can use the setData() method, then we associate a slot with the slider and in that slot we insert a new size to the icon and set it to the QLabel
for(const QFileInfo &info: infolist){
[...]
qlistwidgetitem->setData(Qt::UserRole, QVariant::fromValue(icon));
[...]
}
void Widget::on_horizontalSlider_valueChanged(int value)
{
for(int i=0; i< ui->listWidget->count(); i++){
QListWidgetItem *it = ui->listWidget->item(i);
QIcon icon = it->data(Qt::UserRole).value<QIcon>();
pic[i]->setPixmap(icon.pixmap(value, value));
QWidget *w = ui->listWidget->itemWidget(it);
it->setSizeHint(w->sizeHint());
}
}
The complete example can be found in the following link.
In QtCreater I added a table to my project. in my code I am generating some data to output into the table. I want to add a QCheckbox into each row to allow the row to be selected. All the content of the table is aligned left, how do I make only these checkboxes in the first column of every row align to the center?
I'm adding the QCheckbox using:
ui->data_table->setCellWidget(rowCount,0, new QCheckBox);
Two thumbs up for Barry Mavin! You don't even have to subclass.
one line...
pCheckBox->setStyleSheet("margin-left:50%; margin-right:50%;");
done!!
I usually use a layout and a container widget for this. It is an ugly solution, but it works:
QWidget * w = new QWidget();
QHBoxLayout *l = new QHBoxLayout();
l->setAlignment( Qt::AlignCenter );
l->addWidget( <add your checkbox here> );
w->setLayout( l );
ui->data_table->setCellWidget(rowCount,0, w);
So basically you will have:
Table Cell -> Widget -> Layout -> Checkbox
you'll have to consider it if you will need to access the checkbox through the table.
This is an old post but in fact there is a much easier and lighter way of achieving this, just subclass QCheckBox and set the stylesheet to
margin-left:50%;
margin-right:50%;
It works for me, but my checkbox is not completely displayed.
To have a complete view of the widget, remove margins in layout :
l->setContentsMargins(0,0,0,0);
As stated in similar question around Stack Overflow, it's currently an open BUG:
https://bugreports.qt-project.org/browse/QTBUG-5368
can be center like this too using layout if want to add more customization
// Create a widget that will contain a checkbox
QWidget *checkBoxWidget = new QWidget();
QCheckBox *checkBox = new QCheckBox(); // We declare and initialize the checkbox
QHBoxLayout *layoutCheckBox = new QHBoxLayout(checkBoxWidget); // create a layer with reference to the widget
layoutCheckBox->addWidget(checkBox); // Set the checkbox in the layer
layoutCheckBox->setAlignment(Qt::AlignCenter); // Center the checkbox
layoutCheckBox->setContentsMargins(0,0,0,0); // Set the zero padding
ui->my_table_view->setCellWidget(row_number,column_number, checkBoxWidget); // set cell widget
OR simply add left right margins
checkBox->setStyleSheet("margin-left:50%; margin-right:50%;");
#if QT_VERSION < 0x046000
#include <QCommonStyle>
class MyStyle : public QCommonStyle {
public:
QRect subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget = 0) const {
switch(subElement) {
case QStyle::SE_CheckBoxIndicator: {
QRect r = QCommonStyle::subElementRect(subElement, option, widget);
r.setRect( (widget->width() - r.width())/2, r.top(), r.width(), r.height());
return QRect(r);
}
default: return QCommonStyle::subElementRect(subElement, option, widget);
}
}
};
#else
#include <QProxyStyle>
#include <QStyleFactory>
class MyStyle: public QProxyStyle {
public:
MyStyle():QProxyStyle(QStyleFactory::create("Fusion")) {}
QRect subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget = 0) const {
switch(subElement) {
case QStyle::SE_CheckBoxIndicator: {
QRect r = QProxyStyle::subElementRect(subElement, option, widget);
r.setRect( (widget->width() - r.width())/2, r.top(), r.width(), r.height());
return QRect(r);
}
default: return QProxyStyle::subElementRect(subElement, option, widget);
}
}
};
#endif
QCheckBox *box = new QCheckBox();
box->setStyle(new MyStyle());
I'm trying to draw with QPainter in a QPixmap, put that QPixmap in a QLabel, and that QLabel in a QScrollArea.
Sometimes the painted pixmap is bigger then my ScrollArea allows, but somehow it doesn't scroll to show the rest. What am I doing wrong?
QPixmap *pixmap = new QPixmap(10000,500);
QLabel *labeltime = new QLabel;
QHBoxLayout *layout = new QHBoxLayout;
pixmap->fill(QColor("transparent"));
int currentX = 0;
const int currentY = 220;
const int height = 50; // Coming from some static data initialization
QPainter *painter = new QPainter(pixmap);
QPen pen(Qt::gray, 2);
painter->setPen(pen);
for(int i = 0; i< viewlist.size(); i++)
{
QBrush brush(QColor(viewlist[i].color));
painter->setBrush(brush);
painter->drawRect(currentX, currentY, viewlist[i].length, height);
currentX += viewlist[i].length;
}
labeltime->setPixmap(*pixmap);
layout->addWidget(labeltime);
ui->overview->setLayout(layout);
I know this is a long and weird way to add a pixmap, but I want it to be scrollable, and I can't paint on a QScrollArea. Is there a better way? Or can someone tell me what is wrong?
Thanks
Since you only have one child widget, it is simpler to eliminate your layout. Change these lines:
layout->addWidget(labeltime);
ui->overview->setLayout(layout);
to:
ui->overview->setWidget(labeltime);
In QtCreater I added a table to my project. in my code I am generating some data to output into the table. I want to add a QCheckbox into each row to allow the row to be selected. All the content of the table is aligned left, how do I make only these checkboxes in the first column of every row align to the center?
I'm adding the QCheckbox using:
ui->data_table->setCellWidget(rowCount,0, new QCheckBox);
Two thumbs up for Barry Mavin! You don't even have to subclass.
one line...
pCheckBox->setStyleSheet("margin-left:50%; margin-right:50%;");
done!!
I usually use a layout and a container widget for this. It is an ugly solution, but it works:
QWidget * w = new QWidget();
QHBoxLayout *l = new QHBoxLayout();
l->setAlignment( Qt::AlignCenter );
l->addWidget( <add your checkbox here> );
w->setLayout( l );
ui->data_table->setCellWidget(rowCount,0, w);
So basically you will have:
Table Cell -> Widget -> Layout -> Checkbox
you'll have to consider it if you will need to access the checkbox through the table.
This is an old post but in fact there is a much easier and lighter way of achieving this, just subclass QCheckBox and set the stylesheet to
margin-left:50%;
margin-right:50%;
It works for me, but my checkbox is not completely displayed.
To have a complete view of the widget, remove margins in layout :
l->setContentsMargins(0,0,0,0);
As stated in similar question around Stack Overflow, it's currently an open BUG:
https://bugreports.qt-project.org/browse/QTBUG-5368
can be center like this too using layout if want to add more customization
// Create a widget that will contain a checkbox
QWidget *checkBoxWidget = new QWidget();
QCheckBox *checkBox = new QCheckBox(); // We declare and initialize the checkbox
QHBoxLayout *layoutCheckBox = new QHBoxLayout(checkBoxWidget); // create a layer with reference to the widget
layoutCheckBox->addWidget(checkBox); // Set the checkbox in the layer
layoutCheckBox->setAlignment(Qt::AlignCenter); // Center the checkbox
layoutCheckBox->setContentsMargins(0,0,0,0); // Set the zero padding
ui->my_table_view->setCellWidget(row_number,column_number, checkBoxWidget); // set cell widget
OR simply add left right margins
checkBox->setStyleSheet("margin-left:50%; margin-right:50%;");
#if QT_VERSION < 0x046000
#include <QCommonStyle>
class MyStyle : public QCommonStyle {
public:
QRect subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget = 0) const {
switch(subElement) {
case QStyle::SE_CheckBoxIndicator: {
QRect r = QCommonStyle::subElementRect(subElement, option, widget);
r.setRect( (widget->width() - r.width())/2, r.top(), r.width(), r.height());
return QRect(r);
}
default: return QCommonStyle::subElementRect(subElement, option, widget);
}
}
};
#else
#include <QProxyStyle>
#include <QStyleFactory>
class MyStyle: public QProxyStyle {
public:
MyStyle():QProxyStyle(QStyleFactory::create("Fusion")) {}
QRect subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget = 0) const {
switch(subElement) {
case QStyle::SE_CheckBoxIndicator: {
QRect r = QProxyStyle::subElementRect(subElement, option, widget);
r.setRect( (widget->width() - r.width())/2, r.top(), r.width(), r.height());
return QRect(r);
}
default: return QProxyStyle::subElementRect(subElement, option, widget);
}
}
};
#endif
QCheckBox *box = new QCheckBox();
box->setStyle(new MyStyle());
Say I have a QHBoxLayout where there are 2 QTextEdits and between them a button with an arrow to the right. When you click on the button, the right-side QTextEdit gradually closes by moving the left border until it meets the right one. Simultaneously, the right border of the left QTextEdit takes the place which the right QTextEdit released. And after pressing on the button, the state of the system is coming to the former one.
EDIT: In order to organize this I have done the following:
1) In header file:
class MyWidget : public QWidget
{
Q_OBJECT
QTextEdit *m_textEditor1;
QTextEdit *m_textEditor2;
QPushButton *m_pushButton;
QHBoxLayout *m_layout;
int m_deltaX;
public:
MyWidget(QWidget * parent = 0);
~MyWidget(){}
private slots:
void closeOrOpenTextEdit2(bool isClosing);
};
2) In the source file:
MyWidget::MyWidget(QWidget * parent):QWidget(parent),m_deltaX(0)
{
m_pushButton = new QPushButton(this);
m_pushButton->setText(">");
m_pushButton->setCheckable(true);
connect(m_pushButton, SIGNAL(clicked(bool)), this, SLOT(closeOrOpenTextEdit2(bool)));
m_textEditor1 = new QTextEdit(this);
m_textEditor1->setText("AAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAA AAAAAAAAAAA AAAAAAAAAAA AA");
m_textEditor2 = new QTextEdit(this);
m_layout = new QHBoxLayout;
m_layout->addWidget(m_textEditor1);
m_layout->addWidget(m_pushButton);
m_layout->addWidget(m_textEditor2);
setLayout(m_layout);
}
void MyWidget::closeOrOpenTextEdit2(bool isClosing)
{
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "geometry");
QPropertyAnimation *animation2 = new QPropertyAnimation(m_pushButton, "geometry");
QPropertyAnimation *animation3 = new QPropertyAnimation(m_textEditor1, "geometry");
if(isClosing) //close the second textEdit
{
m_pushButton->setText("<");
QRect te2_1 = m_textEditor2->geometry();
m_deltaX = te2_1.width()-3;
QRect te2_2(te2_1.x()+m_deltaX, te2_1.y(), 3 ,te2_1.height());
QRect pb_1 = m_pushButton->geometry();
QRect pb_2(pb_1.x()+m_deltaX, pb_1.y(), pb_1.width() ,pb_1.height());
QRect te1_1 = m_textEditor1->geometry();
QRect te1_2(te1_1.x(), te1_1.y(), te1_1.width()+m_deltaX, te1_1.height());
//animation->setDuration(10000);
animation1->setStartValue(te2_1);
animation1->setEndValue(te2_2);
animation2->setStartValue(pb_1);
animation2->setEndValue(pb_2);
animation3->setStartValue(te1_1);
animation3->setEndValue(te1_2);
}
else //open
{
m_pushButton->setText(">");
QRect te2_1 = m_textEditor2->geometry();
QRect te2_2(te2_1.x()-m_deltaX, te2_1.y(), 3+m_deltaX ,te2_1.height());
QRect pb_1 = m_pushButton->geometry();
QRect pb_2(pb_1.x()-m_deltaX, pb_1.y(), pb_1.width() ,pb_1.height());
QRect te1_1 = m_textEditor1->geometry();
QRect te1_2(te1_1.x(), te1_1.y(), te1_1.width()-m_deltaX, te1_1.height());
//animation->setDuration(10000);
animation1->setStartValue(te2_1);
animation1->setEndValue(te2_2);
animation2->setStartValue(pb_1);
animation2->setEndValue(pb_2);
animation3->setStartValue(te1_1);
animation3->setEndValue(te1_2);
}
animation1->start();
animation2->start();
animation3->start();
}
EDIT:
And I have the following problem:
When I close the second QTextEdit (by clicking on the button) and resize the MyWidget, then the QTextEdit restores its state (but it should stay closed of course). How can I solve this problem?
Please provide me with a code snippet.
Qt's Animation framework sounds like a good place to start. You could just try to follow their tutorials, adapting for you use case. I have used it already, and it seemed quite straight forward.
1) You could replace your button with a vertical layout, place the button inside this layout and finally add a vertical spacer below the button (in same the layout).
...
QVBoxLayout* m_buttonLayout = new QVBoxLayout();
m_layout = new QHBoxLayout();
m_layout->addWidget(m_textEditor1);
m_layout->addLayout(m_buttonLayout);
m_layout->addWidget(m_textEditor2);
m_buttonLayout->addWidget(m_pushButton);
m_buttonLayout->addItem( new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding) );
2) I guess you could (and should) animate widget's maximumSize (or just maximumWidth) property and let the layout take care of calculating actual geometries. This would also simplify your calculations. E.g.
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "maximumWidth");
QPropertyAnimation *animation2 = new QPropertyAnimation(m_textEditor, "maximumWidth");
if (isClosing)
{
int textEdit2_start = m_textEditor2->maximumWidth();
int textEdit2_end = 0;
int textEdit_start = m_textEditor->maximumWidth();
int textEdit_end = textEdit_start + textEdit2_start;
animation1->setStartValue(textEdit2_start);
...
}
Also, now you don't have to animate buttons geometry at all (assuming that you have set fixed size to it).
PS. I didn't compile codes so there might be minor errors but you should get the idea.
Here what I wanted:
Header file
class MyWidget : public QWidget
{
Q_OBJECT
QTextEdit *m_textEditor1;
QTextEdit *m_textEditor2;
QPushButton *m_pushButton;
QHBoxLayout *m_layout;
QVBoxLayout *m_buttonLayout;
int m_deltaX;
bool m_isClosed;
public:
MyWidget(QWidget * parent = 0);
~MyWidget(){}
void resizeEvent( QResizeEvent * event );
private slots:
void closeOrOpenTextEdit2(bool isClosing);
};
Source file
MyWidget::MyWidget(QWidget * parent):QWidget(parent),m_deltaX(0)
{
m_pushButton = new QPushButton(this);
m_pushButton->setText(">");
m_pushButton->setCheckable(true);
m_pushButton->setFixedSize(16,16);
connect(m_pushButton, SIGNAL(clicked(bool)), this, SLOT(closeOrOpenTextEdit2(bool)));
m_textEditor1 = new QTextEdit(this);
m_textEditor1->setText("AAAAA AAAAAAAAAAA AAAAAAAAAAA AAAAAAA AAAAAAAAAAA AAAAAAAAAAA AA");
m_textEditor2 = new QTextEdit(this);
m_buttonLayout = new QVBoxLayout();
m_buttonLayout->addWidget(m_pushButton);
m_buttonLayout->addItem( new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding) );
m_layout = new QHBoxLayout;
m_layout->addWidget(m_textEditor1, 10);
m_layout->addSpacing(15);
m_layout->addLayout(m_buttonLayout);
m_layout->setSpacing(0);
m_layout->addWidget(m_textEditor2, 4);
setLayout(m_layout);
resize(800,500);
}
void MyWidget::closeOrOpenTextEdit2(bool isClosing)
{
m_isClosed = isClosing;
QPropertyAnimation *animation1 = new QPropertyAnimation(m_textEditor2, "maximumWidth");
if(isClosing) //close the second textEdit
{
m_textEditor2->setMaximumWidth(m_textEditor2->width());
int textEdit2_start = m_textEditor2->maximumWidth();
m_deltaX = textEdit2_start;
int textEdit2_end = 3;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText("<");
}
else //open
{
int textEdit2_start = m_textEditor2->maximumWidth();
int textEdit2_end = m_deltaX;
animation1->setDuration(500);
animation1->setStartValue(textEdit2_start);
animation1->setEndValue(textEdit2_end);
m_pushButton->setText(">");
}
animation1->start();
}
void MyWidget::resizeEvent( QResizeEvent * event )
{
if(!m_isClosed)
m_textEditor2->setMaximumWidth( QWIDGETSIZE_MAX );
}