質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.53%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

849閲覧

Pythonでプログラムによるキーの押し下げを無視して物理的な押し下げを検出する方法

user10

総合スコア37

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

0クリップ

投稿2023/08/12 03:36

編集2023/08/15 06:30

実現したいこと

Pythonでプログラムによるキーの押し下げを無視して物理的な押し下げを検出する方法を知りたいです

前提

プログラムによる押し下げとは左クリックで言いますと
pyautogui.mouseDown(button='left')
pyautogui.mouseUp(button='left')
といったものです。

物理的な押し下げは実際にマウスで左クリックしたときです。

これをwindowsのapictypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000等では判別できず、
おそらくそれ
を利用している?AutoHotKeyのGetKeyState(#InstallMouseHook使用)ではできることなのでいまいち腑に落ちないのです。
(https://ahkwiki.net/GetKeyState)
一応、判別自体はahk Python wrapper(https://ahk.readthedocs.io/en/latest/)を使用することによって解決しているといえばしています。

下記を動かしてもらえばどのようなことかわかると思います。(AutoHotKeyのインストールが必須です)

発生している問題・エラーメッセージ

エラーメッセージ エラー等は出ません ### 該当のソースコード import tkinter as tk import ctypes import threading import pyautogui import time from pynput import mouse, keyboard from ahk import AHK from ahk.directives import InstallMouseHook class Application(tk.Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.master.attributes("-topmost", True) self.my_title = "test" self.back_color = "#FFFFFF" self.master.title(self.my_title) self.master.geometry("300x200") self.create_widgets() self.pack() self.virtual_left_click=False self.virtual_left_click_thread=None self.keyboard_listener = keyboard.Listener(on_press=self.on_key_press) self.keyboard_listener.name = "KeyboardListenerThread" self.keyboard_listener.start() self.mouse_listener = mouse.Listener(on_click=self.on_mouse_click) self.mouse_listener.name = "MouseListenerThread" self.mouse_listener.start() self.ahk = AHK(executable_path="AutoHotKeyのパス", directives=[InstallMouseHook]) def create_widgets(self): self.desc1 = tk.Label(self, text='F3キーを押すことで左クリックがホールドされます') self.desc1.pack(pady=10) self.desc2 = tk.Label(self, text='物理ボタン:') self.desc2.pack() self.physical_left_click_label = tk.Label(self, text='左クリックは押されてません') self.physical_left_click_label.config(bg="gray", fg="white") self.physical_left_click_label.pack(pady=10) self.desc3 = tk.Label(self, text='物理ボタンおよびプログラムによる押下:') self.desc3.pack() self.virtual_left_click_label = tk.Label(self, text='左クリックは押されてません') self.virtual_left_click_label.config(bg="gray", fg="white") self.virtual_left_click_label.pack(pady=10) def on_key_press(self, key): if key == keyboard.Key.f3: self.toggle_left_click() def on_mouse_click(self, x, y, button, pressed): if button == mouse.Button.left: if pressed: self.virtual_left_click_label.config(text='左クリック押下中...', bg="red") if self.ahk.key_state("LButton",mode ="P") == 1: #AHKのGetKeyStateのmode ="P"はソフトウェア的なキーボードイベント生成を無視し、 #実際にユーザーがキーを押しているかを取得できる。 self.physical_left_click_label.config(text='左クリック押下中...', bg="red") else: self.virtual_left_click_label.config(text='左クリックは押されてません', bg="gray") if self.ahk.key_state("LButton",mode ="P") == 0: self.physical_left_click_label.config(text='左クリックは押されてません', bg="gray") def toggle_left_click(self): if self.virtual_left_click_thread is None: self.virtual_left_click_thread = threading.Thread(target=self.left_click_func, name="HoldLeftClickThread") if not self.virtual_left_click: self.virtual_left_click= True self.virtual_left_click_label.config(text='左クリック押下中...', bg="red") if self.virtual_left_click_thread.is_alive(): self.virtual_left_click_thread.join() self.virtual_left_click_thread = threading.Thread(target=self.left_click_func, name="HoldLeftClickThread") self.virtual_left_click_thread.start() else: self.virtual_left_click_label.config(text='左クリックは押されてません', bg="gray") self.virtual_left_click = False def left_click_func(self): while self.virtual_left_click: pyautogui.mouseDown(button='left') time.sleep(0.0001) pyautogui.mouseUp(button='left') if __name__ == "__main__": root = tk.Tk() app = Application(master=root) app.mainloop() ### 試したこと pyautoguiではなく、ctypes.windll.user32.SendInputでマウスを動かしたりしました。 ### 補足情報(FW/ツールのバージョンなど) Windows 10 Python 3.10.8 AutoHotKey 3.10.8
tatsu99👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2023/08/12 03:53

> 物理的な押し下げを検出する方法を知りたいです > 解決しているといえばしています。 > AutoHotKeyのGetKeyStateではできることなのでいまいち腑に落ちないのです。 どこに本当の課題があるのでしょうか? たぶん「できるできないで言えばできているけれども、本当の課題はそこではなく、どうやって解決しているのかメカニズムを知りたい」ではないかと思います。いかがでしょうか。
user10

2023/08/12 04:32

あまりわかってないので間違った説明かもしれませんが、補足させていただきます。 AutoHotKeyのGetKeyStateはWin32apiを使用しているのだと思います。(憶測) AutoHotKeyのGetKeyStateは(#InstallKeybdHook)というコマンドを書いたうえで、 if GetKeyState("LButton", "P")と書けば物理的な押し下げのみを検出することができるのです。 pythonでもWin32apiを使用して、GetKeyStateをすることができますが 同じことができないのでしょうか
jbpb0

2023/08/12 21:57

> 「これをwindowsのapi、ctypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000等では判別できず」 と思ってしまった 質問を編集して、新たに分かったことを追記してください
guest

回答2

0

ベストアンサー

Raw Input API を使うとハードウエアで操作されたか、ソフトウエアで操作されたかを判別できるようです。

RegisterRawInputDevices API でウインドウを登録すると、WM_INPUT メッセージがウインドウに飛んでくるので GetRawInputData API で LPARAM から情報を取り出します。

取り出した RAWINPUTHEADER 構造体の hDevice メンバにデバイスハンドルが入ってくるのですが、ソフトウエアから入力された場合は、ゼロになっています。

Python はわからないので C# Windows Forms アプリケーションのコードを載せておきます。(キーボードとマウスボタンを検出)

あくまで変更されたタイミングが分かるだけなので、現在押されているかどうかは、状態を管理してください。

csharp

1using System; 2using System.Diagnostics; 3using System.Runtime.InteropServices; 4using System.Windows.Forms; 5using System.ComponentModel; 6 7public partial class Form1 : Form 8{ 9 private const int WM_INPUT = 0x00FF; 10 11 private enum HIDUsagePage : short 12 { 13 Undefined = 0x00, 14 Generic = 0x01, 15 Simulation = 0x02, 16 VR = 0x03, 17 Sport = 0x04, 18 Game = 0x05, 19 Keyboard = 0x07, 20 LED = 0x08, 21 Button = 0x09, 22 Ordinal = 0x0A, 23 Telephony = 0x0B, 24 Consumer = 0x0C, 25 Digitizer = 0x0D, 26 PID = 0x0F, 27 Unicode = 0x10, 28 AlphaNumeric = 0x14, 29 Medical = 0x40, 30 MonitorPage0 = 0x80, 31 MonitorPage1 = 0x81, 32 MonitorPage2 = 0x82, 33 MonitorPage3 = 0x83, 34 PowerPage0 = 0x84, 35 PowerPage1 = 0x85, 36 PowerPage2 = 0x86, 37 PowerPage3 = 0x87, 38 BarCode = 0x8C, 39 Scale = 0x8D, 40 MSR = 0x8E 41 } 42 43 private enum HIDUsage : short 44 { 45 Pointer = 0x01, 46 Mouse = 0x02, 47 Joystick = 0x04, 48 Gamepad = 0x05, 49 Keyboard = 0x06, 50 Keypad = 0x07, 51 } 52 53 [Flags()] 54 public enum RawInputDeviceFlags 55 { 56 None = 0, 57 Remove = 0x00000001, 58 Exclude = 0x00000010, 59 PageOnly = 0x00000020, 60 NoLegacy = 0x00000030, 61 InputSink = 0x00000100, 62 CaptureMouse = 0x00000200, 63 NoHotKeys = 0x00000200, 64 AppKeys = 0x00000400 65 } 66 67 [StructLayout(LayoutKind.Sequential)] 68 private struct RAWINPUTDEVICE 69 { 70 public HIDUsagePage usUsagePage; 71 public HIDUsage usUsage; 72 public RawInputDeviceFlags dwFlags; 73 public IntPtr hwndTarget; 74 } 75 76 private enum RawInputType 77 { 78 Mouse = 0, 79 Keyboard = 1, 80 Hid = 2 81 } 82 83 [StructLayout(LayoutKind.Sequential)] 84 private struct RAWINPUTHEADER 85 { 86 public RawInputType dwType; 87 public int dwSize; 88 public IntPtr hDevice; 89 public IntPtr wParam; 90 } 91 92 [Flags] 93 private enum RawKeyboardFlags : short 94 { 95 Make = 0, 96 Break = 1, 97 E0 = 2, 98 E1 = 4, 99 } 100 101 [StructLayout(LayoutKind.Sequential)] 102 private struct RAWKEYBOARD 103 { 104 public short MakeCode; 105 public RawKeyboardFlags Flags; 106 public short Reserved; 107 public short VKey; 108 public int Message; 109 public int ExtraInformation; 110 } 111 112 [Flags()] 113 private enum RawMouseButtons : short 114 { 115 None = 0, 116 LeftDown = 0x0001, 117 LeftUp = 0x0002, 118 RightDown = 0x0004, 119 RightUp = 0x0008, 120 MiddleDown = 0x0010, 121 MiddleUp = 0x0020, 122 Button4Down = 0x0040, 123 Button4Up = 0x0080, 124 Button5Down = 0x0100, 125 Button5Up = 0x0200, 126 MouseWheel = 0x0400 127 } 128 129 [Flags()] 130 public enum RawMouseFlags : short 131 { 132 MoveRelative = 0, 133 MoveAbsolute = 1, 134 VirtualDesktop = 2, 135 AttributesChanged = 4 136 } 137 138 [StructLayout(LayoutKind.Explicit)] 139 private struct RowMouseData 140 { 141 [FieldOffset(0)] 142 public int Buttons; 143 [FieldOffset(2)] 144 public short ButtonData; 145 [FieldOffset(0)] 146 public RawMouseButtons ButtonFlags; 147 } 148 149 [StructLayout(LayoutKind.Sequential)] 150 private struct RAWMOUSE 151 { 152 public RawMouseFlags Flags; 153 public RowMouseData Data; 154 public int RawButtons; 155 public int LastX; 156 public int LastY; 157 public int ExtraInformation; 158 } 159 160 [StructLayout(LayoutKind.Explicit)] 161 private struct RAWINPUT 162 { 163 [FieldOffset(0)] 164 public RAWINPUTHEADER Header; 165 [FieldOffset(16)] 166 public RAWKEYBOARD Keyboard; 167 [FieldOffset(16)] 168 public RAWMOUSE Mouse; 169 } 170 171 [DllImport("user32.dll")] 172 private static extern bool RegisterRawInputDevices( 173 RAWINPUTDEVICE[] pRawInputDevices, int uiNumDevices, int cbSize); 174 175 private enum RawInputCommand 176 { 177 Header = 0x10000005, 178 Input = 0x10000003, 179 } 180 181 [DllImport("user32.dll")] 182 private static extern int GetRawInputData( 183 IntPtr hRawInput, RawInputCommand uiCommand, IntPtr pData, 184 ref int pcbSize, int cbSizeHeader); 185 186 public Form1() 187 { 188 InitializeComponent(); 189 } 190 191 protected override void OnHandleCreated(EventArgs e) 192 { 193 base.OnHandleCreated(e); 194 195 RAWINPUTDEVICE[] rawInputDevices = new RAWINPUTDEVICE[2]; 196 197 rawInputDevices[0].usUsagePage = HIDUsagePage.Generic; 198 rawInputDevices[0].usUsage = HIDUsage.Keyboard; 199 rawInputDevices[0].dwFlags = RawInputDeviceFlags.InputSink; 200 rawInputDevices[0].hwndTarget = Handle; 201 202 rawInputDevices[1].usUsagePage = HIDUsagePage.Generic; 203 rawInputDevices[1].usUsage = HIDUsage.Mouse; 204 rawInputDevices[1].dwFlags = RawInputDeviceFlags.InputSink; 205 rawInputDevices[1].hwndTarget = Handle; 206 207 var result = RegisterRawInputDevices(rawInputDevices, 208 rawInputDevices.Length, 209 Marshal.SizeOf(typeof(RAWINPUTDEVICE))); 210 if (!result) 211 { 212 throw new Win32Exception(); 213 } 214 } 215 216 protected override void WndProc(ref Message m) 217 { 218 if (m.Msg == WM_INPUT) 219 { 220 int dwSize = 0; 221 GetRawInputData(m.LParam, RawInputCommand.Input, IntPtr.Zero, ref dwSize, 222 Marshal.SizeOf(typeof(RAWINPUTHEADER))); 223 224 IntPtr buffer = Marshal.AllocHGlobal(dwSize); 225 try 226 { 227 if (GetRawInputData(m.LParam, RawInputCommand.Input, buffer, ref dwSize, 228 Marshal.SizeOf(typeof(RAWINPUTHEADER))) == dwSize) 229 { 230 RAWINPUT raw = Marshal.PtrToStructure<RAWINPUT>(buffer); 231 if (raw.Header.hDevice == IntPtr.Zero) 232 { 233 Debug.WriteLine("ソフトウエアだよ!"); 234 } 235 else 236 { 237 switch (raw.Header.dwType) 238 { 239 case RawInputType.Keyboard: 240 if (raw.Keyboard.Flags.HasFlag(RawKeyboardFlags.Break)) 241 { 242 Debug.WriteLine($"キーが離されました: {(Keys)raw.Keyboard.VKey}"); 243 } 244 else 245 { 246 Debug.WriteLine($"キーが押されました: {(Keys)raw.Keyboard.VKey}"); 247 } 248 break; 249 250 case RawInputType.Mouse: 251 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.LeftDown)) 252 { 253 Debug.WriteLine($"左ボタンが押されました"); 254 } 255 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.LeftUp)) 256 { 257 Debug.WriteLine($"左ボタンが離されました"); 258 } 259 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.RightDown)) 260 { 261 Debug.WriteLine($"右ボタンが押されました"); 262 } 263 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.RightUp)) 264 { 265 Debug.WriteLine($"右ボタンが離されました"); 266 } 267 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.MiddleDown)) 268 { 269 Debug.WriteLine($"中ボタンが押されました"); 270 } 271 if (raw.Mouse.Data.ButtonFlags.HasFlag(RawMouseButtons.MiddleUp)) 272 { 273 Debug.WriteLine($"中ボタンが離されました"); 274 } 275 break; 276 } 277 } 278 } 279 } 280 finally 281 { 282 Marshal.FreeHGlobal(buffer); 283 } 284 } 285 base.WndProc(ref m); 286 } 287}

投稿2023/08/12 21:07

編集2023/08/13 01:32
KOZ6.0

総合スコア2597

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

user10

2023/08/13 03:00

ありがとうございます。試してみます。 これでも出来れば解決としたいですね。
guest

0

これをwindowsのapi、ctypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000等では判別できず、
おそらくそれを利用している?AutoHotKeyのGetKeyStateではできることなのでいまいち腑に落ちないのです。

「GetAsyncKeyState() ではできず、AutoHotKeyではできる」というのは本当ですか
AutoHoKeyの"P"はGetAsyncKeyState()の結果を返しているはずです。

https://github.com/AutoHotkey/AutoHotkey/blob/alpha/source/script2.cpp#L3800
https://github.com/AutoHotkey/AutoHotkey/blob/alpha/source/keyboard_mouse.h#L328

投稿2023/08/12 04:42

編集2023/08/14 10:05
sigsegv

総合スコア895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

user10

2023/08/12 08:12 編集

すいません。確かに私の間違いです。(ちなみにここに書いたものはここでの説明用のコードであり、本番は別にあります。) 再度検証しました。確かに判別はできてるのかもしれません。 on_mouse_clickの内容を以下に差し変えたところ、 ---------------------------------------------------------------------------------------------- def on_mouse_click(self, x, y, button, pressed): if button == mouse.Button.left: if pressed: self.virtual_left_click_label.config(text='左クリック押下中...', bg="red") else: self.virtual_left_click_label.config(text='左クリックは押されてません', bg="gray") if ctypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000: self.physical_left_click_label.config(text='左クリックは押されてません', bg="gray") print("物理ボタンが押されていません") else: self.physical_left_click_label.config(text='左クリック押下中...', bg="red") print("物理ボタンが押されています") ---------------------------------------------------------------------------------------------- F3キーを押した後一瞬物理左クリックを押されていないにも関わらず、押されたことになりその後に押されていないことになるという挙動がわかりました。 恐らくこの挙動が私の本番のプログラムに干渉して不具合を起こすことから、 私は「これをwindowsのapi、ctypes.windll.user32.GetAsyncKeyState(0x01) & 0x8000等では判別できず」 と思ってしまったのです。 ただ、一瞬でも物理左クリックを押されていないにも関わらず、押されたこと判定されるのは納得がいかないのです。 という経緯で、わだかまりがあるので回答の受付を続けさせてください。 質問は「pythonでもWin32apiを使用して、正確に物理的なボタン押し下げを検出できる方法はありませんか」という方向で進めさせてください。
KOZ6.0

2023/08/13 00:20 編集

GetAsyncKeyState は、過去に押されたかどうか、を取得します。 複数回 GetAsyncKeyState を呼ぶことを考えてください。 前回呼び出してから次に呼び出すまでに1回でも押されていれば、その時点で離れていても押された、と返ってきます。
user10

2023/08/13 02:56

そうですよね。 僕の例を立ち上げて左クリックせず、真っ先にF3キーを押しても反応したので、 もしかしたら、GetAsyncKeyStateはpyautogui.mouseDown(button='left')に反応している可能性もまだあります。
user10

2023/08/13 09:44

回答者様への返信となります。 https://github.com/AutoHotkey/AutoHotkey/blob/alpha/source/script2.cpp#L3800 について調べました。 AHKで#InstallMouseHook がついているとGetAsyncKeyState()にそのままいかず、g_PhysicalKeyState[aVK] & STATE_DOWN;という配列?とフラグ?がつくように処理が分岐しているのではないでしょうか、 そこからはhook.cppで処理していると思います。その先で複雑な状態管理がされてるようですが私の頭程度では追い切れなかったです。GetAsyncKeyStateが使われているかどうかはわかりませんでした
sigsegv

2023/08/14 10:17

すいません、installmousehookのルートを追っていませんでした。 ざっと見た感じ https://github.com/AutoHotkey/AutoHotkey/blob/alpha/source/hook.cpp#L4232 で Windows API でフックを登録すると https://github.com/AutoHotkey/AutoHotkey/blob/alpha/source/hook.cpp#L228 が、Windowsから、マウスボタン状態を引数にして呼ばれるようになり、 それを g_PhysicalKeyState 配列に設定しておく、 という流れだと思います。 Hook と GetAsyncKeyState が異なるのかは Windows の内部仕様になってわかりません。 少なくとも GetAsyncKeyState を呼ぶわけではないので、回答からその記述は削除します。
user10

2023/08/15 06:28

こちらこそすいません。 コードでしか#InstallMouseHook が使われてることがわからないため、 私の説明不足です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問