Module Euro-Truck-Simulator-2-Lane-Assist.src.controls
Input manager for other plugins. This plugin will handle all the inputs and provide a way for other plugins to use them.
# Will register a keybind to the input manager. This is necessary to use the keybind.
RegisterKeybind(name, callback=None, description="")
# Will get the value of a keybind.
GetKeybindValue(name)
Expand source code
"""
Input manager for other plugins. This plugin will handle all the inputs and provide a way for other plugins to use them.
```python
# Will register a keybind to the input manager. This is necessary to use the keybind.
RegisterKeybind(name, callback=None, description="")
# Will get the value of a keybind.
GetKeybindValue(name)
```
"""
import tkinter as tk
from tkinter import ttk
import src.helpers as helpers
import src.mainUI as mainUI
import src.variables as variables
import src.settings as settings
import os
from plugins.plugin import PluginInformation
import math
import pygame
import keyboard
from tktooltip import ToolTip
from src.logger import print
PluginInfo = PluginInformation(
name="controls", # This needs to match the folder name under plugins (this would mean plugins\Panel\main.py)
description="Provides a way to manage inputs unified for all plugins.",
version="0.1",
author="Tumppi066",
url="https://github.com/Tumppi066/Euro-Truck-Simulator-2-Lane-Assist",
type="static" # = Panel
)
KEYBOARD_GUID = 1
KEYBINDS = []
def RegisterKeybind(name:str, callback=None, notBoundInfo:str="", description:str="", axis:bool=False, defaultButtonIndex:int=-1, defaultAxisIndex:int=-1):
"""Will register a keybind to the input manager. This is necessary to use the keybind.
Args:
name (str): Keybind name. This is used to identify the keybind.
callback (_type_, optional): Callback when the keybind is pressed. Defaults to None.
notBoundInfo (str, optional): Will be shown to the user when nothing is bound. Useful for notifying of optional keybinds. Defaults to "".
description (str, optional): Additional description to the keybind. Defaults to "".
axis (bool, optional): Should the keybind be an axis.
"""
keybind = GetKeybindFromName(name)
if keybind == None: # This is the first time we've seen the keybind
SaveKeybind(name, description=description,
deviceGUID=KEYBOARD_GUID if type(defaultButtonIndex) == type("n") else -1,
buttonIndex=defaultButtonIndex,
axisIndex=defaultAxisIndex,
shouldBeAxis=axis,
notBoundInfo=notBoundInfo)
KEYBINDS.append({"name": name,
"callback": callback,
"description": description,
"deviceGUID": KEYBOARD_GUID if type(defaultButtonIndex) == type("n") else -1,
"buttonIndex": defaultButtonIndex,
"axisIndex": defaultAxisIndex,
"shouldBeAxis": axis,
"notBoundInfo": notBoundInfo})
else: # We already have data for the keybind
KEYBINDS.append({"name": name,
"callback": callback,
"description": description if description != keybind["description"] else keybind["description"],
"deviceGUID": keybind["deviceGUID"],
"buttonIndex": keybind["buttonIndex"],
"axisIndex": keybind["axisIndex"],
"shouldBeAxis": axis,
"notBoundInfo": notBoundInfo if notBoundInfo != keybind["notBoundInfo"] else keybind["notBoundInfo"]})
def GetKeybindFromName(name):
"""Get a keybind from the settings file.
Args:
name (str): Keybind name.
Returns:
dict: Keybind data.
"""
keybind = settings.GetSettings("Input", name)
return keybind
def SaveKeybind(name, description="", deviceGUID=-1, buttonIndex=-1, axisIndex=-1, shouldBeAxis=False, notBoundInfo=""):
"""Save a keybind to the settings file.
Args:
name (str): Keybind name to save.
description (str, optional): Description to save. Defaults to "".
deviceGUID (str, optional): Device GUID. Defaults to -1.
buttonIndex (int, optional): Button index. If -1 buttons will not be considered. Defaults to -1.
axisIndex (int, optional): Axis index. If -1 axis will not be considered. Defaults to -1.
notBoundInfo (str, optional): Info to show when the keybind is not bound. Defaults to "".
"""
settings.CreateSettings("Input", name, {"description": description, "deviceGUID": deviceGUID, "buttonIndex": buttonIndex, "axisIndex": axisIndex, "shouldBeAxis": shouldBeAxis, "notBoundInfo": notBoundInfo})
pygame.init()
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
def plugin(data):
"""Handles calling back the keybinds. Should not be called directly.
Args:
data (dict): Data dictionary from main.py
Returns:
dict: Data dictionary to main.py
"""
pygame.event.pump()
for keybind in KEYBINDS:
if keybind["callback"] != None:
if keybind["deviceGUID"] == KEYBOARD_GUID:
if keyboard.is_pressed(keybind["buttonIndex"]):
keybind["callback"]()
else:
for joystick in joysticks:
if joystick.get_guid() == keybind["deviceGUID"]:
if keybind["buttonIndex"] != -1:
if joystick.get_button(keybind["buttonIndex"]):
keybind["callback"]()
elif keybind["axisIndex"] != -1:
if abs(joystick.get_axis(keybind["axisIndex"])) > 0.4:
keybind["callback"]()
pass
return data
def ChangeKeybind(name:str, updateUI:bool=True, callback=None):
"""Will run the keybind change window code.
Args:
name (str): Keybind to change (name).
updateUI (bool): Whether the UI should be updated (should be False if the function is called from other files).
callback (function): Callback to run after the keybind has been changed.
"""
global save
global ignore
global currentbinding
print("Changing keybind " + name)
# Make a new window to get the keybind on
window = tk.Toplevel()
window.title("Change keybind")
mainUIPos = mainUI.root.winfo_x(), mainUI.root.winfo_y()
window.geometry(f"300x200+{mainUIPos[0] + 100}+{mainUIPos[1] + 100}")
window.resizable(False, False)
window.grab_set()
window.focus_set()
keybindToChange = KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))]
if keybindToChange == None:
print("Keybind not found")
return
# Slider for the axis
if keybindToChange["shouldBeAxis"]:
axisSlider = tk.Scale(window, from_=-1, to=1, orient="horizontal", length=200, resolution=0.01)
axisSlider.pack()
# Make a label to show the current keybind
label = ttk.Label(window, text="Listening for input...\n(expecting an axis)")
label.pack()
else:
# Make a label to show the current keybind
label = ttk.Label(window, text="Listening for input...\n(expecting a button)")
label.pack()
ttk.Label(window, text=" ").pack()
def IgnoreBind():
global ignore
ignore = True
print("Ignoring next input")
def SaveBind():
global save
save = True
window.destroy()
# Ignore button
ignoreButton = ttk.Button(window, text="Ignore", command=lambda: IgnoreBind(), width=30)
ignoreButton.pack()
# Save button
saveButton = ttk.Button(window, text="Save", command=lambda: SaveBind(), width=30)
saveButton.pack(pady=10)
# Get all devices from pygame
pygame.init()
pygame.joystick.init()
pygame.event.pump()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
import time
time.sleep(0.2)
pygame.event.pump()
# For all devices, save the default state
defaultStates = []
for joystick in joysticks:
defaultStates.append({"name": joystick.get_name(), "buttons": [joystick.get_button(i) for i in range(joystick.get_numbuttons())], "axes": [joystick.get_axis(i) for i in range(joystick.get_numaxes())]})
save = False
ignore = False
currentbinding = None
def KeyboardEvent(event):
global currentbinding
if not keybindToChange["shouldBeAxis"]:
if len(event.keysym) > 2:
return # Ignore special keys
label.config(text=f"Key: '{event.keysym}'")
currentbinding = {"deviceGUID": KEYBOARD_GUID, "buttonIndex": event.keysym}
window.bind("<Key>", KeyboardEvent)
window.protocol("WM_DELETE_WINDOW", lambda: SaveBind())
def GetDistanceFromDefault(currentVal, defaultVal):
return abs(currentVal - defaultVal)
ignoredAxis = []
ignoredButtons = []
foundAxis = False
while not save:
# Check if any of the states change
pygame.event.pump()
for i in range(len(joysticks)):
joystick = joysticks[i]
defaultState = defaultStates[i]
if not keybindToChange["shouldBeAxis"]:
for j in range(joystick.get_numbuttons()):
if defaultState["buttons"][j] != joystick.get_button(j):
if ignore:
ignoredButtons.append(j)
ignore = False
label.config(text=f"Listening for input...\n(expecting a button)")
continue
if j in ignoredButtons:
continue
label.config(text=f"Button: {j}")
currentbinding = {"deviceGUID": joystick.get_guid(), "buttonIndex": j}
if keybindToChange["shouldBeAxis"]:
for j in range(joystick.get_numaxes()):
if GetDistanceFromDefault(joystick.get_axis(j), defaultState["axes"][j]) > 0.2:
if ignore:
print("Ignoring axis " + str(j))
ignoredAxis.append(j)
ignore = False
label.config(text=f"Listening for input...\n(expecting an axis)")
axisSlider.set(0)
continue
if j in ignoredAxis:
print("Ignoring axis " + str(j))
continue
label.config(text=f"Axis: {j}")
axisSlider.set(joystick.get_axis(j))
currentbinding = {"deviceGUID": joystick.get_guid(), "axisIndex": j}
foundAxis = True
if not foundAxis and keybindToChange["shouldBeAxis"]:
axisSlider.set(0)
mainUI.root.update()
window.update()
foundAxis = False
if currentbinding != None:
SaveKeybind(name, deviceGUID=currentbinding["deviceGUID"], buttonIndex=currentbinding["buttonIndex"] if "buttonIndex" in currentbinding else -1, axisIndex=currentbinding["axisIndex"] if "axisIndex" in currentbinding else -1)
KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))] = {"name": name,
"callback": next((item for item in KEYBINDS if item["name"] == name), None)["callback"],
"description": next((item for item in KEYBINDS if item["name"] == name), None)["description"],
"deviceGUID": currentbinding["deviceGUID"],
"buttonIndex": currentbinding["buttonIndex"] if "buttonIndex" in currentbinding else -1,
"axisIndex": currentbinding["axisIndex"] if "axisIndex" in currentbinding else -1,
"shouldBeAxis": next((item for item in KEYBINDS if item["name"] == name), None)["shouldBeAxis"],
"notBoundInfo": next((item for item in KEYBINDS if item["name"] == name), None)["notBoundInfo"]}
print(f"Saved keybind {name}")
if updateUI:
mainUI.closeTabName("controls")
mainUI.switchSelectedPlugin("src.controls")
if callback != None:
callback()
def UnbindKeybind(name, updateUI=True):
"""Remove the binding of a keybind.
Args:
name (str): Keybind to remove (name).
updateUI (bool, optional): Should the UI be updated (should be False if the function is called from other files). Defaults to True.
"""
SaveKeybind(name, deviceGUID=-1, buttonIndex=-1, axisIndex=-1)
KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))] = {"name": name,
"callback": next((item for item in KEYBINDS if item["name"] == name), None)["callback"],
"description": next((item for item in KEYBINDS if item["name"] == name), None)["description"],
"deviceGUID": -1,
"buttonIndex": -1,
"axisIndex": -1,
"shouldBeAxis": next((item for item in KEYBINDS if item["name"] == name), None)["shouldBeAxis"],
"notBoundInfo": next((item for item in KEYBINDS if item["name"] == name), None)["notBoundInfo"]}
if updateUI:
mainUI.closeTabName("controls")
mainUI.switchSelectedPlugin("src.controls")
def GetKeybindValue(name:str):
"""Will get the value of a keybind.
Args:
name (str): The name of the keybind to fetch.
Returns:
float | bool | str: Depending on whether the keybind is a button, axis or key, the value will be either a float, bool or str.
"""
keybind = None
for bind in KEYBINDS:
if bind["name"] == name:
keybind = bind
break
if keybind == None:
return False
if keybind["deviceGUID"] == KEYBOARD_GUID:
try:
return True if keyboard.is_pressed(keybind["buttonIndex"]) else False
except:
return False
if keybind["buttonIndex"] == -1 and keybind["axisIndex"] == -1:
return False
pygame.event.pump()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
try:
for joystick in joysticks:
if joystick.get_guid() == keybind["deviceGUID"]:
if keybind["buttonIndex"] != -1:
return True if joystick.get_button(keybind["buttonIndex"]) == 1 else False
elif keybind["axisIndex"] != -1:
return joystick.get_axis(keybind["axisIndex"])
except:
return False
return False
class UI():
try: # The panel is in a try loop so that the logger can log errors if they occur
def __init__(self, master) -> None:
self.master = master # "master" is the mainUI window
# Check if the KEYBINDS list is empty. If so, then don't load the UI
self.loadUI()
def destroy(self):
self.done = True
self.root.destroy()
del self
def loadUI(self):
try:
self.root.destroy() # Load the UI each time this plugin is called
except: pass
self.root = tk.Canvas(self.master, width=700, height=600, border=0, highlightthickness=0)
self.root.grid_propagate(0) # Don't fit the canvast to the widgets
self.root.pack_propagate(0)
self.controlsNotebook = ttk.Notebook(self.root, width=700, height=600)
keybindCount = len(KEYBINDS)
pages = []
for i in range(math.ceil(keybindCount/6)):
pages.append(ttk.Frame(self.controlsNotebook))
self.controlsNotebook.add(pages[i], text="Page " + str(i+1))
self.controlsNotebook.pack(anchor="center", expand=False)
i = 0
page = 0
pygame.init()
pygame.joystick.init()
joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())]
for i in range(keybindCount):
keybind = KEYBINDS[i]
frame = ttk.LabelFrame(pages[page], text="Keybind - " + keybind["name"], width=700)
# Make labels for the keybind information
if keybind["deviceGUID"] != -1:
noDevice = True
if keybind["deviceGUID"] == KEYBOARD_GUID:
label = helpers.MakeLabel(frame, "Device: Keyboard", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"])
ToolTip(label, msg="GUID: " + str(KEYBOARD_GUID) + "\nThis is the keyboard connected to the computer.")
noDevice = False
for joystick in joysticks:
if joystick.get_guid() == keybind["deviceGUID"]:
label = helpers.MakeLabel(frame, "Device: " + joystick.get_name(), 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], tooltip=f"GUID: {str(joystick.get_guid())}")
ToolTip(label, msg=f"GUID: {str(joystick.get_guid())}")
noDevice = False
break
if noDevice:
label = helpers.MakeLabel(frame, "Device: Missing", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="yellow")
ToolTip(label, msg="The device that was used to bind this keybind is not connected to the computer.\nGUID: " + str(keybind["deviceGUID"]))
if keybind["buttonIndex"] != -1:
if type(keybind["buttonIndex"]) == type(""):
helpers.MakeLabel(frame, "Key: " + keybind["buttonIndex"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"])
else:
helpers.MakeLabel(frame, "Button index: " + str(keybind["buttonIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"])
elif keybind["axisIndex"] != -1:
helpers.MakeLabel(frame, "Axis index: " + str(keybind["axisIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"])
if keybind["deviceGUID"] == -1:
helpers.MakeLabel(frame, "Not bound", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="red")
if keybind["notBoundInfo"] != "":
helpers.MakeLabel(frame, keybind["notBoundInfo"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"])
button = helpers.MakeButton(frame, "Change" if not keybind["deviceGUID"] == -1 else "Bind", lambda i=i: ChangeKeybind(KEYBINDS[i]["name"]), 0, 0, sticky="e", rowspan=3)
if keybind["description"] != "":
ToolTip(button, msg=keybind["description"])
helpers.MakeButton(frame, "Remove", lambda i=i: UnbindKeybind(KEYBINDS[i]["name"]), 0, 1, sticky="e", rowspan=3, state="disabled" if keybind["deviceGUID"] == -1 else "!disabled")
frame.pack(anchor="w", fill="x", expand=False)
i += 1
if i % 6 == 0:
page += 1
i = 0
for i in range(len(pages)):
self.controlsNotebook.tab(i, text="Page " + str(i+1))
self.controlsNotebook.pack(anchor="center", expand=False)
self.root.pack(anchor="center", expand=False)
self.root.update()
def update(self, data): # When the panel is open this function is called each frame
self.root.update()
except Exception as ex:
print(ex.args)
Functions
def ChangeKeybind(name: str, updateUI: bool = True, callback=None)
-
Will run the keybind change window code.
Args
name
:str
- Keybind to change (name).
updateUI
:bool
- Whether the UI should be updated (should be False if the function is called from other files).
callback
:function
- Callback to run after the keybind has been changed.
Expand source code
def ChangeKeybind(name:str, updateUI:bool=True, callback=None): """Will run the keybind change window code. Args: name (str): Keybind to change (name). updateUI (bool): Whether the UI should be updated (should be False if the function is called from other files). callback (function): Callback to run after the keybind has been changed. """ global save global ignore global currentbinding print("Changing keybind " + name) # Make a new window to get the keybind on window = tk.Toplevel() window.title("Change keybind") mainUIPos = mainUI.root.winfo_x(), mainUI.root.winfo_y() window.geometry(f"300x200+{mainUIPos[0] + 100}+{mainUIPos[1] + 100}") window.resizable(False, False) window.grab_set() window.focus_set() keybindToChange = KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))] if keybindToChange == None: print("Keybind not found") return # Slider for the axis if keybindToChange["shouldBeAxis"]: axisSlider = tk.Scale(window, from_=-1, to=1, orient="horizontal", length=200, resolution=0.01) axisSlider.pack() # Make a label to show the current keybind label = ttk.Label(window, text="Listening for input...\n(expecting an axis)") label.pack() else: # Make a label to show the current keybind label = ttk.Label(window, text="Listening for input...\n(expecting a button)") label.pack() ttk.Label(window, text=" ").pack() def IgnoreBind(): global ignore ignore = True print("Ignoring next input") def SaveBind(): global save save = True window.destroy() # Ignore button ignoreButton = ttk.Button(window, text="Ignore", command=lambda: IgnoreBind(), width=30) ignoreButton.pack() # Save button saveButton = ttk.Button(window, text="Save", command=lambda: SaveBind(), width=30) saveButton.pack(pady=10) # Get all devices from pygame pygame.init() pygame.joystick.init() pygame.event.pump() joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())] import time time.sleep(0.2) pygame.event.pump() # For all devices, save the default state defaultStates = [] for joystick in joysticks: defaultStates.append({"name": joystick.get_name(), "buttons": [joystick.get_button(i) for i in range(joystick.get_numbuttons())], "axes": [joystick.get_axis(i) for i in range(joystick.get_numaxes())]}) save = False ignore = False currentbinding = None def KeyboardEvent(event): global currentbinding if not keybindToChange["shouldBeAxis"]: if len(event.keysym) > 2: return # Ignore special keys label.config(text=f"Key: '{event.keysym}'") currentbinding = {"deviceGUID": KEYBOARD_GUID, "buttonIndex": event.keysym} window.bind("<Key>", KeyboardEvent) window.protocol("WM_DELETE_WINDOW", lambda: SaveBind()) def GetDistanceFromDefault(currentVal, defaultVal): return abs(currentVal - defaultVal) ignoredAxis = [] ignoredButtons = [] foundAxis = False while not save: # Check if any of the states change pygame.event.pump() for i in range(len(joysticks)): joystick = joysticks[i] defaultState = defaultStates[i] if not keybindToChange["shouldBeAxis"]: for j in range(joystick.get_numbuttons()): if defaultState["buttons"][j] != joystick.get_button(j): if ignore: ignoredButtons.append(j) ignore = False label.config(text=f"Listening for input...\n(expecting a button)") continue if j in ignoredButtons: continue label.config(text=f"Button: {j}") currentbinding = {"deviceGUID": joystick.get_guid(), "buttonIndex": j} if keybindToChange["shouldBeAxis"]: for j in range(joystick.get_numaxes()): if GetDistanceFromDefault(joystick.get_axis(j), defaultState["axes"][j]) > 0.2: if ignore: print("Ignoring axis " + str(j)) ignoredAxis.append(j) ignore = False label.config(text=f"Listening for input...\n(expecting an axis)") axisSlider.set(0) continue if j in ignoredAxis: print("Ignoring axis " + str(j)) continue label.config(text=f"Axis: {j}") axisSlider.set(joystick.get_axis(j)) currentbinding = {"deviceGUID": joystick.get_guid(), "axisIndex": j} foundAxis = True if not foundAxis and keybindToChange["shouldBeAxis"]: axisSlider.set(0) mainUI.root.update() window.update() foundAxis = False if currentbinding != None: SaveKeybind(name, deviceGUID=currentbinding["deviceGUID"], buttonIndex=currentbinding["buttonIndex"] if "buttonIndex" in currentbinding else -1, axisIndex=currentbinding["axisIndex"] if "axisIndex" in currentbinding else -1) KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))] = {"name": name, "callback": next((item for item in KEYBINDS if item["name"] == name), None)["callback"], "description": next((item for item in KEYBINDS if item["name"] == name), None)["description"], "deviceGUID": currentbinding["deviceGUID"], "buttonIndex": currentbinding["buttonIndex"] if "buttonIndex" in currentbinding else -1, "axisIndex": currentbinding["axisIndex"] if "axisIndex" in currentbinding else -1, "shouldBeAxis": next((item for item in KEYBINDS if item["name"] == name), None)["shouldBeAxis"], "notBoundInfo": next((item for item in KEYBINDS if item["name"] == name), None)["notBoundInfo"]} print(f"Saved keybind {name}") if updateUI: mainUI.closeTabName("controls") mainUI.switchSelectedPlugin("src.controls") if callback != None: callback()
def GetKeybindFromName(name)
-
Get a keybind from the settings file.
Args
name
:str
- Keybind name.
Returns
dict
- Keybind data.
Expand source code
def GetKeybindFromName(name): """Get a keybind from the settings file. Args: name (str): Keybind name. Returns: dict: Keybind data. """ keybind = settings.GetSettings("Input", name) return keybind
def GetKeybindValue(name: str)
-
Will get the value of a keybind.
Args
name
:str
- The name of the keybind to fetch.
Returns
float | bool | str: Depending on whether the keybind is a button, axis or key, the value will be either a float, bool or str.
Expand source code
def GetKeybindValue(name:str): """Will get the value of a keybind. Args: name (str): The name of the keybind to fetch. Returns: float | bool | str: Depending on whether the keybind is a button, axis or key, the value will be either a float, bool or str. """ keybind = None for bind in KEYBINDS: if bind["name"] == name: keybind = bind break if keybind == None: return False if keybind["deviceGUID"] == KEYBOARD_GUID: try: return True if keyboard.is_pressed(keybind["buttonIndex"]) else False except: return False if keybind["buttonIndex"] == -1 and keybind["axisIndex"] == -1: return False pygame.event.pump() joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())] try: for joystick in joysticks: if joystick.get_guid() == keybind["deviceGUID"]: if keybind["buttonIndex"] != -1: return True if joystick.get_button(keybind["buttonIndex"]) == 1 else False elif keybind["axisIndex"] != -1: return joystick.get_axis(keybind["axisIndex"]) except: return False return False
def RegisterKeybind(name: str, callback=None, notBoundInfo: str = '', description: str = '', axis: bool = False, defaultButtonIndex: int = -1, defaultAxisIndex: int = -1)
-
Will register a keybind to the input manager. This is necessary to use the keybind.
Args
name
:str
- Keybind name. This is used to identify the keybind.
callback
:_type_
, optional- Callback when the keybind is pressed. Defaults to None.
notBoundInfo
:str
, optional- Will be shown to the user when nothing is bound. Useful for notifying of optional keybinds. Defaults to "".
description
:str
, optional- Additional description to the keybind. Defaults to "".
axis
:bool
, optional- Should the keybind be an axis.
Expand source code
def RegisterKeybind(name:str, callback=None, notBoundInfo:str="", description:str="", axis:bool=False, defaultButtonIndex:int=-1, defaultAxisIndex:int=-1): """Will register a keybind to the input manager. This is necessary to use the keybind. Args: name (str): Keybind name. This is used to identify the keybind. callback (_type_, optional): Callback when the keybind is pressed. Defaults to None. notBoundInfo (str, optional): Will be shown to the user when nothing is bound. Useful for notifying of optional keybinds. Defaults to "". description (str, optional): Additional description to the keybind. Defaults to "". axis (bool, optional): Should the keybind be an axis. """ keybind = GetKeybindFromName(name) if keybind == None: # This is the first time we've seen the keybind SaveKeybind(name, description=description, deviceGUID=KEYBOARD_GUID if type(defaultButtonIndex) == type("n") else -1, buttonIndex=defaultButtonIndex, axisIndex=defaultAxisIndex, shouldBeAxis=axis, notBoundInfo=notBoundInfo) KEYBINDS.append({"name": name, "callback": callback, "description": description, "deviceGUID": KEYBOARD_GUID if type(defaultButtonIndex) == type("n") else -1, "buttonIndex": defaultButtonIndex, "axisIndex": defaultAxisIndex, "shouldBeAxis": axis, "notBoundInfo": notBoundInfo}) else: # We already have data for the keybind KEYBINDS.append({"name": name, "callback": callback, "description": description if description != keybind["description"] else keybind["description"], "deviceGUID": keybind["deviceGUID"], "buttonIndex": keybind["buttonIndex"], "axisIndex": keybind["axisIndex"], "shouldBeAxis": axis, "notBoundInfo": notBoundInfo if notBoundInfo != keybind["notBoundInfo"] else keybind["notBoundInfo"]})
def SaveKeybind(name, description='', deviceGUID=-1, buttonIndex=-1, axisIndex=-1, shouldBeAxis=False, notBoundInfo='')
-
Save a keybind to the settings file.
Args
name
:str
- Keybind name to save.
description
:str
, optional- Description to save. Defaults to "".
deviceGUID
:str
, optional- Device GUID. Defaults to -1.
buttonIndex
:int
, optional- Button index. If -1 buttons will not be considered. Defaults to -1.
axisIndex
:int
, optional- Axis index. If -1 axis will not be considered. Defaults to -1.
notBoundInfo
:str
, optional- Info to show when the keybind is not bound. Defaults to "".
Expand source code
def SaveKeybind(name, description="", deviceGUID=-1, buttonIndex=-1, axisIndex=-1, shouldBeAxis=False, notBoundInfo=""): """Save a keybind to the settings file. Args: name (str): Keybind name to save. description (str, optional): Description to save. Defaults to "". deviceGUID (str, optional): Device GUID. Defaults to -1. buttonIndex (int, optional): Button index. If -1 buttons will not be considered. Defaults to -1. axisIndex (int, optional): Axis index. If -1 axis will not be considered. Defaults to -1. notBoundInfo (str, optional): Info to show when the keybind is not bound. Defaults to "". """ settings.CreateSettings("Input", name, {"description": description, "deviceGUID": deviceGUID, "buttonIndex": buttonIndex, "axisIndex": axisIndex, "shouldBeAxis": shouldBeAxis, "notBoundInfo": notBoundInfo})
def UnbindKeybind(name, updateUI=True)
-
Remove the binding of a keybind.
Args
name
:str
- Keybind to remove (name).
updateUI
:bool
, optional- Should the UI be updated (should be False if the function is called from other files). Defaults to True.
Expand source code
def UnbindKeybind(name, updateUI=True): """Remove the binding of a keybind. Args: name (str): Keybind to remove (name). updateUI (bool, optional): Should the UI be updated (should be False if the function is called from other files). Defaults to True. """ SaveKeybind(name, deviceGUID=-1, buttonIndex=-1, axisIndex=-1) KEYBINDS[KEYBINDS.index(next((item for item in KEYBINDS if item["name"] == name), None))] = {"name": name, "callback": next((item for item in KEYBINDS if item["name"] == name), None)["callback"], "description": next((item for item in KEYBINDS if item["name"] == name), None)["description"], "deviceGUID": -1, "buttonIndex": -1, "axisIndex": -1, "shouldBeAxis": next((item for item in KEYBINDS if item["name"] == name), None)["shouldBeAxis"], "notBoundInfo": next((item for item in KEYBINDS if item["name"] == name), None)["notBoundInfo"]} if updateUI: mainUI.closeTabName("controls") mainUI.switchSelectedPlugin("src.controls")
def plugin(data)
-
Handles calling back the keybinds. Should not be called directly.
Args
data
:dict
- Data dictionary from main.py
Returns
dict
- Data dictionary to main.py
Expand source code
def plugin(data): """Handles calling back the keybinds. Should not be called directly. Args: data (dict): Data dictionary from main.py Returns: dict: Data dictionary to main.py """ pygame.event.pump() for keybind in KEYBINDS: if keybind["callback"] != None: if keybind["deviceGUID"] == KEYBOARD_GUID: if keyboard.is_pressed(keybind["buttonIndex"]): keybind["callback"]() else: for joystick in joysticks: if joystick.get_guid() == keybind["deviceGUID"]: if keybind["buttonIndex"] != -1: if joystick.get_button(keybind["buttonIndex"]): keybind["callback"]() elif keybind["axisIndex"] != -1: if abs(joystick.get_axis(keybind["axisIndex"])) > 0.4: keybind["callback"]() pass return data
Classes
class UI (master)
-
Expand source code
class UI(): try: # The panel is in a try loop so that the logger can log errors if they occur def __init__(self, master) -> None: self.master = master # "master" is the mainUI window # Check if the KEYBINDS list is empty. If so, then don't load the UI self.loadUI() def destroy(self): self.done = True self.root.destroy() del self def loadUI(self): try: self.root.destroy() # Load the UI each time this plugin is called except: pass self.root = tk.Canvas(self.master, width=700, height=600, border=0, highlightthickness=0) self.root.grid_propagate(0) # Don't fit the canvast to the widgets self.root.pack_propagate(0) self.controlsNotebook = ttk.Notebook(self.root, width=700, height=600) keybindCount = len(KEYBINDS) pages = [] for i in range(math.ceil(keybindCount/6)): pages.append(ttk.Frame(self.controlsNotebook)) self.controlsNotebook.add(pages[i], text="Page " + str(i+1)) self.controlsNotebook.pack(anchor="center", expand=False) i = 0 page = 0 pygame.init() pygame.joystick.init() joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())] for i in range(keybindCount): keybind = KEYBINDS[i] frame = ttk.LabelFrame(pages[page], text="Keybind - " + keybind["name"], width=700) # Make labels for the keybind information if keybind["deviceGUID"] != -1: noDevice = True if keybind["deviceGUID"] == KEYBOARD_GUID: label = helpers.MakeLabel(frame, "Device: Keyboard", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) ToolTip(label, msg="GUID: " + str(KEYBOARD_GUID) + "\nThis is the keyboard connected to the computer.") noDevice = False for joystick in joysticks: if joystick.get_guid() == keybind["deviceGUID"]: label = helpers.MakeLabel(frame, "Device: " + joystick.get_name(), 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], tooltip=f"GUID: {str(joystick.get_guid())}") ToolTip(label, msg=f"GUID: {str(joystick.get_guid())}") noDevice = False break if noDevice: label = helpers.MakeLabel(frame, "Device: Missing", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="yellow") ToolTip(label, msg="The device that was used to bind this keybind is not connected to the computer.\nGUID: " + str(keybind["deviceGUID"])) if keybind["buttonIndex"] != -1: if type(keybind["buttonIndex"]) == type(""): helpers.MakeLabel(frame, "Key: " + keybind["buttonIndex"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) else: helpers.MakeLabel(frame, "Button index: " + str(keybind["buttonIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) elif keybind["axisIndex"] != -1: helpers.MakeLabel(frame, "Axis index: " + str(keybind["axisIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) if keybind["deviceGUID"] == -1: helpers.MakeLabel(frame, "Not bound", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="red") if keybind["notBoundInfo"] != "": helpers.MakeLabel(frame, keybind["notBoundInfo"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) button = helpers.MakeButton(frame, "Change" if not keybind["deviceGUID"] == -1 else "Bind", lambda i=i: ChangeKeybind(KEYBINDS[i]["name"]), 0, 0, sticky="e", rowspan=3) if keybind["description"] != "": ToolTip(button, msg=keybind["description"]) helpers.MakeButton(frame, "Remove", lambda i=i: UnbindKeybind(KEYBINDS[i]["name"]), 0, 1, sticky="e", rowspan=3, state="disabled" if keybind["deviceGUID"] == -1 else "!disabled") frame.pack(anchor="w", fill="x", expand=False) i += 1 if i % 6 == 0: page += 1 i = 0 for i in range(len(pages)): self.controlsNotebook.tab(i, text="Page " + str(i+1)) self.controlsNotebook.pack(anchor="center", expand=False) self.root.pack(anchor="center", expand=False) self.root.update() def update(self, data): # When the panel is open this function is called each frame self.root.update() except Exception as ex: print(ex.args)
Methods
def destroy(self)
-
Expand source code
def destroy(self): self.done = True self.root.destroy() del self
def loadUI(self)
-
Expand source code
def loadUI(self): try: self.root.destroy() # Load the UI each time this plugin is called except: pass self.root = tk.Canvas(self.master, width=700, height=600, border=0, highlightthickness=0) self.root.grid_propagate(0) # Don't fit the canvast to the widgets self.root.pack_propagate(0) self.controlsNotebook = ttk.Notebook(self.root, width=700, height=600) keybindCount = len(KEYBINDS) pages = [] for i in range(math.ceil(keybindCount/6)): pages.append(ttk.Frame(self.controlsNotebook)) self.controlsNotebook.add(pages[i], text="Page " + str(i+1)) self.controlsNotebook.pack(anchor="center", expand=False) i = 0 page = 0 pygame.init() pygame.joystick.init() joysticks = [pygame.joystick.Joystick(i) for i in range(pygame.joystick.get_count())] for i in range(keybindCount): keybind = KEYBINDS[i] frame = ttk.LabelFrame(pages[page], text="Keybind - " + keybind["name"], width=700) # Make labels for the keybind information if keybind["deviceGUID"] != -1: noDevice = True if keybind["deviceGUID"] == KEYBOARD_GUID: label = helpers.MakeLabel(frame, "Device: Keyboard", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) ToolTip(label, msg="GUID: " + str(KEYBOARD_GUID) + "\nThis is the keyboard connected to the computer.") noDevice = False for joystick in joysticks: if joystick.get_guid() == keybind["deviceGUID"]: label = helpers.MakeLabel(frame, "Device: " + joystick.get_name(), 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], tooltip=f"GUID: {str(joystick.get_guid())}") ToolTip(label, msg=f"GUID: {str(joystick.get_guid())}") noDevice = False break if noDevice: label = helpers.MakeLabel(frame, "Device: Missing", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="yellow") ToolTip(label, msg="The device that was used to bind this keybind is not connected to the computer.\nGUID: " + str(keybind["deviceGUID"])) if keybind["buttonIndex"] != -1: if type(keybind["buttonIndex"]) == type(""): helpers.MakeLabel(frame, "Key: " + keybind["buttonIndex"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) else: helpers.MakeLabel(frame, "Button index: " + str(keybind["buttonIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) elif keybind["axisIndex"] != -1: helpers.MakeLabel(frame, "Axis index: " + str(keybind["axisIndex"]), 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) if keybind["deviceGUID"] == -1: helpers.MakeLabel(frame, "Not bound", 0, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"], fg="red") if keybind["notBoundInfo"] != "": helpers.MakeLabel(frame, keybind["notBoundInfo"], 1, 2, sticky="w", padx=10, pady=0, font=["Segoe UI", 10, "bold"]) button = helpers.MakeButton(frame, "Change" if not keybind["deviceGUID"] == -1 else "Bind", lambda i=i: ChangeKeybind(KEYBINDS[i]["name"]), 0, 0, sticky="e", rowspan=3) if keybind["description"] != "": ToolTip(button, msg=keybind["description"]) helpers.MakeButton(frame, "Remove", lambda i=i: UnbindKeybind(KEYBINDS[i]["name"]), 0, 1, sticky="e", rowspan=3, state="disabled" if keybind["deviceGUID"] == -1 else "!disabled") frame.pack(anchor="w", fill="x", expand=False) i += 1 if i % 6 == 0: page += 1 i = 0 for i in range(len(pages)): self.controlsNotebook.tab(i, text="Page " + str(i+1)) self.controlsNotebook.pack(anchor="center", expand=False) self.root.pack(anchor="center", expand=False) self.root.update()
def update(self, data)
-
Expand source code
def update(self, data): # When the panel is open this function is called each frame self.root.update()