From 2b30bf9ec3cc901fbe535c0189d6a945cc1c3d3d Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 02:00:09 +0000 Subject: [PATCH 01/15] Hotkey Widget --- .gitignore | 1 + widgets/hotkeys/hotkeys.py | 481 ++++++++++++++++++++++++++++++++++ widgets/hotkeys/info.py | 13 + widgets/hotkeys/ui/hotkeys.ui | 131 +++++++++ 4 files changed, 626 insertions(+) create mode 100644 widgets/hotkeys/hotkeys.py create mode 100644 widgets/hotkeys/info.py create mode 100644 widgets/hotkeys/ui/hotkeys.ui diff --git a/.gitignore b/.gitignore index fd37c95..51bfbd3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ __pycache__ /widgets/map/res/*.png /widgets/playerinfo/res/*.svg /widgets/playerinfo/res/*.png +/pypipboy/ diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py new file mode 100644 index 0000000..a150b42 --- /dev/null +++ b/widgets/hotkeys/hotkeys.py @@ -0,0 +1,481 @@ +# -*- coding: utf-8 -*- +import datetime +import os +from PyQt5 import QtWidgets, QtCore, uic +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from pypipboy.types import eValueType +from .. import widgets + +import logging + +class HotkeyWidget(widgets.WidgetBase): + _signalInfoUpdated = QtCore.pyqtSignal() + + hotkeysModel = QStandardItemModel() + + def __init__(self, mhandle, parent): + super().__init__('HotkeyWidget', parent) + self.widget = uic.loadUi(os.path.join(mhandle.basepath, 'ui', 'hotkeys.ui')) + self._logger = logging.getLogger('pypipboyapp.llhookey') + self.setWidget(self.widget) + self.pipPlayerInfo = None + self.pipInventoryInfo = None + + self._signalInfoUpdated.connect(self._slotInfoUpdated) + + def init(self, app, datamanager): + super().init(app, datamanager) + self.dataManager = datamanager + self.dataManager.registerRootObjectListener(self._onPipRootObjectEvent) + self._app = app + + self.widget.pushButton.clicked.connect(self._testButtonHandler) + + self.llh = LLHookey() + + self.llh.addHotkey(70, action=self.doThing) #f + self.llh.addHotkey(71, action=self.doOtherThing, control=True) #ctrl-g + self.llh.addHotkey(72, action=self.llh.disableHotkey, params=70) #h + self.llh.addHotkey(74, action=self.llh.enableHotkey, params=70) #j + + self.llh.addHotkey(223, action=self.useJet) #` + self.llh.addHotkey(36, action=self.useNamedItem, params=("48", "psycho")) #home + self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y + self.llh.addHotkey(71, action=self.equipNextGrendae) #g + + + + self.availableGrenades = [] + + def equipNextGrendae(self): + getIndex = -1 + numGrenades = len(self.availableGrenades) + if (numGrenades > 0): + for i in range(0, numGrenades): + if (self.availableGrenades[i][1]): + getIndex = i + 1 + break + + if (getIndex == numGrenades): + getIndex = 0 + + if (getIndex >= 0): + self.useNamedItem("43", self.availableGrenades[getIndex][0]) + + + + def useJet(self): + self.useNamedItem("48", "jet") + + def useNamedItem(self,inventorySection, itemName): + itemName = itemName.lower() + if (self.pipInventoryInfo): + inventory = self.pipInventoryInfo.child(inventorySection) + for i in range(0, inventory.childCount()): + name = inventory.child(i).child('text').value() + if (name.lower() == itemName): + self.dataManager.rpcUseItem(inventory.child(i)) + + def doThing(self): + print("THING!!!!!!!!!!!!!!!!!!!!!") + + def doOtherThing(self): + print("!!!!!!!!!!!!OTHERTHING!!!!!!!!!!!!!!!!!!!!!") + + def _onPipRootObjectEvent(self, rootObject): + self.pipInventoryInfo = rootObject.child('Inventory') + if self.pipInventoryInfo: + self.pipInventoryInfo.registerValueUpdatedListener(self._onPipPlayerInfoUpdate, 1) + self._signalInfoUpdated.emit() + pass + + def _onPipPlayerInfoUpdate(self, caller, value, pathObjs): + self._signalInfoUpdated.emit() + + @QtCore.pyqtSlot() + def _slotInfoUpdated(self): + self.availableGrenades = [] + if (self.pipInventoryInfo): + weapons = self.pipInventoryInfo.child('43') + for i in range(0, weapons.childCount()): + equipped = False + name = weapons.child(i).child('text').value() + if (name.lower().find('mine') > -1 + or name.lower().find('grenade') > -1 + or name.lower().find('molotov') > -1 ): + count = str(weapons.child(i).child('count').value()) + if (weapons.child(i).child('equipState').value() == 3): + equipped = True + + self.availableGrenades.append([name.lower(), equipped]) + + + @QtCore.pyqtSlot() + def _testButtonHandler(self): + keys = self.llh.getHotkeys() + for hk in keys: + self.addToModel(hk ) + + + #self.addToModel("bob") + + def addToModel(self, key): + + + item = [ + QStandardItem(""), + QStandardItem(str(key.keycode)), + QStandardItem(str(key.control)), + QStandardItem(str(key.alt)), + QStandardItem(str(key.shift)), + QStandardItem(str(key.action)), + QStandardItem(str(key.params)), + QStandardItem(str(key.enabled)) + ] + + + self.hotkeysModel.insertRow(0,item) + self.hotkeysModel.setHeaderData(0, QtCore.Qt.Horizontal, "key") + self.hotkeysModel.setHeaderData(1, QtCore.Qt.Horizontal, "key_code") + self.hotkeysModel.setHeaderData(2, QtCore.Qt.Horizontal, "ctrl") + self.hotkeysModel.setHeaderData(3, QtCore.Qt.Horizontal, "alt") + self.hotkeysModel.setHeaderData(4, QtCore.Qt.Horizontal, "shift") + self.hotkeysModel.setHeaderData(5, QtCore.Qt.Horizontal, "action") + self.hotkeysModel.setHeaderData(6, QtCore.Qt.Horizontal, "params") + self.hotkeysModel.setHeaderData(7, QtCore.Qt.Horizontal, "enabled") + + self.widget.hotkeysView.setModel(self.hotkeysModel) + + + +import ctypes +from ctypes import wintypes +from ctypes import windll +from collections import namedtuple +from win32gui import GetWindowText, GetForegroundWindow +#from win32api import MapVirtualKey +import threading + + +KeyEvent=namedtuple("KeyEvent",(['event_type', 'key_code', + 'scan_code', 'alt_pressed', + 'time'])) +handlers=[] + +Hotkey=namedtuple("Hotkey", (['keycode', 'action', 'control', 'alt', 'shift', 'enabled', 'params'])) + +class LLHookey(QtCore.QObject): + Hotkeys=[] + _signalKeyEvent = QtCore.pyqtSignal(KeyEvent) + + def __init__(self): + super().__init__() + self.ctrldown = False + self.windown = False + self.shiftdown = False + self.altdown = False + self._signalKeyEvent.connect(self._onKeyEvent) + handlers.append(self._handleKeyHookEvent) + + t = threading.Thread(target=listener) + t.daemon = True + t.start() + + + def _handleKeyHookEvent(self, event): + #print("_handleKeyHookEvent") + self._signalKeyEvent.emit(event) + + @QtCore.pyqtSlot(KeyEvent) + def _onKeyEvent(self, event): + activeWin = GetWindowText(GetForegroundWindow()) + if (activeWin != "Fallout4"): + return + + #print("_onKeyEvent") + if(event.event_type == 'key up'): + if(event.key_code == 160 or event.key_code == 161): + self.shiftdown = False + if(event.key_code == 162 or event.key_code == 163): + self.ctrldown = False + if(event.key_code == 164): + self.altdown = False + if(event.key_code == 91): + self.windown = False + if(event.event_type == 'key down'): + if(event.key_code == 160 or event.key_code == 161): + self.shiftdown = True + if(event.key_code == 162 or event.key_code == 163): + self.ctrldown = True + if(event.key_code == 164): + self.altdown = True + if(event.key_code == 91): + self.windown = True + + for hk in self.Hotkeys: + if (hk.keycode == event.key_code + and hk.control == self.ctrldown + and hk.shift == self.shiftdown + and hk.alt == self.altdown): + if(hk.enabled and hk.action): + if(hk.params): + args = hk.params + hk.action(*args) + else: + hk.action() + + + #if(event.event_type == 'key down' and not event.key_code in [160,161,162,163,164,91]): + #self.addToModel(VK_KEY.get(event.key_code), str(event.key_code), str(event.scan_code), self.getModifiers(event)) + + + def getModifiers(self,event): + retstr = "" + if self.altdown: + retstr += "alt" + if self.ctrldown: + retstr += " ctrl" + if self.shiftdown: + retstr += " shift" + if self.windown: + retstr += "win" + + return retstr + + def addHotkey(self, key, action=None, control=False, alt=False, shift=False, enabled=True, params=None): + self.Hotkeys.append( Hotkey(key, action, control, alt, shift, enabled, params) ) + + def removeHotkey(self, key, control=False, alt=False, shift=False): + for hk in self.Hotkeys: + if (hk.keycode == key + and hk.control == control + and hk.shift == shift + and hk.alt == alt): + self.Hotkeys.remove(hk) + + def disableHotkey(self, key, control=False, alt=False, shift=False): + for hk in self.Hotkeys: + if (hk.keycode == key + and hk.control == control + and hk.shift == shift + and hk.alt == alt): + newhk = hk._replace(enabled=False) + self.Hotkeys.remove(hk) + self.Hotkeys.append(newhk) + + + def enableHotkey(self, key, control=False, alt=False, shift=False): + for hk in self.Hotkeys: + if (hk.keycode == key + and hk.control == control + and hk.shift == shift + and hk.alt == alt): + newhk = hk._replace(enabled=True) + self.Hotkeys.remove(hk) + self.Hotkeys.append(newhk) + + def getHotkeys(self): + return self.Hotkeys + + + + +def listener(): + #print("in listener") + """The listener listens to events and adds them to handlers""" + from ctypes import windll, CFUNCTYPE, POINTER, c_int, c_void_p, byref + import atexit + event_types = {0x100: 'key down', #WM_KeyDown for normal keys + 0x101: 'key up', #WM_KeyUp for normal keys + 0x104: 'key down', # WM_SYSKEYDOWN, used for Alt key. + 0x105: 'key up', # WM_SYSKEYUP, used for Alt key. + } + def low_level_handler(nCode, wParam, lParam): + """ + """ + """ + """ + """ + """ + """ + Processes a low level Windows keyboard event. + """ + + event = KeyEvent(event_types[wParam], lParam[0], lParam[1], + lParam[2] == 32, lParam[3]) + #print("hooked") + for h in handlers: + h(event) + #Be nice, return next hook + return windll.user32.CallNextHookEx(hook_id, nCode, wParam, lParam) + + + # Our low level handler signature. + CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p)) + # Convert the Python handler into C pointer. + pointer = CMPFUNC(low_level_handler) + #Added 4-18-15 for move to ctypes: + windll.kernel32.GetModuleHandleW.restype = wintypes.HMODULE + windll.kernel32.GetModuleHandleW.argtypes = [wintypes.LPCWSTR] + # Hook both key up and key down events for common keys (non-system). + hook_id = windll.user32.SetWindowsHookExA(0x00D, pointer, + windll.kernel32.GetModuleHandleW(None), 0) + + # Register to remove the hook when the interpreter exits. + atexit.register(windll.user32.UnhookWindowsHookEx, hook_id) + msg = windll.user32.GetMessageW(None, 0, 0,0) + windll.user32.TranslateMessage(byref(msg)) + windll.user32.DispatchMessageW(byref(msg)) + + +VK_CODE = {'backspace':0x08, + 'tab':0x09, + 'clear':0x0C, + 'enter':0x0D, + 'shift':0x10, + 'ctrl':0x11, + 'alt':0x12, + 'pause':0x13, + 'caps_lock':0x14, + 'esc':0x1B, + 'spacebar':0x20, + 'page_up':0x21, + 'page_down':0x22, + 'end':0x23, + 'home':0x24, + 'left_arrow':0x25, + 'up_arrow':0x26, + 'right_arrow':0x27, + 'down_arrow':0x28, + 'select':0x29, + 'print':0x2A, + 'execute':0x2B, + 'print_screen':0x2C, + 'ins':0x2D, + 'del':0x2E, + 'help':0x2F, + '0':0x30, + '1':0x31, + '2':0x32, + '3':0x33, + '4':0x34, + '5':0x35, + '6':0x36, + '7':0x37, + '8':0x38, + '9':0x39, + 'a':0x41, + 'b':0x42, + 'c':0x43, + 'd':0x44, + 'e':0x45, + 'f':0x46, + 'g':0x47, + 'h':0x48, + 'i':0x49, + 'j':0x4A, + 'k':0x4B, + 'l':0x4C, + 'm':0x4D, + 'n':0x4E, + 'o':0x4F, + 'p':0x50, + 'q':0x51, + 'r':0x52, + 's':0x53, + 't':0x54, + 'u':0x55, + 'v':0x56, + 'w':0x57, + 'x':0x58, + 'y':0x59, + 'z':0x5A, + 'numpad_0':0x60, + 'numpad_1':0x61, + 'numpad_2':0x62, + 'numpad_3':0x63, + 'numpad_4':0x64, + 'numpad_5':0x65, + 'numpad_6':0x66, + 'numpad_7':0x67, + 'numpad_8':0x68, + 'numpad_9':0x69, + 'multiply_key':0x6A, + 'add_key':0x6B, + 'separator_key':0x6C, + 'subtract_key':0x6D, + 'decimal_key':0x6E, + 'divide_key':0x6F, + 'F1':0x70, + 'F2':0x71, + 'F3':0x72, + 'F4':0x73, + 'F5':0x74, + 'F6':0x75, + 'F7':0x76, + 'F8':0x77, + 'F9':0x78, + 'F10':0x79, + 'F11':0x7A, + 'F12':0x7B, + 'F13':0x7C, + 'F14':0x7D, + 'F15':0x7E, + 'F16':0x7F, + 'F17':0x80, + 'F18':0x81, + 'F19':0x82, + 'F20':0x83, + 'F21':0x84, + 'F22':0x85, + 'F23':0x86, + 'F24':0x87, + 'num_lock':0x90, + 'scroll_lock':0x91, + 'left_shift':0xA0, + 'right_shift ':0xA1, + 'left_control':0xA2, + 'right_control':0xA3, + 'left_menu':0xA4, + 'right_menu':0xA5, + 'browser_back':0xA6, + 'browser_forward':0xA7, + 'browser_refresh':0xA8, + 'browser_stop':0xA9, + 'browser_search':0xAA, + 'browser_favorites':0xAB, + 'browser_start_and_home':0xAC, + 'volume_mute':0xAD, + 'volume_Down':0xAE, + 'volume_up':0xAF, + 'next_track':0xB0, + 'previous_track':0xB1, + 'stop_media':0xB2, + 'play/pause_media':0xB3, + 'start_mail':0xB4, + 'select_media':0xB5, + 'start_application_1':0xB6, + 'start_application_2':0xB7, + 'attn_key':0xF6, + 'crsel_key':0xF7, + 'exsel_key':0xF8, + 'play_key':0xFA, + 'zoom_key':0xFB, + 'clear_key':0xFE, + '+':0xBB, + ',':0xBC, + '-':0xBD, + '.':0xBE, + '/':0xBF, + '`':0xC0, + ';':0xBA, + '[':0xDB, + '\\':0xDC, + ']':0xDD, + "'":0xDE, + '`':0xC0} +VK_KEY = {v: k for k, v in VK_CODE.items()} + + + diff --git a/widgets/hotkeys/info.py b/widgets/hotkeys/info.py new file mode 100644 index 0000000..a37bc33 --- /dev/null +++ b/widgets/hotkeys/info.py @@ -0,0 +1,13 @@ + +from widgets import widgets + +from .hotkeys import HotkeyWidget + +class ModuleInfo(widgets.ModuleInfoBase): + + LABEL = 'hotkeyswidget' + NAME = 'Hotkeys' + + @staticmethod + def createWidgets(handle, parent): + return HotkeyWidget(handle, parent) diff --git a/widgets/hotkeys/ui/hotkeys.ui b/widgets/hotkeys/ui/hotkeys.ui new file mode 100644 index 0000000..5835c74 --- /dev/null +++ b/widgets/hotkeys/ui/hotkeys.ui @@ -0,0 +1,131 @@ + + + llhookeyWidget + + + + 0 + 0 + 389 + 708 + + + + Form + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 0 + + + + + Consolas + 12 + + + + Qt::NoFocus + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustIgnored + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + true + + + false + + + true + + + false + + + 24 + + + false + + + 24 + + + false + + + + + + + PushButton + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + From 1e700255e8aa917bc8304cadec4bd2751797a665 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 12:03:34 +0000 Subject: [PATCH 02/15] UseItemByFormID --- widgets/hotkeys/hotkeys.py | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index a150b42..2cfe6a9 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -33,23 +33,27 @@ def init(self, app, datamanager): self.widget.pushButton.clicked.connect(self._testButtonHandler) self.llh = LLHookey() + + #addHotkey(self, key, action=None, control=False, alt=False, shift=False, enabled=True, params=None) - self.llh.addHotkey(70, action=self.doThing) #f - self.llh.addHotkey(71, action=self.doOtherThing, control=True) #ctrl-g - self.llh.addHotkey(72, action=self.llh.disableHotkey, params=70) #h - self.llh.addHotkey(74, action=self.llh.enableHotkey, params=70) #j + #self.llh.addHotkey(70, action=self.doThing) #f + #self.llh.addHotkey(71, action=self.doOtherThing, control=True) #ctrl-g + #self.llh.addHotkey(72, action=self.llh.disableHotkey, params=70) #h + #self.llh.addHotkey(74, action=self.llh.enableHotkey, params=70) #j self.llh.addHotkey(223, action=self.useJet) #` - self.llh.addHotkey(36, action=self.useNamedItem, params=("48", "psycho")) #home + self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y self.llh.addHotkey(71, action=self.equipNextGrendae) #g + self.llh.addHotkey(188, action=self.useItemByName, params=("29", "formal hat")) #, + self.llh.addHotkey(190, action=self.useItemByFormID, params=("43", 598551)) #. self.availableGrenades = [] def equipNextGrendae(self): - getIndex = -1 + getIndex = 0 numGrenades = len(self.availableGrenades) if (numGrenades > 0): for i in range(0, numGrenades): @@ -60,15 +64,22 @@ def equipNextGrendae(self): if (getIndex == numGrenades): getIndex = 0 - if (getIndex >= 0): - self.useNamedItem("43", self.availableGrenades[getIndex][0]) + self.useItemByName("43", self.availableGrenades[getIndex][0]) def useJet(self): - self.useNamedItem("48", "jet") + self.useItemByName("48", "jet") + + def useItemByFormID(self,inventorySection, itemFormID): + if (self.pipInventoryInfo): + inventory = self.pipInventoryInfo.child(inventorySection) + for i in range(0, inventory.childCount()): + formid = inventory.child(i).child('formID').value() + if (formid == itemFormID): + self.dataManager.rpcUseItem(inventory.child(i)) - def useNamedItem(self,inventorySection, itemName): + def useItemByName(self,inventorySection, itemName): itemName = itemName.lower() if (self.pipInventoryInfo): inventory = self.pipInventoryInfo.child(inventorySection) @@ -283,7 +294,6 @@ def getHotkeys(self): def listener(): #print("in listener") - """The listener listens to events and adds them to handlers""" from ctypes import windll, CFUNCTYPE, POINTER, c_int, c_void_p, byref import atexit event_types = {0x100: 'key down', #WM_KeyDown for normal keys @@ -292,15 +302,6 @@ def listener(): 0x105: 'key up', # WM_SYSKEYUP, used for Alt key. } def low_level_handler(nCode, wParam, lParam): - """ - """ - """ - """ - """ - """ - """ - Processes a low level Windows keyboard event. - """ event = KeyEvent(event_types[wParam], lParam[0], lParam[1], lParam[2] == 32, lParam[3]) From 27928db75deecd49c2217d8f4684fc9b37c384c0 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 13:16:16 +0000 Subject: [PATCH 03/15] Hotkey improvements Refactored Hotkey from namedtuple to class Added toggleAllHotkeys Corrected(?) some VK_CODE mappings --- widgets/hotkeys/hotkeys.py | 143 ++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 80 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 2cfe6a9..956bc14 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -34,19 +34,25 @@ def init(self, app, datamanager): self.llh = LLHookey() - #addHotkey(self, key, action=None, control=False, alt=False, shift=False, enabled=True, params=None) + #self.llh.addHotkey(223, action=self.useJet) #` + #self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home + #self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y + #self.llh.addHotkey(71, action=self.equipNextGrendae) #g + #self.llh.addHotkey(188, action=self.useItemByName, params=("29", "formal hat")) #, + #self.llh.addHotkey(190, action=self.useItemByFormID, params=("43", 598551)) #. - #self.llh.addHotkey(70, action=self.doThing) #f - #self.llh.addHotkey(71, action=self.doOtherThing, control=True) #ctrl-g - #self.llh.addHotkey(72, action=self.llh.disableHotkey, params=70) #h - #self.llh.addHotkey(74, action=self.llh.enableHotkey, params=70) #j + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('`'), action=self.useJet) ) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('g'), action=self.equipNextGrendae) ) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('y'), action=datamanager.rpcUseStimpak)) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), control=True, action=self.llh.toggleAllHotkeys) ) - self.llh.addHotkey(223, action=self.useJet) #` - self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home - self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y - self.llh.addHotkey(71, action=self.equipNextGrendae) #g - self.llh.addHotkey(188, action=self.useItemByName, params=("29", "formal hat")) #, - self.llh.addHotkey(190, action=self.useItemByFormID, params=("43", 598551)) #. + #h1 = Hotkey(keycode=VK_CODE.get(','), action=self.useItemByName, params=("29", "formal hat")) + #h2 = Hotkey(keycode=VK_CODE.get('/'), action=self.llh.removeHotkey, params=(h1)) + #h3 = Hotkey(keycode=VK_CODE.get('/'), action=self.llh.disableHotkey, params=(h1)) + #self.llh.addHotkey( h1 ) + #self.llh.addHotkey( h2 ) + #self.llh.addHotkey( h3 ) + #self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('#'), action=self.llh.removeAllHotkeys) ) @@ -66,8 +72,6 @@ def equipNextGrendae(self): self.useItemByName("43", self.availableGrenades[getIndex][0]) - - def useJet(self): self.useItemByName("48", "jet") @@ -88,12 +92,6 @@ def useItemByName(self,inventorySection, itemName): if (name.lower() == itemName): self.dataManager.rpcUseItem(inventory.child(i)) - def doThing(self): - print("THING!!!!!!!!!!!!!!!!!!!!!") - - def doOtherThing(self): - print("!!!!!!!!!!!!OTHERTHING!!!!!!!!!!!!!!!!!!!!!") - def _onPipRootObjectEvent(self, rootObject): self.pipInventoryInfo = rootObject.child('Inventory') if self.pipInventoryInfo: @@ -128,12 +126,8 @@ def _testButtonHandler(self): for hk in keys: self.addToModel(hk ) - - #self.addToModel("bob") - def addToModel(self, key): - item = [ QStandardItem(""), QStandardItem(str(key.keycode)), @@ -174,7 +168,15 @@ def addToModel(self, key): 'time'])) handlers=[] -Hotkey=namedtuple("Hotkey", (['keycode', 'action', 'control', 'alt', 'shift', 'enabled', 'params'])) +class Hotkey(): + def __init__(self, keycode=None, action=None, control=False, alt=False, shift=False, enabled=True, params=None): + self.keycode = keycode + self.action = action + self.control = control + self.alt = alt + self.shift = shift + self.enabled = enabled + self.params = params class LLHookey(QtCore.QObject): Hotkeys=[] @@ -186,6 +188,9 @@ def __init__(self): self.windown = False self.shiftdown = False self.altdown = False + + self.allKeysDisabled = False + self._signalKeyEvent.connect(self._onKeyEvent) handlers.append(self._handleKeyHookEvent) @@ -203,7 +208,7 @@ def _onKeyEvent(self, event): activeWin = GetWindowText(GetForegroundWindow()) if (activeWin != "Fallout4"): return - + #print("_onKeyEvent") if(event.event_type == 'key up'): if(event.key_code == 160 or event.key_code == 161): @@ -230,61 +235,37 @@ def _onKeyEvent(self, event): and hk.shift == self.shiftdown and hk.alt == self.altdown): if(hk.enabled and hk.action): - if(hk.params): - args = hk.params - hk.action(*args) - else: - hk.action() - + if (not self.allKeysDisabled or hk.action == self.toggleAllHotkeys): - #if(event.event_type == 'key down' and not event.key_code in [160,161,162,163,164,91]): - #self.addToModel(VK_KEY.get(event.key_code), str(event.key_code), str(event.scan_code), self.getModifiers(event)) - - - def getModifiers(self,event): - retstr = "" - if self.altdown: - retstr += "alt" - if self.ctrldown: - retstr += " ctrl" - if self.shiftdown: - retstr += " shift" - if self.windown: - retstr += "win" - - return retstr + if(hk.params): + args = hk.params + try: + hk.action(*args) + except: + hk.action(hk.params) + else: + hk.action() - def addHotkey(self, key, action=None, control=False, alt=False, shift=False, enabled=True, params=None): - self.Hotkeys.append( Hotkey(key, action, control, alt, shift, enabled, params) ) + def addHotkey(self, hotkey): + self.Hotkeys.append( hotkey ) - def removeHotkey(self, key, control=False, alt=False, shift=False): - for hk in self.Hotkeys: - if (hk.keycode == key - and hk.control == control - and hk.shift == shift - and hk.alt == alt): - self.Hotkeys.remove(hk) - - def disableHotkey(self, key, control=False, alt=False, shift=False): - for hk in self.Hotkeys: - if (hk.keycode == key - and hk.control == control - and hk.shift == shift - and hk.alt == alt): - newhk = hk._replace(enabled=False) - self.Hotkeys.remove(hk) - self.Hotkeys.append(newhk) - + def removeHotkey(self, hotkey): + self.Hotkeys.remove(hotkey) + + def removeAllHotkeys(self): + self.Hotkeys.clear() + + def toggleAllHotkeys(self): + self.allKeysDisabled = not self.allKeysDisabled + + def toggleHotkey(self, hotkey): + hotkey.enabled = not hotkey.enabled + + def disableHotkey(self, hotkey): + hotkey.enabled = False - def enableHotkey(self, key, control=False, alt=False, shift=False): - for hk in self.Hotkeys: - if (hk.keycode == key - and hk.control == control - and hk.shift == shift - and hk.alt == alt): - newhk = hk._replace(enabled=True) - self.Hotkeys.remove(hk) - self.Hotkeys.append(newhk) + def enableHotkey(self, hotkey): + hotkey.enabled = True def getHotkeys(self): return self.Hotkeys @@ -465,17 +446,19 @@ def low_level_handler(nCode, wParam, lParam): 'zoom_key':0xFB, 'clear_key':0xFE, '+':0xBB, - ',':0xBC, + ',':0xBC, '-':0xBD, '.':0xBE, '/':0xBF, - '`':0xC0, + #'`':0xC0, #us layout? + "'":0xC0, #uk\euro layout + '`':0xDF, #uk\euro layout ';':0xBA, '[':0xDB, '\\':0xDC, ']':0xDD, - "'":0xDE, - '`':0xC0} + #"'":0xDE, # us layout? + '#':0xDE} #uk\euro layout VK_KEY = {v: k for k, v in VK_CODE.items()} From 164368009db5b7a5d0c2b97f6b554754fcee3393 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 17:36:10 +0000 Subject: [PATCH 04/15] Rudimentary CR_D Hotkey UI, and persistence --- widgets/hotkeys/hotkeys.py | 299 +++++++++++++++++++++++++++++----- widgets/hotkeys/ui/hotkeys.ui | 238 +++++++++++++++------------ 2 files changed, 396 insertions(+), 141 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 956bc14..4e50cad 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -6,14 +6,15 @@ from PyQt5.QtGui import * from pypipboy.types import eValueType from .. import widgets +from collections import namedtuple import logging +Action=namedtuple("Action", (['name', 'description', 'action', 'numParams'])) + class HotkeyWidget(widgets.WidgetBase): _signalInfoUpdated = QtCore.pyqtSignal() - hotkeysModel = QStandardItemModel() - def __init__(self, mhandle, parent): super().__init__('HotkeyWidget', parent) self.widget = uic.loadUi(os.path.join(mhandle.basepath, 'ui', 'hotkeys.ui')) @@ -29,11 +30,34 @@ def init(self, app, datamanager): self.dataManager = datamanager self.dataManager.registerRootObjectListener(self._onPipRootObjectEvent) self._app = app + self.llh = LLHookey() - self.widget.pushButton.clicked.connect(self._testButtonHandler) + self.widget.btnLoad.clicked.connect(self._loadButtonHandler) + self.widget.btnSave.clicked.connect(self._saveButtonHandler) + self.widget.btnDelete.clicked.connect(self._deleteButtonHandler) + self.widget.btnAdd.clicked.connect(self._addButtonHandler) - self.llh = LLHookey() + self.Actions = [] + self.Actions.append( Action('Use Jet', '', self.useJet, 0 ) ) + self.Actions.append( Action('Use Stimpak', '', datamanager.rpcUseStimpak, 0 ) ) + self.Actions.append( Action('Cycle Equipped Grenade', '', self.equipNextGrendae, 0 ) ) + self.Actions.append( Action('Toggle Hotkeys On/Off', '', self.llh.toggleAllHotkeys, 0 ) ) + self.Actions.append( Action('Use Named Item' , '(param1: Inventory Section [ie:48], param2: ItemName [ie: psycho])', self.useItemByName, 2 ) ) + + for action in self.Actions: + self.widget.actionComboBox.addItem(action.name + action.description) + + self.widget.actionComboBox.currentIndexChanged.connect(self._actionComboBoxCurrentIndexChanged) + + self.widget.param1Label.setVisible(False) + self.widget.param1LineEdit.setVisible(False) + self.widget.param2Label.setVisible(False) + self.widget.param2LineEdit.setVisible(False) + self.widget.param3Label.setVisible(False) + self.widget.param3LineEdit.setVisible(False) + + #self.llh.addHotkey(223, action=self.useJet) #` #self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home #self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y @@ -41,10 +65,12 @@ def init(self, app, datamanager): #self.llh.addHotkey(188, action=self.useItemByName, params=("29", "formal hat")) #, #self.llh.addHotkey(190, action=self.useItemByFormID, params=("43", 598551)) #. - self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('`'), action=self.useJet) ) - self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('g'), action=self.equipNextGrendae) ) - self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('y'), action=datamanager.rpcUseStimpak)) - self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), control=True, action=self.llh.toggleAllHotkeys) ) +# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('`'), action=self.useJet) ) +# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('g'), action=self.equipNextGrendae) ) +# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('y'), action=datamanager.rpcUseStimpak)) +# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), control=True, action=self.llh.toggleAllHotkeys) ) +# +# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('home'), control=True , alt=True, shift=True, action=self.useItemByName, params=("48", "psycho"))) #h1 = Hotkey(keycode=VK_CODE.get(','), action=self.useItemByName, params=("29", "formal hat")) #h2 = Hotkey(keycode=VK_CODE.get('/'), action=self.llh.removeHotkey, params=(h1)) @@ -54,7 +80,7 @@ def init(self, app, datamanager): #self.llh.addHotkey( h3 ) #self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('#'), action=self.llh.removeAllHotkeys) ) - + self.updateTable() self.availableGrenades = [] @@ -121,43 +147,219 @@ def _slotInfoUpdated(self): @QtCore.pyqtSlot() - def _testButtonHandler(self): - keys = self.llh.getHotkeys() - for hk in keys: - self.addToModel(hk ) + def _loadButtonHandler(self): + self.loadHotkeys() - def addToModel(self, key): + def loadHotkeys(self): + self.llh.Hotkeys.clear() - item = [ - QStandardItem(""), - QStandardItem(str(key.keycode)), - QStandardItem(str(key.control)), - QStandardItem(str(key.alt)), - QStandardItem(str(key.shift)), - QStandardItem(str(key.action)), - QStandardItem(str(key.params)), - QStandardItem(str(key.enabled)) - ] - - - self.hotkeysModel.insertRow(0,item) - self.hotkeysModel.setHeaderData(0, QtCore.Qt.Horizontal, "key") - self.hotkeysModel.setHeaderData(1, QtCore.Qt.Horizontal, "key_code") - self.hotkeysModel.setHeaderData(2, QtCore.Qt.Horizontal, "ctrl") - self.hotkeysModel.setHeaderData(3, QtCore.Qt.Horizontal, "alt") - self.hotkeysModel.setHeaderData(4, QtCore.Qt.Horizontal, "shift") - self.hotkeysModel.setHeaderData(5, QtCore.Qt.Horizontal, "action") - self.hotkeysModel.setHeaderData(6, QtCore.Qt.Horizontal, "params") - self.hotkeysModel.setHeaderData(7, QtCore.Qt.Horizontal, "enabled") - - self.widget.hotkeysView.setModel(self.hotkeysModel) + for index in range (0,100): + settingPath = 'hotkeys/'+str(index)+'/' + keycode = self._app.settings.value(settingPath+'keycode', None) + if(not keycode): + break + + control = self._app.settings.value(settingPath+'control', False) + alt = self._app.settings.value(settingPath+'alt', False) + shift = self._app.settings.value(settingPath+'shift', False) + params = self._app.settings.value(settingPath+'params', None) + actionname = self._app.settings.value(settingPath+'action', None) + + for action in self.Actions: + if (action.name == actionname): + hk = Hotkey(keycode=keycode, control=control, alt=alt, shift=shift, params=params, action=action.action) + self.llh.addHotkey(hk) + break + + self.updateTable() + + @QtCore.pyqtSlot() + def _saveButtonHandler(self): + self.saveHotkeys() + + def saveHotkeys(self): + for index, hk in enumerate(self.llh.Hotkeys): + settingPath = 'hotkeys/'+str(index)+'/' + self._app.settings.setValue(settingPath+'keycode', int(hk.keycode)) + self._app.settings.setValue(settingPath+'control', int(hk.control)) + self._app.settings.setValue(settingPath+'alt', int(hk.alt)) + self._app.settings.setValue(settingPath+'shift', int(hk.shift)) + if(hk.params): + self._app.settings.setValue(settingPath+'params', hk.params) + if(hk.action): + for action in self.Actions: + if (action.action == hk.action): + self._app.settings.setValue(settingPath+'action', action.name) + break + + + @QtCore.pyqtSlot() + def _addButtonHandler(self): + kc = self.widget.keycodeLineEdit.text() + kc = int(kc, 0) + actionIndex = self.widget.actionComboBox.currentIndex() + if (kc != ""): + hk = Hotkey( + keycode=kc, + control=self.widget.cbxControl.isChecked(), + alt=self.widget.cbxAlt.isChecked(), + shift=self.widget.cbxShift.isChecked(), + action=self.Actions[actionIndex].action) + + params = [] + if (self.Actions[actionIndex].numParams > 0): + params.append(self.widget.param1LineEdit.text()) + if (self.Actions[actionIndex].numParams > 1): + params.append(self.widget.param2LineEdit.text()) + if (self.Actions[actionIndex].numParams > 2): + params.append(self.widget.param3LineEdit.text()) + + hk.params = params + + + self.llh.addHotkey(hk) + self.updateTable() + + self.widget.keycodeLineEdit.setText("") + self.widget.param1LineEdit.setText("") + self.widget.param1LineEdit.setText("") + self.widget.param1LineEdit.setText("") + + self.widget.cbxControl.setChecked(False) + self.widget.cbxAlt.setChecked(False) + self.widget.cbxShift.setChecked(False) + + + @QtCore.pyqtSlot(int) + def _actionComboBoxCurrentIndexChanged(self, index): + self.widget.param1Label.setVisible(False) + self.widget.param1LineEdit.setVisible(False) + self.widget.param2Label.setVisible(False) + self.widget.param2LineEdit.setVisible(False) + self.widget.param3Label.setVisible(False) + self.widget.param3LineEdit.setVisible(False) + + if (self.Actions[index].numParams > 0): + self.widget.param1Label.setVisible(True) + self.widget.param1LineEdit.setVisible(True) + if (self.Actions[index].numParams > 1): + self.widget.param2Label.setVisible(True) + self.widget.param2LineEdit.setVisible(True) + if (self.Actions[index].numParams > 2): + self.widget.param3Label.setVisible(True) + self.widget.param3LineEdit.setVisible(True) + + @QtCore.pyqtSlot() + def _deleteButtonHandler(self): + table = self.widget.tableWidget + curIndex = table.currentIndex().row() + + #print ("curIndex:" + str(curIndex)) + if(curIndex >= 0): + item = table.item(curIndex, 0) + #print ("item:" + str(item)) + hkid = item.data(QtCore.Qt.UserRole) + if(hkid): + print ("hkid:" + str(hkid)) + hk = self.llh.getHotkeyById(hkid) + print(str(hk)) + if (hk): + self.llh.removeHotkey(hk) + self.updateTable() + + + def updateTable(self, current=None): + table = self.widget.tableWidget + table.clear() + table.setRowCount(len(self.llh.Hotkeys)) + table.setColumnCount(6) + table.setHorizontalHeaderLabels(["key", "keycode", "modifiers" ,"action", "params", "enabled"]) + table.setAlternatingRowColors(True) + table.setEditTriggers(QTableWidget.NoEditTriggers) + table.setSelectionBehavior(QTableWidget.SelectRows) + table.setSelectionMode(QTableWidget.SingleSelection) + + selected = None + + for row, hk in enumerate(self.llh.Hotkeys): + item = QTableWidgetItem(VK_KEY.get(hk.keycode)) + table.setItem(row, 0, item) + if current is not None and current == id(hk): + selected = item + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(id(hk))) + + item = QTableWidgetItem(str(hk.keycode)) + table.setItem(row, 1, item) + + item = QTableWidgetItem(hk.getModifierString()) + table.setItem(row, 2, item) + + tokenisedAction = str(hk.action).split(" ") + if (len(tokenisedAction) > 3): + item = QTableWidgetItem(tokenisedAction[2]) + else: + item = QTableWidgetItem(str(hk.action)) + + table.setItem(row, 3, item) + + item = QTableWidgetItem(str(hk.params)) + table.setItem(row, 4, item) + item = QTableWidgetItem(str(hk.enabled)) + table.setItem(row, 5, item) + + table.resizeColumnsToContents() + if selected is not None: + selected.setSelected(True) + table.setCurrentItem(selected) + table.scrollToItem(selected) + +# year = movie.year +# if year != movie.UNKNOWNYEAR: +# item = QTableWidgetItem("%d" % year) +# item.setTextAlignment(Qt.AlignCenter) +# self.table.setItem(row, 1, item) +# minutes = movie.minutes +# if minutes != movie.UNKNOWNMINUTES: +# item = QTableWidgetItem("%d" % minutes) +# item.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) +# self.table.setItem(row, 2, item) +# item = QTableWidgetItem(movie.acquired.toString(moviedata.DATEFORMAT)) +# item.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) +# self.table.setItem(row, 3, item) +# notes = movie.notes +# if notes.length() > 40: +# notes = notes.left(39) + "..." +# self.table.setItem(row, 4, QTableWidgetItem(notes)) + +# item = [ +# QStandardItem(""), +# QStandardItem(str(key.keycode)), +# QStandardItem(str(key.control)), +# QStandardItem(str(key.alt)), +# QStandardItem(str(key.shift)), +# QStandardItem(str(key.action)), +# QStandardItem(str(key.params)), +# QStandardItem(str(key.enabled)) +# ] +# +# +# self.hotkeysModel.insertRow(0,item) +# self.hotkeysModel.setHeaderData(0, QtCore.Qt.Horizontal, "key") +# self.hotkeysModel.setHeaderData(1, QtCore.Qt.Horizontal, "key_code") +# self.hotkeysModel.setHeaderData(2, QtCore.Qt.Horizontal, "ctrl") +# self.hotkeysModel.setHeaderData(3, QtCore.Qt.Horizontal, "alt") +# self.hotkeysModel.setHeaderData(4, QtCore.Qt.Horizontal, "shift") +# self.hotkeysModel.setHeaderData(5, QtCore.Qt.Horizontal, "action") +# self.hotkeysModel.setHeaderData(6, QtCore.Qt.Horizontal, "params") +# self.hotkeysModel.setHeaderData(7, QtCore.Qt.Horizontal, "enabled") +# +# self.widget.hotkeysView.setModel(self.hotkeysModel) import ctypes from ctypes import wintypes from ctypes import windll -from collections import namedtuple from win32gui import GetWindowText, GetForegroundWindow #from win32api import MapVirtualKey import threading @@ -177,6 +379,20 @@ def __init__(self, keycode=None, action=None, control=False, alt=False, shift=Fa self.shift = shift self.enabled = enabled self.params = params + + def getModifierString(self): + seperator = " " + modifiers = [] + if self.control: + modifiers.append("ctrl") + if self.alt: + modifiers.append("alt") + if self.shift: + modifiers.append("shift") + + retstr = seperator.join(modifiers) + return retstr + class LLHookey(QtCore.QObject): Hotkeys=[] @@ -270,6 +486,13 @@ def enableHotkey(self, hotkey): def getHotkeys(self): return self.Hotkeys + def getHotkeyById(self, hkid): + for hk in self.Hotkeys: + if (id(hk) == hkid): + return hk + + return None + diff --git a/widgets/hotkeys/ui/hotkeys.ui b/widgets/hotkeys/ui/hotkeys.ui index 5835c74..308a798 100644 --- a/widgets/hotkeys/ui/hotkeys.ui +++ b/widgets/hotkeys/ui/hotkeys.ui @@ -6,123 +6,155 @@ 0 0 - 389 - 708 + 564 + 414 Form - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - - - - - 0 - 0 - + + + + 0 + 200 + - - - Consolas - 12 - - - - Qt::NoFocus - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustIgnored - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - true - - - false - - - true - - - false - - - 24 - - - false - - - 24 - - - false - - - - PushButton - - - - - - - TextLabel - - + + + + + Keycode + + + + + + + + + + Action + + + + + + + + + + param1 + + + + + + + + + + param2 + + + + + + + + + + param3 + + + + + + + + + + + + Alt + + + + + + + Control + + + + + + + Shift + + + + + + - - - TextLabel + + + 0 - - - - - - TextLabel - - + + + + + 0 + 0 + + + + Add + + + + + + + + 0 + 0 + + + + delete + + + + + + + Save + + + + + + + + 0 + 0 + + + + Load + + + + From 79b2a2f508c65254ee17be29b7725f9b7541da2f Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 18:03:11 +0000 Subject: [PATCH 05/15] Changed Actions from list to dict to reduce loops --- widgets/hotkeys/hotkeys.py | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 4e50cad..8ce0cd8 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -38,15 +38,15 @@ def init(self, app, datamanager): self.widget.btnAdd.clicked.connect(self._addButtonHandler) - self.Actions = [] - self.Actions.append( Action('Use Jet', '', self.useJet, 0 ) ) - self.Actions.append( Action('Use Stimpak', '', datamanager.rpcUseStimpak, 0 ) ) - self.Actions.append( Action('Cycle Equipped Grenade', '', self.equipNextGrendae, 0 ) ) - self.Actions.append( Action('Toggle Hotkeys On/Off', '', self.llh.toggleAllHotkeys, 0 ) ) - self.Actions.append( Action('Use Named Item' , '(param1: Inventory Section [ie:48], param2: ItemName [ie: psycho])', self.useItemByName, 2 ) ) + self.Actions = {} + self.Actions['useJet'] = Action('Use Jet', '', self.useJet, 0 ) + self.Actions['useStimpak'] =Action('Use Stimpak', '', datamanager.rpcUseStimpak, 0 ) + self.Actions['useequipNextGrenade'] =Action('Cycle Equipped Grenade', '', self.equipNextGrendae, 0 ) + self.Actions['toggleAllHotkeys'] =Action('Toggle Hotkeys On/Off', '', self.llh.toggleAllHotkeys, 0 ) + self.Actions['useNamedItem'] =Action('Use Named Item' , '(param1: Inventory Section [ie:48], param2: ItemName [ie: psycho])', self.useItemByName, 2 ) - for action in self.Actions: - self.widget.actionComboBox.addItem(action.name + action.description) + for k, v in self.Actions.items(): + self.widget.actionComboBox.addItem(v.name + v.description, k) self.widget.actionComboBox.currentIndexChanged.connect(self._actionComboBoxCurrentIndexChanged) @@ -165,11 +165,11 @@ def loadHotkeys(self): params = self._app.settings.value(settingPath+'params', None) actionname = self._app.settings.value(settingPath+'action', None) - for action in self.Actions: - if (action.name == actionname): - hk = Hotkey(keycode=keycode, control=control, alt=alt, shift=shift, params=params, action=action.action) - self.llh.addHotkey(hk) - break + action = self.Actions.get(actionname, None) + + if (action): + hk = Hotkey(keycode=keycode, control=control, alt=alt, shift=shift, params=params, action=action.action) + self.llh.addHotkey(hk) self.updateTable() @@ -187,31 +187,33 @@ def saveHotkeys(self): if(hk.params): self._app.settings.setValue(settingPath+'params', hk.params) if(hk.action): - for action in self.Actions: - if (action.action == hk.action): - self._app.settings.setValue(settingPath+'action', action.name) + for k, v in self.Actions.items(): + if (v.action == hk.action): + self._app.settings.setValue(settingPath+'action', k) break + @QtCore.pyqtSlot() def _addButtonHandler(self): kc = self.widget.keycodeLineEdit.text() kc = int(kc, 0) - actionIndex = self.widget.actionComboBox.currentIndex() + data = self.widget.actionComboBox.currentData(QtCore.Qt.UserRole) + if (kc != ""): hk = Hotkey( keycode=kc, control=self.widget.cbxControl.isChecked(), alt=self.widget.cbxAlt.isChecked(), shift=self.widget.cbxShift.isChecked(), - action=self.Actions[actionIndex].action) + action=self.Actions[data].action) params = [] - if (self.Actions[actionIndex].numParams > 0): + if (self.Actions[data].numParams > 0): params.append(self.widget.param1LineEdit.text()) - if (self.Actions[actionIndex].numParams > 1): + if (self.Actions[data].numParams > 1): params.append(self.widget.param2LineEdit.text()) - if (self.Actions[actionIndex].numParams > 2): + if (self.Actions[data].numParams > 2): params.append(self.widget.param3LineEdit.text()) hk.params = params @@ -232,6 +234,8 @@ def _addButtonHandler(self): @QtCore.pyqtSlot(int) def _actionComboBoxCurrentIndexChanged(self, index): + data = self.widget.actionComboBox.currentData(QtCore.Qt.UserRole) + self.widget.param1Label.setVisible(False) self.widget.param1LineEdit.setVisible(False) self.widget.param2Label.setVisible(False) @@ -239,13 +243,13 @@ def _actionComboBoxCurrentIndexChanged(self, index): self.widget.param3Label.setVisible(False) self.widget.param3LineEdit.setVisible(False) - if (self.Actions[index].numParams > 0): + if (self.Actions[data].numParams > 0): self.widget.param1Label.setVisible(True) self.widget.param1LineEdit.setVisible(True) - if (self.Actions[index].numParams > 1): + if (self.Actions[data].numParams > 1): self.widget.param2Label.setVisible(True) self.widget.param2LineEdit.setVisible(True) - if (self.Actions[index].numParams > 2): + if (self.Actions[data].numParams > 2): self.widget.param3Label.setVisible(True) self.widget.param3LineEdit.setVisible(True) From 1c9f397c28c5e5861861c1daaf542d8e2e0ea932 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 19:24:31 +0000 Subject: [PATCH 06/15] UI Improvements and minor refactoring Keys are now picked from a combobox Actions and VK_Codes switched to OrderedDict to improve UI Hotkey now contains a key to the Actions Dict instead of the action itself Simple default hotkeys created if none could be loaded --- widgets/hotkeys/hotkeys.py | 403 +++++++++++++++++----------------- widgets/hotkeys/ui/hotkeys.ui | 58 ++--- 2 files changed, 236 insertions(+), 225 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 8ce0cd8..4a59a2c 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -7,10 +7,12 @@ from pypipboy.types import eValueType from .. import widgets from collections import namedtuple +from collections import OrderedDict import logging Action=namedtuple("Action", (['name', 'description', 'action', 'numParams'])) +Actions = OrderedDict() class HotkeyWidget(widgets.WidgetBase): _signalInfoUpdated = QtCore.pyqtSignal() @@ -38,14 +40,16 @@ def init(self, app, datamanager): self.widget.btnAdd.clicked.connect(self._addButtonHandler) - self.Actions = {} - self.Actions['useJet'] = Action('Use Jet', '', self.useJet, 0 ) - self.Actions['useStimpak'] =Action('Use Stimpak', '', datamanager.rpcUseStimpak, 0 ) - self.Actions['useequipNextGrenade'] =Action('Cycle Equipped Grenade', '', self.equipNextGrendae, 0 ) - self.Actions['toggleAllHotkeys'] =Action('Toggle Hotkeys On/Off', '', self.llh.toggleAllHotkeys, 0 ) - self.Actions['useNamedItem'] =Action('Use Named Item' , '(param1: Inventory Section [ie:48], param2: ItemName [ie: psycho])', self.useItemByName, 2 ) + Actions['equipNextGrenade'] =Action('Cycle Equipped Grenade', '', self.equipNextGrendae, 0 ) + Actions['toggleAllHotkeys'] =Action('Toggle Hotkeys On/Off', '', self.llh.toggleAllHotkeys, 0 ) + Actions['useJet'] = Action('Use Jet', '', self.useJet, 0 ) + Actions['useNamedItem'] =Action('Use Named Item' , '(param1: Inventory Section [ie:48], param2: ItemName [ie: psycho])', self.useItemByName, 2 ) + Actions['useStimpak'] =Action('Use Stimpak', '', datamanager.rpcUseStimpak, 0 ) - for k, v in self.Actions.items(): + for k, v in VK_CODE.items(): + self.widget.keyComboBox.addItem(k, v) + + for k, v in Actions.items(): self.widget.actionComboBox.addItem(v.name + v.description, k) self.widget.actionComboBox.currentIndexChanged.connect(self._actionComboBoxCurrentIndexChanged) @@ -57,6 +61,13 @@ def init(self, app, datamanager): self.widget.param3Label.setVisible(False) self.widget.param3LineEdit.setVisible(False) + self.loadHotkeys() + if(len(self.llh.Hotkeys) == 0): + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('`'), actionkey='useJet') ) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('g'), actionkey='equipNextGrenade') ) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('y'), actionkey='useStimpak')) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('pause'), shift=True, actionkey='toggleAllHotkeys') ) + self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), actionkey='useNamedItem', params=["48", "psycho"]) ) #self.llh.addHotkey(223, action=self.useJet) #` #self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home @@ -163,12 +174,12 @@ def loadHotkeys(self): alt = self._app.settings.value(settingPath+'alt', False) shift = self._app.settings.value(settingPath+'shift', False) params = self._app.settings.value(settingPath+'params', None) - actionname = self._app.settings.value(settingPath+'action', None) + actionkey = self._app.settings.value(settingPath+'action', None) - action = self.Actions.get(actionname, None) + action = Actions.get(actionkey, None) if (action): - hk = Hotkey(keycode=keycode, control=control, alt=alt, shift=shift, params=params, action=action.action) + hk = Hotkey(keycode=keycode, control=control, alt=alt, shift=shift, params=params, actionkey=actionkey) self.llh.addHotkey(hk) self.updateTable() @@ -186,34 +197,31 @@ def saveHotkeys(self): self._app.settings.setValue(settingPath+'shift', int(hk.shift)) if(hk.params): self._app.settings.setValue(settingPath+'params', hk.params) - if(hk.action): - for k, v in self.Actions.items(): - if (v.action == hk.action): - self._app.settings.setValue(settingPath+'action', k) - break + if(hk.actionkey): + self._app.settings.setValue(settingPath+'action', hk.actionkey) @QtCore.pyqtSlot() def _addButtonHandler(self): - kc = self.widget.keycodeLineEdit.text() - kc = int(kc, 0) - data = self.widget.actionComboBox.currentData(QtCore.Qt.UserRole) + kc = self.widget.keyComboBox.currentData(QtCore.Qt.UserRole) + actionkey = self.widget.actionComboBox.currentData(QtCore.Qt.UserRole) - if (kc != ""): + if (kc): hk = Hotkey( keycode=kc, control=self.widget.cbxControl.isChecked(), alt=self.widget.cbxAlt.isChecked(), shift=self.widget.cbxShift.isChecked(), - action=self.Actions[data].action) + actionkey=actionkey) - params = [] - if (self.Actions[data].numParams > 0): + params = None + if (Actions[actionkey].numParams > 0): + params = [] params.append(self.widget.param1LineEdit.text()) - if (self.Actions[data].numParams > 1): + if (Actions[actionkey].numParams > 1): params.append(self.widget.param2LineEdit.text()) - if (self.Actions[data].numParams > 2): + if (Actions[actionkey].numParams > 2): params.append(self.widget.param3LineEdit.text()) hk.params = params @@ -222,10 +230,9 @@ def _addButtonHandler(self): self.llh.addHotkey(hk) self.updateTable() - self.widget.keycodeLineEdit.setText("") - self.widget.param1LineEdit.setText("") - self.widget.param1LineEdit.setText("") self.widget.param1LineEdit.setText("") + self.widget.param2LineEdit.setText("") + self.widget.param3LineEdit.setText("") self.widget.cbxControl.setChecked(False) self.widget.cbxAlt.setChecked(False) @@ -243,13 +250,13 @@ def _actionComboBoxCurrentIndexChanged(self, index): self.widget.param3Label.setVisible(False) self.widget.param3LineEdit.setVisible(False) - if (self.Actions[data].numParams > 0): + if (Actions[data].numParams > 0): self.widget.param1Label.setVisible(True) self.widget.param1LineEdit.setVisible(True) - if (self.Actions[data].numParams > 1): + if (Actions[data].numParams > 1): self.widget.param2Label.setVisible(True) self.widget.param2LineEdit.setVisible(True) - if (self.Actions[data].numParams > 2): + if (Actions[data].numParams > 2): self.widget.param3Label.setVisible(True) self.widget.param3LineEdit.setVisible(True) @@ -282,6 +289,9 @@ def updateTable(self, current=None): table.setEditTriggers(QTableWidget.NoEditTriggers) table.setSelectionBehavior(QTableWidget.SelectRows) table.setSelectionMode(QTableWidget.SingleSelection) + table.setColumnHidden(1, True) + table.setColumnHidden(5, True) + selected = None @@ -298,11 +308,9 @@ def updateTable(self, current=None): item = QTableWidgetItem(hk.getModifierString()) table.setItem(row, 2, item) - tokenisedAction = str(hk.action).split(" ") - if (len(tokenisedAction) > 3): - item = QTableWidgetItem(tokenisedAction[2]) - else: - item = QTableWidgetItem(str(hk.action)) + action = Actions.get(hk.actionkey, None) + if (action): + item = QTableWidgetItem(action.name) table.setItem(row, 3, item) @@ -375,9 +383,9 @@ def updateTable(self, current=None): handlers=[] class Hotkey(): - def __init__(self, keycode=None, action=None, control=False, alt=False, shift=False, enabled=True, params=None): + def __init__(self, keycode=None, actionkey=None, control=False, alt=False, shift=False, enabled=True, params=None): self.keycode = keycode - self.action = action + self.actionkey = actionkey self.control = control self.alt = alt self.shift = shift @@ -454,17 +462,18 @@ def _onKeyEvent(self, event): and hk.control == self.ctrldown and hk.shift == self.shiftdown and hk.alt == self.altdown): - if(hk.enabled and hk.action): - if (not self.allKeysDisabled or hk.action == self.toggleAllHotkeys): - - if(hk.params): - args = hk.params - try: - hk.action(*args) - except: - hk.action(hk.params) - else: - hk.action() + + action = Actions.get(hk.actionkey, None) + if ( (action and hk.enabled) + and ( not self.allKeysDisabled or hk.actionkey == 'toggleAllHotkeys') ): + if(hk.params): + args = hk.params + try: + action.action(*args) + except: + action.action(hk.params) + else: + action.action() def addHotkey(self, hotkey): self.Hotkeys.append( hotkey ) @@ -537,155 +546,157 @@ def low_level_handler(nCode, wParam, lParam): windll.user32.TranslateMessage(byref(msg)) windll.user32.DispatchMessageW(byref(msg)) + +VK_CODE = OrderedDict() -VK_CODE = {'backspace':0x08, - 'tab':0x09, - 'clear':0x0C, - 'enter':0x0D, - 'shift':0x10, - 'ctrl':0x11, - 'alt':0x12, - 'pause':0x13, - 'caps_lock':0x14, - 'esc':0x1B, - 'spacebar':0x20, - 'page_up':0x21, - 'page_down':0x22, - 'end':0x23, - 'home':0x24, - 'left_arrow':0x25, - 'up_arrow':0x26, - 'right_arrow':0x27, - 'down_arrow':0x28, - 'select':0x29, - 'print':0x2A, - 'execute':0x2B, - 'print_screen':0x2C, - 'ins':0x2D, - 'del':0x2E, - 'help':0x2F, - '0':0x30, - '1':0x31, - '2':0x32, - '3':0x33, - '4':0x34, - '5':0x35, - '6':0x36, - '7':0x37, - '8':0x38, - '9':0x39, - 'a':0x41, - 'b':0x42, - 'c':0x43, - 'd':0x44, - 'e':0x45, - 'f':0x46, - 'g':0x47, - 'h':0x48, - 'i':0x49, - 'j':0x4A, - 'k':0x4B, - 'l':0x4C, - 'm':0x4D, - 'n':0x4E, - 'o':0x4F, - 'p':0x50, - 'q':0x51, - 'r':0x52, - 's':0x53, - 't':0x54, - 'u':0x55, - 'v':0x56, - 'w':0x57, - 'x':0x58, - 'y':0x59, - 'z':0x5A, - 'numpad_0':0x60, - 'numpad_1':0x61, - 'numpad_2':0x62, - 'numpad_3':0x63, - 'numpad_4':0x64, - 'numpad_5':0x65, - 'numpad_6':0x66, - 'numpad_7':0x67, - 'numpad_8':0x68, - 'numpad_9':0x69, - 'multiply_key':0x6A, - 'add_key':0x6B, - 'separator_key':0x6C, - 'subtract_key':0x6D, - 'decimal_key':0x6E, - 'divide_key':0x6F, - 'F1':0x70, - 'F2':0x71, - 'F3':0x72, - 'F4':0x73, - 'F5':0x74, - 'F6':0x75, - 'F7':0x76, - 'F8':0x77, - 'F9':0x78, - 'F10':0x79, - 'F11':0x7A, - 'F12':0x7B, - 'F13':0x7C, - 'F14':0x7D, - 'F15':0x7E, - 'F16':0x7F, - 'F17':0x80, - 'F18':0x81, - 'F19':0x82, - 'F20':0x83, - 'F21':0x84, - 'F22':0x85, - 'F23':0x86, - 'F24':0x87, - 'num_lock':0x90, - 'scroll_lock':0x91, - 'left_shift':0xA0, - 'right_shift ':0xA1, - 'left_control':0xA2, - 'right_control':0xA3, - 'left_menu':0xA4, - 'right_menu':0xA5, - 'browser_back':0xA6, - 'browser_forward':0xA7, - 'browser_refresh':0xA8, - 'browser_stop':0xA9, - 'browser_search':0xAA, - 'browser_favorites':0xAB, - 'browser_start_and_home':0xAC, - 'volume_mute':0xAD, - 'volume_Down':0xAE, - 'volume_up':0xAF, - 'next_track':0xB0, - 'previous_track':0xB1, - 'stop_media':0xB2, - 'play/pause_media':0xB3, - 'start_mail':0xB4, - 'select_media':0xB5, - 'start_application_1':0xB6, - 'start_application_2':0xB7, - 'attn_key':0xF6, - 'crsel_key':0xF7, - 'exsel_key':0xF8, - 'play_key':0xFA, - 'zoom_key':0xFB, - 'clear_key':0xFE, - '+':0xBB, - ',':0xBC, - '-':0xBD, - '.':0xBE, - '/':0xBF, - #'`':0xC0, #us layout? - "'":0xC0, #uk\euro layout - '`':0xDF, #uk\euro layout - ';':0xBA, - '[':0xDB, - '\\':0xDC, - ']':0xDD, - #"'":0xDE, # us layout? - '#':0xDE} #uk\euro layout +VK_CODE['backspace'] = 0x08 +VK_CODE['tab'] = 0x09 +VK_CODE['clear'] = 0x0C +VK_CODE['enter'] = 0x0D +VK_CODE['shift'] = 0x10 +VK_CODE['ctrl'] = 0x11 +VK_CODE['alt'] = 0x12 +VK_CODE['pause'] = 0x13 +VK_CODE['caps_lock'] = 0x14 +VK_CODE['esc'] = 0x1B +VK_CODE['spacebar'] = 0x20 +VK_CODE['page_up'] = 0x21 +VK_CODE['page_down'] = 0x22 +VK_CODE['end'] = 0x23 +VK_CODE['home'] = 0x24 +VK_CODE['left_arrow'] = 0x25 +VK_CODE['up_arrow'] = 0x26 +VK_CODE['right_arrow'] = 0x27 +VK_CODE['down_arrow'] = 0x28 +VK_CODE['select'] = 0x29 +VK_CODE['print'] = 0x2A +VK_CODE['execute'] = 0x2B +VK_CODE['print_screen'] = 0x2C +VK_CODE['ins'] = 0x2D +VK_CODE['del'] = 0x2E +VK_CODE['help'] = 0x2F +VK_CODE['0'] = 0x30 +VK_CODE['1'] = 0x31 +VK_CODE['2'] = 0x32 +VK_CODE['3'] = 0x33 +VK_CODE['4'] = 0x34 +VK_CODE['5'] = 0x35 +VK_CODE['6'] = 0x36 +VK_CODE['7'] = 0x37 +VK_CODE['8'] = 0x38 +VK_CODE['9'] = 0x39 +VK_CODE['a'] = 0x41 +VK_CODE['b'] = 0x42 +VK_CODE['c'] = 0x43 +VK_CODE['d'] = 0x44 +VK_CODE['e'] = 0x45 +VK_CODE['f'] = 0x46 +VK_CODE['g'] = 0x47 +VK_CODE['h'] = 0x48 +VK_CODE['i'] = 0x49 +VK_CODE['j'] = 0x4A +VK_CODE['k'] = 0x4B +VK_CODE['l'] = 0x4C +VK_CODE['m'] = 0x4D +VK_CODE['n'] = 0x4E +VK_CODE['o'] = 0x4F +VK_CODE['p'] = 0x50 +VK_CODE['q'] = 0x51 +VK_CODE['r'] = 0x52 +VK_CODE['s'] = 0x53 +VK_CODE['t'] = 0x54 +VK_CODE['u'] = 0x55 +VK_CODE['v'] = 0x56 +VK_CODE['w'] = 0x57 +VK_CODE['x'] = 0x58 +VK_CODE['y'] = 0x59 +VK_CODE['z'] = 0x5A +VK_CODE['numpad_0'] = 0x60 +VK_CODE['numpad_1'] = 0x61 +VK_CODE['numpad_2'] = 0x62 +VK_CODE['numpad_3'] = 0x63 +VK_CODE['numpad_4'] = 0x64 +VK_CODE['numpad_5'] = 0x65 +VK_CODE['numpad_6'] = 0x66 +VK_CODE['numpad_7'] = 0x67 +VK_CODE['numpad_8'] = 0x68 +VK_CODE['numpad_9'] = 0x69 +VK_CODE['multiply_key'] = 0x6A +VK_CODE['add_key'] = 0x6B +VK_CODE['separator_key'] = 0x6C +VK_CODE['subtract_key'] = 0x6D +VK_CODE['decimal_key'] = 0x6E +VK_CODE['divide_key'] = 0x6F +VK_CODE['F1'] = 0x70 +VK_CODE['F2'] = 0x71 +VK_CODE['F3'] = 0x72 +VK_CODE['F4'] = 0x73 +VK_CODE['F5'] = 0x74 +VK_CODE['F6'] = 0x75 +VK_CODE['F7'] = 0x76 +VK_CODE['F8'] = 0x77 +VK_CODE['F9'] = 0x78 +VK_CODE['F10'] = 0x79 +VK_CODE['F11'] = 0x7A +VK_CODE['F12'] = 0x7B +VK_CODE['F13'] = 0x7C +VK_CODE['F14'] = 0x7D +VK_CODE['F15'] = 0x7E +VK_CODE['F16'] = 0x7F +VK_CODE['F17'] = 0x80 +VK_CODE['F18'] = 0x81 +VK_CODE['F19'] = 0x82 +VK_CODE['F20'] = 0x83 +VK_CODE['F21'] = 0x84 +VK_CODE['F22'] = 0x85 +VK_CODE['F23'] = 0x86 +VK_CODE['F24'] = 0x87 +VK_CODE['num_lock'] = 0x90 +VK_CODE['scroll_lock'] = 0x91 +VK_CODE['left_shift'] = 0xA0 +VK_CODE['right_shift '] = 0xA1 +VK_CODE['left_control'] = 0xA2 +VK_CODE['right_control'] = 0xA3 +VK_CODE['left_menu'] = 0xA4 +VK_CODE['right_menu'] = 0xA5 +VK_CODE['browser_back'] = 0xA6 +VK_CODE['browser_forward'] = 0xA7 +VK_CODE['browser_refresh'] = 0xA8 +VK_CODE['browser_stop'] = 0xA9 +VK_CODE['browser_search'] = 0xAA +VK_CODE['browser_favorites'] = 0xAB +VK_CODE['browser_start_and_home'] = 0xAC +VK_CODE['volume_mute'] = 0xAD +VK_CODE['volume_Down'] = 0xAE +VK_CODE['volume_up'] = 0xAF +VK_CODE['next_track'] = 0xB0 +VK_CODE['previous_track'] = 0xB1 +VK_CODE['stop_media'] = 0xB2 +VK_CODE['play/pause_media'] = 0xB3 +VK_CODE['start_mail'] = 0xB4 +VK_CODE['select_media'] = 0xB5 +VK_CODE['start_application_1'] = 0xB6 +VK_CODE['start_application_2'] = 0xB7 +VK_CODE['attn_key'] = 0xF6 +VK_CODE['crsel_key'] = 0xF7 +VK_CODE['exsel_key'] = 0xF8 +VK_CODE['play_key'] = 0xFA +VK_CODE['zoom_key'] = 0xFB +VK_CODE['clear_key'] = 0xFE +VK_CODE['+'] = 0xBB +VK_CODE[','] = 0xBC, +VK_CODE['-'] = 0xBD +VK_CODE['.'] = 0xBE +VK_CODE['/'] = 0xBF +#VK_CODE['`'] = 0xC0, #us layout? +VK_CODE["'"] = 0xC0 #uk\euro layout +VK_CODE['`'] = 0xDF #uk\euro layout +VK_CODE[';'] = 0xBA +VK_CODE['['] = 0xDB +VK_CODE['\\'] = 0xDC +VK_CODE[']'] = 0xDD +#VK_CODE["'"]:0xDE, # us layout? +VK_CODE['#'] = 0xDE #uk\euro layout VK_KEY = {v: k for k, v in VK_CODE.items()} diff --git a/widgets/hotkeys/ui/hotkeys.ui b/widgets/hotkeys/ui/hotkeys.ui index 308a798..b2a2516 100644 --- a/widgets/hotkeys/ui/hotkeys.ui +++ b/widgets/hotkeys/ui/hotkeys.ui @@ -7,7 +7,7 @@ 0 0 564 - 414 + 412 @@ -27,14 +27,39 @@ - + - Keycode + Key - + + + + + + + + Alt + + + + + + + Control + + + + + + + Shift + + + + @@ -76,31 +101,6 @@ - - - - - - Alt - - - - - - - Control - - - - - - - Shift - - - - - From 584c4b3d2d2fb6e5d41e9fbe9f3a65f18f8ef641 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 20:30:38 +0000 Subject: [PATCH 07/15] Tidied up UI and added Instructions panel --- widgets/hotkeys/hotkeys.py | 12 +- widgets/hotkeys/ui/hotkeys.html | 44 +++++ widgets/hotkeys/ui/hotkeys.ui | 319 +++++++++++++++++++------------- 3 files changed, 243 insertions(+), 132 deletions(-) create mode 100644 widgets/hotkeys/ui/hotkeys.html diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 4a59a2c..5cab14c 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -20,6 +20,7 @@ class HotkeyWidget(widgets.WidgetBase): def __init__(self, mhandle, parent): super().__init__('HotkeyWidget', parent) self.widget = uic.loadUi(os.path.join(mhandle.basepath, 'ui', 'hotkeys.ui')) + self.widget.textBrowser.setSource(QtCore.QUrl.fromLocalFile(os.path.join(mhandle.basepath, 'ui', 'hotkeys.html'))) self._logger = logging.getLogger('pypipboyapp.llhookey') self.setWidget(self.widget) self.pipPlayerInfo = None @@ -34,6 +35,7 @@ def init(self, app, datamanager): self._app = app self.llh = LLHookey() + self.widget.btnLoad.clicked.connect(self._loadButtonHandler) self.widget.btnSave.clicked.connect(self._saveButtonHandler) self.widget.btnDelete.clicked.connect(self._deleteButtonHandler) @@ -60,6 +62,7 @@ def init(self, app, datamanager): self.widget.param2LineEdit.setVisible(False) self.widget.param3Label.setVisible(False) self.widget.param3LineEdit.setVisible(False) + self.widget.btnLoad.setVisible(False) self.loadHotkeys() if(len(self.llh.Hotkeys) == 0): @@ -165,7 +168,7 @@ def loadHotkeys(self): self.llh.Hotkeys.clear() for index in range (0,100): - settingPath = 'hotkeys/'+str(index)+'/' + settingPath = 'hotkeyswidget/keys/'+str(index)+'/' keycode = self._app.settings.value(settingPath+'keycode', None) if(not keycode): break @@ -189,8 +192,13 @@ def _saveButtonHandler(self): self.saveHotkeys() def saveHotkeys(self): + + self._app.settings.beginGroup("hotkeyswidget/keys/"); + self._app.settings.remove(""); + self._app.settings.endGroup(); + for index, hk in enumerate(self.llh.Hotkeys): - settingPath = 'hotkeys/'+str(index)+'/' + settingPath = 'hotkeyswidget/keys/'+str(index)+'/' self._app.settings.setValue(settingPath+'keycode', int(hk.keycode)) self._app.settings.setValue(settingPath+'control', int(hk.control)) self._app.settings.setValue(settingPath+'alt', int(hk.alt)) diff --git a/widgets/hotkeys/ui/hotkeys.html b/widgets/hotkeys/ui/hotkeys.html new file mode 100644 index 0000000..0ca4251 --- /dev/null +++ b/widgets/hotkeys/ui/hotkeys.html @@ -0,0 +1,44 @@ + + + + + + + +

Hotkey Instructions

+

Actions

+
+

Cycle Equipped Grenades

+
Cycles your currently equipped grenade through each of the different grenades and mines in you are carrying
+
Parameters: None
+

Use Stimpak

+
Uses a Stimpak(surprise!), if you have any
+
Parameters: None
+

Toggle Hotkeys On/Off

+
Disable/Reenable all hotkeys, potentially useful if you've mapped keys already used by the game in certain situations
+
Parameters: None
+

Use Jet

+
Take a hit of Jet, if you have any
+
Parameters: None
+

Use Named Item

+
Use any item identified by name and the Inventory section in which to find it
+
Parameters: +
    +
  1. The inventory section to search for the item named in the second parameter
  2. +
  3. The fullname of the item you want to use. This is case insensitive string comparison, so (other than case) the name must match exactly
  4. +
+
+
+ + \ No newline at end of file diff --git a/widgets/hotkeys/ui/hotkeys.ui b/widgets/hotkeys/ui/hotkeys.ui index b2a2516..be96927 100644 --- a/widgets/hotkeys/ui/hotkeys.ui +++ b/widgets/hotkeys/ui/hotkeys.ui @@ -6,155 +6,214 @@ 0 0 - 564 - 412 + 687 + 408
Form - + + + 0 + + + 0 + + + 0 + + + 0 + - - - - 0 - 200 - + + + Qt::Horizontal - - - - - - - - Key - - - - - - - - + + - - - Alt + + + + 300 + 0 + + + + false + + + QFrame::StyledPanel + + + QFrame::Sunken + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + false + + + + - - - Control + + + + 300 + 200 + - - - Shift + + + + + Key + + + + + + + + + + Modifiers + + + + + + + + + Control + + + + + + + Alt + + + + + + + Shift + + + + + + + + + Action + + + + + + + + + + Param 1 + + + + + + + + + + Param 2 + + + + + + + + + + Param 2 + + + + + + + + + + + + 0 - + + + + + 0 + 0 + + + + Add New Hotkey + + + + + + + + 0 + 0 + + + + Delete Selected Hotkey + + + + + + + Save Hotkeys + + + + + + + + 0 + 0 + + + + Load + + + + - - - - - Action - - - - - - - - - - param1 - - - - - - - - - - param2 - - - - - - - - - - param3 - - - - - - - - - - - - 0 - - - - - - 0 - 0 - - - - Add - - - - - - - - 0 - 0 - - - - delete - - - - - - - Save - - - - - - - - 0 - 0 - - - - Load - - - - + + From 50a2bd0917c7cafce331c04c56cbc417ec04bcb5 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 21:06:47 +0000 Subject: [PATCH 08/15] Hack to allow multiple actions on a single key to be more reliable --- widgets/hotkeys/hotkeys.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 5cab14c..9fb6aa1 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -383,7 +383,7 @@ def updateTable(self, current=None): from win32gui import GetWindowText, GetForegroundWindow #from win32api import MapVirtualKey import threading - +import time KeyEvent=namedtuple("KeyEvent",(['event_type', 'key_code', 'scan_code', 'alt_pressed', @@ -482,6 +482,14 @@ def _onKeyEvent(self, event): action.action(hk.params) else: action.action() + + ### This is a vile hack to allow each rpc call to complete + ### before sending the next in cases where a single hotkey + ### is bound to multiple actions + ### It'll do for now, but should really find a better way + ### of handling this, queue action method on datamanager.py + ### perhaps? + time.sleep(0.1) def addHotkey(self, hotkey): self.Hotkeys.append( hotkey ) From 10678a4d63e70abe42cf5141efefc4ca0024341c Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 21:09:28 +0000 Subject: [PATCH 09/15] Guard to prevent creation on non-Windows platforms --- widgets/hotkeys/info.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/widgets/hotkeys/info.py b/widgets/hotkeys/info.py index a37bc33..66e57a2 100644 --- a/widgets/hotkeys/info.py +++ b/widgets/hotkeys/info.py @@ -10,4 +10,7 @@ class ModuleInfo(widgets.ModuleInfoBase): @staticmethod def createWidgets(handle, parent): - return HotkeyWidget(handle, parent) + if platform.system() == 'Windows': + return HotkeyWidget(handle, parent) + else + return None From 716389c521280da38168d967c10918e916ddc45d Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 21:12:12 +0000 Subject: [PATCH 10/15] Made platform guard actually work --- widgets/hotkeys/info.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/widgets/hotkeys/info.py b/widgets/hotkeys/info.py index 66e57a2..02a797f 100644 --- a/widgets/hotkeys/info.py +++ b/widgets/hotkeys/info.py @@ -1,7 +1,7 @@ from widgets import widgets - from .hotkeys import HotkeyWidget +import platform class ModuleInfo(widgets.ModuleInfoBase): @@ -12,5 +12,5 @@ class ModuleInfo(widgets.ModuleInfoBase): def createWidgets(handle, parent): if platform.system() == 'Windows': return HotkeyWidget(handle, parent) - else + else: return None From 88b9a5efb83a917d288f9a8814699c3b3a7d7b35 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 21:50:05 +0000 Subject: [PATCH 11/15] Fleshed out documentation --- widgets/hotkeys/ui/hotkeys.html | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/widgets/hotkeys/ui/hotkeys.html b/widgets/hotkeys/ui/hotkeys.html index 0ca4251..f9fa2b5 100644 --- a/widgets/hotkeys/ui/hotkeys.html +++ b/widgets/hotkeys/ui/hotkeys.html @@ -3,40 +3,37 @@

Hotkey Instructions

-

Actions

+

Basics

+

This widget allows use hotkeys to trigger various in game actions. A small set of predefined hotkeys are included as defaults, which you can customise:

You can delete an existing hotkey by selecting it in the list, and then clicking the 'Delete Hotkey' button.

To create a new hotkey, fill in the fields below the table, and then click 'Add New Hotkey'.

To save your changes so that they are remembered when you next start PyPipBoyApp, click the 'Save Hotkeys' button.

If you've saved changes to the hotkey list and want to revert to the defaults, delete each of the hotkeys, click Save Hotkeys, and restart PyPipBoyApp.

+

If you assign more than one action to the same hotkey, all of those actions will be triggered (in the order they were defined)

+

Actions

Cycle Equipped Grenades

Cycles your currently equipped grenade through each of the different grenades and mines in you are carrying
Parameters: None
-

Use Stimpak

-
Uses a Stimpak(surprise!), if you have any
-
Parameters: None

Toggle Hotkeys On/Off

Disable/Reenable all hotkeys, potentially useful if you've mapped keys already used by the game in certain situations
Parameters: None
+

Use Stimpak

+
Uses a Stimpak(surprise!), if you have any
+
Parameters: None

Use Jet

Take a hit of Jet, if you have any
Parameters: None

Use Named Item

-
Use any item identified by name and the Inventory section in which to find it
+

Use any item identified by name and the Inventory section in which to find it. The exact meaning of use depends on item: drugs\food will be consumed, weapons\armour will be equipped (or unequipped if already equipped).

Binding multiple instances of this command to a single hotkey can be used to switch between sets of armour, or take multiple aid items at once.

Note: as this works by matching the name of the item, it will stop working if an item is renamed (ie: when armour\weapons are modified).

Parameters:
    -
  1. The inventory section to search for the item named in the second parameter
  2. -
  3. The fullname of the item you want to use. This is case insensitive string comparison, so (other than case) the name must match exactly
  4. +
  5. The inventory section to search for the item named in the second parameter. You can use the DataBrowser Widget to locate your item and determine the correct section.
  6. +
  7. The fullname of the item you want to use, without quotes. This is case insensitive string comparison, so (other than case) the name must match exactly
From 1c9b5ab9f0c0766d27866dae104013245a7a8e57 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 22:17:10 +0000 Subject: [PATCH 12/15] Removed a load of redundant code --- widgets/hotkeys/hotkeys.py | 58 ++------------------------------------ 1 file changed, 2 insertions(+), 56 deletions(-) diff --git a/widgets/hotkeys/hotkeys.py b/widgets/hotkeys/hotkeys.py index 9fb6aa1..1f4abef 100644 --- a/widgets/hotkeys/hotkeys.py +++ b/widgets/hotkeys/hotkeys.py @@ -72,19 +72,8 @@ def init(self, app, datamanager): self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('pause'), shift=True, actionkey='toggleAllHotkeys') ) self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), actionkey='useNamedItem', params=["48", "psycho"]) ) - #self.llh.addHotkey(223, action=self.useJet) #` - #self.llh.addHotkey(36, action=self.useItemByName, params=("48", "psycho")) #home - #self.llh.addHotkey(89, action=datamanager.rpcUseStimpak) #y - #self.llh.addHotkey(71, action=self.equipNextGrendae) #g - #self.llh.addHotkey(188, action=self.useItemByName, params=("29", "formal hat")) #, #self.llh.addHotkey(190, action=self.useItemByFormID, params=("43", 598551)) #. - -# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('`'), action=self.useJet) ) -# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('g'), action=self.equipNextGrendae) ) -# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('y'), action=datamanager.rpcUseStimpak)) -# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('h'), control=True, action=self.llh.toggleAllHotkeys) ) -# -# self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('home'), control=True , alt=True, shift=True, action=self.useItemByName, params=("48", "psycho"))) + #self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('home'), control=True , alt=True, shift=True, actionkey='useNamedItem', params=("48", "psycho"))) #h1 = Hotkey(keycode=VK_CODE.get(','), action=self.useItemByName, params=("29", "formal hat")) #h2 = Hotkey(keycode=VK_CODE.get('/'), action=self.llh.removeHotkey, params=(h1)) @@ -92,7 +81,6 @@ def init(self, app, datamanager): #self.llh.addHotkey( h1 ) #self.llh.addHotkey( h2 ) #self.llh.addHotkey( h3 ) - #self.llh.addHotkey( Hotkey(keycode=VK_CODE.get('#'), action=self.llh.removeAllHotkeys) ) self.updateTable() @@ -332,48 +320,7 @@ def updateTable(self, current=None): selected.setSelected(True) table.setCurrentItem(selected) table.scrollToItem(selected) - -# year = movie.year -# if year != movie.UNKNOWNYEAR: -# item = QTableWidgetItem("%d" % year) -# item.setTextAlignment(Qt.AlignCenter) -# self.table.setItem(row, 1, item) -# minutes = movie.minutes -# if minutes != movie.UNKNOWNMINUTES: -# item = QTableWidgetItem("%d" % minutes) -# item.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) -# self.table.setItem(row, 2, item) -# item = QTableWidgetItem(movie.acquired.toString(moviedata.DATEFORMAT)) -# item.setTextAlignment(Qt.AlignRight|Qt.AlignVCenter) -# self.table.setItem(row, 3, item) -# notes = movie.notes -# if notes.length() > 40: -# notes = notes.left(39) + "..." -# self.table.setItem(row, 4, QTableWidgetItem(notes)) - -# item = [ -# QStandardItem(""), -# QStandardItem(str(key.keycode)), -# QStandardItem(str(key.control)), -# QStandardItem(str(key.alt)), -# QStandardItem(str(key.shift)), -# QStandardItem(str(key.action)), -# QStandardItem(str(key.params)), -# QStandardItem(str(key.enabled)) -# ] -# -# -# self.hotkeysModel.insertRow(0,item) -# self.hotkeysModel.setHeaderData(0, QtCore.Qt.Horizontal, "key") -# self.hotkeysModel.setHeaderData(1, QtCore.Qt.Horizontal, "key_code") -# self.hotkeysModel.setHeaderData(2, QtCore.Qt.Horizontal, "ctrl") -# self.hotkeysModel.setHeaderData(3, QtCore.Qt.Horizontal, "alt") -# self.hotkeysModel.setHeaderData(4, QtCore.Qt.Horizontal, "shift") -# self.hotkeysModel.setHeaderData(5, QtCore.Qt.Horizontal, "action") -# self.hotkeysModel.setHeaderData(6, QtCore.Qt.Horizontal, "params") -# self.hotkeysModel.setHeaderData(7, QtCore.Qt.Horizontal, "enabled") -# -# self.widget.hotkeysView.setModel(self.hotkeysModel) + @@ -381,7 +328,6 @@ def updateTable(self, current=None): from ctypes import wintypes from ctypes import windll from win32gui import GetWindowText, GetForegroundWindow -#from win32api import MapVirtualKey import threading import time From 66b7780404119beb0c8a5a85da25916935112b65 Mon Sep 17 00:00:00 2001 From: akamal Date: Tue, 8 Dec 2015 23:49:36 +0000 Subject: [PATCH 13/15] Added guard on style load incase saved style does not exist --- pypipboyapp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pypipboyapp.py b/pypipboyapp.py index 2b7cc99..df09f82 100644 --- a/pypipboyapp.py +++ b/pypipboyapp.py @@ -430,6 +430,7 @@ def _genSlotSetStyles(app, name): def setStyle(self, name): + name = self.styles.get(name, 'default') if name == 'default': self.setStyleSheet('') else: From 4d3f93bae46689b3c39052297cc154f4a667d911 Mon Sep 17 00:00:00 2001 From: akamal Date: Wed, 9 Dec 2015 00:19:00 +0000 Subject: [PATCH 14/15] Fixed guard for missing saved style Previous change broke switching styles at runtime --- pypipboyapp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pypipboyapp.py b/pypipboyapp.py index df09f82..fa15afe 100644 --- a/pypipboyapp.py +++ b/pypipboyapp.py @@ -430,8 +430,7 @@ def _genSlotSetStyles(app, name): def setStyle(self, name): - name = self.styles.get(name, 'default') - if name == 'default': + if (name == 'default' or not name in self.styles): self.setStyleSheet('') else: style = self.styles[name] From e026b91cb963057c6e5af2399e7489b256d4b73d Mon Sep 17 00:00:00 2001 From: matzman666 Date: Thu, 10 Dec 2015 16:34:41 +0100 Subject: [PATCH 15/15] Moved the hotkey import statement into the operation system guard so that it does not get imported on non-Windows systems. --- widgets/hotkeys/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widgets/hotkeys/info.py b/widgets/hotkeys/info.py index 02a797f..84c41d7 100644 --- a/widgets/hotkeys/info.py +++ b/widgets/hotkeys/info.py @@ -1,6 +1,5 @@ from widgets import widgets -from .hotkeys import HotkeyWidget import platform class ModuleInfo(widgets.ModuleInfoBase): @@ -11,6 +10,7 @@ class ModuleInfo(widgets.ModuleInfoBase): @staticmethod def createWidgets(handle, parent): if platform.system() == 'Windows': + from .hotkeys import HotkeyWidget return HotkeyWidget(handle, parent) else: return None