Qt adding a derived QListwidgetitem to QListWidget - c++

I have subclassed QTreeWidget, QTreeWidgetItem and QListWidgetItem. I am trying to add my custom QListWidgetItem to QListWidget but its not getting displayed in the view.
I Have created a dialog. I have inserted a QTreeWidget and QListWidget.
What I want is when I click a Item in QTreeWidget, The QListWidget should get populated with the my custom QListWidgetItems i.e. CustomListWidgetItems contained in the clicked CustomTreeWidgetItem (QTreeWidgetItem derived class).
If I use QListWidgetItem instead of CustomListWidgetItems, every thing works fine.
Header file contents:
class CustomTreeWidgetItem : public QTreeWidgetItem
{
public:
explicit CustomTreeWidgetItem(int type = Type);
public:
QList<CustomListWidgetItem*> projects;//Items to populate ListWidget
};
class CustomListWidgetItem : public QListWidgetItem
{
public:
explicit CustomListWidgetItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type);
CustomListWidgetItem(const int& id,QString Name,QString Description,QString icon);
public:
int Id;
QString Name;
QString Description;
QString Icon;
};
Source contents of Custom Listwidgetitem:
CustomListWidgetItem::CustomListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent, int type) : QListWidgetItem(icon,text,parent,type)
{
}
CustomListWidgetItem::CustomListWidgetItem(const int& id,QString Name,QString Description,QString icon)
{
this->Id = id;
this->Name = Name;
this->Description = Description;
this->Icon = icon;
QString iconPath = QCoreApplication::applicationDirPath() + "/" + icon + ".png";
QIcon qIcon(iconPath);
QListWidgetItem::QListWidgetItem(qIcon,Name);
}
Source contents:
void CustomTreeWidget::treeitemSelected(QTreeWidgetItem * item, int)//Tree item click event
{
CustomTreeWidgetItem *project = static_cast<CustomTreeWidgetItem*>(item);
if(LISTWIDGET)
{
m_listWidget->clear();
m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
int count = project->projects.count();
if(count == 0)
{
m_listWidget->addItem("No Projects Here");//This gets displayed
m_listWidget->setSelectionMode(QAbstractItemView::NoSelection);
}
else
{
for (int i = 0; i < count; ++i)
{
m_listWidget->addItem(project->projects[i]);//This does not get displayed
}
}
}
}
During Debugging I can see that the data i.e Name, Description, etc. is present in "project->projects[i]". No error is shown during addItem function call still the items are not visible in view.

Few questions:
- Why do I have the feeling you mix listwidgetitem and treewidgetitem?
- Why do you fill your view everytime an item is selected? is it initialized?
- Can you show where you create the view and start adding items?
Check you have the following:
create a qtreewidget that you add to your view layout
add a root node using something like
QTreeWidgetItem *treeItemRoot = new QTreeWidgetItem(myTreeWidget);
treeItemRoot->setText(0, projectName);
add children nodes
QTreeWidgetItem *treeItemChild = new QTreeWidgetItem();
treeItemRoot->addChild(treeItem);
and post the results.

Related

How to drag and drop a specific QPixmap into a QGraphicsView?

On a subclassed QListWidget I have several items. Every QListWidget item (e.g. "ROS Init", "Images" etc) that is shown below is associated with a specific icon.
The problem I have is that I am trying to drag and drop the specific icon corresponding to that QListWidget item, but nothing happens.
Below the function responsible for the dragging:
void ListView::startDrag(Qt::DropActions supportedActions)
{
QMap<int, QString> icons;
icons.insert(IT_RosInit, "ROS Init");
icons.insert(IT_Images, "Images");
icons.insert(IT_Path, "Path");
icons.insert(IT_RosShutDown, "ROS Shutdown");
if (supportedActions & Qt::CopyAction)
{
const QList<QListWidgetItem *> &m_items(selectedItems());
if (m_items.isEmpty())
return;
QPixmap pixmapLaser("/home/images/laserscan.png");
QPixmap pixmapPCloud2("/home/images/pcloud2.png");
// etc ...
QStringList iconImages;
for(int i = 0; i < icons.count(); ++i)
{
for (const QString &tableType : iconImages) {
if (tableType == "ROS Init")
{
auto *data = mimeData(m_items);
auto *drag = new QDrag(this);
drag->setPixmap(pixmapLaser);
drag->setMimeData(data);
drag->setHotSpot(pixmapLaser.rect().center());
drag->exec(Qt::CopyAction);
}
else if(tableType == "Images")
{
auto *data2 = mimeData(m_items);
auto *drag2 = new QDrag(this);
drag2->setPixmap(pixmapPCloud2);
drag2->setMimeData(data2);
drag2->setHotSpot(pixmapPCloud2.rect().center());
drag2->exec(Qt::CopyAction);
}
}
}
}
else
{
QListWidget::startDrag(supportedActions);
}
}
After subclassing the QListWidget I just reimplemented the usual drag and drop function. All other function are working properly but the startDrag and in fact as I try to drag the proper QPixmap, I actually see nothing being dragged.
I consulted this source, useful, and also this other source which was useful but it didn't reimplement the startDrag but instead dropEvent which for me is not a problem because that is working well.
I also consulted this source and this other source but that also didn't help fixing the problem.
Thanks for shedding light on this matter for solving the problem
Solution
I would approach this problem in the following way:
Set the ItemIsDragEnabled flag of QListWidgetItem to enable the item for dragging:
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
Set the desired data for each item:
item->setData(Qt::UserRole, type);
where type is one of the enumerated values IT_RosInit, IT_Images, etc.
Enable the drag functionality of the ListWidget:
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
Use the item settings to setup the QDrag object, created in startDrag.
Example
Here is an example I have prepared for you to demonstrate how the proposed solution could be implemented:
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent)
{
auto *l = new QVBoxLayout(this);
auto *list = new ListView(this);
list->addItem(createItem(":/pix/images/laserscan.png", tr("RosInit"), IT_RosInit));
list->addItem(createItem(":/pix/images/icons/pcloud2.png", tr("Images"), IT_Images));
list->addItem(createItem(":/pix/images/icons/some_icon.png", tr("Path"), IT_Path));
list->addItem(createItem(":/pix/images/icons/another_icon.png", tr("RosShutDown"), IT_RosShutDown));
l->addWidget(list);
resize(300, 400);
setWindowTitle("IconDrag");
}
QListWidgetItem *MainWindow::createItem(const QString &pm, const QString &text, int type)
{
auto *item = new QListWidgetItem(QIcon(QPixmap(pm)), text);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
item->setData(Qt::UserRole, type);
return item;
}
ListView.cpp
ListView::ListView(QWidget *parent) :
QListWidget(parent)
{
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::SingleSelection);
}
void ListView::startDrag(Qt::DropActions supportedActions)
{
if (supportedActions & Qt::CopyAction) {
const QList<QListWidgetItem *> &items(selectedItems());
if (items.isEmpty())
return;
const QPixmap &pm(items.first()->icon().pixmap(64));
auto *item = items.first();
auto *mimeData = new QMimeData();
auto *drag = new QDrag(this);
mimeData->setData("text/plain", item->data(Qt::UserRole).toByteArray());
drag->setPixmap(pm);
drag->setMimeData(mimeData);
drag->setHotSpot(pm.rect().center());
drag->exec(Qt::CopyAction);
}
}

Use setWindowModified() on QTabWidget

Because QTabWidget inherits QWidget we have setWindowModified() available.
But it seems it doesn't work for the tab title:
ui->tab1->setWindowTitle(QString("%1[*]").arg(tr("Tab title")));
ui->tab1->setWindowModified(true);
but it doesn't show the '*' nor changes the tab text.
Is there a way to handle this automatically instead of manually use setTabText()?
I don't think there's any way to get the tab text to follow the widget title by default. Having said that, it should be very easy to fix up by overriding QTabWidget::tabInserted.
class tab_widget: public QTabWidget {
using super = QTabWidget;
using this_class = tab_widget;
public:
using super::super;
protected:
virtual void tabInserted (int index) override
{
super::tabInserted(index);
if (auto *w = widget(index)) {
connect(w, &QWidget::windowTitleChanged, this, &this_class::handle_window_title_change);
}
}
virtual void tabRemoved (int index) override
{
super::tabRemoved(index);
if (auto *w = widget(index)) {
disconnect(w, &QWidget::windowTitleChanged, this, &this_class::handle_window_title_change);
}
}
private:
void handle_window_title_change (const QString &title)
{
if (auto *w = qobject_cast<QWidget *>(sender())) {
setTabText(indexOf(w), title);
}
}
};
Using the above class rather than QTabWidget should result in the tab text mirroring the title of the widget associated with that tab.

Qt - How to show image/icon/data while dragging an item?

I have an application where I can drag an item to a QGraphicsScene and create a new object depending on the item text, but how can I change the data being displayed while moving around the item?
for example, instead of a text, I want to show an icon:
I have a list with some itens:
OptionList::OptionList(QWidget *parent) : QListWidget(parent)
{
this->setDragEnabled(true);
this->setDropIndicatorShown(true);
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setDefaultDropAction(Qt::CopyAction);
this->setViewMode(QListView::ListMode);
for(const QString &color : {"Blue", "Red", "Green", "Yellow"})
{
OptionItem *item = new OptionItem;
item->setText(color);
item->setFlags(Qt::ItemIsEnabled| Qt::ItemIsSelectable| Qt::ItemIsDragEnabled);
addItem(item);
}
}
I drop the itens into the scene to create a new object:
MyScene::MyScene()
{
setBackgroundBrush(Qt::lightGray);
}
void MyScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QStringList colors;
while (!stream.atEnd())
{
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
colors << roleDataMap[Qt::DisplayRole].toString();
}
QPointF posView = event->scenePos() ;
for(const QString & color: colors)
{
Block *newBlock = new Block(color);
newBlock->setPos(posView);
addItem(newBlock);
}
}
Then, I created OptionItem class, derived from QListWidgetItem, and reimplemented the mousePressEvent, mouseMoveEvent and mouseReleaseEvent
OptionItem::OptionItem()
{
}
void OptionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(true);
}
void OptionItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QDrag *drag = new QDrag(event->widget());
QMimeData *mime = new QMimeData;
QImage image(":/images/MyIcon_icon.png");
mime->setImageData(image);
drag->setMimeData(mime);
drag->setPixmap(QPixmap::fromImage(image));
drag->setHotSpot(QPoint(15, 30));
drag->exec();
event->setAccepted(true);
}
void OptionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(true);
}
I tried to follow the drag drop robot example in the Qt Creator but it isn't the same thing
It seems that the image appears very quickly when I start dragging an item
Is there a way to show the icon while dragging the item through the whole operation?
The classes that inherit from QAbstractItemView support the default drag so they already have implemented methods, instead the example you point out shows how to implement this functionality for some class that does not have it, the task in your case is simple, you must overwrite the method startDrag of QListWidget.
optionilist.h
#ifndef OPTIONLIST_H
#define OPTIONLIST_H
#include <QListWidget>
class OptionList: public QListWidget{
public:
OptionList(QWidget* parent=nullptr);
protected:
void startDrag(Qt::DropActions supportedActions);
};
#endif // OPTIONLIST_H
optionlist.cpp
#include "optionlist.h"
#include <QDrag>
OptionList::OptionList(QWidget *parent): QListWidget(parent){
setDragEnabled(true);
setDropIndicatorShown(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setDefaultDropAction(Qt::CopyAction);
setViewMode(QListView::ListMode);
for(const QString &color : {"Blue", "Red", "Green", "Yellow"}){
QListWidgetItem *blue = new QListWidgetItem;
blue->setText(color);
blue->setFlags(Qt::ItemIsEnabled| Qt::ItemIsSelectable| Qt::ItemIsDragEnabled);
addItem(blue);
}
}
void OptionList::startDrag(Qt::DropActions supportedActions){
if(supportedActions & Qt::CopyAction){
QList<QListWidgetItem *> m_items = selectedItems();
if(m_items.isEmpty())
return;
QMimeData *data = mimeData(m_items);
QDrag *drag = new QDrag(this);
QPixmap pixmap(":/images/MyIcon_icon.png");
drag->setPixmap(pixmap);
drag->setMimeData(data);
drag->setHotSpot(pixmap.rect().center());
drag->exec(Qt::CopyAction);
}
else
QListWidget::startDrag(supportedActions);
}
The complete code can be found at the following link.
The above is correct!
Also should note that drag->setMimeData(data); needs to be called after drag->setPixmap(pixmap);.
Otherwise, during the drag moving, it will show the original mimedata type instead of showing an image/icon.

Drag and drop listwidget items with a combobox inside

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.

QT C++ SelectedIndex of QListWidget

How can I get the selected index of QListWidget. I can get the selected color but now sure how can I get the selected Index of the item.
I have written the selected color function. Please help me in getting the selected index of the color.
ColorList::ColorList(QWidget *parent)
: QListWidget(parent)
{
init();
}
QString ColorList::selectedColor() const
{
return currentItem() ? currentItem()->data(Qt::UserRole).toString() : QString();
}
void ColorList::init()
{
setFrameShape(QFrame::NoFrame);
QMap<QString, QString> names;
names["Air"] = "#FFFFFF";
names["Resist"] ="#B22222";
names["BARC"] = "#F2CBC5";
names["Oxide"] = "#34AAD1";
names["Low"] = "#FD7E00";
// add color names and their icons
foreach(const QString &key, names.keys())
{
QPixmap px(16,16);
px.fill(QColor(names[key]));
QListWidgetItem *item = new QListWidgetItem(QIcon(px), key);
item->setData(Qt::UserRole, names[key]);
addItem(item);
}
}
It's currentRow(). Please see here for the documentation http://doc.qt.io/qt-4.8/qlistwidget.html#currentRow-prop