I just want to disable form's close button while doing a task(by QThread), So I connected the thread's signal "started()" and "finished()" to my two slots, for controlling my form's close button.
//...
m_pTestThread = new TestThread();
connect(m_pTestThread, SIGNAL(started()), this, SLOT(onThreadStart()));
connect(m_pTestThread, SIGNAL(finished()), this, SLOT(onThreadFinish()));
m_pTestThread->start();
//...
void QTest::onThreadStart()
{
this->setWindowFlags(this->windowFlags() & (~Qt::WindowCloseButtonHint));
}
void QTest::onThreadFinish()
{
this->setWindowFlags(this->windowFlags() | Qt::WindowCloseButtonHint);
}
After the thread started, my form was hidden... that is strange.
So I call show() after setWindowFlags() function to avoid this problem,
but still don't know why this happened...
Is this the expected behaviour? Should I call show() after setWindowFlags()?
Check the documentation for setWindowFlags here:
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..
Related
I need to block specific buttons on an MMI.
I implemented a button blocking function in a subclass of QPushButton.
For this, I used the clicked() signal and blocked the button with blockSignals(true).
This means that with each button clicked on my MMI, 2 SLOTS are always called.
But when calling the blocking of a specific button, I get the first SLOT (clicked()) of my subclass, in which I block the button, then I then arrive in the original SLOT linked to this button, which is still called despite the blocking (the first time only).
How can I in my QPushButton subclass know the subsequent SLOTs linked to this button and avoid them (delete them)?
void QbtnStandardButton::slotButtonClicked(void)
{
if (modeProtection)
{
// Special mode to protect/unprotect the button
if (isProtected())
{
// Reset the protection
this->blockSignals(false);
}
else
{
// Set the protection: button will be unclickable
this->blockSignals(true);
}
modeProtection = false;
}
if (isProtected())
{
QMessageBox *pMsgBox = new QMessageBox(QMessageBox::Information,
"Protection",
"This button is protected!",
QMessageBox::Ok);
pMsgBox->exec();
pMsgBox->deleteLater();
// Here: remove subsequent SLOT of this button ?
}
}
I think it's very difficult if not impossible to find SLOTS linked to a button.
I worked around the problem by using an eventFilter() instead of a SIGNAL() in my base class.
In this case, I can filter the "clicked()" event before it is reissued.
Suppose I have some action to happen. For that I can create a QAction object and connect its triggered() signal to the slot that executes the desired function. Also, I can have a shortcut associated with the action; by changing the shortcut I'll be able to execute the same action with that shortcut.
My problem now is that the "shortcut" I wanna set to the action, contains also a mouse button press (and mouse events cannot be assigned to action shortcuts); say I want Shift+Left mouse button. Maybe this sounds a little bit harsh but bear with me.
What do I need? Well, I have a button, and an action (say "execute a script"). I want the script to execute when Shift+Left click is clicked, and I want this "shortcut" to be customized, i.e. the user should be able to change to shortcut to, say Ctrl+Left click (from some GUI element, e.g. button text), and now Ctrl+Left click should execute the script.
How can I achieve this?
Note: I as a user would expect an action triggered by a mouse button to be position dependent. If so, the following gets a bit simpler.
Qt doesn't have an option to specify such a shortcut.
You can roll your own by reacting to mouse events:
Maybe you have an event handler mousePressEvent(),
or a generic eventFilter(QObject *obj, QEvent *evt),
or utilize QApplication::notify
Whichever, at some place you need to catch a QMouseEvent *mouseEvt.
Choose the widget (or qApp) that is as outmost as needed.
There, compare mouseEvt->button() and mouseEvt->modifiers() to your list of actions and trigger the selected action. When the user chooses another trigger method, adjust your list of actions.
Let's put this to practice:
class MainWindow : public QWidget {
Q_OBJECT
public:
QMap<QPair<Qt::MouseButton, Qt::KeyboardModifiers>, QAction*> mapMouseShortcuts;
QAction *pLaunchScript;
MainWindow() : QWidget() {
mapMouseShortcuts.insert(qMakePair(Qt::LeftButton, Qt::ControlModifier), pLaunchScript);
}
void mousePressEvent(QMouseEvent *me) {
QAction *action = mapMouseShortcuts.value(qMakePair(me->button(), me->modifiers()), Q_NULLPTR);
if(action != Q_NULLPTR) {
action->trigger();
me->accept(); // optional
}
// optional:
if(!me->isAccepted()) {
QWidget::mousePressEvent(me);
}
}
};
I am learning qt, and experimenting with examples from a textbook.
The original textbook code has the following, set up to save and close on the x button:
void MainWindow::closeEvent(QCloseEvent *event)
{
if (okToContinue()) {
writeSettings();
event->accept();
} else {
event->ignore();
}
}
i experimented with a simple exit in its menu - and it works:
void MainWindow::close()
{
if (okToContinue()) {
QApplication::quit();
}
}
But I want to take advantage of the already written closeEvent, so i replaced the code above with
void MainWindow::close()
{
QCloseEvent *event = new QCloseEvent();
closeEvent(event);
}
I get the checking for changes and saving app, implemented through the okToContinue function. But the application does not close.
i tried to follow through debugging and.. with my small understanding, it seems that there is a close signal being sent...
I don't have a good understanding of this, can somebody please help me figure out what am i doing wrong and how to fix it ?
(the sample code is from C++ GUI Programming with Qt 4, chapter 3)
You don't have to reimplement MainWindow::close() in your subclass.
From the Qt Docs:
...QCloseEvent sent when you call QWidget::close() to close a widget
programmatically...
So you just have to reimplement MainWindow::closeEvent(QCloseEvent *event) if you want to control this event.
This event fires when you click x or call close() from the code.
The closeEvent and related methods don't actually execute the action that happens when a given event is received. They merely allow you to act on the event and perhaps disable its further processing. The closing of the window is done within QWidget::event, where closeEvent is called from.
I have a Qt main window where I call another window, actually a kind of submenu wich contains parameters for the first one; here is a part of this main window :
void Ui_MainWindow::createSignals()
{
connect(actionDisk_galaxy, SIGNAL(triggered()), this, SLOT(ICMenu()));
}
void Ui_MainWindow::ICMenu()
{
qmenu = new QMainWindow;
DiskMenu = new Ui_DiskGalMenu;
DiskMenu->setupUi(qmenu,this);
setInitialDiskMenuPosition(qmenu, this);
qmenu->show();
}
As you can see, I call another QMainwindow ("qmenu"); here's the code of this new window (whose type is "Ui_DiskGalMenu"):
void Ui_DiskGalMenu::createMenuSignals()
{
connect(pushButton_4, SIGNAL(clicked()), this, SLOT(closeMenu()));
}
void Ui_DiskGalMenu::closeMenu()
{
close(true);
}
After setting parameters in this sub-menu, I would like to close it with a pushButton (here "pushButton_4").
My problem is that when I click on "pushButton_4", this window doesn't close.
I have also tried to reimplement closeEvent but without success.
This function call looks like a mistake:
close(true);
QWidget::close() doesn't take any parameters. So what you're doing here, is call the close(int) function of the C library (for closing file descriptors.) bool is implicitly converted to int, so you end up with this call:
::close(1);
Which is (probably) closing stderr.
You can see what's happening if you change the above to:
this->close(true);
You should get a compilation error since no such function exists. So the correct call would have been:
this->close();
However, QWidget::close() is already a slot, so you don't need the Ui_DiskGalMenu::closeMenu() function at all. All you need is connect to the close() slot to begin with:
connect(actionDisk_galaxy, SIGNAL(triggered()), this, SLOT(close()));
If you need to do more stuff when the window closes, you can override closeEvent() which will be called before the window gets closed.
In my MainWindow, I have a push button and a menu bar item whose signals are both connected to the same slot. In the slot function, I have written:
mainWindow->setCursor(QCursor(Qt::WaitCursor));
This works as expected when the slot function is invoked via the button; however, when the same function is invoked from the menu, the wait cursor doesn't appear. Any idea why?
I also considered using QApplication::setOverrideCursor; however, that causes other problems.
Any recommendations? Thanks!
(I am using Qt 4.7 and doing my development on Windows 7 using Qt Creator with the default MinGW compiler.)
Here's more detail.
in MainWindow constructor: this->setCursor(Qt::CrossCursor);
signal/slot connections:
QObject::connect(button, SIGNAL(clicked()), MainWindow, SLOT(showMessageBox()));
QObject::connect(action, SIGNAL(triggered()), MainWindow, SLOT(showMessageBox()));
showMessageBox function:
void MainWindow::showMessageBox()
{
this->setCursor(Qt::WaitCursor);
// display wait cursor briefly before showing message box
for (int i = 0; i < 1<<30; ) {++i;}
QMessageBox msgBox;
msgBox.setText("Hello!");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setCursor(Qt::PointingHandCursor);
msgBox.exec();
this->setCursor(Qt::CrossCursor);
}
When showMessageBox is invoked with 'button', the wait cursor is displayed as expected.
When showMessageBox is invoked through 'action', the wait cursor does not appear; instead the cursor changes from Qt::CrossCursor to a Qt::ArrowCursor as soon as the user selects the 'action' menu item, and then changes to Qt::PointingHandCursor once the message box opens. The wait cursor never appears.
Your code is synchronous and uses a delay loop. When you're in the delay loop, there's no way for any Qt code to execute. A cursor change requires the event loop to be spinning -- so it appears from the symptoms you give.
Here's how to do it correctly -- remember, if you use delays/sleeps and other blocking calls in your GUI code, your users will hate you, and rightly so. Using exec() in message/dialog boxes is also bad style. Your application is asynchronous, code it so. Make sure your slots are declared as such (in the protected slots: section of MainWindow declaration).
void MainWindow::showMessageBox()
{
this->setCursor(Qt::WaitCursor);
QTimer::singleSlot(200, this, SLOT(slot1()); // fire slot1 after 200ms
}
void MainWindow::slot1()
{
QMessageBox * msgBox = new QMessageBox(this);
msgBox->setText("Hello!");
msgBox->setStandardButtons(QMessageBox::Ok);
msgBox->setCursor(Qt::PointingHandCursor);
msgBox->show();
connect(msgBox, buttonClicked(QAbstractButton*), SLOT(slot2(QAbstractButton*)));
}
void MainWindow::slot2(QAbstractButton* button)
{
// a button was clicked on the message box
this->setCursor(Qt::CrossCursor);
}