PDA

View Full Version : QtSingleApplication



mule
27th February 2006, 16:33
(Qt user for 6 months, but this makes me realize I am still a newbie ...
Thank you in advance for your assistance.)

Environment:
Qt 4.1.0
Windows 2000 SP4
Microsoft VC 6.0

What is desired:
I am using the QtSingleApplication class to guarantee only one instance of an application
is running.

When a second instance is started, I want the original instance to become the application
of focus and the second instance to terminate.

What is observed:
The second instance terminates; however, the original instance does not gain
the input focus. The original instance's taskbar button usually flashes, but its
main window remains hidden behind other windows.

vRaiseWindowForThisInst() is being called because the window title is changed.

I have attempted to strip down the source files to the essentials ...

File main.h:


extern QtSingleApplication * pApp;


File main.cpp:


QtSingleApplication * pApp;

int main( int argc, char * argv [] )
{
pApp = new QtSingleApplication( "UniqueName", argc, argv );
if ( pApp->sendMessage( "RUNNING?" ) ) {
return 0;
}
pApp->initialize();

pMain = new clMain();

return pApp->exec();
}


File clMain.h:


class clMain : public QMainWindow
{
Q_OBJECT

public:
clMain( QWidget * parent = 0 );
virtual ~clMain( void );

private:
void vRaiseWindowForThisInst( void );
void customEvent( QEvent * event );

private slots:
void vMessageReceivedFromOtherInst( const QString & msg );
};


File clMain.cpp:


clMain::clMain( QWidget * parent )
: QMainWindow( parent )
{
QPushButton * pExitAllButton = new QPushButton( "Exit All" );
setCentralWidget( pExitAllButton );

connect( pExitAllButton, SIGNAL( clicked() ),
this, SLOT( vExitAllPressed() ) );

connect( pApp, SIGNAL( messageReceived( const QString & ) ),
this, SLOT( vMessageReceivedFromOtherInst( const QString & ) ) );

setWindowTitle( "Original Title" );
show();
}

clMain::~clMain( void )
{
;
}

void clMain::vRaiseWindowForThisInst( void )
{
setWindowTitle( "CHANGED TITLE" );

//setFocus( Qt::OtherFocusReason );
//show();
raise();
}

enum QEvent::Type {
Event_Main_Raise = QEvent::User + 1
};

void clMain::customEvent( QEvent * event )
{
switch ( event->type() ) {
default :
QMainWindow::customEvent( event );
break;

case Event_Main_Raise :
vRaiseWindowForThisInst();
break;
}
}

void clMain::vMessageReceivedFromOtherInst( const QString & msg )
{
QApplication::postEvent( this, new QEvent( Event_Main_Raise ) );
}

void clMain::vExitAllPressed( void )
{
close();
}

jacek
27th February 2006, 16:54
Did you try to invoke the QWidget::activateWindow() method (together with raise())?

mule
27th February 2006, 17:06
Yes, I see I failed to copy&paste all the things I tried. Here they are:
activateWindow();
setFocus( Qt::OtherFocusReason );
show();
setWindowState( Qt::WindowActive );
update();
repaint();
setVisible( true );
raise();

As you can see, I was getting frustrated/desperate/confused ...

jacek
27th February 2006, 17:52
According to the docs, you should call QWidget::setActiveWindow() (or rather QApplication::setActiveWindow() as it should be written):
void QtSingleApplication::activateMainWidget () [virtual slot]
Calls QWidget::setActiveWindow() on this application's mainWidget(). This function does nothing if no main widget has been set.
This slot is typically connected to the messageReceived() signal.
See also messageReceived().
So such combo:
show();
raise();
activateWindow();should be enough. Maybe somebody else knows the answer, but you can always try the OS specific functions as the last resort.

mule
27th February 2006, 19:32
Thank you for your help, jacek, and thank you for redirecting me back to the doc. I had placed the doc aside a while back after many failed attempts and was just trying things by brute force and trial/error.

I need to go back and re-think this from the beginning. The doc mentions setMainWidget() and its relation to activateMainWidget(). Since setMainWidget() was abandoned from Qt3 to Qt4 (I am using Qt4) I was trying to do the equivalent with QMainWindow, setCentralWidget(), and such.

If/When I figure this out, I will post the solution.

Other input/thoughts/suggestions still welcome ...

mule
28th February 2006, 17:48
(Admin: Originally posted in Newbie forum, but now believe beyond that. If this violates forum policy, please pull this thread. However, this post does contain more complete information and code.)

Qt 4.1.0 / Ms VC 6.0 / Windows 2000 SP4.

What: Using QtSingleApplication class to guarantee single instance of application.
Desired: When second instance starts, it should terminate and the original instance should become the application of focus.
Observed: When the second instance starts, it terminates; however, the original instance is not brought forward as the application of focus.

The QtSingleApplication doc states that activateMainWidget() ...

Calls QWidget::setActiveWindow() on this application's mainWidget(). This function does nothing if no main widget has been set.
The example in the doc also shows setMainWidget() being invoked by main().

Questions:
1) Since setMainWidget() was removed from Qt3 to Qt4, is QMainWindow::setCentralWidget() not functionally equivalent? If it is not, what is?
2) Since this Solution was only tested using Qt 4.1.1 and I am using 4.1.0, is this a version issue? (I did not see anything in the changelog for 4.1.1 that would indicate this.) (I cannot go to 4.1.1 at this time.)

File main.h

#ifndef _main_h_
#define _main_h_

#include <QtSingleApplication>

extern QtSingleApplication * pApp;

#endif //_main_h_

File main.cpp

#include "main.h"

#include "clMain.h"

QtSingleApplication * pApp;

int main( int argc, char * argv [] )
{
pApp = new QtSingleApplication( "UniqueName", argc, argv );
if ( pApp->sendMessage( "RUNNING?" ) ) {
return 0;
}
pApp->initialize();

pMain = new clMain();

return pApp->exec();
}


File CustomEvent.h

#ifndef _customevent_h_
#define _customevent_h_

#include <QEvent>

enum QEvent::Type {
Event_Main_Raise = QEvent::User + 1
};

#endif //_customevent_h_


File clMain.h

#ifndef _clMain_h_
#define _clMain_h_

#include <QMainWindow>
#include <QWidget>
#include <QPushButton>

class clMain : public QMainWindow
{
Q_OBJECT

public:

clMain( QWidget * parent = 0 );
virtual ~clMain( void );

private:

QPushButton * pExitAllButton;
QWidget * pMainWidget;

void vRaiseWindowForThisInst( void );

void customEvent( QEvent * event );

private slots:

void vMessageReceivedFromOtherInst( void );

void vExitAllClicked( void );
};

extern clMain * pMain;

#endif //_clMain_h_

File clMain.cpp
( NOTE to all the #define options:
Used to select one of many attempts.
Set ONLY ONE non-zero at a time.
See connect() attempts in clMain().
See various method attempts in vRaiseWindowForThisInst(). )

// != 0 -> use activateMainWidget() directly
// == 0 -> use indirect method (following ...)
#define _dir_activateMainWidget 0

// indirectly ...

// != 0 -> use activateMainWidget()
#define _indir_activateMainWidget 0

// indirectly on this ...

// != 0 -> use show(); raise(); activateWindow()
#define _indir_this_show_raise_activateWindow 0
// != 0 -> use show(); raise(); activateWindow(); setFocus()
#define _indir_this_show_raise_activateWindow_setFocus 0
// != 0 -> use setWindowState(); show(); raise(); activateWindow()
#define _indir_this_setWindowState_show_raise_activateWind ow 0
// != 0 -> use setWindowState(); show(); raise(); activateWindow(); setFocus()
#define _indir_this_setWindowState_show_raise_activateWind ow_setFocus 1

// indirectly on pMainWidget widget ...

// != 0 -> use show(); raise(); activateWindow()
#define _indir_MainWidget_show_raise_activateWindow 0
// != 0 -> use show(); raise(); activateWindow(); setFocus()
#define _indir_MainWidget_show_raise_activateWindow_setFoc us 0
// != 0 -> use setWindowState(); show(); raise(); activateWindow()
#define _indir_MainWidget_setWindowState_show_raise_activa teWindow 0
// != 0 -> use setWindowState(); show(); raise(); activateWindow(); setFocus()
#define _indir_MainWidget_setWindowState_show_raise_activa teWindow_setFocus 0

// indirectly on pExitAllButton button ...

// != 0 -> use show(); raise(); activateWindow()
#define _indir_ExitAllButton_show_raise_activateWindow 0
// != 0 -> use show(); raise(); activateWindow(); setFocus()
#define _indir_ExitAllButton_show_raise_activateWindow_set Focus 0

#include "clMain.h"

#include "Main.h"
#include "CustomEvent.h"
#include <QGridLayout>

clMain * pMain = 0;

clMain::clMain( QWidget * parent )
: QMainWindow( parent )
{
pExitAllButton = new QPushButton( "Exit All" );

QGridLayout * pLayout = new QGridLayout;
pLayout->addWidget( pExitAllButton, 0, 0, 1, 1 );

pMainWidget = new QWidget();
pMainWidget->setLayout( pLayout );

setCentralWidget( pMainWidget );

connect( pExitAllButton, SIGNAL( clicked() ),
this, SLOT( vExitAllClicked() ) );

#if _dir_activateMainWidget
connect( pApp, SIGNAL( messageReceived( const QString & ) ),
pApp, SLOT( activateMainWidget() ) );
#else //_USE_activateMainWidget
connect( pApp, SIGNAL( messageReceived( const QString & ) ),
this, SLOT( vMessageReceivedFromOtherInst() ) );
#endif //_USE_activateMainWidget

setWindowTitle( "Original Title" );
show();
}

clMain::~clMain( void )
{
;
}

void clMain::vRaiseWindowForThisInst( void )
{
// indirectly ...

#if _indir_activateMainWidget
setWindowTitle( "** CHANGED #1 **" );

pApp->activateMainWidget();
#endif

// indirectly on this ...

#if _indir_this_show_raise_activateWindow
setWindowTitle( "** CHANGED #2a **" );

show();
raise();
activateWindow();
#endif

#if _indir_this_show_raise_activateWindow_setFocus
setWindowTitle( "** CHANGED #3a **" );

show();
raise();
activateWindow();

setFocus( Qt::OtherFocusReason );
#endif

#if _indir_this_setWindowState_show_raise_activateWind ow
setWindowTitle( "** CHANGED #4a **" );

setWindowState( ( windowState() & ~ Qt::WindowMinimized ) | Qt::WindowActive );

show();
raise();
activateWindow();
#endif

#if _indir_this_setWindowState_show_raise_activateWind ow_setFocus
setWindowTitle( "** CHANGED #5a **" );

setWindowState( ( windowState() & ~ Qt::WindowMinimized ) | Qt::WindowActive );

show();
raise();
activateWindow();

setFocus( Qt::OtherFocusReason );
#endif

// indirectly on pMainWidget widget ...

#if _indir_MainWidget_show_raise_activateWindow
setWindowTitle( "** CHANGED #2b **" );

pMainWidget->show();
pMainWidget->raise();
pMainWidget->activateWindow();
#endif

#if _indir_MainWidget_show_raise_activateWindow_setFoc us
setWindowTitle( "** CHANGED #3b **" );

pMainWidget->show();
pMainWidget->raise();
pMainWidget->activateWindow();

pMainWidget->setFocus( Qt::OtherFocusReason );
#endif

#if _indir_MainWidget_setWindowState_show_raise_activa teWindow
setWindowTitle( "** CHANGED #4b **" );

pMainWidget->setWindowState(
( pMainWidget->windowState() & ~ Qt::WindowMinimized ) | Qt::WindowActive );

pMainWidget->show();
pMainWidget->raise();
pMainWidget->activateWindow();
#endif

#if _indir_MainWidget_setWindowState_show_raise_activa teWindow_setFocus
setWindowTitle( "** CHANGED #5b **" );

pMainWidget->setWindowState(
( pMainWidget->windowState() & ~ Qt::WindowMinimized ) | Qt::WindowActive );

pMainWidget->show();
pMainWidget->raise();
pMainWidget->activateWindow();

pMainWidget->setFocus( Qt::OtherFocusReason );
#endif

// indirectly on pExitAllButton button ...

#if _indir_ExitAllButton_show_raise_activateWindow
setWindowTitle( "** CHANGED #2c **" );

pExitAllButton->show();
pExitAllButton->raise();
pExitAllButton->activateWindow();
#endif

#if _indir_ExitAllButton_show_raise_activateWindow_set Focus
setWindowTitle( "** CHANGED #3c **" );

pExitAllButton->show();
pExitAllButton->raise();
pExitAllButton->activateWindow();

pExitAllButton->setFocus( Qt::OtherFocusReason );
#endif
}

void clMain::customEvent( QEvent * event )
{
switch ( event->type() ) {
default :
QMainWindow::customEvent( event );
break;

case Event_Main_Raise :
vRaiseWindowForThisInst();
break;
}
}

void clMain::vMessageReceivedFromOtherInst( void )
{
QApplication::postEvent( this, new QEvent( Event_Main_Raise ) );
}

void clMain::vExitAllClicked( void )
{
close();
}

jacek
28th February 2006, 18:06
Originally posted in Newbie forum, but now believe beyond that. If this violates forum policy, please pull this thread. However, this post does contain more complete information and code.
Next time, please, just ask to move the thread instead of starting a new one.


1) Since setMainWidget() was removed from Qt3 to Qt4, is QMainWindow::setCentralWidget() not functionally equivalent?
No, QMainWindow::setCentralWidget() just sets the central widget for the main window (i.e. the widget that fills the area between toolbars and statusbar).


Since this Solution was only tested using Qt 4.1.1 and I am using 4.1.0, is this a version issue?
It looks like the solution itself works. This might be a problem with Qt itself (a bug or limitation).

Chicken Blood Machine
28th February 2006, 19:50
What is observed:
The second instance terminates; however, the original instance does not gain
the input focus. The original instance's taskbar button usually flashes, but its
main window remains hidden behind other windows.


There's no bug in your code. It's a restriction of Windows 2000 onwards, see here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/setforegroundwindow.asp

mule
28th February 2006, 20:21
That's it! Thank you, Chicken Blood Machine. I will design a different approach.
(Anybody know of any hair-restoring products?)