Qt moving a QWidget without triggering the moveEvent method? - c++

I am trying to set a widget to remain fixed at the location where it is created initially and never move. I am doing so by setting its position to its old position in an overridden moveEvent method. The problem is that the only way to move it is to...call move, which triggers another moveEvent, entering an infinite loop. Is there a way to set the position directly and bypass any events?

bool m_firstTimeMove; // class member for QMyWidget
QMyWidget::QMyWidget(QObject* parent) : QWidget(parent),
m_firstTimeMove(true)
{
}
void QMyWidget::moveEvent(QMoveEvent* event)
{
if (m_firstTimeMove)
{
onlyForFirstTimeMove(); // do one time move action
m_firstTimeMove = false;
}
event->accept();
}

Related

How to know when the VerticalScrollBar is showing?

I need to know when the verticalScrollBar of my QTableWidget is being shown.
I am currently using the following instruction:
Header:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QDebug>
#include <QWidget>
#include <QScrollBar>
namespace Ui {
class MyClass;
}
class MyClass: public QWidget
{
Q_OBJECT
public:
explicit MyClass(QWidget *parent = 0);
~MyClass();
private:
void populateTable(QVector<QString> content);
private:
Ui::MyClass *ui;
};
#endif // MYCLASS_H
Populate table function:
void MyClass::populateTable(QVector<QString> content)
{
while( ui->myTableWidget->rowCount() > 0 )
{
ui->myTableWidget->removeRow(0);
}
QTableWidgetItem* item;
for (int row = 0; row < content.length(); ++row)
{
ui->myTableWidget->insertRow(row);
item = new QTableWidgetItem( QString::number(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 0, item);
item = new QTableWidgetItem( content.at(row) );
item->setTextAlignment(Qt::AlignCenter);
ui->myTableWidget->setItem(row, 1, item);
}
qDebug() << "This : " << this->isVisible();
qDebug() << "QTableWidget : " << ui->myTableWidget->isVisible();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible(); // <-HERE
}
Output:
// Called from the constructor
This : false
QTableWidget : false
VerticalScrollBar : false
// Called by a button pressed
This : true
QTableWidget : true
VerticalScrollBar : true
This : true
QTableWidget : true
VerticalScrollBar : false
But it returns a wrong value. When the ScrollBar is visible it returns false and when it is not visible it returns true. Note: myTableWidget (QTableWidget) is always visible.
Is there any other way that I can do this?
I'm using Qt version 5.3.2
In general the code you are using is supposed to work - checked on Qt 5.3.0.
However, you must be sure that when you are making the call the QTableWidget itself is visible.
For example if you make the call inside the MainWindow constructor you will certainly get a false answer. Only after the form is shown the call to isVisible() on particular scrollbar would return the correct value.
EDIT:
With your code pasted I was able to reproduce the issue. I needed to go through the Qt code a bit to see whats going on. Basically it turns out that for QTableView which is parent class of QTableWidget scroll bar values are updated via updateGeometries (do not confuse it with the regular updateGeometry the one I'm mentioning is protected). Internally this method is called either directly or the event is processed through the event loop. In short, it depends on whether you add columns or rows.
In your example, if you insertColumn instead of insertRow (and switch the arguments in setItem) after checking the visibility of horizontalScrollBar you will get the proper result right away.
I could confirm this by subclassing the QTableWidget and overriding event method. It shows that when adding columns following events are executed: MetaCall (invoke call) and LayoutRequest. On the other hand, when adding rows first event passed is Timer.
I'm not Qt implementer so I'm not sure what is the purpose the difference. However, this info helps solving your problem in a more elegant way.
You can implement MyTableWidget which overrides the event method.
class MyTableWidget: public QTableWidget
{
Q_OBJECT
public:
bool event(QEvent *e) override
{
const bool result = QTableWidget::event(e);
if (e->type() == QEvent::LayoutRequest)
{
// call what you need here
// or emit layoutUpdated(); and connect some slots which perform
// processing dependent on scrollbar visibility
}
return result;
}
signals:
void layoutUpdated();
}
However, such event might get called in other situations not only when the view needs to be updated due to model data updates.
Another solution would be to avoid overriding the event method but creating your own method to trigger the required updates. For example:
void MyTableWidget::updateLayout()
{
QEvent ev{QEvent::LayoutRequest};
QTableWidget::updateGeometries();
QTableWidget::event(&ev);
}
This would call directly updateGeometries which recalculates scrollbar min/max values and perform a direct event method call for LayoutRequest (without processing through eventloop). Which if I'm correct indirectly updates scrollbar visibility.
Calling this method before checking the visibility should also fix your problem.
ui->myTableWidget->updateLayout();
qDebug() << "VerticalScrollBar : " << ui->myTableWidget->verticalScrollBar()->isVisible();
// prints "VerticalScrollBar : true false"
I wanted to get notified via a signal, when a QScrollBar gets visible. I'm writing this answer here, since this is the first google result I got and no result had a proper answer.
First of all, there is no signal for changed visibility, so we will use valueChanged.
Now you can add a function that checks if the scrollbar isVisible() or not.
Problem: It won't work (and someone with more knowledge in Qt could probably explain why, I sadly cannot).
The trick is, to use a QTimer (Python code):
self.horizontalScrollBar().valueChanged.connect(lambda x: QTimer.singleShot(0, some_func))

QToolbar force expand on too many QActions

Hi all is there any way to automatically expand a QToolbar if there is too many QActions in?
Using Qt version 5.4.1 C++11
Ive tried :ui->mainToolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred)
But this only expands it horizontally. I need it to expand vertically like the Expand button does.
Always expanding a toolbar vertically is not possible as far as I know (never seen it). A solution would be to add multiple toolbars. This way, you can arrange them one under the other.
What you can try is to add a custom widget to the toolbar that grows horizontally. This was proposed here by using a QScrollArea... Not sure whether this is exactly what you want, but it may work good enough.
This is how you can make a function to expand/retract a QToolbar. Firstly using a Forloop get all the child widgets from the QToolbar. You can use a Bool to lock to only get the first Widget which is the Expanding button/Action.
bool get_first_action{true};
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(get_first_action)
{
get_first_action = false;
// This is the expanding action!
m_action_expand = widget;
}
}
Or you can do this which is probably a bit safer.
for(QWidget* widget : ui->myToolBar->findChildren<QWidget*>())
{
if(widget->objectName() == "qt_toolbar_ext_button")
{
// This is the expanding action!
m_action_expand = widget;
}
}
Once you have the sneaky expanding action assign it to a member varible
// Make sure to initialize this in the constructor!
// m_action_expand = new QWidget(this // parent)
QWidget* m_action_expand;
Now create a handy function with a good name;
void MainWindow::forceToolbarExpand()
{
// Grab the position of the expanding action/widget
QPointF pos(m_action_expand->pos());
// Create a fake/dummy event that replicates the mouse button press
QMouseEvent event_press(QEvent::MouseButtonPress, pos, Qt::LeftButton,0, 0);
// Create a fake/dummy event that replicates the mouse button release
QMouseEvent event_release(QEvent::MouseButtonRelease, pos, Qt::LeftButton,0, 0);
// These two events together will fire the QAction::Toggled signal.
// Make sure to send the events!
QApplication::sendEvent(m_action_expand, &event_press);
QApplication::sendEvent(m_action_expand, &event_release);
}
And there we have it your QToolbar, if it can be expanded/retracted now will when you call this function. I'm not too sure if you can directly Moc/fake the toggled event but you can try it. I know this method works so yeah.

how to get touchevent for mainwindow in qt

I am writing an application for arm board. I have a mainwindow and I need to get the touch co-ordinates for single touch. Due to lack of Qt knowledge I am facing a problem of getting the touch event.
As per the QTouchEvent Class document, I have declared a slot as below.
#include <QTouchEvent>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(int x, int y, QWidget *parent = 0);
public slots:
void touchEvent(QTouchEvent *ev);
};
and in mainwindow.cpp I redefine it as below. The FFLabel[1] is already declared by me and I am writing it to confirm if the event is received.
void MainWindow::touchEvent(QTouchEvent *ev)
{
ui->FFLabel[1]->setText(QString("Event"));
switch (ev->type())
{
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
{
}
}
}
I am not able to get the touchEvent. Can someone please help me.
I have verified the /dev/input/event2 and am receiving the event there.
QWidget has a number of protected virtual members, e.g., QWidget::mousePressEvent. They are called when the event occurs.
Usually one overrides such a function to run some code when a certain event happens.
The problem is, that, unlike for mouse press, there is no such virtual function QWidget::touchEvent (as of Qt 5.12). That is, you can't override it and it cannot be called when a touch event happens as QWidget is not aware of such a method.
C++11 introduced the override-keyword. If you had used it, you would have noticed that issue.
In order to receive touch events, you must override the more general QWidget::event method. Then filter for the events you're actually interested in:
bool MainWindow::event(QEvent* event)
{
switch (event->type()) {
case QEvent::TouchBegin:
qDebug() << "touch!";
return true;
default:
// call base implementation
return QMainWindow::event(event);
}
}
Note that you must state setAttribute(Qt::WA_AcceptTouchEvents); in order to receive any touch events (put it in the MainWindow-constructor).
You can read more details in the docs.
Events aren't slots. Declare it as a protected function.
protected:
void touchEvent(QTouchEvent *ev);
Edit: And you didn't put anything in your switch statement to show if you get the event.
void MainWindow::touchEvent(QTouchEvent *ev)
{
switch (ev->type())
{
case QEvent::TouchBegin:
ui->FFLabel[1]->setText("Event began.");
break;
case QEvent::TouchEnd:
ui->FFLabel[1]->setText("Event ended.");
break;
case QEvent::TouchUpdate:
{
ui->FFLabel[1]->setText("Event updated.");
break;
}
}
}
Edit: If it's still not working, it means your mainwindow isn't accepting touch events. You need to do this in your mainwindow's constructor:
setAttribute(Qt::WA_AcceptTouchEvents, true);
Disclaimer:
I had a similar issue and this is not a solution but may be a workaround depending on what you want to do.
In my case, the touch events were interpreted as mouse events so, MainWindow::touchEvent was never called but, the mouse events handler were.
I used MainWindow::mousePressEvent to get the touch point and MainWindow::mouseMoveEvent to get the drag.
This does not handle the multi-touch (it works for the separate contact points but you will have only one move event for all your points)
From Qt5.4, one may try (I didn't test as I'm stuck with Qt4), in the mouse events handler, to check the MouseEventSource If it's MouseEventSynthesizedBySystem it means from the documentation that:
the mouse event was synthesized from a touch event by the platform.
One can find more details checking this solution provided by Chirag Sachdeva in this post
And of course, with regards to the OP code, as user4516901 said, events aren't slots.

QGraphicsItem setPos() not triggered

And again, trying to implement bezier curves redactor. There is
class BezierNode : public QGraphicsItem
BezierNode::BezierNode(QPointF point, Type type) : QGraphicsItem()
{
setPos(point);
setFlags(ItemIsMovable | ItemSendsScenePositionChanges | ItemSendsGeometryChanges);
}
It properly moves around in scene on mousePress + mouseMove, and I can catch events in itemChange() for some additional acting with control points. In QGraphicsItem::mouseMoveEvent() (according to Qt source on gitorious) there is a call to item->setPos(...). However, if I try to reimplement BezierNode::setPos(..), it's never triggered on moving object.
void BezierNode::setPos(const QPointF &pos) {
qDebug() << "setPos " << pos;
m_point = pos;
QGraphicsItem::setPos(pos);
}
In my case setPos() triggers only in constructor (there I call it manually). Yes, it moves in scene properly, I can get its position with pos() and use it everywhere instead of m_point, but I want to understand, what happens there.
Thanks in advance.
QGraphicsItem::setPos() is not virtual, so you can't override it. That's why BezierNode::setPos() will never be called.

form properties not called during resize

I have a .NET form with a boolean property called _isResized, which is set to true when the SizeChanged event is called and is set to false when the bool isResized() function is called.
ref class net_window : public System::Windows::Forms::Form
{
private:
bool _isResized;
public:
net_window(void)
: _isResized(false){
InitializeComponent();
}
void InitializeComponent(void){
this->SizeChanged += gcnew EventHandler(this, &net_window::eventResized);
}
bool isResized(void){
bool temp = _isResized;
_isResized = false;
return temp;
}
Void eventResized(Object^ sender, EventArgs^ e){
_isResized = true;
}
};
I also have an native C++ class called window which acts as a layer around the .NET form. This class is updated every frame of the main loop and uses the bool isResized() function of the form to see if the size has changed.
class Window
{
private:
void* _net_window_handle;
Window(){
_net_window_handle = new gcroot<net_window^>;
(*(gcroot<net_window^>*)_net_window_handle) = gcnew net_window;
}
~Window(){
delete _net_window_handle;
}
void update(void)//Called every frame.
{
if( (*(gcroot<net_window^>*)_element)->isResized() )
//Do stuff.
}
};
The delegate added to SizeChanged is called whenever the form is being resized, so _isResized is set to true as soon as the form size changes, but for some reason the thread of the main loop freezes whenever the window class uses the bool isResized() function, until the user has released the edge of the form and thus the resizing has finished.
Is it not possible to access .NET form values as long as it is being resized. Is there an alternative?
Thanks
EDIT
I use a main loop which calls System::Windows::Forms::Application::DoEvents() every frame. When the thread of the loop enters this function and the main window is resizing, it freezes. Is there a way to avoid this problem.
DoEvents strikes again. Windows pumps a modal message loop when the user starts dragging a window edge. Which means that your DoEvents loop isn't running anymore. Use a timer instead, SetTimer with a 15 or 31 msec interval is about right. Having your code respond to the Resize event directly of course highly preferred.
Thanks Hans Passant