prevent focus out for a QWidget - c++

I'd like to write a QDialog lookalike class. I've managed to filter out mouse events to non-dialog widgets pretty well, but I still have a problem with focus. As the QDialog lookalike class is just a usual widget it can lose focus by way of key presses (tabs). Hence widgets not related to the QDialog lookalive, that I cannot click, but are focus-able, may get the focus. Is there a neat way to prevent the user from focusing away from my dialog lookalike's child widgets?

Assuming that your QDialog-like widget is an individual window, I think you are looking for QWdiget::setModal( true ). It prevents widgets in other windows of your application to receive any input events.

Here is a solution:
// somewhere in your code
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
SLOT(focusChanged(QWidget*,QWidget*)));
void MyDialog::focusChanged(QWidget*, QWidget* to)
{
if (!isAncestorOf(to))
{
QWidget* widget(qobject_cast<QWidget*>(children().front()));
widget->setFocus(Qt::OtherFocusReason);
QKeyEvent event(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier);
qApp->sendEvent(widget, &event);
}
// else do nothing
}
Assuming the child is an instance of QFrame or QWidget.

Related

mouse tracking does not work In the widget in the main widget

I implemented showing button when mouse is under QTextEdit in the QWidget.
when I test code, I found it sometimes didn't work showing button
bug cause is that MouseMoveEvent doesn't work in Qwidget when I move in the QTextEdit.
I don't understand why MouseMoveEvent doesn't work...
here is my code
this->setMouseTracking(true); // this is QWidget
ui.textEdit_log->setMouseTracking(true); // this is textEdit
void QWidget::mouseMoveEvent(QMouseEvent* event) // reImplemented mouseMoveEvent
{
if (ui.textEdit_log->underMouse())
{
m_pButton->show();
}
else
{
m_pButton->hide();
}
}
Mouse tracking for QTextEdit will only work by subclassing QTextEdit and reimplementing it's mouseMoveEvent() from where you can pass the event up to the parent's mouseMoveEvent().
OR
Instead of using ui.textEdit_log->setMouseTracking(true) use ui.textEdit_log->setAttribute(Qt::WA_TransparentForMouseEvents) this will make your textedit_log invisible for mouse events. In this case you can use the childrenRect() function of your QWidget to determine if your mouse is over the textedit_log.
As far as software design goes, subclassing is a conceptually very heavy operation - it indicates a very deep coupling. It should be used with due concern for the maintainability impact it has. An almost no case should subclassing be used to merely observe the state of an object! - any API that forces you to do that is essentially broken.
Qt provides convenience "event listener" methods, but these methods are not meant to be used as external observers, i.e. you should never override them if all you want is to observe the objectfrom outside. The event filters should be used instead, since they introduce the weakest of possible couplings, and are very generic.
The mouse tracking events will be sent to the widget that tracks the mouse cursor - the editor widget. So no other widget will hear them. You have to implement QObject::filterEvent in a class of your choice (whatever class is convenient), then install that event filter on the editor widget. Your filter will intercept all events sent to the editor, including mouse move events. You should not subclass QTextEdit - use event filters instead!
In any case, the idea to do this via mouse movement tracking is overkill and has some overhead. Instead, look for QEnterEvent and QLeaveEvent - those should be sent to the editor widget without mouse tracking enabled. But still - do not intercept those via any subclassing!
Do note that QWidget is-a QObject, so the event filter can be a widget class, or a non-widget class.
I solved this problem like this
void Log::enterEvent(QEvent* event)
{
m_pButton->show();
}
void Log::leaveEvent(QEvent* event)
{
m_mousePos = this->mapFromGlobal(QCursor::pos());
QPoint logPos = mapToGlobal(this->normalGeometry().bottomLeft());
QPoint buttonPos = m_pButton->pos();
QPoint buttonRelatviePos = buttonPos - logPos;
// hide button when button is not in the button pos
if ((m_mousePos.x() < buttonRelatviePos.x()) or (m_mousePos.x() > buttonRelatviePos.x() + m_pButton->width()) or
(m_mousePos.y() < buttonRelatviePos.y()) or (m_mousePos.y() > buttonRelatviePos.y() + m_pButton->height()))
{
m_pButton->hide();
}
}

QWidget listening to other QWidgets events

To explain the situation, I have a QMainWindow with plenty of stuff inside.
My goal is to make some kind of a notification that appears at the bottom right corner of this window.
The notification widget must be over every other widgets.
So far I've created that Notification widget that inherits from QWidget.
By setting the QMainWindow as its parent I've got it 'floating' on top of every other widgets. It doesn't belong to any layout.
Now the big issue is to make it stick to the bottom right corner. I managed to place it manually but what I want now is to move it upon a resize event.
Unfortunately since it doesn't belong to any layout it doesn't receive any resizeEvent, so I can't overload the resizeEvent() function.
An attempt was to make the QMainWindow emit a signal in its resizeEvent method but I am not really happy with this method. Especially because in the Notification widget constructor I have this connect(static_cast<MainWindow*>(parent), &MainWindow::resized, this, &Notification::update_position);, and it kind of breaks the genericity of the widget since its parent must be a MainWindow widget.
So the question is how can one widget react to another widget event ?
In my case here how can the notification widget be notified when its parent is getting resized ?
And I forgot to mention that I don't want the MainWindow widget to know anything about this notification widget. The notification widget can be managed on its own. You just call something like new Notification(parent) and it will do its stuff on its own
Any ideas are welcome,
Thanks in advance :)
You can install the notification widget as an event filter for its parent, e.g.:
NotificationWidget::NotificationWidget(QWidget *parent) : QWidget(parent) {
parent->installEventFilter(this);
}
bool NotificationWidget::eventFilter(QObject *obj, QEvent *event) {
// normal event handling
bool ret = QObject::eventFilter(obj, event);
if (event->type() == QEvent::Resize) {
// Parent resized, adjust position
}
return ret;
}

How to receive scrollbar mouse events for QGraphicsView

In qt widget QGraphicsView I want to detect mouse-press, mouse-release and mouse-move events.
I derived a class from QGraphicsView and I have overridden the following functions:
mousePressEvent(QMouseEvent *event)
mouseReleaseEvent(QMouseEvent *event)
mouseMoveEvent(QMouseEvent *event)
Now I can detect these mouse events almost everywhere, except for the area where are the scroll bars, which are part of QGraphicsView.
I want to be able to catch these events and move the scroll bar manually.
Edit:
I am trying to simulate second mouse in windows environment sending WM_LBUTTONDOWN,... events. I would like to be able to detect this events also for scroll bars in QGraphicsView.
Besides detecting the events I would like to know the event->x() and event->()y position.
QGraphicsView inherits the QAbstractScrollArea class. Thus the scrollbar widgets can be accessed with the QAbstractScrollArea::verticalScrollbar and QAbstractScrollArea::horizontalScrollbar methods.
You don't even have to use event filters once you can access the scrollbar object as the QScrollBar inherits QAbstractSlider and thus offers the signals:
QAbstractSlider::sliderPressed()
QAbstractSlider::sliderReleased()
So you can just connect them to your slots like:
connect(horizontalScrollBar(), SIGNAL(sliderPressed()), this, SLOT(doSomething()));
connect(horizontalScrollBar(), SIGNAL(sliderReleased()), this, SLOT(doSomething()));
If you need the position of the event, you will have to use event filter though. You can install an event filter on the widget object.

How to keep a QSlider activated to allow movements with arrows at any time

I would like to be able to move a QSlider with arrows of the keyboard at any time.
I want to be able to click anywhere on the QWindow and keep QSlider activated to move the cursor with the arrows.
My problem is that move the cursor with arrows is only allowed if we click on the QSlider before.
I hope my question is clear enough.
Does anyone know how to move the QSlider with arrows of the keyboard without clicking on the QSlider before please?
There are two approaches:
In Qt terms, you'd like to give slider the focus. Widgets have the setFocus method, so you need to call slider->setFocus(Qt::OtherFocusReason).
Since you want the slider to get focus whenever the underlying window has focus, you need to put the setFocus call in your implementation of focusInEvent for the parent widget.
You can forward the key events from the underlying widget to the slider. In the parent widget, reimplement keyPressEvent and keyReleaseEvent. When the desired keys are detected, forward them to the slider:
// same for keyReleaseEvent!
void MyWindow::keyPressEvent(QKeyEvent * ev) {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
slider->event(ev);
}
}

Setting a QDialog to be an alien

In a standalone GUI application where I don't have a windowmanager nor a composite manager I want to display a QDialog to the user to ask for values.
The dialog is quite big, so I want to make it translucent so that the user can look through it to see what happens in the application while the dialog is shown.
The problem is that for translucency of X native windows, there needs to be a composite manager. Qt internal widgets can be painted translucent because they don't correspond to native X windows (aliens) and are completely only known to Qt.
Is there a way to make the background of a QDialog translucent without having a composite manager running? Perhaps making it a normal child widget/alien of the application's main window? Is there a better alternative to this?
I don't know of any way of turning a QDialog into a normal child widget. Looking at the Qt for X11 code, I can't figure out a way of not setting the Qt::WindowFlags passed to the QWidget (parent) constructor so that it would be a plain widget and not a window of its own (but I could be wrong, didn't spend a lot of time on that).
A simple alternative is to use a plain QWidget as your container, instead of a QDialog. Here's a sample "PopupWidget" that paints a half-transparent-red background.
#include <QtGui>
class PopupWidget: public QWidget
{
Q_OBJECT
public:
PopupWidget(QWidget *parent): QWidget(parent)
{
QVBoxLayout *vl = new QVBoxLayout;
QPushButton *pb = new QPushButton("on top!", this);
vl->addWidget(pb);
connect(pb, SIGNAL(clicked()), this, SLOT(hide()));
}
public slots:
void popup() {
setGeometry(0, 0, parentWidget()->width(), parentWidget()->height());
raise();
show();
}
protected:
void paintEvent(QPaintEvent *)
{
QPainter p(this);
QBrush b(QColor(255,0,0,128));
p.fillRect(0, 0, width(), height(), b);
}
};
To show it, call it's popup() slot which will raise it to the top of the widget stack, make it as large as its parent, and show it. This will mask all the widgets behind it (you can't interact with them with the mouse). It hides itself when you click on that button.
Caveats:
this doesn't prevent the user from using Tab to reach the widgets underneath. This could be fixed by toggling the enabled property on your "normal" widget container for example. (But don't disable the PopupWidget's parent: that would disable the popup widget itself.)
this doesn't allow for a blocking call like QDialog::exec
the widgets in that popup won't be transparent, you'd have to create custom transparent-background versions for all the widget types you need AFAIK.
But that's probably less of a hassel than integarting a compositing manager in your environment.