PySide2 (Qt fot Python) QtSingleApplicationでスタンドアロンアプリケーションの多重起動を防ぐ
アプリケーションの多重起動を防ぐ
同じウィンドウが複数立ち上がって欲しくないスタンドアロンのアプリケーションを開発していたので、何か良い方法は無いかと探してみました。
MayaでWindowを作成するときにglobalに逃がす方法をよく使いますが、
似たような方法が出来ないかと探していたところ、Qt
(C++
)にQtSingleApplication
なるものがあると分かりました。
細かい説明は良いから実行したい という方は飛ばしてください。完成したコードはこちら
目次
QtSingleApplication とは
QtSingleApplication Class Reference
原文
TheQtSingleApplication
class provides an API to detect and communicate with running instances of an application.
This class allows you to create applications that cannot have multiple instances running on the same machine for the same user.Google翻訳
QtSingleApplication
クラスは、アプリケーションの実行中のインスタンスを検出して通信するためのAPIを提供します。
このクラスを使用すると、同じユーザーに対して同じマシン上で複数のインスタンスを実行できないアプリケーションを作成できます。
QtSingleApplication
Qtの公式な機能ではなくQtSolutions
という追加機能として実装されているものらしいです。ぶっちゃけ詳しくはよく分からない...
PySide2.QtSingleApplication
QtSolutions
はPySide2
には組み込まれていない
なので自前で用意する必要があるのですが、QtSingleApplication
をPython
で構築したものが公開されていたので有り難く使わせて頂く事にしました。
同じidのウィンドウが app .isRunning() == True
動いていた場合、処理を終了するってことですね。(ざっくり
app = QtSingleApplication(appGuid, sys.argv) if app.isRunning(): sys.exit(0)
QtSingleApplication.activateWindow 実行時に最小化を解除してraiseする。
同じウィンドウが app .isRunning() == False
動いていなかった場合は QtSingleApplication
が継承したQApplication
を利用してwindowを立ち上げます。
w = QWidget() w.show() app.setActivationWindow(w)
def activateWindow(self): if not self._activationWindow: return self._activationWindow.setWindowState(self._activationWindow.windowState() & ~Qt.WindowMinimized) self._activationWindow.raise_() self._activationWindow.activateWindow()
QWidget.setWindowState
self._activationWindow.setWindowState(self._activationWindow.windowState() & ~Qt.WindowMinimized)
windowState
を確認してWindowMinimized
を解除しているらしいQWidget.raise_
self._activationWindow.raise_()
の部分でwindowを一番手前に表示するのですが、どうやらこのraise_()
は同じQApplication間のみで動作しないらしく別のアプリケーションより手前には来てくれません。原文
Raises this widget to the top of the parent widget’s stack.Google翻訳
このウィジェットを親ウィジェットのスタックの一番上に上げます。
QWidget.raise_()で最前面に表示できないのでwin32gui.SetWindowPosを使う
win32API
を利用して 常に最前面に表示する & 常に最前面に表示するを解除する を実行して疑似的にウィンドウを最前面に表示させます。
SetWindowPos(self._activationWindow.winId(), win32con.HWND_TOPMOST, # = always on top. only reliable way to bring it to the front on windows 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW) SetWindowPos(self._activationWindow.winId(), win32con.HWND_NOTOPMOST, # disable the always on top, but leave window at its top position 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW)
原文
always on top. only reliable way to bring it to the front on windows
disable the always on top, but leave window at its top positionGoogle翻訳
常にトップに。 窓の前にそれを持って来る唯一の信頼できる方法
常に手前に表示するのを無効にして、ウィンドウを一番上の位置にします。
この処理をQSingleApplication.activateWindow
に組み込めば完成です。
出来たやつ
これでスタンドアロンアプリケーションの多重起動を防ぐ処理が完了しました。 追加機能として最小化を解除し、最前面に表示することも可能になりました。
メモ
PySideの機能にも最前面に表示するフラグを使用することは可能です。
しかしウィンドウを再度表示しなければならず、一瞬ウィンドウが消えて挙動がおかしいように感じさせてしまいそうだったので今回のwin32APIを利用する手法を取りました。