Skip to content

Instantly share code, notes, and snippets.

@benui-dev
Forked from jeremyabel/KeyboardKeyIcons.csv
Created March 24, 2025 20:30
Show Gist options
  • Save benui-dev/2000ce84bd0e1c24896353e5ed1877f3 to your computer and use it in GitHub Desktop.
Save benui-dev/2000ce84bd0e1c24896353e5ed1877f3 to your computer and use it in GitHub Desktop.
Unreal Common Input Keyboard Action Icon Generator
import argparse
import csv
import pyperclip
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
supersample = 3
fontsize = 80
padding = 64
def main():
parser = argparse.ArgumentParser(description='Generate keyboard button icons from an input csv file')
parser.add_argument('FILEPATH', type=CSVPath, help='the input csv file')
parser.add_argument('-s', '--size', type=int, default=128)
args = vars(parser.parse_args())
size = (args['size'] * supersample, args['size'] * supersample)
font = ImageFont.truetype(font="Fonts/OpenSans-Bold.ttf", size=fontsize*supersample)
pasters = []
pasters.append('(Key=LeftMouseButton,KeyBrush=(ImageSize=(X=256.000000,Y=256.000000),ResourceObject="/Script/Engine.Texture2D\'/Game/UI/Platform/KeyboardMouse/Icon-Mouse-Button-L.Icon-Mouse-Button-L\'"))')
pasters.append('(Key=RightMouseButton,KeyBrush=(ImageSize=(X=256.000000,Y=256.000000),ResourceObject="/Script/Engine.Texture2D\'/Game/UI/Platform/KeyboardMouse/Icon-Mouse-Button-R.Icon-Mouse-Button-R\'"))')
with open(args['FILEPATH'], newline='', encoding='utf-8') as csvfile:
csvreader = csv.DictReader(csvfile)
for row in csvreader:
label = row['Label']
text = label if label else row['Key']
xoff = int(row['XOffset']) if row['XOffset'] else 0
yoff = int(row['YOffset']) if row['YOffset'] else 0
textbox = font.getbbox(text)
textsize = (textbox[2] - textbox[0], textbox[3] - textbox[1])
size = (max(args['size']*supersample, textsize[0] + (padding*supersample)), size[1])
textcenter = (((size[0] - textsize[0]) // 2) + (xoff*supersample), 20 + yoff*supersample)
im = Image.new('RGBA', size)
draw = ImageDraw.Draw(im)
draw.rounded_rectangle([(0, 0), size], radius=16*supersample, width=9*supersample)
draw.text(textcenter, text, fill="white", font=font)
im = im.resize((size[0] // supersample, size[1] // supersample), resample=Image.Resampling.LANCZOS)
filename = f'Icon-Keyboard-{row["Key"]}'
im.save(f"output/{filename}.png")
pasters.append(f'(Key={row["Key"]},KeyBrush=(ImageSize=(X={im.size[0]},Y={im.size[1]}),ResourceObject="/Script/Engine.Texture2D\'/Game/UI/Platform/KeyboardMouse/{filename}.{filename}\'"))')
joinedpasters = ','.join(pasters)
pyperclip.copy(f'({joinedpasters})')
def CSVPath(pathstr):
path = Path(pathstr)
if path.is_file():
if path.exists():
ext = path.suffix.lower()
if ext == '.csv':
return pathstr
else:
raise argparse.ArgumentTypeError(f"\"{pathstr}\" must be a .csv file")
else:
raise argparse.ArgumentTypeError(f"\"{pathstr}\" does not exist")
else:
raise argparse.ArgumentTypeError(f"\"{pathstr}\" is not a valid file path")
if __name__ == "__main__":
main();
Key Label Icon XOffset YOffset
BackSpace
Tab
Enter
Pause
CapsLock CapsLk -5
Escape Esc
SpaceBar
PageUp PgUp -5
PageDown PgDn -5
End
Home
Left
Up
Right
Down
Insert Ins
Delete Del
Zero 0
One 1
Two 2
Three 3
Four 4
Five 5
Six 6
Seven 7
Eight 8
Nine 9
A
B
C -2 1
D
E -1
F
G
H
I
J 6 -6
K
L
M
N
O
P
Q -1 -5
R
S
T
U
V
W
X
Y
Z
NumPadZero Num 0
NumPadOne Num 1
NumPadTwo Num 2
NumPadThree Num 3
NumPadFour Num 4
NumPadFive Num 5
NumPadSix Num 6
NumPadSeven Num 7
NumPadEight Num 8
NumPadNine Num 9
Multiply Num *
Add Num +
Subtract Num -
Decimal Num .
Divide Num /
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
NumLock NumLk
ScrollLock ScrLk
LeftShift L Shift
RightShift R Shift
LeftControl L Ctrl
RightControl R Ctrl
LeftAlt L Alt
RightAlt R Alt
LeftCommand L Cmd
RightCommand R Cmd
Semicolon ;
Equals =
Comma ,
Underscore _
Hyphen -
Period .
Slash /
Tilde ~
LeftBracket [ -3 -5
LeftParantheses ( -3 -5
Backslash \
RightBracket ] 3 -5
RightParantheses ) 3 -5
Apostrophe '
Quote "
Asterix *
Ampersand &
Caret ^
Dollar $
Exclamation !
Colon :
A_AccentGrave À 7
E_AccentGrave È -1 7
E_AccentAigu É -1 7
C_Cedille Ç -2 -6
Section § 2
pillow==10.2.0
pyperclip==1.8.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment