この記事はTakumi Akashiro ひとり Advent Calendar 2020の4日目の記事になります。
前置き
はい、昨日に引き続いて、
「別アプリケーションにオーバーレイさせるアプリケーション」に必要な技術を書いていきます。
そもオーバーレイするアプリをには何が必要なん?って話ですよね。
過去にもQiitaでオーバーレイフォームを作った方が記事を書いていて、そこから引用すると
方針
オーバーレイは、以下のような特徴があります。
それらを順番に実現します。
・拡大・縮小とかの枠がない
・タスクバーに表示されない
・必要なところは半透明、必要ないところは透明
・指定の位置にある
・常に画面の前面にある
・クリックは透過する
大体自分もこんな感じだと思ってるので、この方針を真似て作っていきます。
現状の確認
先に上がった特徴のうち、今の状況は
課題 | 備考 |
---|---|
拡大・縮小とかの枠がない | WindowFlagsに「FramelessWindowHint」をセット |
必要なところは半透明、必要ないところは透明 | 「WA_TranslucentBackground」を有効にする |
クリックは下のレイヤへ透過する |
「WA_TransparentForMouseEvents」を有効にする |
常に画面の前面にある | |
タスクバーに表示されない | |
指定の位置にある | 常に対象アプリのウィンドウに追従させたい |
という感じですね。
このうち、「指定の位置にある」は下調べした感じ面倒なのはわかったので、
後日に回して、本日は以下の2つを解決したいと思います。
- 常に画面の前面にある
- タスクバーに表示されない
常に画面の前面にある
めちゃくちゃ簡単です。
WindowFlagにWindowStaysOnTopHintをセットするだけです。
from PySide2 import QtWidgets
from PySide2 import QtCore, QtGui
class Window(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle("Sample")
self.setFixedSize(480, 320)
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
if __name__ == "__main__":
app = QtWidgets.QApplication()
window = Window()
window.show()
exit(app.exec_())
できましたね!
タスクバーに表示されない
タスクバーから表示を消すのは、WindowFlagに「Tool」をセットするだけですので簡単ですね。
ただし、WindowFlagの「FramelessWindowHint」を組み合わせてしまうと、
プロセスを終了する手段がコマンドラインによるプロセスキルか、
タスクマネージャーに頼らざるを得ないため、以下の要素も必要となります。
- タスクトレイに表示される。
- タスクトレイからアプリケーションを終了させる。
そこで役に立つのが、「QSystemTrayIcon」です。
QSystemTrayIconはタスクトレイ内のアイコンを扱うクラスです。
余談ですが、QSystemTrayIconのshowMessageメソッドを呼び出すと、以下のようにトースト通知も出せます。
(通常のアプリケーションだとタスクトレイに登録とかしないので、あまり実用性はないですが……)
アイコンにはコンテキストメニューを追加できるので、アプリケーションの終了処理を登録しておきます。
使ってみると以下のような感じです!
#!python3
# encoding:utf-8
from PySide2 import QtWidgets
from PySide2 import QtCore
from PySide2 import QtGui
class Window(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowFlags(QtCore.Qt.Tool|QtCore.Qt.FramelessWindowHint|QtCore.Qt.WindowStaysOnTopHint)
# 撮影用に位置を変更
self.setGeometry(1400, 850, 200, 200)
label = QtWidgets.QLabel("Frame Less Window Sample", self)
label.setGeometry(0, 0, 200, 200)
label.setStyleSheet("background-color: #222; color: #EEE;")
class TaskTray_Icon(QtWidgets.QSystemTrayIcon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
menu = QtWidgets.QMenu()
quit_action = menu.addAction("Quit")
quit_action.triggered.connect(self.__quit)
# コンテキストメニューに作成したメニューをセット
self.setContextMenu(menu)
# NOTE: アイコンには通常はpngなどを指定するが、
# サンプルなのでPixmapでもモックを作ってお茶を濁す。
# icon = QtGui.QIcon('icon.png')
pixmap = QtGui.QPixmap(QtCore.QSize(32, 32))
pixmap.fill(QtGui.QColor("red"))
icon = QtGui.QIcon(pixmap)
self.setIcon(icon)
def __quit(self):
QtWidgets.QApplication.quit()
if __name__ == "__main__":
app = QtWidgets.QApplication()
# トレイアイコンを生成して表示。
trayicon = TaskTray_Icon()
trayicon.show()
window = Window()
window.show()
exit(app.exec_())
締め
以上で今日の記事は終了です。
やったことを踏まえると、こんな感じですね。
課題 | 備考 |
---|---|
拡大・縮小とかの枠がない | WindowFlagsに「FramelessWindowHint」をセット |
必要なところは半透明、必要ないところは透明 | 「WA_TranslucentBackground」を有効にする |
クリックは下のレイヤへ透過する |
「WA_TransparentForMouseEvents」を有効にする |
常に画面の前面にある | WindowFlagsに「WindowStaysOnTopHint」をセット |
タスクバーに表示されない | WindowFlagsに「Tool」をセット QSystemTrayIconでタスクトレイに登録 コンテキストメニューからアプリケーションを終了 |
指定の位置にある | 常に対象アプリのウィンドウに追従させたい |
まあ、いい感じにオーバーレイウィンドウに近づいてきましたね!
評価ラベル | ランク(おそらく5段階) |
---|---|
おすすめ度 | ★★ |
難易度 | ★ |
ニッチ | ★★★★ |
汎用性 | ★★ |
OBSとAEとPS使って 説明用Gif作るのめんどい |
ScreenToGifを使え |
ScreenToGif | ★★★★★★★★★★ |
明日は遂にWinApiとか使いだすので、よろしくお願いします。
では、また明日!