How to let QComboBox have context menu? - qt

I have a Qt combo box. When it pops, items are listed down. When right clicking an item, I hope a context menu to pop up. Any way to implement it? I find a function onContextMenuEvent under QComboBox. Does it help? Thanks.

You can obtain the list widget using QComboBox::view. You can add a context menu to the list as usual. But also you should install event filter on the view's viewport and block right click events because such events cause popup list to close.
In the initialization:
QAbstractItemView* view = ui->comboBox->view();
view->viewport()->installEventFilter(this);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(list_context_menu(QPoint)));
Event filter:
bool MainWindow::eventFilter(QObject *o, QEvent *e) {
if (e->type() == QEvent::MouseButtonRelease) {
if (static_cast<QMouseEvent*>(e)->button() == Qt::RightButton) {
return true;
}
}
return false;
}
In the slot:
void MainWindow::list_context_menu(QPoint pos) {
QAbstractItemView* view = ui->comboBox->view();
QModelIndex index = view->indexAt(pos);
if (!index.isValid()) { return; }
QMenu menu;
QString item = ui->comboBox->model()->data(index, Qt::DisplayRole).toString();
menu.addAction(QString("test menu for item: %1").arg(item));
menu.exec(view->mapToGlobal(pos));
}
In this example items are identified by their displayed texts. But you also can attach additional data to items using QComboBox::setItemData. You can retrieve this data using ui->comboBox->model()->data(...) with the role that was used in setItemData.

Related

graphicsview receives mouse event before the item

I have implemented the panning view on the QGraphicsView, using the mouse move event using
void View::mouseMoveEvent(QMouseEvent* event) {
pan();
QGraphicsView::mouseMoveEvent(event);
}
and in the scene of this view I have added few items where some of the items are resizable, so I implemented
void Item::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_resizeMode)
{
resize();
e->accept();
}
}
I tried to filter the mouse move not to propagate to any further using e->accept()
but my View mouseMove event has been called first , so when ever I tried to resize the item, the view started to pan all the way.
How can I avoid this event propagation from view to scene.
You can call the base class implementation of the QGraphicsView and check if the event is accepted. However I would do this in the mousePressEvent instead of the mouseMoveEvent. After all this is where you determine if you should resize an item or do some panning. This is how I do something similar in my project:
void View::mousePressEvent(QMouseEvent *event)
{
...
QGraphicsView::mousePressEvent(event);
if(event->isAccepted())
move = false;
else
move = true;
}
void View::mouseMoveEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseMoveEvent(event);
return;
}
... // you would do the panning here
QGraphicsView::mouseMoveEvent(event);
}
void View::mouseReleaseEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseReleaseEvent(event);
return;
}
else
{
...
move = false;
}
QGraphicsView::mouseReleaseEvent(event);
}
The view will always receive the mouse event first.
So, in the view, check to see if the mouse is over an item before allowing it to pan by getting the mouse pos in scene coordinates and retrieving the items at that position with QGraphicsScene::items( )

QTreeView: show "empty view" item?

I want to show an item "There are no elements in this view" if the connected QTreeView model (set by QSortFilterProxyModel) has no elements to show.
How can I implement such things?
Thanks for small hints.
One of the solution is overriding the tree view's paint event and draw the custom text when there is no items in the view. You need to sub class the QTreeView in the following way:
class TreeView : public QTreeView
{
[..]
protected:
void paintEvent(QPaintEvent * event)
{
if (model() && model()->rowCount() > 0) {
QTreeView::paintEvent(event);
} else {
// If no items draw a text in the center of the viewport.
QPainter painter(viewport());
QString text(tr("There are no elements in this view"));
QRect textRect = painter.fontMetrics().boundingRect(text);
textRect.moveCenter(viewport()->rect().center());
painter.drawText(textRect, Qt::AlignCenter, text);
}
}
};

How to make a Qt dialog read-only?

How to make a QT dialog read-only? Any general way to implement it easily? For example
(1) set all its containing widgets disable. (how to implement it?)
(2) Intercept edit events like key pressed, mouse pressed but how not to intercept the one to close the dialog?
I think this feature should be very helpful.
Disabling the widgets can be done similar to the following:
void myDialog::disableWidgets()
{
QList<QWidget *> widgets = this->findChildren<QWidget *>();
foreach(QWidget* widget, widgets)
{
widget->setEnabled(false);
}
}
To intercept events, QDialog includes the function installEventFilter(QObject*).
This allows you to use a separate object to receive all events passed to the dialog. You can then choose to handle the event in the object, or pass it on to the dialog itself by calling the base class QObject::eventFilter
class MyEventHandler : public QObject
{
Q_OBJECT
protected:
bool MyEventHandler::eventFilter(QObject *obj, QEvent *event)
{
// handle key press events
if (event->type() == QEvent::KeyPress)
{
// Do something
// ...
return true; // event handled by the class
}
else
{ // ignore this event and pass it to the dialog as usual
return QObject::eventFilter(obj, event);
}
}
return false;
};
QDialog* dlg = new QDialog;
MyEventHandler evtHandler = new MyEventHandler;
dlg->installEventFilter(evtHandler);
Read-only is a strange term to apply to a dialog. Disabling all widgets as above does the trick. If you only wanted to make the input part of a QInputDialog read-only (while leaving scrollbars, buttons, etc. enabled), you could adapt that code as below:
QInputDialog dialog(this);
dialog.setOptions(QInputDialog::UsePlainTextEditForTextInput);
dialog.setWindowTitle("Title");
dialog.setLabelText("Label");
dialog.setTextValue("1\n2\n3\n");
QList<QWidget *> widgets = dialog.findChildren<QWidget *>();
foreach(QWidget* widget, widgets) {
if (strcmp(widget->metaObject()->className(),"QPlainTextEdit")==0) {
QPlainTextEdit *t = static_cast<QPlainTextEdit*>(widget);
t->setReadOnly(true);
}
}
dialog.exec();

auto select first proposition of QCompleter in PopupCompletion mode

I didn't manage to select the first item of the completer when the popup is displayed in PopupCompletion mode.
My current code that doesnt work:
completer->setCompletionPrefix(text);
QItemSelectionModel* sm = new QItemSelectionModel(completer->completionModel());
sm->select(completer->completionModel()->index(0,0), QItemSelectionModel::Select);
completer->popup()->setSelectionModel(sm);
Any suggestions?
I would try changing the order of the last 2 lines:
completer->popup()->setSelectionModel(sm);
sm->select(completer->completionModel()->index(0,0), QItemSelectionModel::Select);
Probably the change of the selection for the popup (its a view) ocurs when selectionChanged() is emited.
So you have to set first the selection model, then do the select.
void QItemSelectionModel::select ( const QModelIndex & index,
QItemSelectionModel::SelectionFlags command ) [virtual slot]
Selects the model item index using the specified command, and emits
selectionChanged().
BTW, u dont have to create a new selection model, just ask the popup for it (Againt, its a view):
completer->popup()->selectionModel();
http://qt-project.org/doc/qt-5.0/qtwidgets/qabstractitemview.html#selectionModel
I don't know if this is what you wanted, but in my case I wanted to be able to press enter and auto-select the first item in the pop-up list (as it does with UnfilteredPopupCompletion).
What worked for me was:
class AutoSelectFirstFilter : public QObject
{
Q_OBJECT
protected:
virtual bool eventFilter(QObject *obj, QEvent *event) override
{
if (event->type() == QEvent::KeyPress)
{
if(static_cast<QKeyEvent *>(event)->key() == Qt::Key_Return)
{
QAbstractItemView* l = static_cast<QAbstractItemView*>(obj);
QModelIndex i = l->model()->index(0,0);
if(i.isValid())
l->selectionModel()->select(i, QItemSelectionModel::Select);
}
}
return false;
}
};
and than:
AutoSelectFirstFilter tmp;
completer->popup()->installEventFilter(&tmp);
PS: Don't forget to re-run qmake.

Qt custom context menu

Am creating a Qt Application which consists of a tree view and a webview. when a item from a tree view is clicked it should load the corresponding url. It Works Fine. when am right clicking on the item a custom context menu will appear n it will open it in a new webview. This is also working. But my problem is when am Right clicking on the treeview item my context menu comes and if am clicking it outside the pop up menu the url of that item gets loaded. how to solve this.. Help me friends..
Here's my coding:
QStandardItem *rootItem = new QStandardItem("Google");
QStandardItem *stackItem = new QStandardItem("Stack Overflow");
QStandardItem *yahooItem = new QStandardItem("Yahoo");
rootItem->appendRow(stackItem);
standardModel->appendRow(rootItem);
standardModel->appendRow(yahooItem);
***// private slot for loading the url if a treeview item is clicked:***
void MainWindow::treeViewClicked(const QModelIndex &index)
{
str = index.data().toString();
if(!(str.isEmpty()) && str=="Google")
{
url = "http://www.google.com";
}
else if (!(str.isEmpty()) && str == "stack Overflow")
{
url = "http://www.stackoverflow.com";
}
else if (!(str.isEmpty()) && str == "Yahoo")
{
url = "http://www.yahoo.com";
}
WebView *wv = dynamic_cast<WebView *>(ui->tabWidget->currentWidget());
wv->load(QUrl(url));
ui->tabWidget->setTabText(ui->tabWidget->currentIndex(),str);
treeView->setModel(standardModel);
**//Creating custom context menu for QtreeView:**
void MainWindow::showContextMenu(const QPoint& point)
{
QList<QAction *> actions;
if(treeView->indexAt(point).isValid())
{
actions.append(m_treeViewAction);
}
else if(actions.count() > 0)
{
QMenu::exec(actions, MainWindow::treeView->mapToGlobal(point));
QModelIndex index = treeView->indexAt(point);
QStandardItem *item = standardModel->itemFromIndex(index);
treeView->setCurrentIndex(index);
treeViewClicked(index);
}
}
For what I know, the situation you describe is standard with context menus in views: When you right click, the item is also selected.
If you want another behaviour, you must implement the mousePressEvent and implement the behaviour you want to achieve.
Here is a hint:
void MyTreeView::mousePressEvent ( QMouseEvent * event )
{
if (event->button() == Qt::LeftButton) {
// set the current item based on event->pos() / deselect if no item
}
else if (event->button() == Qt::RightButton) {
// show context menu for the item / different context menu if no item
}
}
Yes, you must derive the QTreeView class and make one of your own.
I've done this long time ago, and I remember this as the starting point. I don't remember now if I had to reimplement all four basic mouse events: press, release, move and doubleclick, as they are internally related.

Resources