Russian Qt Forum
Ноябрь 23, 2024, 18:07 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2]   Вниз
  Печать  
Автор Тема: Запуск программы только один раз (QtSingleApplication)  (Прочитано 17977 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #15 : Март 23, 2012, 19:18 »

для винды я видел, что несложно - помню это обсуждалось совсем недавно. а как с этим обстоят дела в мак ос?
Там нет "instance" - второй раз копию пользователь не запустит (во всяком случае из Finder). Если же надо пресекать запуск приложения с тем же именем, то я делал через

::GetCurrentProcess
::GetNextProcess
::GetProcessInformation

Может есть и лучший способ, но и это работает
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #16 : Март 23, 2012, 19:41 »

спасибо за информацию, может когда-нибудь пригодится Улыбающийся

а со студией всё оказалось просто: надо было просто не добавлять файлы qtlockedfile*.cpp в проект.

но кто-то может объяснить зачем используется такая магия в qtlocalpeer.cpp?
Код
C++ (Qt)
namespace QtLP_Private {
#include "qtlockedfile.cpp"
#if defined(Q_OS_WIN)
#include "qtlockedfile_win.cpp"
#else
#include "qtlockedfile_unix.cpp"
#endif
}
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #17 : Март 24, 2012, 16:06 »

QtSingleApplication использует следующий код для активации существующей копии приложения:
Код
C++ (Qt)
void QtSingleApplication::activateWindow()
{
   if (actWin) {
       actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized);
       actWin->raise();
       actWin->activateWindow();
   }
}
но ассистент нам ясно даёт понять, что под виндой такой трюк плохо работает:
Цитировать
On Windows, if you are calling this when the application is not currently the active one then it will not make it the active window. It will change the color of the taskbar entry to indicate that the window has changed in some way. This is because Microsoft does not allow an application to interrupt what the user is currently doing in another application.
поэтому я добавил WinAPI код для явного показа окна (идея взята из исходников Notepad++, за что автору большое спасибо):
Код
C++ (Qt)
if (sendMessage("hello world"))
{
#ifdef Q_WS_WIN32
   extern const QString qt_getRegisteredWndClass();
 
   const QString windowClassName = qt_getRegisteredWndClass(); // usually returns "QWidget"
   HWND existingWindowHandle = NULL;
   for (int i = 0; i < 10; ++i) // assume there're 10 different Qt windows maximum at the moment
   {
       HWND qtWindowHandle = ::FindWindow(windowClassName.utf16(), NULL);
       if (qtWindowHandle)
       {
           WCHAR captionWstr[100];
           if (::GetWindowText(qtWindowHandle, captionWstr, 100))
           {
               QString caption = QString::fromWCharArray(captionWstr);
               qDebug("found Qt window with caption %s", qPrintable(caption));
               if (caption.endsWith(qApp->applicationName())) // this condition is for my application
               {
                   qDebug("this is our window");
                   existingWindowHandle = qtWindowHandle;
                   break;
               }
           }
           else
               qDebug("failed to get caption of window %#x", qtWindowHandle);
       }
       else
       {
           qDebug("WTF?!?!?!");
           break;
       }
       Sleep(100);
   }
 
   if (existingWindowHandle)
   {
       ::ShowWindow(existingWindowHandle, ::IsIconic(existingWindowHandle) ? SW_RESTORE : SW_SHOW);
       ::SetForegroundWindow(existingWindowHandle);
   }
#endif
   return;
}
этот код вызывается из конструктора класса-наследника QtSingleApplication, но его можно вынести и в main(). правда тут есть одно ограничение: сравниваются заголовки окон, что теоретически не всегда может быть приемлемым. по идее можно перерегистрировать класс окна со своим именем вместо стандартного "QWidget", но я решил с этим не заморачиваться.

и ещё я не уверен насчёт недокументированной функции qt_getRegisteredWndClass() - вдруг в будущем она исчезнет. по-хорошему надо пользоваться ::GetClassName() Улыбающийся
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #18 : Март 25, 2012, 18:42 »

У меня в main() сделано так:

Код
C++ (Qt)
AllowSetForegroundWindow(ASFW_ANY);
 
if(app.sendMessage("wake up"))
   return 0;
 
// normal startup
...
 

Через AllowSetForegroundWindow разрешается другим процессам ставить свои окна в foreground. Это будет работать при условии, что вторая запущенная копия вашей программы будет сама в foreground (насколько я понимаю, в винде это всегда так). Таким образом, в первой копии программы эта функция ни на что не влияет, я в во второй копии разрешает первой копии поставить своё окно на передний план. Проверял в XP и в 7, работает.
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #19 : Март 25, 2012, 19:11 »

спасибо, действительно работает!

но я заметил один недостаток у данного подхода: если первая копия покажет месседж бокс сразу после активации, то ни окно, ни месседж бокс, на первый план не выходят (в 7, в ХР ещё не смотрел). думаю надо ещё добавить вызов ShowWindow() и/или SetForegroundWindow().
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #20 : Март 25, 2012, 19:52 »

желаемого эффекта добился двумя путями:
Код
C++ (Qt)
void Application::activateWindow()
{
   QtSingleApplication::activateWindow();
   ::ShowWindow(_mainWindow->winId(), SW_SHOW);
   ::SetForegroundWindow(_mainWindow->winId());
}
Код
C++ (Qt)
void Application::activateWindow()
{
   ::ShowWindow(_mainWindow->winId(), ::IsIconic(_mainWindow->winId()) ? SW_RESTORE : SW_SHOW);
   ::SetForegroundWindow(_mainWindow->winId());
}
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #21 : Март 25, 2012, 21:09 »

дело оказалось даже не в этом, т.к. предыдущий код тоже на самом деле не помог. при более пристальном изучении внутренностей QtSingleApplication я заметил, что
Код
C++ (Qt)
connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&)));
совершается в конструкторе, а
Код
C++ (Qt)
connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow()));
только в setActivationWindow(), т.е. позже. у меня в приложении было
Код
C++ (Qt)
setActivationWindow(_mainWindow);
connect(this, SIGNAL(messageReceived(const QString &)), _mainWindow, SLOT(loadFile(const QString &)));
значит когда отправлялся сигнал messageReceived(), то сначала грузился файл, вылазил месседжбокс, а потом уже только шёл вызов activateWindow() - видно тут собака и зарылась. также иногда наблюдал артефакты с появлением окна (на мгновение появлялось и исчезало, или месседжбокс 2 раза подряд вылезал), но я это списывал на особенности винды, а теперь понял что к чему Улыбающийся

в итоге пришлось чуть схитрить, чтобы загружать файл после показа окна:
Код
C++ (Qt)
setActivationWindow(_mainWindow);
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(setParam(const QString &))); // просто сохраняем параметр
...
void Application::activateWindow()
{
   QtSingleApplication::activateWindow();
   _mainWindow->loadFile(_param);
}
и теперь всё отрабатывает идеально.
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Страниц: 1 [2]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.554 секунд. Запросов: 23.