How to re-enable close button in qt dialog - qt

I am using QProgressDialogand I disable the close (x) button when I start the progress bar.
progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowCloseButtonHint);
After the operation is complete in QProcess, in the finished slot, I am re-enabling the close button but it doesn't work. It instead closes the progress window. I have tried both lines below but it does the same.
progress->setWindowFlags(progress->windowFlags() | Qt::WindowCloseButtonHint);
or
progress->setWindowFlags(progress->windowFlags() | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint);
Why is it not working the way it should?

I figured out problem. You dialog is hidden and there are no way to solve that. You can only show() it again.
As doc said:
Note: This function calls setParent() when changing the flags for a
window, causing the widget to be hidden. You must call show() to make
the widget visible again.
From Qt source:
void QWidget::setWindowFlags(Qt::WindowFlags flags)
{
if (data->window_flags == flags)
return;
Q_D(QWidget);
if ((data->window_flags | flags) & Qt::Window) {
// the old type was a window and/or the new type is a window
QPoint oldPos = pos();
bool visible = isVisible();
setParent(parentWidget(), flags);
^^^^^^^^^
// if both types are windows or neither of them are, we restore
// the old position
if (!((data->window_flags ^ flags) & Qt::Window)
&& (visible || testAttribute(Qt::WA_Moved))) {
move(oldPos);
}
// for backward-compatibility we change Qt::WA_QuitOnClose attribute value only when the window was recreated.
d->adjustQuitOnCloseAttribute();
} else {
data->window_flags = flags;
}
}
And as doc said again:
Note: The widget becomes invisible as part of changing its parent,
even if it was previously visible. You must call show() to make the
widget visible again.
For example:
MainWindow w;w.show();
w.setWindowFlags(w.windowFlags() & ~Qt::WindowCloseButtonHint);
w.setWindowFlags(w.windowFlags() | Qt::WindowCloseButtonHint);
w.show();

Related

QWidget not closing - leaving empty window

I have a problem with a QWidget derivate (NewPayment). It's a simple window, with some controls and a QDialogButtonBox . It has 2 slots:
void NewPayment::on_buttonBox_accepted() {
//(some action going in here)
this->close();
}
void NewPayment::on_buttonBox_rejected() {
this->close();
}
When I click either OK or Cancel - the slot is triggered as expected. The problem is, that the window does not close. All the contents dissappears, and an empty window is left (window title is left).
The widget exists as a MDISubwindow, and is created like so:
void HurBudClientGUI::addNewPayment(int direction, int contractorid) {
foreach(QMdiSubWindow* it, this->ui.mainArea->subWindowList()) {
if ( NewPayment* np = qobject_cast<NewPayment*>( it->widget() ) ) {
if (np->getContractorID() == contractorid) {
this->ui.mainArea->setActiveSubWindow(it);
return;
}
}
}
NewPayment* np = new NewPayment(direction, contractorid, this);
np->setAttribute(Qt::WA_DeleteOnClose);
this->ui.mainArea->addSubWindow(np);
np->show();
}
The interesting part is, that when I either:
click on the 'X' in tre upper right corner
call QMdiArea::closeActiveSubWindow() from the main window
call QMdiArea::closeAllSubWindows() from the main window
the window is closed properly. I have overwritten a QWidget::closeEvent(QCloseEvent * event) for my class:
void NewPayment::closeEvent(QCloseEvent * event) {
qDebug() << "[" << __FUNCTION__ << "]:" << "event: " << event << "; sender:" << sender();
}
and it shows preety much the same event every time - no matter how I try to close it:
[ NewPayment::closeEvent ]: event: QCloseEvent(Close, 0x40bd64, type = 19) ; sender: QDialogButtonBox(0x4dfa7a8, name = "buttonBox") // I hit cancel
[ NewPayment::closeEvent ]: event: QCloseEvent(Close, 0x40b634, type = 19) ; sender: QObject(0x0) // I hit the 'X' in the window corner
[ NewPayment::closeEvent ]: event: QCloseEvent(Close, 0x40b468, type = 19) ; sender: QObject(0x0) // I hit "close active sub window" from parent window
[ NewPayment::closeEvent ]: event: QCloseEvent(Close, 0x40b454, type = 19) ; sender: QObject(0x0) // I hit "close all sub windows" from parent window
The best part is, that when I hit "cancel" (the windows is cleared, but stays open), and then click "X" or whatever - the window is closed, but the control does not pass through my NewPayment::closeEvent (i have a brakepoint there - and it does not fire) .
It works preety much the same in other windows. What is strange, that I'm preety sure that it worked previously (+- a week ago) for other windows (they closed after clicking OK ant performing all necessary operations) . I guess I will end up analyzyig diff from SVN, but maybe someone had similar issue? I have had very little sleep lately, so maybe I missed something trivial?
I will appreciate any help.
What do you expect, a widget is not a window. You get output from closing the widget, but that's not the same as closing the window.
You need to have a handle to the window if you want to close it. You could:
keep the pointer returned from addSubWindow()
create the window in advance, create the widget parented to the window, set the window's widget to the widget, and then use the widget's parent() to access the window.
I followed #ddriver 's suggestion and ended up with
void NewPayment::on_buttonBox_rejected() {
if (QMdiSubWindow* psw = qobject_cast<QMdiSubWindow*>(this->parent()) ) {
psw->close();
} else {
this->close();
}
}
Now it works as it was supposed to.

How to detect QTableWidget scroll source (code itself/user (wheel)/user (scrollbar))?

I'm writing a program using Qt 4.8 that displays a table (QTableWidget) filled with filenames and file's params. First an user adds files to the list and then clicks process. The code itself updates the contents of the table with simple progress description. I want the table by default to be scrolled automatically to show the last processed file and that code is ready.
If I want to scroll it by hand the widget is being scrolled automatically as soon as something changes moving the viewport to the last element. I want to be able to override the automated scroll if I detect that it was the user who wanted to change view.
This behavior can be seen in many terminal emulator programs. When there's a new line added the view is scrolled but when user forces the terminal to see some previous lines the terminal does not try to scroll down.
How could I do that?
Solution:
I created an object which filters event processed by my QTableWidget and QScrollBar embedded inside. If I spot the event that should turn off automatic scrolling I just set a flag and stop scrolling view if that flag is set.
Everything is implemented inside tableController class. Here are parts of three crucial methods.
bool tableController::eventFilter(QObject* object, QEvent* event)
{
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::Wheel:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
_autoScrollEnabled = false;
default:
break;
}
return QObject::eventFilter(object, event);
}
void tableController::changeFile(int idx)
{
[...]
if (_autoScrollEnabled)
{
QTableWidgetItem* s = _table.item(_engine.getLastProcessed(), 1);
_table.scrollToItem(s);
}
[...]
}
void tableController::tableController()
{
[...]
_autoScrollEnabled = true;
_table.installEventFilter(this);
_table.verticalTableScrollbar()->installEventFilter(this);
[...]
}
Thanks for all the help. I hope somebody will find it useful :)
Subclass QTableWidget and overload its wheelEvent. You can use the parameters of the supplied QWheelEvent object in order to determine if the user scrolled up or down.
Then use a simple boolean flag which is set (or reset) in your wheelEvent override. The method which is responsible for calling scrollToBottom() should then consider this boolean flag.
You will have to find a way to figure out when to set or reset that flag, e.g. always set it when the user scrolls up and reset it when the user scrolls down and the currently displayed area is at the bottom.
connect(_table->view()->verticalScrollBar(), &QAbstractSlider::actionTriggered, this, [this](int) {
_autoScrollEnabled = false;
});

Close Widget Window if mouse clicked outside of it

This is sort of a chicken and egg problem. I'd like my widget window to be closed when the mouse clicks outside. As I understand it, there will be no mouse events for my widget for a click occurring outside of it. There is a SetFocus slot, but where is its counterpart or focus loss? "focusOutEvent" doesn't get called for my class.
My widget window is a child window of a widget always shown on my main window and it's a "Qt::ToolTip", so I assume some problems could arise from that fact. Any way around that?
My Goal: I have a custom toolbar widget where buttons on it may have “drop down” widgets. These drop down widgets have no standard windows frame. I don’t want them to “steal” caption focus from the main window and I want them to disappear as soon as the user clicks ANYWHERE on the screen outside of their region. I have having serious difficulties finding a strategy that’s not compromise on Qt to get this done.
Am I missing something? (bet I am).
I used:
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
This seems to work well on OSX and Windows. My window appears correctly, does not steal the focus from my main window's caption, and the focus loss event is called correctly as soon as I click outside of it.
If your widget could have focus, and 'steal' the caption focus of some of your other widgets, it would have been easier. Something like this could work:
class ToolBarWidget : public QWidget
{
Q_OBJECT
public:
explicit ToolBarWidget(QWidget * parent = 0)
{
setFocusPolicy(Qt::ClickFocus);
}
protected:
void focusOutEvent(QFocusEvent * event)
{
close();
}
}
And when you create any of your widgets you'd do:
ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->show();
pWidget->setFocus();
Done! Well, I guess not quiet. first, you don't want the ToolBarWidget to get any focus in the first place. And second, you want for the user to be able to click anywhere and the ToolBarWidget to be hidden.
So, you may keep track of every ToolBarWidget that you create. For example, in a 'QList ttWidgets' member variable. Then, whenever you create a new ToolBarWidget, you'd do this:
ToolBarWidget * pWidget = new ToolBarWidget(this);
pWidget->installEventFilter(this);
pWidget->show();
and in your main widget class, implement the eventFilter() function. Something like:
bool MainWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::FocusOut ||
event->type() == QEvent::KeyPress ||
event->type() == QEvent::MouseButtonPress)
{
while (!ttWidgets.isEmpty()) {
ToolBarWidget * p = ttWidgets->takeFirst();
p->close();
p->deleteLater();
}
}
return MainWidget::eventFilter(obj, event);
}
And that will work. Because this way, even though your ToolTabWidgets aren't getting focus, some other widget in your main widget has focus. And once that changes (whether the user clicked out of your window, or on another control inside it, or in this case, a key or mouse button is pressed, the control will reach that eventFilter() function and close all your tab widgets.
BTW, in order to capture the MouseButtonPress, KeyPress etc. from the other widgets, you would either need to installEventFilter on them too, or just reimplement the QWidget::event(QEvent * event) function in your main widget, and look for those events there.
you can do this by using QDesktopWidget.h like this
void MainWindow::on_actionAbout_triggered()
{
AboutDialog aboutDialog;
//Set location of player in center of display
aboutDialog.move(QApplication::desktop()->screen()->rect().center() -aboutDialog.rect().center());
// Adding popup flags so that dialog closes when it losses focus
aboutDialog.setWindowFlags(Qt::Popup);
//finally opening dialog
aboutDialog.exec();
}
This is what worked for me in order to not steel the focus from the main application:
.h
bool eventFilter(QObject *obj, QEvent *event) override;
.cpp
bool Notification::eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::MouseButtonPress)
deleteLater();
return QObject::eventFilter(obj, event);
}
...
// somewhere else (i.e. constructor, main window,...)
qApp->installEventFilter(this);
OP's own answer is great for Qt versions below 4.8, but as they mention in their answer, it does not work for versions above that. The Qt::Popup widget will not disappear when the mouse is clicked outside of the widget, and it will sink all of the input that would normally close it.
Upon further investigation, this is only an issue for non-dialog widgets. A QDialog using Qt::Popup will properly close when the user clicks outside of it, but any other QWidget, like a QFrame, will not. So in order to work around this behavior change in Qt 4.8, all that is necessary is to wrap the widget in a QDialog.

How to tell the mouse button using QApplication::mouseButtons() in a "click" slot?

I have a QMainWindow, and want to handle the "clicked" signal from a smaller widget (such as tableview) inside it.
Originally I connect the signal to a slot of this QMainWindow, this is the most common approach.
Now I need to tell which mouse button is clicked, and do different things for left and right button, I found that the "clicked" signal don't have the mouse event information.
I tried to implement the "mousePressEvent" function,but there are still some problem. if the mouse action is acted on the smaller widget, the MainWindow won't go into its mousePressEvent.
Some document says that we can tell the button by QQApplication::mousebuttons()
http://bugreports.qt-project.org/browse/QTBUG-1067
and I also found some sample code. However, this is for "press event", but I want to get the mouse button for "click event".
Follows is the sample code :
connect(moduleTree,SIGNAL(itemPressed(QTreeWidgetItem *, int)),this,SLOT(SlotItemClicked(QTreeWidgetItem *, int)));
void CGuiMainwindow::SlotItemClicked(QTreeWidgetItem *item, int column)
{
if (qApp->mouseButtons() == Qt::LeftButton)
{ return; }
if (qApp->mouseButtons() == Qt::RightButton)
{
......
}
}
When I try to do this, neither of the 2 if statements will be satisfied, I don't know why. the qApp->mouseButtons() always return 0, how can I get the clicked mouse button by QApplication::mouseButtons?
In my code, the slot looks like that :
void clickItem( const QModelIndex & idx){.....}
You get 0 becouse clicked is emited after mouse release, not at mouse press. What do you want to achieve ? Maybe try settings on you widget contextMenuPolicy to custom, and than connect to signal contextMenuRequested (for the right click) and clicked for the left click ?
for "connect" use this:
connect(moduleTree,SIGNAL(itemClicked(QTreeWidgetItem *,int )),this
,SLOT(SlotItemClicked(QTreeWidgetItem *, int)));
define a global flag:
public:
Qt::MouseButton mouseClickedBtnFlag;
and then reimplement "mouseReleaseEvent":
CGuiMainwindow::mouseReleaseEvent ( QMouseEvent * event )
{
mouseClickedBtnFlag = event->button();
}
and then:
void CGuiMainwindow::SlotItemClicked(QTreeWidgetItem *item, int column)
{
if (mouseClickedBtnFlag == Qt::LeftButton)
{ return; }
if (mouseClickedBtnFlag == Qt::RightButton)
{
......
}
}
Qt::MouseButtons is a QFlags type. You can't test it with == operator. Use & operator for testing:
if(QApplication::mouseButtons() & Qt:LeftButton) {
...
}

Getting MouseMoveEvents in Qt

In my program, I'd like to have mouseMoveEvent(QMouseEvent* event) called whenever the mouse moves (even when it's over another window).
Right now, in my mainwindow.cpp file, I have:
void MainWindow::mouseMoveEvent(QMouseEvent* event) {
qDebug() << QString::number(event->pos().x());
qDebug() << QString::number(event->pos().y());
}
But this seems to only be called when I click and drag the mouse while over the window of the program itself. I've tried calling
setMouseTracking(true);
in MainWindow's constructor, but this doesn't seem to do anything differently (mouseMoveEvent still is only called when I hold a mouse button down, regardless of where it is). What's the easiest way to track the mouse position globally?
You can use an event filter on the application.
Define and implement bool MainWindow::eventFilter(QObject*, QEvent*). For example
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
}
return false;
}
Install the event filter when the MainWindows is constructed (or somewhere else). For example
MainWindow::MainWindow(...)
{
...
qApp->installEventFilter(this);
...
}
I had the same problem, further exacerbated by the fact that I was trying to call this->update() to repaint the window on a mouse move and nothing would happen.
You can avoid having to create the event filter by calling setMouseTracking(true) as #Kyberias noted. However, this must be done on the viewport, not your main window itself. (Same goes for update).
So in your constructor you can add a line this->viewport()->setMouseTracking(true) and then override mouseMoveEvent rather than creating this filter and installing it.

Resources