I have a reimplemented QDoubleSpinBox. I would like to catch mouseDoubleClickEvent to enable the user to change singleStep by way of QInputDialog::getDouble().
My problem is that when I reimplement the mouseDoubleClickEvent I only catch double clicks that occur over the arrow buttons. I actually want to ignore double clicks that occur in the arrows and only catch double clicks that occur in the text field. I have a feeling that I need to reimplement the mouseDoubleClickEvent of a child of the QDoubleSpinBox, but I'm not sure how to reimplement a child event nor how to select the correct child See my attempt at limiting to a child QRect in code: I think I need to specify which child...?
Thanks.
Edit: corrected class declaration/definition name mismatch.
MyQDoubleSpinBox.h
class MyQDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
MyQDoubleSpinBox(QString str, QWidget *parent = 0);
~MyQDoubleSpinBox();
public slots:
void setStepSize(double step);
private:
double stepSize;
QString name;
protected:
void mouseDoubleClickEvent(QMouseEvent *e);
};
MyQDoubleSpinBox.cpp
#include "MyQDoubleSpinBox.h"
MyQDoubleSpinBox::MyQDoubleSpinBox(QString str, QWidget *parent)
: QDoubleSpinBox(parent), stepSize(1.00), name(str)
{
this->setMinimumWidth(150);
this->setSingleStep(stepSize);
this->setMinimum(0.0);
this->setMaximum(100.0);
}
MyQDoubleSpinBox::~MyQDoubleSpinBox()
{
}
void MyQDoubleSpinBox::setStepSize(double step)
{
this->setSingleStep(step);
}
void MyQDoubleSpinBox::mouseDoubleClickEvent(QMouseEvent *e)
{
if( this->childrenRect().contains(e->pos()) )
{
bool ok;
double d = QInputDialog::getDouble(this,
name,
tr("Step Size:"),
this->singleStep(),
0.0,
1000.0,
2,
&ok);
if(ok)
this->setSingleStep(d);
}
}
A bit of the hack getting ref to child, but it works =)
MyQDoubleSpinBox.h:
class MyQDoubleSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
MyQDoubleSpinBox(QString str, QWidget *parent = 0);
~MyQDoubleSpinBox();
public slots:
void setStepSize(double step);
private:
double stepSize;
QString name;
protected:
bool eventFilter(QObject *, QEvent *e);
};
MyQDoubleSpinBox.cpp
MyQDoubleSpinBox::MyQDoubleSpinBox(QString str, QWidget *parent)
: QDoubleSpinBox(parent), stepSize(1.00), name(str)
{
this->setMinimumWidth(150);
this->setSingleStep(stepSize);
this->setMinimum(0.0);
this->setMaximum(100.0);
QLineEdit *editor = this->findChild<QLineEdit *>("qt_spinbox_lineedit");
editor->installEventFilter(this);
}
MyQDoubleSpinBox::~MyQDoubleSpinBox()
{
}
void MyQDoubleSpinBox::setStepSize(double step)
{
this->setSingleStep(step);
}
bool MyQDoubleSpinBox::eventFilter(QObject *, QEvent *e)
{
if (e->type() == QMouseEvent::MouseButtonDblClick)
{ bool ok;
double d = QInputDialog::getDouble(this,
name,
tr("Step Size:"),
this->singleStep(),
0.0,
1000.0,
2,
&ok);
if(ok)
this->setSingleStep(d);
}
return false;
}
Instead of overwriting events, i got ref to underlying QLineEdit and assigned event filter to it. In event filter catch only mouse double click.
You have direct access to QLineEdit using this->lineEdit()
MyQDoubleSpinBox::MyQDoubleSpinBox(QString str, QWidget *parent)
: QDoubleSpinBox(parent), stepSize(1.00), name(str)
{
this->setMinimumWidth(150);
this->setSingleStep(stepSize);
this->setMinimum(0.0);
this->setMaximum(100.0);
QLineEdit *editor = this->lineEdit(); // change here
editor->installEventFilter(this);
}
Related
I'm working on a TreeViewNode class derived from QObject and QGraphicsItem, responsible for creating the individual nodes of the tree view (which displays a family tree) and adding them to the scene. I made sure to include the setFlag(QGraphicsItem::ItemIsSelectable); method in the class constructor and I overloaded mousePressEvent like so:
void TreeViewNode::mousePressEvent(QGraphicsSceneMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
qDebug() << "Mouse pressed on node: " << m_node->getPatient()->get_Name().c_str();
emit clicked(this);
}
QGraphicsItem::mousePressEvent(event);
}
The clicked signal is defined in the header file as void clicked(TreeViewNode* node);
Then I have a updateSelectedPatient slot in MainWindow
void MainWindow::updateSelectedPatient(TreeViewNode* node) {
selected->setSelectedPatient(node->getNode()->getPatient());
}
and the corresponding connect statement in the MainWindow constructor
connect(treeView, &TreeViewNode::clicked, this, &MainWindow::updateSelectedPatient);
When I execute I see the nodes that have been added to the scene in the view but when I click on them nothing happens (I know through debugging and also because the setSelectedPatient() method updates a widget that shows the patient information of the currently selected patient); it seems like mousePressEvent() is not being called at all.. any help would be really appreciated, thank you for your time!
edit:
//TreeViewNode.hpp
class TreeViewNode : public QObject, public QGraphicsItem {
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
private:
node* m_node;
Family_tree* m_family;
QGraphicsScene* m_scene;
static std::set<node*> addedNodes;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
public:
TreeViewNode(node* node, Family_tree* family, QGraphicsScene* scene, QGraphicsItem* parent = nullptr);
TreeViewNode* getTreeViewNode(node* n);
node* getNode() const;
static void clearAddedNodes();
void updateNode(Patient& patient);
QRectF boundingRect() const override;
void drawBranches(QPainter* painter);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
signals:
void clicked(TreeViewNode* node);
};
//TreeViewNode.cpp
TreeViewNode::TreeViewNode(node* node, Family_tree* family, QGraphicsScene* scene, QGraphicsItem* parent) : QGraphicsItem(parent), m_node(node), m_family(family), m_scene(scene) {
if (m_node == m_family->get_root()) {m_scene->addItem(this);}
addedNodes.insert(m_node);
setFlag(QGraphicsItem::ItemIsSelectable);
qDebug() << "Scene item count: " << m_scene->items().count();
if (m_node->getFather()) {
auto father = m_node->getFather();
if (addedNodes.count(father) > 0) {return;}
TreeViewNode* fatherNode = new TreeViewNode(m_node->getFather(), family, m_scene, this);
fatherNode->setPos(-50, -100);
}
if (m_node->getMother()) {
auto mother = m_node->getMother();
if (addedNodes.count(mother) > 0) {return;}
TreeViewNode* motherNode = new TreeViewNode(m_node->getMother(), family, m_scene, this);
motherNode->setPos(50, -100);
}
if (m_node->getSpouse()) {
auto spouse = m_node->getSpouse();
if (addedNodes.count(spouse) > 0) {return;}
TreeViewNode* spouseNode = new TreeViewNode(m_node->getSpouse(), family, m_scene, this);
spouseNode->setPos(100, 0);
}
int childCount = 0;
for (auto child : m_node->getChildren()) {
if (addedNodes.count(child) > 0) {return;}
TreeViewNode* childNode = new TreeViewNode(child, family, m_scene, this);
childCount++;
double xPos, yPos;
xPos = pos().x() + 50 + 100 * (childCount - (m_node->getChildren().size() + 1) / 2.0);
yPos = pos().y() + 100;
childNode->setPos(xPos, yPos);
}
}
You could use a normal event filter and check if is it a mouse press, release or move event then cast it to a mouse event and then just do whatever you want with it.
Example:
bool YourWidgetClass::eventFilter(QObject* object, QEvent* event)
{
if (object == YourInstance)
{
switch (event->type())
{
case QEvent::GraphicsSceneMousePress:
{
QMouseEvent * mouseEvent = static_cast<QMouseEvent*>(event);
switch (mouseEvent->button())
{
case Qt::LeftButton:
// Handle left button pressed here
return true;
case Qt::RightButton:
// Handle right button pressed here
return true;
default:
break;
}
break;
}
}
}
Don't forget to install the event filter on your class
YourClassCtor->installEventFilter(this)
I don't see from your question that you need it for anything special. the signal &QGraphicsScene::selectionChanged is not enough for you? Is the item set to interactive?
So simply put:
declare a slot:
public slots:
void selectionChanged();
define the slot:
void MainWindow::selectionChanged()
{
qDebug() << ...... ;
}
and connect:
connect(scene,&QGraphicsScene::selectionChanged,this,&MainWindow::selectionChanged);
I have a class which inherit from QDoubleSpinBox.
class NumericEdit : public QDoubleSpinBox
{
public:
NumericEdit( QWidget *p_parent = nullptr );
protected:
bool event( QEvent *p_event ) override;
void keyPressEvent( QKeyEvent *p_event ) override;
void keyReleaseEvent( QKeyEvent *p_event ) override;
void focusInEvent( QFocusEvent *p_event ) override;
void focusOutEvent( QFocusEvent *p_event ) override;
............
};
NumericEdit::NumericEdit( QWidget *p_parent ) : QDoubleSpinBox( p_parent )
{
initStyleSheet();
setButtonSymbols( QAbstractSpinBox::NoButtons );
setGroupSeparatorShown( true );
..........
}
The result when I double click into the editing field is like this, only the part in between group separators is marked. If I triple click, the whole text is then marked.
How should I change, sothat when I double click into the editing field (no matter in integer part or decimal part), the whole text is marked?
Solution is reimplementing of QLineEdit::mouseDoubleClickEvent method (not QDoubleSpinBox::mouseDoubleClickEvent).
Custom line edit:
class ExtendedLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit ExtendedLineEdit(QWidget *parent = nullptr);
protected:
void mouseDoubleClickEvent(QMouseEvent *event);
}
void ExtendedLineEdit::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
selectAll();
event->accept();
return;
}
QLineEdit::mouseDoubleClickEvent(event);
}
And then set it to your custom spin box
NumericEdit::NumericEdit(QWidget *p_parent) : QDoubleSpinBox(p_parent)
{
//...
ExtendedLineEdit* lineEdit = new ExtendedLineEdit(this);
setLineEdit(lineEdit);
}
I'm trying to use a keyPressEvent, but it is only working when the window has focus and not any of the QWidgets.
Here is my code:
In customdialog.h:
class CustomDialog : public QDialog, public Ui::CustomDialog
{
Q_OBJECT
private:
Ui::CustomDialog *ui;
QString lastKey;
public:
CustomDialog(QWidget * parent = 0);
protected:
void keyPressEvent(QKeyEvent *e);
};
In customdialog.cpp:
void CustomDialog::keyPressEvent(QKeyEvent *e)
{
lastKey = e->text();
qDebug() << lastKey;
}
How can I make all widgets within this class use the same keyPressEvent?
You can solve your problem by installing event filters to every child of CustomDialog:
void CustomDialog::childEvent(QChildEvent *event)
{
if (event->added()) {
event->child()->installEventFilter(this);
}
}
bool CustomDialog::eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
keyPressEvent(static_cast<QKeyEvent*>(event));
return false;
}
But since every ignored keyPress event is sent to the parent widget, you can get keyPressEvent called multiple times for the same event.
I ended up deciding not to use keyPressEvent in this case for my purposes. I just needed to get the last key pressed in a QTextBrowser. Here is what I ended up doing:
connect(ui->textBrowser, SIGNAL(textChanged()), this, SLOT(handleTextBrowser()));
void CustomDialog::handleTextBrowser()
{
QTextCursor cursor(ui->textBrowser->textCursor());
QString key = ui->textBrowser->toPlainText().mid(cursor.position() - 1, 1);
qDebug() << key;
}
I currently have a QtableView that is attached to a QstandardItemModel. I want to insert a clickable Qlabel in the last column of each row. Initially I wanted to go with a QPushButton but it turns out that it requires extra overhead so now I am planning to go with a clickable Qlabel. Any suggetsions on how I could do that ? Also I would appreciate it if someone could sugget options that are available for simulating click events in a TableView using QStandardItemModel
connect(ui.tableView,SIGNAL(clicked(const QModelIndex& ) ),
this,SLOT( itemClicked(const QModelIndex& ) ) );
slot:
void itemClicked( const QModelIndex& idx) {
int row = idx.row();
int column = idx.column();
}
if you really need just a clickable label you can create class derived from QLabel and add custom signals to handle click event:
class CustomWidget : public QLabel {
Q_OBJECT
public:
explicit CustomWidget(const QString& text, QWidget *parent = 0);
signals:
void released(void);
void clicked(void);
protected:
void mousePressEvent(QMouseEvent* e);
void mouseReleaseEvent(QMouseEvent* e);
private:
bool mousePressed;
};
CustomWidget::CustomWidget(const QString& text, QWidget* parent)
: QLabel(text, parent), mousePressed(false) {
}
void CustomWidget::mousePressEvent(QMouseEvent* e) {
mousePressed = true;
}
void CustomWidget::mouseReleaseEvent(QMouseEvent* e) {
emit released();
if(mousePressed) {
emit clicked();
mousePressed = false;
}
}
full code snippet:
http://www.qtcentre.org/archive/index.php/t-42296.html?s=e9f0fd408147a1cd1048f252967895a0
I have a little problem, I need to set my event filter to QComboBox popup.
I need to catch events when left and right keys are pressed.
How can I do this?
Thank you!
You need to set the eventFilter on QComboBox's view() (http://qt-project.org/doc/qt-4.8/qcombobox.html#view).
You may need to add following code somewhere in your code.
void MyComboBox::keyPressEvent (QKeyEvent *event)
{
if (event->button() == Qt::Key_Left)
{
// handle left key press
}
if (event->button() == Qt::Key_Right)
{
// handle right key press
}
}
Hope this helps!
The question is quite old, but I provide my answer since it can help someone else.
After popup all events will be sent to the list view used for the QComboBox popup. You can get the things done using key handler class watching on events for list view.
KeyPressHandler.h:
class KeyPressHandler : public QObject
{
Q_OBJECT
public:
explicit KeyPressHandler(QObject *parent = nullptr);
virtual ~KeyPressHandler() override;
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};
KeyPressHandler.cpp:
#include <QCoreApplication>
KeyPressHandler::KeyPressHandler(QObject *parent) : QObject(parent)
{
}
KeyPressHandler::~KeyPressHandler()
{
}
bool KeyPressHandler::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch(keyEvent->key())
{
case Qt::Key_Left:
// Send press event for the Key_Up which understood by list view
QCoreApplication::postEvent(obj, new QKeyEvent(QEvent::KeyPress,
Qt::Key_Up,
Qt::NoModifier));
return true;
case Qt::Key_Right:
QCoreApplication::postEvent(obj, new QKeyEvent(QEvent::KeyPress,
Qt::Key_Down,
Qt::NoModifier));
return true;
default:
break;
}
}
// standard event processing
return QObject::eventFilter(obj, event);
}
In ComboBox you will need to install event filter when popup is shown.
It can be done in different ways, for example by overriding QComboBox::showPopup() function.
MyComboBox.h:
#include <memory>
#include <QComboBox>
class MyComboBox : public QComboBox
{
Q_OBJECT
public:
explicit MyComboBox(QWidget *parent = 0);
protected:
void showPopup() override;
void hidePopup() override;
private:
std::unique_ptr<KeyPressHandler> m_key_press_handler;
};
MyComboBox.cpp:
...
void MyComboBox::showPopup()
{
if(!m_key_press_handler)
{
m_key_press_handler.reset(new KeyPressHandler());
QAbstractItemView *v = view();
v->installEventFilter(m_key_press_handler.get());
}
QComboBox::showPopup();
}
void MyComboBox::hidePopup()
{
m_key_press_handler.reset(nullptr);
QComboBox::hidePopup();
}