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.
Related
I have two listwidgets in icon mode list1 is a list of 100 widget items (each Widget item is an icon packed with a combobox with 3-4 items inside) .
Those combobox items are data of the icon this will never change ,they are packed with the Widget item ,listwidget2 is empty and I just want to be able to drag the widget item(icon and combobox with items) from 1 and make a favorite list with some widgets of list1, nothing will change to combobox items or the widget items of list1 they will have always the same data,
the problem is that each time I drag the widget only the icon is copied to the other listwidget.
QDir dir ("icons");
QFileInfoList list = dir.entryInfoList(QDir::AllEntries |
QDir::Dirs|QDir::NoDotAndDotDot);
for(int i=0 ; i < list.length() ; i++){
dir_names.push_back(list.at(i).baseName());
/*Setting the icon*/
QIcon icon;
icon.addFile(list.at(i).absoluteFilePath(), QSize(), QIcon::Normal,
QIcon::Off);
QListWidgetItem *iconItem = new QListWidgetItem(ui->listWidget);
iconItem->setIcon(icon);
QComboBox *box = new QComboBox;
QListWidgetItem *textItem = ui->listWidget->item(i);
ui->listWidget->setItemWidget( textItem,box);
box->setFixedHeight(18);
box->addItem(list.at(i).baseName());
}
If you want the combobox to be moved you must overwrite the dropEvent method so you must create a class that inherits from QListWidget, get the widget and copy the necessary data. If you want to use it in Qt Designer you must promote it.
listwidget.h
#ifndef LISTWIDGET_H
#define LISTWIDGET_H
#include <QListWidget>
class ListWidget : public QListWidget
{
Q_OBJECT
public:
ListWidget(QWidget * parent = 0);
void dropEvent(QDropEvent * event);
protected:
void mouseMoveEvent(QMouseEvent * event);
};
#endif // LISTWIDGET_H
listwidget.cpp
#include "listwidget.h"
#include <QDropEvent>
#include <QComboBox>
ListWidget::ListWidget(QWidget *parent):QListWidget(parent)
{
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
setDefaultDropAction(Qt::MoveAction);
}
void ListWidget::dropEvent(QDropEvent *event)
{
if(event->dropAction() == Qt::MoveAction && event->source()){
ListWidget *listWidget = qobject_cast<ListWidget *>(event->source());
if(!listWidget)
return;
QList<QPersistentModelIndex> pIndexes;
for(QModelIndex index: listWidget->selectedIndexes()){
pIndexes << QPersistentModelIndex(index);
}
std::sort(pIndexes.begin(), pIndexes.end());
QListWidgetItem *item = itemAt(event->pos());
int rowStart = item? row(item) : count();
for(QPersistentModelIndex pindex: pIndexes){
int r = QModelIndex(pindex).row();
QComboBox *input = qobject_cast<QComboBox *>(listWidget->itemWidget(listWidget->item(r)));
QComboBox *output;
if(input){
// move data to QComboBox
output = new QComboBox;
for(int i=0; i<input->count(); i++){
output->addItem(input->itemText(i));
output->setCurrentText(input->currentText());
}
}
QListWidgetItem *it = listWidget->takeItem(r);
insertItem(rowStart, it);
if(input)
setItemWidget(it, output);
}
setState(QAbstractItemView::NoState);
}
}
void ListWidget::mouseMoveEvent(QMouseEvent *event)
{
setState(QAbstractItemView::DraggingState);
QListWidget::mouseMoveEvent(event);
}
In the following link there is an example.
I'm using a QListWidget to display custom widgets by setting them with setItemWidget. Something like this:
QListWidget* listWidget = new QListWidget;
listWidget->setAcceptDrops(true);
listWidget->setDragDropMode(QAbstractItemView::InternalMove);
listWidget->setDragEnabled(true);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
for ( int i = 0 ; i < 50 ; ++i )
{
ItemWidget* item = new ItemWidget;
QListWidgetItem* listItem = new QListWidgetItem;
listItem->setSizeHint(item->sizeHint());
listWidget->addItem(listItem);
listWidget->setItemWidget(listItem, item);
}
ItemWidget is derived from QWidget, and just displays some custom data in a layout, like this:
ItemWidget::ItemWidget()
{
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
QHBoxLayout* contentLayout = new QHBoxLayout;
contentLayout->setSizeConstraint(QLayout::SetFixedSize);
contentLayout->setSpacing(0);
contentLayout->addSpacing(5);
contentLayout->setContentsMargins(10, 20, 10, 20);
QLabel* iconLbl = new QLabel;
iconLbl->setPixmap(QPixmap(":/icon.png"));
iconLbl->setMaximumWidth(20);
contentLayout->addWidget(iconLbl, 0, Qt::AlignTop);
contentLayout->addSpacing(14);
QVBoxLayout* infoLayout = new QVBoxLayout;
infoLayout->setContentsMargins(0, 0, 0, 0);
infoLayout->setSpacing(0);
QLabel* firstLbl = new QLabel("First line of text");
infoLayout->addWidget(firstLbl);
infoLayout->addSpacing(4);
QLabel* secondLbl = new QLabel("Second line of text");
infoLayout->addWidget(secondLbl);
contentLayout->addLayout(infoLayout);
layout->addLayout(contentLayout);
setLayout(layout);
}
I'd like to implement drag & drop to be able to rearrange the items in the list. However, when using setItemWidget, when the mouse is dragging the item, only the background rectangle (the QListWidgetItem ?) is dragged around, with none of the custom content that's part of the ItemWidget showing. I'd like the item being dragged to include the ItemWidget content as well, so the user sees what's being dragged and dropped.
Does anyone have a working approach of implementing this?
I've already tried using a custom class derived from both QListWidgetItem and QWidget, and setting a custom layout directly in that class, thereby perhaps not needing an ItemWidget or using setItemWidget, but it didn't work out as I had hoped.
To customize the QPixmap associated with the QDrag of QListWidget we must override the startDrag() method.
The main task is to get a QPixmap of the elements selected for it is created a QPixmap the size of the visible image of the viewport() that is transparent and then we paint them with QPixmap of each item selected for it we use QPainter.
To obtain the QPixmap of each item, use the grab() method, indicating the rectangle obtained through visualRect().
#ifndef LISTWIDGET_H
#define LISTWIDGET_H
#include <QListWidget>
#include <QDrag>
#include <QMimeData>
#include <QPainter>
class ListWidget : public QListWidget
{
protected:
void startDrag(Qt::DropActions supportedActions){
QDrag *drag = new QDrag(this);
drag->setMimeData(model()->mimeData(selectedIndexes()));
QPixmap pixmap(viewport()->visibleRegion().boundingRect().size());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
for(QModelIndex index: selectedIndexes()){
painter.drawPixmap(visualRect(index), viewport()->grab(visualRect(index)));
}
drag->setPixmap(pixmap);
drag->setHotSpot(viewport()->mapFromGlobal(QCursor::pos()));
drag->exec(supportedActions, Qt::MoveAction);
}
};
#endif // LISTWIDGET_H
The complete example can be found in the following link
I created a Qtreewidget which has a Qtreewidgetitem is a widget (combobox, edit box and so on).
For example, this is one part of my code.cpp:
m_pPropertyTree = new QTreeWidget();
m_pPropertyTree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_pPropertyTree->setColumnCount(2);
m_pPropertyTree->setColumnWidth(0, 155);
m_pPropertyTree->setStyleSheet("QTreeView::item { height: 20px;}");
m_pPropertyTree->setHeaderLabels(QStringList() << "Property" << "Value");
...
QTreeWidgetItem *pButtonItem = new QTreeWidgetItem(m_pPropertyTree);
pButtonItem->setText(0, "Button");
//caption
QTreeWidgetItem *pCaptionItem = new QTreeWidgetItem();
pCaptionItem->setText(0, "caption");
pCaptionItem->setText(1, "Button");
pButtonItem->addChild(pCaptionItem);
//style
QTreeWidgetItem *pStyleItem = new QTreeWidgetItem();
pStyleItem->setText(0, "style");
QComboBox *pCombobox = new QComboBox();
pCombobox->setFixedHeight(20);
pCombobox->addItem("normal");
pCombobox->addItem("bold");
pButtonItem->addChild(pStyleItem);
m_pPropertyTree->setItemWidget(pStyleItem, 1, pCombobox);
And this is what i get:
The combobox has original size which is the red rectangle. I want to adjust it's size or position to the new one, which is the black rectangle size. How can i do that? Thanks.
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
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);