clipboard機能のパイプラインを接続

This commit is contained in:
misyaguziya
2025-11-20 00:25:28 +09:00
parent 3cd37cf458
commit 4eac9654a7
4 changed files with 62 additions and 13 deletions

View File

@@ -734,6 +734,10 @@ class Config:
SELECTED_TRANSLATION_COMPUTE_DEVICE = ValidatedProperty('SELECTED_TRANSLATION_COMPUTE_DEVICE', _compute_device_validator) SELECTED_TRANSLATION_COMPUTE_DEVICE = ValidatedProperty('SELECTED_TRANSLATION_COMPUTE_DEVICE', _compute_device_validator)
SELECTED_TRANSCRIPTION_COMPUTE_DEVICE = ValidatedProperty('SELECTED_TRANSCRIPTION_COMPUTE_DEVICE', _compute_device_validator) SELECTED_TRANSCRIPTION_COMPUTE_DEVICE = ValidatedProperty('SELECTED_TRANSCRIPTION_COMPUTE_DEVICE', _compute_device_validator)
# -- Clipboard control ---
ENABLE_COPY_TO_CLIPBOARD = ManagedProperty('ENABLE_COPY_TO_CLIPBOARD', type_=bool)
ENABLE_PASTE_FROM_CLIPBOARD = ManagedProperty('ENABLE_PASTE_FROM_CLIPBOARD', type_=bool)
def init_config(self): def init_config(self):
# Read Only # Read Only
self._VERSION = "3.3.1" self._VERSION = "3.3.1"
@@ -997,6 +1001,8 @@ class Config:
self._WEBSOCKET_SERVER = False self._WEBSOCKET_SERVER = False
self._WEBSOCKET_HOST = "127.0.0.1" self._WEBSOCKET_HOST = "127.0.0.1"
self._WEBSOCKET_PORT = 2231 self._WEBSOCKET_PORT = 2231
self._ENABLE_COPY_TO_CLIPBOARD = False
self._ENABLE_PASTE_FROM_CLIPBOARD = False
def load_config(self): def load_config(self):
if os_path.isfile(self.PATH_CONFIG) is not False: if os_path.isfile(self.PATH_CONFIG) is not False:

View File

@@ -396,6 +396,12 @@ class Controller:
) )
model.updateOverlayLargeLog(overlay_image) model.updateOverlayLargeLog(overlay_image)
if config.ENABLE_COPY_TO_CLIPBOARD is True:
clipboard_message = self.messageFormatter("SEND", translation, message)
model.setCopyToClipboard(clipboard_message)
if config.ENABLE_PASTE_FROM_CLIPBOARD is True:
model.setPasteFromClipboard()
if model.checkWebSocketServerAlive() is True: if model.checkWebSocketServerAlive() is True:
model.websocketSendMessage( model.websocketSendMessage(
{ {
@@ -728,6 +734,12 @@ class Controller:
) )
model.updateOverlayLargeLog(overlay_image) model.updateOverlayLargeLog(overlay_image)
if config.ENABLE_COPY_TO_CLIPBOARD is True:
clipboard_message = self.messageFormatter("SEND", translation, message)
model.setCopyToClipboard(clipboard_message)
if config.ENABLE_PASTE_FROM_CLIPBOARD is True:
model.setPasteFromClipboard()
if model.checkWebSocketServerAlive() is True: if model.checkWebSocketServerAlive() is True:
model.websocketSendMessage( model.websocketSendMessage(
{ {

View File

@@ -32,6 +32,7 @@ from models.overlay.overlay import Overlay
from models.overlay.overlay_image import OverlayImage from models.overlay.overlay_image import OverlayImage
from models.watchdog.watchdog import Watchdog from models.watchdog.watchdog import Watchdog
from models.websocket.websocket_server import WebSocketServer from models.websocket.websocket_server import WebSocketServer
from models.clipboard.clipboard import Clipboard
from utils import errorLogging, setupLogger from utils import errorLogging, setupLogger
class threadFnc(Thread): class threadFnc(Thread):
@@ -138,6 +139,7 @@ class Model:
# default no-op callbacks for energy check functions # default no-op callbacks for energy check functions
self.check_mic_energy_fnc: Callable[[float], None] = lambda v: None self.check_mic_energy_fnc: Callable[[float], None] = lambda v: None
self.check_speaker_energy_fnc: Callable[[float], None] = lambda v: None self.check_speaker_energy_fnc: Callable[[float], None] = lambda v: None
self.clipboard = Clipboard()
self._inited = True self._inited = True
def ensure_initialized(self) -> None: def ensure_initialized(self) -> None:
@@ -1201,4 +1203,27 @@ class Model:
errorLogging() errorLogging()
return False return False
def setCopyToClipboard(self, text:str) -> bool:
self.ensure_initialized()
try:
if isinstance(self.clipboard, Clipboard):
self.clipboard.copy(text)
return True
else:
return False
except Exception:
errorLogging()
return False
def setPasteFromClipboard(self) -> bool:
self.ensure_initialized()
try:
if isinstance(self.clipboard, Clipboard):
return self.clipboard.paste()
else:
return False
except Exception:
errorLogging()
return False
model = Model() model = Model()

View File

@@ -1,6 +1,8 @@
import sys import sys
import time import time
import os
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from psutil import process_iter
import openvr import openvr
try: try:
@@ -9,6 +11,10 @@ except ImportError:
def printLog(data, *args, **kwargs): def printLog(data, *args, **kwargs):
print(data, *args, **kwargs) print(data, *args, **kwargs)
def checkSteamvrRunning() -> bool:
_proc_name = "vrmonitor.exe" if os.name == "nt" else "vrmonitor"
return _proc_name in (p.name() for p in process_iter())
# Windows-specific imports via ctypes will be used when focusing windows # Windows-specific imports via ctypes will be used when focusing windows
if sys.platform == 'win32': if sys.platform == 'win32':
import ctypes import ctypes
@@ -117,11 +123,10 @@ def paste_via_pyautogui(countdown: int = 0) -> bool:
printLog('pyautogui not installed. Install with: pip install pyautogui') printLog('pyautogui not installed. Install with: pip install pyautogui')
return False return False
printLog(f'Give focus to the target input. Pasting in {countdown} seconds...')
for i in range(countdown, 0, -1): for i in range(countdown, 0, -1):
print(i, end=' ', flush=True) print(i, end=' ', flush=True)
time.sleep(1) time.sleep(1)
printLog('\nSending Ctrl+V...')
try: try:
# pyautogui.hotkey is a safe cross-platform way to send keys # pyautogui.hotkey is a safe cross-platform way to send keys
pyautogui.hotkey('ctrl', 'v') pyautogui.hotkey('ctrl', 'v')
@@ -132,8 +137,8 @@ def paste_via_pyautogui(countdown: int = 0) -> bool:
class Clipboard: class Clipboard:
def __init__(self, vr_mode: bool = True): def __init__(self):
if not vr_mode: if not checkSteamvrRunning():
self.app_name = None self.app_name = None
else: else:
openvr.init(openvr.VRApplication_Background) openvr.init(openvr.VRApplication_Background)
@@ -156,6 +161,7 @@ class Clipboard:
self.app_name = name self.app_name = name
break break
openvr.shutdown() openvr.shutdown()
printLog(f"Clipboard initialized. SteamVR App Name: {self.app_name}")
def copy(self, message: str) -> bool: def copy(self, message: str) -> bool:
"""Copy `message` to clipboard. """Copy `message` to clipboard.
@@ -179,18 +185,17 @@ class Clipboard:
True if paste command was sent, False otherwise. True if paste command was sent, False otherwise.
""" """
if window_name is None: window_name = window_name if window_name is not None else self.app_name
window_name = self.app_name
# focus target window (Windows only) — window_name is required # If window_name is provided, attempt to focus it (Windows only).
focused = False # If window_name is None, skip focusing and paste into the currently focused window.
if sys.platform == 'win32': if window_name is not None and sys.platform == 'win32':
if not window_name: printLog(f"paste: attempting to focus window matching '{window_name}'")
printLog('paste: window_name is required on Windows') focused = False
return False
# try title substring match first # try title substring match first
wins = find_windows_by_title_substring(window_name) wins = find_windows_by_title_substring(window_name)
printLog(f"paste: found {wins} windows matching title substring '{window_name}'")
for hwnd in wins: for hwnd in wins:
if focus_window(hwnd): if focus_window(hwnd):
focused = True focused = True
@@ -199,6 +204,7 @@ class Clipboard:
# if not found by title, try treating window_name as process name # if not found by title, try treating window_name as process name
if not focused: if not focused:
wins = find_windows_by_process_name(window_name) wins = find_windows_by_process_name(window_name)
printLog(f"paste: found {wins} windows matching process name '{window_name}'")
for hwnd in wins: for hwnd in wins:
if focus_window(hwnd): if focus_window(hwnd):
focused = True focused = True
@@ -218,4 +224,4 @@ class Clipboard:
if __name__ == '__main__': if __name__ == '__main__':
clipboard = Clipboard() clipboard = Clipboard()
clipboard.copy("Sample text to copy to clipboard.") clipboard.copy("Sample text to copy to clipboard.")
clipboard.paste(window_name=None, countdown=0) clipboard.paste(window_name=None, countdown=3)