How to prevent default context menu on QGraphicsTextItem? - c++

Is it possible to prevent right-click from opening the default context menu on QGraphicsTextItem ? The menu with "Undo, Redo, Cut, Copy, Paste..". On Ubuntu 18.04, that is. I don't know how this behaves on Windows.
I have overridden the mouse press handler to eat right-clicks in my view and tried to do that also in the item class itself. This actually did prevent the menu on Qt 5.10.0, but for some reason not anymore on 5.11.1:
void EditorView::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::RightButton)
{
return;
}
...
doOtherHandlingStuff();
...
}
In the item itself it doesn't have any effect if I do this:
void TextEdit::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
event->ignore();
return;
}

You have to override the contextMenuEvent method of QGraphicsTextItem:
#include <QtWidgets>
class GraphicsTextItem: public QGraphicsTextItem
{
public:
using QGraphicsTextItem::QGraphicsTextItem;
protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override
{
event->ignore();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
QGraphicsView w{&scene};
auto it = new GraphicsTextItem("Hello World");
it->setTextInteractionFlags(Qt::TextEditable);
scene.addItem(it);
w.show();
return a.exec();
}

Related

Receive mouse inputs in inactive region

I have horizontal slider element which is disabled. I want to enable it when I click on it. As the slider is disabled I am unable to capture mouse events on it.
Thanks in advance
I must admit that my first suggestion was wrong:
However, if the slider is disabled the mouse event will be captured probably by something else - I would expect the parent widget.
That expectation was wrong.
I found out about this by trying myself in an MCVE.
For my luck, the other option – using an event filter – does work.
Sample testQClickDisabled.cc:
// Qt header:
#include <QtWidgets>
void populate(QListWidget &qLst)
{
for (int i = 1; i <= 20; ++i) {
qLst.addItem(QString("item %0").arg(i));
}
}
class EventFilter: public QObject {
private:
QListWidget &qLst;
public:
EventFilter(QListWidget &qLst): QObject(), qLst(qLst)
{
qApp->installEventFilter(this);
}
~EventFilter() { qApp->removeEventFilter(this); }
EventFilter(const EventFilter&) = delete;
EventFilter& operator=(const EventFilter&) = delete;
protected:
virtual bool eventFilter(QObject *pQObj, QEvent *pQEvent) override;
};
bool EventFilter::eventFilter(QObject *pQObj, QEvent *pQEvent)
{
if (QScrollBar *const pQScrBar = dynamic_cast<QScrollBar*>(pQObj)) {
if (pQScrBar == qLst.verticalScrollBar()
&& pQEvent->type() == QEvent::MouseButtonPress) {
qDebug() << "Vertical scrollbar hit.";
pQScrBar->setEnabled(true);
}
}
return QObject::eventFilter(pQObj, pQEvent);
}
// main application
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// setup GUI
QListWidget qLst;
qLst.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
qLst.verticalScrollBar()->setEnabled(false);
qLst.resize(200, 200);
qLst.show();
populate(qLst);
EventFilter qEventFilter(qLst);
// runtime loop
return app.exec();
}
Output:
I would just use a sibling MouseArea, like this:
Slider {
id: slider
enabled: false
}
MouseArea {
anchors.fill: slider
visible: !slider.enabled
onClicked: slider.enabled = true
}

How to disable shortcuts for QComboBox in qt?

I searched for the answers online, but I did not really find one that solved my problem. My question is like: I have a QComboBox, let's say I added three items to this:
ui->comboBox->addItem("First");
ui->comboBox->addItem("Second");
ui->comboBox->addItem("Third");
Then if I press the S on the keyboard, the item will change to Second, if I press T, so item will just change to Third. How can I disable this?
A possible solution is to implement an eventfilter that prevents the letters from being used in the QComboBox:
#include <QApplication>
#include <QComboBox>
#include <QKeyEvent>
class Helper: public QObject{
QComboBox *m_combo;
public:
using QObject::QObject;
void setComboBox(QComboBox *combo){
m_combo = combo;
m_combo->installEventFilter(this);
}
bool eventFilter(QObject *watched, QEvent *event){
if(m_combo){
if(m_combo == watched && event->type() == QEvent::KeyPress){
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if(!ke->text().isEmpty())
return true;
}
}
return QObject::eventFilter(watched, event);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QComboBox w;
w.addItems({"First", "Second","Third"});
Helper helper;
helper.setComboBox(&w);
w.show();
return a.exec();
}

QGraphicsTextItem editing requires an action performed twice

I want to make a QGraphicsTextItem editable on double click, and make it movable when I click out.
#include <QApplication>
#include <QPainter>
#include <QGraphicsItem>
#include <QGraphicsView>
class TextItem: public QGraphicsTextItem
{
public:
TextItem()
{
setPlainText("hello world");
QFont f;
f.setPointSize(50);
f.setBold(true);
f.setFamily("Helvetica");
setFont(f);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
setTextInteractionFlags(Qt::NoTextInteraction);
}
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL)
{
QGraphicsTextItem::paint(painter, option, widget);
}
protected:
virtual void focusOutEvent (QFocusEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::NoTextInteraction);
}
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::TextEditable); // TextEditorInteraction
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TextItem* t = new TextItem();
QGraphicsView view(new QGraphicsScene(-200, -150, 400, 300) );
view.scene()->addItem(t);
view.show();
return a.exec();
}
It does what I want - except I have to double-click twice
- first time I double click, I see a cursor but am unable to edit text (with either option, TextEditable or TextEditorInteraction (I probably want the latter). Then I double-click again and I can type to add or delete text.
It is a behavior that a user probably doesn't expect - and nothing I do seems to change it.
Am I doing something wrong, or is there anything I need to add ?
I expected a mouse action on a focusable item to give it focus automatically. I guess not...
In the mouseDoubleClickEvent, I added a call to setFocus()
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)
{
Q_UNUSED(event);
setTextInteractionFlags(Qt::TextEditorInteraction);
setFocus();
}

How to execute the mouseevent of qgraphicsitem only ? (ignoring the rest of mouseevent of qgraphicsview)

I know how to pass events from qgraphicsview to qgraphicsitem.
But the problem is after executing the mouseEvent of item ,i have to execute the mouse event of the view which is not desirable in my case.
so,the question is: "Is there a smart way to know if mousePress is on an item or on an empty space?"
Edit:The working code:
#include <QtGui>
class CustomView : public QGraphicsView
{
protected:
void mousePressEvent(QMouseEvent *event)
{
QGraphicsView::mousePressEvent(event);
qDebug() << "Custom view clicked.";
}
};
class CustomItem : public QGraphicsRectItem
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Custom item clicked.";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomItem item;
item.setRect(20, 20, 60, 60);
QGraphicsScene scene(0, 0, 100, 100);
scene.addItem(&item);
CustomView view;
view.setScene(&scene);
view.show();
return a.exec();
}
Appling the same concept in a qgraphics scene instead of view
#include <QtGui>
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(itemAt(event->scenePos()))
QGraphicsScene::mousePressEvent((event));
else
qDebug() << "Custom view clicked.";
}
};
class CustomItem : public QGraphicsRectItem
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Custom item clicked.";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomItem item;
item.setRect(20, 20, 60, 60);
CustomScene scene;
//scene().set
scene.addItem(&item);
QGraphicsView view;
view.setScene(&scene);
view.show();
return a.exec();
}
There are 3 correct ways to solve your task:
1. Reimplement QGraphicsView::mousePressEvent and use QGraphicsView::itemAt to find clicked item.
2. Subclass QGraphicsScene and reimplement QGraphicsScene::mousePressEvent. Its argument event contains position in scene coordinates, and you can use QGraphicsScene::itemAt to determine which item has been clicked.
3. Subclass QGraphicsItem (or any derived class) and reimplement QGraphicsItem::mousePressEvent. It will be called only if this element was clicked.
To determine wether mouse event occured on item or not you can use QGraphicsView::itemAt:
void CustomView::mousePressEvent(QMouseEvent *event)
{
if (itemAt(event->pos()))
// Click on item
else
// Click on empty space
...
}

Possible to make QDialog shake in OSx

Since Qt is using Cocoa under OSX, is it possible to make a modal QDialog to shake if the user enters the wrong password for example? Im not able to find anything about it but it would be really nice to implement on mac.
Thanks!
I'm not aware of a built-in way to do it, but you could implement the shaking yourself, like this:
header.h
#include <QtGui>
class ShakyDialog : public QDialog
{
Q_OBJECT
public slots:
void shake()
{
static int numTimesCalled = 0;
numTimesCalled++;
if (numTimesCalled == 9) {
numTimesCalled = 0;
return;
}
vacillate();
QTimer::singleShot(40, this, SLOT(shake()));
}
private:
void vacillate()
{
QPoint offset(10, 0);
move(((shakeSwitch) ? pos() + offset : pos() - offset));
shakeSwitch = !shakeSwitch;
}
bool shakeSwitch;
};
main.cpp
#include "header.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ShakyDialog dialog;
QHBoxLayout layout(&dialog);
QPushButton button("Push me.");
layout.addWidget(&button);
QObject::connect(&button, SIGNAL(clicked()), &dialog, SLOT(shake()));
dialog.show();
return app.exec();
}