How to find item selected from customContextMenuRequested() on QTreeView item? - qt

I have implemented contextual menus in QTreeView items with the following code
MyDerivedQTreeView->setModel(MyDerivedQAbstractItemModel);
MyDerivedQTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(MyDerivedQTreeView,
SIGNAL(customContextMenuRequested(const QPoint &)),
MyDerivedQAbstractItemModel(),
SLOT(contextualMenu(const QPoint &)));
void MyDerivedQAbstractItemModel::contextualMenu(const QPoint& point)
{
QMenu *menu = new QMenu;
menu->addAction(QString("Test Item"), this, SLOT(test_slot()));
menu->exec(MyDerivedQTreeView->mapToGlobal(point));
}
MyDerivedQAbstractItemModel::contextualMenu() gets called and I can see the contextual menu.
Problem is contextual menu should be visible only if user right-clicks on an item and it should be customized as per the item selected.
How do I get whether/which item is selected from QPoint information? I am on Qt 4.5.3.

Maybe you could use the indexAt() method of QTreeView to get the item where the click is made, before constructing your custom menu.

Maybe this code will help you:
==> dialog.h <==
QStandardItemModel *model;
QSortFilterProxyModel *proxyModel;
QTreeView *treeView;
==> dialog.cpp <==
void CImportTabWidget::createGUI() {
...
proxyModel = new QSortFilterProxyModel;
proxyModel->setDynamicSortFilter(true);
treeView = new QTreeView;
treeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
treeView->setRootIsDecorated(false);
treeView->setAlternatingRowColors(true);
treeView->setModel(proxyModel);
model = new QStandardItemModel(0, 4);
model->setHeaderData(0, Qt::Horizontal, tr("Name"));
model->setHeaderData(1, Qt::Horizontal, tr("Comment"));
model->setHeaderData(2, Qt::Horizontal, tr("Size"));
model->setHeaderData(3, Qt::Horizontal, tr("Date"));
fillTreeViewData();
proxyModel->setSourceModel(model);
...
}
//////////////////////////////////////////////////////////////////////////
void CImportTabWidget::createMenus() {
treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeView,
SIGNAL(customContextMenuRequested(const QPoint &)),
this,
SLOT(contextMenu(const QPoint &)));
}
//////////////////////////////////////////////////////////////////////////
void CImportTabWidget::contextMenu(const QPoint &widgetXY) {
Q_UNUSED(widgetXY);
QMenu menu(this);
/* Условия для меню */
deleteAct->setEnabled((!model->rowCount()) ? false : true );
deleteAllAct->setEnabled((!model->rowCount()) ? false : true );
/* Находим индекс */
QModelIndex index = treeView->currentIndex();
QString fileName = model->data(model->index(index.row(), 0)).toString();
if (!fileName.isEmpty()) {
importAct->setText(tr("Import %1").arg(fileName));
//deleteAct->setText(tr("Delete %1").arg(fileName));
}
/* Формируем меню */
menu.addAction(deleteAct);
menu.addAction(deleteAllAct);
menu.exec(QCursor::pos());
}
Good luck!

The QTreeWidget::currentItem() function returns the item that was right-clicked. It's not clear that it does so based on the description, but based on my usage of it, that is what it does.

Related

How to properly add a QMenu (and submenu) using a QAction

I have been trying to add a submenu to a QMenu via QAction but it is not working.
As you can see below I am able to see the choices via right click on a QTableWidget.
However from the menu "Option" I should see the submenu, but I don't see it.
Below the procedure:
rostreewidget.h
class ROSTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
ROSTreeWidget(QWidget *parent = nullptr);
signals:
void selectFrom();
void editLaserTable();
private:
QAction *mActionSELECT_FROM;
QAction *mActionEditLaserTable;
QAction *mAddMenu;
QActionGroup *actions1, *actions2;
QMenu *mMenu;
QMenu *submenu;
QAction *actionA_Setup;
};
rostreewidget.cpp
// Namespace ROSLaserItemTree
ROSTreeWidget::ROSTreeWidget(QWidget *parent) : QTreeWidget(parent)
{
mActionSELECT_FROM = new QAction(QIcon("/home/mapper/execute-command"), "SELECT * FROM", this);
mActionEditLaserTable = new QAction(QIcon(":"), "Edit LaserScan Table");
mAddMenu = new QAction(QIcon(":"), "Options", this);
addActions({ mActionSELECT_FROM,
mActionEditLaserTable,
mAddMenu});
connect(mActionSELECT_FROM, &QAction::triggered, [&]() {
emit selectFrom();
});
connect(mActionEditLaserTable, &QAction::triggered, [&]() {
emit editLaserTable();
});
connect(mAddMenu, &QAction::triggered, [&]() {
mMenu = new QMenu();
submenu = mMenu->addMenu( "A" );
QAction* actionA_Setup = submenu->addAction( "Setup" );
});
}
What I have done so far:
1) After following this source I was able to prepare the menu and the submenu to be inserted in the QAction but unfortunately it is not working.
As you can see from the print screen I am not able to see the choices under "Option".
2) Another thing I tried was going through this other source which was also clear, but still I could not figure out why the submenu is not being added in the QAction.
Thanks for pointing to the right direction for solving this issue.

Create and specify a QLabel after mouse pressed on another QLabel in QT

Ok so what i'm trying to do is to create a new QLabel added to a QList and put it where I clicked on the other QLabel where I clicked.
So here is my code:
class CustomLabel : public QLabel
{
Q_OBJECT
public:
CustomLabel();
void mousePressEvent( QMouseEvent* event);
private:
QList<QLabel *> pointsL;
QList<QPoint *> points;
};
void CustomLabel::mousePressEvent(QMouseEvent *event)
{
points << new QPoint(event->pos());
pointsL << new QLabel(this);
pointsL.at(pointsL.size()-1)->setText("+");
pointsL.at(pointsL.size()-1)->setGeometry(QRect(points.at(points.size()-1)->rx(),, points.at(points.size()-1)->ry(), 1, 1));
}
I also tried:
pointsL.at(pointsL.size()-1)->move(points.at(points.size()-1)->rx(), points.at(points.size()-1)->ry());
and this:
void CustomLabel::mousePressEvent(QMouseEvent *event)
{
points << new QPoint(event->pos());
pointsL << new QLabel(this);
pointsL.at(pointsL.size()-1)->setText("+");
pointsL.at(pointsL.size()-1)->move(*points.at(points.size()-1));
pointsL.at(pointsL.size()-1)->setTabOrder(pointsL.at(pointsL.size()-1), this);
}
When I click on the Custom Label nothing happens. The constructor is empty.
Thanks for any answer.
New widgets added after the parent is already visible on screen should be shown explicitly unless they are in a layout.
So basically you should add:
pointsL.back()−>show();

QListView and delegate display unintended item

I've a problem with my QListView, it paint an unintended item on the top left of the QListView :
http://s4.postimage.org/64orbk5kd/Screen_Shot_2013_02_14_at_20_23_14.png
I use a QStyledItemDelegate in my QListView :
m_stringList.push_back("FIRST");
m_stringList.push_back("SECOND");
m_stringList.push_back("THIRD");
m_model.setStringList(m_stringList);
ui->processesListView->setFlow(QListView::LeftToRight);
ui->processesListView->setModel(&m_model);
ui->processesListView->setItemDelegate(new ProcessItemDelegate(this, ui->processesListView));
The delegate (ProcessItemDelegate) paint method use a custom QWidget to display the information :
void ProcessItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex &inIndex ) const
{
_listItem->setContent(_listView->model()->data(inIndex).toString());
painter->save();
painter->translate(option.rect.center());
_listItem->render(painter);
painter->restore();
}
The setContent method of the QWidget is very simple :
void ProcessItem::setContent(const QString &s)
{
ui->processId->setText(s);
}
I have another way to add a widget to some list using a QListWidget.
For example knowing that ui->historyView is a QListWidget element and HistoryElementView a subclass of QWidget.
void View::onHistoryChanged(const QList<HistoryElement> &history)
{
clearHistory();
foreach(HistoryElement elt, history)
{
HistoryElementView *historyViewElement = new HistoryElementView(elt.getDateTime("dd/MM/yyyy - hh:mm"), elt.getFilename());
QListWidgetItem *item = new QListWidgetItem();
ui->historyView->addItem(item);
ui->historyView->setItemWidget(item, historyViewElement);
}
}
void View::clearHistory()
{
QListWidgetItem *item;
while (ui->historyView->count() != 0)
{
item = ui->historyView->takeItem(0);
delete item;
}
}
You do not need to delete the widgets inside your QListWidgetItem, it will be handle by Qt.
Once your widgets are inside the list, you can retrieve them using :
// Using index
QListWidgetItem *item = ui->historyView->item(0);
HistoryElementView *elt = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
// Using position
QListWidgetItem *item = ui->historyView->itemAt(pos);
HistoryElementView *historyElement = qobject_cast<HistoryElementView *>(ui->historyView->itemWidget(item));
Hope it helps.

Qt - How to Transfer QListView Items to another QListView?

I have a QLineEdit, 2 QPush buttons (Add & Remove Buttons) and a QListView. The Text which i am entering in the QLineEdit will gets added in the QListView when the add button is clicked. And if I'm selecting any one of the Item from the QListView and click the Remove Button the selected item will be removed. I finished these things and it works fine. Now i want to add a another QListView and if am Double clicking the QListView Items (QListView 1) the items should be transfered (items in the QListView 1 should be removed completely) to the new QListView (QListView 2) and vice versa. plz help me with your suggestions. Thanks in Advance.
Example.h
class Example : public QWidget
{
Q_OBJECT
public:
explicit Example(QWidget *parent = 0);
~Example();
private slots:
void on_listView_doubleClicked(const QModelIndex &index);
void on_listView_2_doubleClicked(const QModelIndex &index);
private:
QStandardItemModel *model; // This model is used when the add button is clicked.
QStandardItemModel *listViewModel;
};
Example.cpp
void Example::on_listView_doubleClicked(const QModelIndex &index)
{
QStandardItem *Item1;
Items1 = new QStandardItem();
Items1->setData(ui->listView->currentIndex().data(), Qt::DisplayRole );
Items1->setEditable( false );
listViewModel->appendRow( Items1 );
listViewModel->sort( 0, Qt::AscendingOrder );
ui->listView_2->setModel( listViewModel );
model->removeRow( ui->listView->currentIndex().row() );
}
void Example::on_listView_2_doubleClicked(const QModelIndex &index)
{
QStandardItem *Items2;
Items2 = new QStandardItem();
Items2->setData( ui->listView_2->currentIndex().data(), Qt::DisplayRole );
Items2->setEditable( false);
model->appendRow( Items2 );
model->sort( 0,Qt::AscendingOrder );
ui->listView->setModel( model );
model->removeRow( ui->listView_2->currentIndex().row() );
}
A more extensible way would be to create a custom model (possibly inheriting the QStringListModel if that suits your needs) and then implementing moveRows and/or the Drag&Drop facilities.

Position of icon in QTreeWidgetItem

My QTreeWidget has a single column. Its items have a check box, an icon, and text. If the user clicks inside an item, I want to know whether the icon was clicked. How can I find the position and size of the icon in a QTreeWidgetItem?
Updated to add: Here is the code for my eventual solution, as requested by webclectic.
First, I sub-classed QItemDelegate so that I could access the coordinates of each part of a QTreeWidgetItem (check box, icon, and text). Here is the header file:
#include <QItemDelegate>
class MyItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
explicit MyItemDelegate (MyTreeWidget *parent)
: QItemDelegate (parent), ParentView (parent) { }
~MyItemDelegate() { }
void GetRects (const QModelIndex &index, QRect& CheckBox, QRect& Icon, QRect& Text) const ;
private:
MyTreeWidget* ParentView ;
} ;
And here is the source file:
void MyItemDelegate::GetRects (const QModelIndex &index, QRect& CheckBox, QRect& Icon, QRect& Text) const
{
QStyleOptionViewItem option = ParentView -> viewOptions() ;
CheckBox = rect (option, index, Qt::CheckStateRole) ;
Icon = rect (option, index, Qt::DecorationRole) ;
Text = rect (option, index, Qt::DisplayRole) ;
doLayout (option, &CheckBox, &Icon, &Text, true) ;
QRect VisualRect = ParentView -> visualRect (index) ;
CheckBox.translate (VisualRect.topLeft()) ;
Icon.translate (VisualRect.topLeft()) ;
Text.translate (VisualRect.topLeft()) ;
}
Then I added a MyItemDelegate* member to MyTreeWidget, and set it as the item view's delegate. In the header:
class MyTreeWidget : public QTreeWidget
{
...
MyItemDelegate* Delegate ;
...
} ;
In the source :
MyTreeWidget::MyTreeWidget (QObject* parent)
{
...
Delegate = new MyItemDelegate (this) ;
setItemDelegate (ItemDelegate) ;
}
Now, to get the coordinates of each part of a QTreeWidgetItem:
QTreeWidgetItem* item ;
...
QModelIndex ModelIndex = indexFromItem (item) ;
QRect CheckBoxRect, IconRect, TextRect ;
ItemDelegate -> GetRects (ModelIndex, &CheckBoxRect, &IconRect, &TextRect) ;
Unfortunately there is no simple way to achieve what you want. The problem is that QTreeWidget is responsible for painting its items so the item itself has no information about the position of its elements in the view.
First of all you have to subclass QTreeWidget and reimplement the mousePressEvent (or mouseReleaseEvent if you prefer). Inside the event you should calculate the position of the icon and handle it correspondingly.
Sample code (but untested) follows:
void mousePressEvent(QMouseEvent *event)
{
QModelIndex clickedIndex = indexAt(event->pos());
// make sure the event was on a valid item
if (clickedIndex.isValid() == false)
return;
// Get the tree widget's x position
int treeX = header()->sectionViewportPosition(0);
// Get the x coordinate of the root item. It is required in order to calculate
// the identation of the item
int rootX = visualRect(rootIndex()).x();
// Get the rectangle of the viewport occupied by the pressed item
QRect vrect = visualRect(clickedIndex);
// Now we can easily calculate the x coordinate of the item
int itemX = treeX + vrect.x() - rootX;
// The item is a checkbox, then an icon and finally the text.
// 1. Get the rect surrounding the checkbox
QRect checkboxRect = QRect(itemX,
vrect.y(),
style()->pixelMetric(QStyle::PM_IndicatorWidth),
vrect.height());
// 2. Get the rect surrounding the icon
QRect iconRect = QRect(itemX + checkboxRect.width(),
vrect.y(),
iconSize().width(),
vrect.height());
// 3. Finally get the rect surrounding the text
QRect textRect = QRect(itemX + checkboxRect.width() + iconRect.width(),
vrect.y(),
vrect.width() - checkboxRect.width() - iconRect.width(),
vrect.height());
// Now check where the press event took place and handle it correspondingly
if(checkboxRect.contains(event->pos()))
{
qDebug() << "Checkbox pressed";
QTreeWidget::mousePressEvent(event);
return;
}
else if (iconRect.contains(event->pos()))
{
qDebug() << "Icon pressed";
QTreeWidget::mousePressEvent(event);
return;
}
else
{
qDebug() << "Text pressed";
QTreeWidget::mousePressEvent(event);
return;
}
}
I repeat that the code is untested but you get the idea about how to achieve what you want.

Resources