Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
gg 2025-05-06 09:13:08 +02:00
commit e1fa612fdb
27 changed files with 12003 additions and 0 deletions

3
open_osk.py Normal file
View File

@ -0,0 +1,3 @@
import src.lib.oskb.cli as oskbcli
oskbcli.main()

3
open_oskb.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
source venv/bin/activat
python open_osk.py

3
src/lib/oskb/__init__.py Normal file
View File

@ -0,0 +1,3 @@
import pkg_resources
from oskb.oskb import *

376
src/lib/oskb/cli.py Normal file
View File

@ -0,0 +1,376 @@
import argparse, sys, os, psutil, subprocess, re, signal
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QTimer
import pkg_resources
import oskb
from oskb import im
linux = sys.platform.startswith("linux")
if linux:
import getpass
from ewmh import EWMH, ewmh
wm = EWMH()
moved_windows = []
def command_line_arguments():
ap = argparse.ArgumentParser()
ap.add_argument(
"keyboards",
help="""Which keyboard(s) to load. These are either files or names of built-in keyboards. If
multiple keyboards are loaded, the user can switch between them with a menu key. If no keyboards are chosen
a full keyboard is shown if display is wider than 600 pixels, otherwise a phone-style keyboard is used. If
oskb comes with a keyboard for the presently set keyboard layout, that keyboard is used. Otherwise oskb
will show a US keyboard layout and switch the system keyboard layout to that.""",
metavar="<kbd>",
nargs="*",
)
ap.add_argument("--version", "-v", help="Print version number and exit.", action="store_true")
ap.add_argument(
"--start",
metavar="<kbd>",
help="""Normally the first keyboard specified is shown first. This allows you to specify one of the
loaded keyboards as the one to start with.""",
)
ap.add_argument(
"--list", action="store_true", help="Lists all built-in keyboards that were shipped with oskb.",
)
ap.add_argument(
"--dump",
help="Keyboards are JSON files. This will write the contents of a built-in keyboard to stdout.",
action="store_true",
)
ap.add_argument(
"--nomap",
help="""Prevent oskb from changing the system keyboard to the keymap specified with the keyboard.""",
action="store_true",
)
ap.add_argument(
"--toggle",
help="""Will turn the keyboard off if one is already active, otherwise starts the keyboard. This
allows one shortcut to be used to turn the keyboard on and off.""",
action="store_true",
)
ap.add_argument("--off", help="Turns off a running keyboard.", action="store_true")
ap.add_argument(
"--nopushaway",
help="Do not attempt to push other windows out of the way when showing the keyboard.",
action="store_true",
)
modmode = ap.add_mutually_exclusive_group()
modmode.add_argument(
"--flashmod",
help="""Only press the modifier keys (Alt, Shift, etc) down briefly during each keypress. This is the
default when --float is specified.""",
action="store_true",
)
modmode.add_argument(
"--steadymod",
help="""Modifier keys are pressed down as shown in interface. This is the default unless --float is
specified.""",
action="store_true",
)
ap.add_argument("--justshow", help="Show keyboard, do not send keys to OS.", action="store_true")
loc = ap.add_argument_group(title="Controlling position on screen")
loc.add_argument("-x", help="Absolute position of left side of keyboard", metavar="<x>", type=int)
loc.add_argument("-y", help="Absolute position of top of keyboard", metavar="<y>", type=int)
loc.add_argument("--width",
help="Keyboard width in pixels. The default is to use the full width of the primary display.",
metavar="<width>",
type=int,
)
loc.add_argument(
"--height",
help="Keyboard height in pixels. The default is a third of the height of the primary display.",
metavar="<height>",
type=int,
)
hpos = loc.add_mutually_exclusive_group()
hpos.add_argument("--left", help="Keyboard docks to the left side of the screen.", action="store_true")
hpos.add_argument(
"--middle",
"--center",
help="Keyboard docks in the middle of the screen. This is the default.",
action="store_true",
)
hpos.add_argument("--right", help="Keyboard docks to the right side of the screen.", action="store_true")
vpos = loc.add_mutually_exclusive_group()
vpos.add_argument(
"--top", help="Keyboard docks to the top of the screen. This is the default.", action="store_true"
)
vpos.add_argument("--bottom", help="Keyboard docks to the bottom of the screen.", action="store_true")
loc.add_argument(
"--float", help="Floating keyboard window instead of fixed docked position.", action="store_true",
)
return ap
def main():
global x, y, w, h
#
# Parse command line arguments
#
ap = command_line_arguments()
cmdline = ap.parse_args("")
if cmdline.version:
print(pkg_resources.get_distribution("oskb").version)
sys.exit(0)
if cmdline.list:
for k in pkg_resources.resource_listdir("oskb", "keyboards"):
if not k.startswith("_"):
print(k)
sys.exit(0)
if cmdline.dump:
if len(cmdline.keyboards) != 1:
sys.stderr.write("Must specify exactly one built-in keyboard to dump.\n")
sys.exit(-1)
if not pkg_resources.resource_exists("oskb", "keyboards/" + cmdline.keyboards[0]):
sys.stderr.write("Built-in keyboard '" + cmdline.keyboards[0] + "' not found.\n")
sys.exit(-1)
print(pkg_resources.resource_string("oskb", "keyboards/" + cmdline.keyboards[0]).decode("utf-8"))
sys.exit(0)
#
# Kill any existing keyboard instances. If we did end up killing existing keyboards
# only start up if '--toggle' wasn't specified. It allows the same command line to
# be used to turn the keyboard on and off. '--off' just kills keyboard processes.
#
ikilled = False
mypid = os.getpid()
myparent = os.getppid()
myname = "oskb"
for proc in psutil.process_iter(attrs=(["pid"])):
try:
if not proc.pid == mypid and not proc.pid == myparent:
itsname = os.path.basename(proc.name())
if re.match("^[Pp]ython\d*$", itsname) and len(proc.cmdline()) > 1:
itsname = os.path.basename(proc.cmdline()[1])
if itsname == myname:
proc.send_signal(9)
ikilled = True
except:
pass
if (ikilled and cmdline.toggle) or cmdline.off:
sys.exit()
#
# Start the Qt context
#
app = QApplication([])
#
# Make sure Ctrl-C can interrupt oskb
#
def sigint_handler(*args):
sys.stderr.write("\r")
QApplication.quit()
signal.signal(signal.SIGINT, sigint_handler)
timer = QTimer()
timer.start(250)
timer.timeout.connect(lambda: None)
#
# Get our keyboard widget instance
#
keyboard = oskb.Keyboard()
if cmdline.float:
keyboard.setWindowTitle("On-Screen Keyboard")
keyboard.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus)
if not cmdline.steadymod:
keyboard.setFlashModifiers(True)
else:
if linux and not cmdline.nopushaway:
keyboard.sendScreenState(receiveScreenState)
if not cmdline.flashmod:
keyboard.setFlashModifiers(False)
# quickly make sure X doesn't make a window frame etc.
keyboard.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint)
# Qt.X11BypassWindowManagerHint means no WM border or title, no application focus, not in taskbar
# also works but creates taskbar entry that will application focus to oskb if pressed
# ( Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus | Qt.FramelessWindowHint)
#
# Figure out where and how big we're going to be on the screen
#
# First take the screen dimensions
desktop = app.desktop()
screen = desktop.screenGeometry()
screenleft = 0
screentop = 0
screenwidth = screen.width()
screenheight = screen.height()
# On Linux see if we can improve on that by asking windowmanager for workarea (minus taskbar etc).
if linux:
try:
screenleft, screentop, screenwidth, screenheight = list(wm.getWorkArea()[0:4])
except:
pass
# set width and height from arguments, defaulting to screen width and quarter of screen height resp.
w = cmdline.width if cmdline.width else screenwidth
h = cmdline.height if cmdline.height else max(250, int(screenheight / 3))
mw, mh = 70, 70 # Size of minimized keyboard button
# Vertical position
if cmdline.y:
y = cmdline.y
else:
if cmdline.top:
y = screentop
my = screentop
else:
y = screentop + screenheight - h
my = screentop + screenheight - mh
# Horizontal position
if cmdline.x:
x = cmdline.x
else:
if cmdline.left:
x = screenleft
mx = screenleft
elif cmdline.right:
x = screenleft + screenwidth - w
mx = screenleft + screenwidth - mw
else:
x = int(screenleft + (screenwidth / 2) - (w / 2))
mx = int(screenleft + (screenwidth / 2) - (mw / 2))
# Set geometry accordingly
keyboard.setMinimizer(mx, my, mw, mh)
keyboard.setGeometry(x, y, w, h)
#
# Figure out default keyboard if nothing selected
#
# (Has to be done before plugging in virtual keyboard, bc that switches map to 'us')
load_keyboards = cmdline.keyboards
if load_keyboards == []:
kbname = 'paddy' if screenwidth > 600 else 'phoney'
tryfirst = kbname + "-" + querySystemKeymap("layout")
if pkg_resources.resource_exists("oskb", "keyboards/" + tryfirst):
load_keyboards = [tryfirst]
else:
load_keyboards = [kbname + "-us"]
#
# Tell keyboard to send the keypresses to the default handler for OS
#
if not cmdline.justshow:
plugged = False
try:
plugged = keyboard.sendKeys(im.default().receiveKeys)
except:
sys.stderr.write("Could not set up the virtual keyboard.\n")
if not plugged:
if linux:
user = getpass.getuser()
sys.stderr.write(
"Try 'sudo setfacl -m m::rw -m u:" + user + ":rw /dev/uinput /dev/input/*'\n"
"See the oskb documentation for more information.\n"
)
else:
sys.stderr.write(
"Your platform is not yet supported by oskd. Try --justshow if\n"
"you just want to see how pretty oskb is.\n"
)
sys.exit(-1)
#
# Tell oskb where to send keymap changes, so we can call setxkbmap to switch as well
#
if not cmdline.nomap:
keyboard.sendMapChanges(receiveMapChanges)
#
# Load the keyboard files
#
for k in load_keyboards:
keyboard.readKeyboard(k)
# Also works if no startup kbd is specified, because None will load first keyboard
keyboard.setKeyboard(cmdline.start)
#
# Display the keyboard
#
keyboard.show()
sys.exit(app.exec_())
def querySystemKeymap(key, default = None):
try:
output = subprocess.check_output(['setxkbmap', '-query']).decode("utf-8")
match = re.search(key + ":\s+(\w+)", output)
if match:
return match.group(1)
else:
return default
except:
return default
def receiveMapChanges(keymap):
try:
subprocess.run(["setxkbmap"] + keymap.split(" "))
except:
pass
def receiveScreenState(maximize):
def get_geometry(window):
g = window.get_geometry()
return g.x, g.y, g.width, g.height
def frame(window):
frame = window
while frame.query_tree().parent != wm.root:
frame = frame.query_tree().parent
return frame
global moved_windows
if maximize:
moved_windows = []
for window in wm.getClientList():
wn = wm.getWmName(window).decode("utf-8")
wx, wy, ww, wh = get_geometry(window)
fx, fy, fw, fh = get_geometry(frame(window))
bottom = fy + fh
if bottom > y and bottom < y + h and fy < y + h:
need = bottom - y + (fh - wh)
moveby = min(fy, need)
shrinkby = need - moveby
nx, nw = fx, fw
ny = fy - moveby
nh = bottom - fy - shrinkby
wm.setMoveResizeWindow(window, gravity=ewmh.X.SouthWestGravity, x=nx, y=ny, w=nw, h=nh)
moved_windows.append((window, (fx, fy, fw - (fw - ww), fh - (fh - wh)), (nx, ny, nw, nh)))
else:
for w in moved_windows:
window = w[0]
(nx, ny, nw, nh) = w[1]
wm.setMoveResizeWindow(window, gravity=ewmh.X.SouthWestGravity, x=nx, y=ny, w=nw, h=nh)
wm.display.flush()

33
src/lib/oskb/default.css Normal file
View File

@ -0,0 +1,33 @@
QWidget {
background-color: #cccccc;
margin:0px;
}
QLabel {
font-size: 50%;
background: transparent;
margin: 2px 5px;
qproperty-alignment: 'AlignBottom | AlignLeft';
}
.key {
background-color: #eeeeee;
border: 1px solid black;
font-size: _OSKB_FONTSIZE_px;
margin: _OSKB_MARGIN_px;
border-radius: _OSKB_RADIUS_px;
}
.spacer, .emptyrow {
background-color: #cccccc;
border: none;
color: #cccccc;
}
.key:not(modifier):pressed, .key.pseudomodifier, .key.held {
background-color: #999999;
}
.view .key.pseudomodifier, .key.locked {
background-color: #cc9999;
}

View File

@ -0,0 +1,20 @@
import sys, importlib, pkg_resources
from os.path import basename
# All this does is "from * import *", if python only accepted that
# https://stackoverflow.com/questions/43059267/how-to-do-from-module-import-using-importlib
for module in pkg_resources.resource_listdir("oskb.im", ""):
if module.startswith("_"):
continue
module = basename(module)[:-3]
modhandle = importlib.import_module("oskb.im." + module)
names = [x for x in modhandle.__dict__ if not x.startswith("_")]
globals().update({k: getattr(modhandle, k) for k in names})
# Return a default handler for a given platform
def default():
if sys.platform.startswith("linux"):
return UInput()

13
src/lib/oskb/im/uinput.py Normal file
View File

@ -0,0 +1,13 @@
import sys
if sys.platform.startswith("linux"):
import evdev
class UInput:
def __init__(self):
self.uinput = evdev.UInput(name="oskb")
def receiveKeys(self, keycode, keyevent):
self.uinput.write(evdev.ecodes.EV_KEY, keycode, keyevent)
self.uinput.syn()

18
src/lib/oskb/keyboards/_new Executable file
View File

@ -0,0 +1,18 @@
{
"format": "oskb keyboard",
"formatversion": 1,
"description": "unnamed keyboard",
"views": {
"default" : {
"columns": [
{
"rows": [
{
"keys": []
}
]
}
]
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

661
src/lib/oskb/oskb.py Normal file
View File

@ -0,0 +1,661 @@
import os, sys, re, json, subprocess
from functools import partial
import pkg_resources
from PyQt5.QtCore import QTimer, QRect, QSysInfo, QEvent, QSize, Qt
from PyQt5.QtWidgets import (
QWidget,
QPushButton,
QMainWindow,
QGridLayout,
QHBoxLayout,
QSizePolicy,
QLayout,
QStackedLayout,
QLabel,
)
RELEASED = 0
PRESSED = 1
COLUMN_MARGIN = 0.1
# key detection timings in milliseconds
LONGPRESS_TIMEOUT = 350
DOUBLECLICK_TIMEOUT = 200
# The keyboard file format has its own version numbering
KEYBOARDFILE_VERSION = 1
class Keyboard(QWidget):
def __init__(self):
super().__init__()
self._modifiers = {}
self._flashmodifiers = True
# This is all for the key-detection state-machine
self._longpresswait = False
self._longtimer = QTimer()
self._stopsinglepress = False
self._doublebutton = None
self._doubletimer = QTimer()
self._doubletimer.setSingleShot(True)
self._doubletimer.timeout.connect(self._doubleTimeout)
self._viewindex = None
self._kbdname = None
self._viewuntil = None
self._thenview = None
self._kbds = {}
# Create the special 'chooser' keyboard that shows all the loaded keyboards
self._kbds["_chooser"] = {
"views": {"default": {"columns": [{"rows": []}]}},
}
# Create the special 'minimized' keyboard that shows one small button
self._kbds["_minimized"] = {
"style": "QWidget {background: transparent;}",
"views": {
"default": {
"columns": [
{"rows": [{"keys": [{"caption": "", "single": {"keyboard": {"name": "back"}}}]}]}
],
}
},
}
self._view = None
self._viewname = "default"
self._kbd = None
self._sendkeys = None
self._sendmapchanges = None
self._sendscreenstate = None
self._buttonhandler = self._oskbButtonHandler
self._minimizerlocation = QRect(0, 0, 70, 70)
self._kbdstack = QStackedLayout(self)
self._stylesheet = pkg_resources.resource_string("oskb", "default.css").decode("utf-8")
#
# Reimplemented Qt methods
#
# Make sure show events also calculate proper sizes and first initialise if that hasn't happened yet.
def showEvent(self, event):
self.updateKeyboard()
QWidget.showEvent(self, event)
# Recalculate the fontsize and mrgaing and change the stylesheets when resizing
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
if self._view and self.isVisible():
self.updateKeyboard()
# We just store the stylesheet, and then only do the super().setStyleSheet() when we've
# recalculated values in updateKeyboard()
def setStyleSheet(self, stylesheet):
self._stylesheet = stylesheet
#
# Our own public
#
# specify callback that receives keymap information when user switches keyboards using _chooser
def sendMapChanges(self, function):
if callable(function):
self._sendmapchanges = function
return True
return False
def sendScreenState(self, function):
if callable(function):
self._sendscreenstate = function
return True
return False
def sendKeys(self, function):
if callable(function):
self._sendkeys = function
return True
return False
def setButtonHandler(self, handler=None):
if not handler:
handler = self._oskbButtonHandler
self._buttonhandler = handler
def setMinimizer(self, mx, my, mw, mh):
self._minimizerlocation = QRect(mx, my, mw, mh)
def setFlashModifiers(self, mode):
self._flashmodifiers = mode
def readKeyboard(self, kbdfile):
kbd = None
if os.access(kbdfile, os.R_OK):
with open(kbdfile, "r", encoding="utf-8") as f:
kbd = json.load(f)
elif kbdfile == os.path.basename(kbdfile) and pkg_resources.resource_exists(
"oskb", "keyboards/" + kbdfile
):
kbd = json.loads(pkg_resources.resource_string("oskb", "keyboards/" + kbdfile))
if not kbd:
raise FileNotFoundError("Could not find " + kbdfile)
if kbd.get("format") != "oskb keyboard":
raise RuntimeError("Not an oskb keyboard file")
if kbd.get("formatversion") > KEYBOARDFILE_VERSION:
raise RuntimeError("oskb keyboard file for newer oskb version. You must upgrade.")
kbdname = os.path.basename(kbdfile)
self._kbds[kbdname] = kbd
self._updateChooser()
self.initKeyboards()
return os.path.basename(kbdfile)
def getView(self):
return self._viewname
def getViews(self):
return self._kbd["views"].keys()
def _updateChooser(self):
if not self._kbds.get("_chooser"):
return
therows = self._kbds["_chooser"]["views"]["default"]["columns"][0]["rows"]
therows.clear()
for kbdname, kbd in self._kbds.items():
if kbdname.startswith("_"):
continue
therows.append(
{"keys": [{"caption": kbd.get("description"), "single": {"keyboard": {"name": kbdname}},}]}
)
def setKeyboard(self, kbdname=None):
if self._sendscreenstate:
self._sendscreenstate(kbdname != "_minimized")
newgeometry = None
if kbdname == "_minimized":
newgeometry = self._minimizerlocation
else:
if kbdname == "back":
kbdname = self._previouskeyboard
if self._previousgeometry != self.geometry():
newgeometry = self._previousgeometry
for n, k in self._kbds.items():
if kbdname and kbdname != n:
continue
if not kbdname and n.startswith("_"):
continue
self._kbdname = n
self._kbd = k
# print("setKeybaord picked ", n)
self._releaseModifiers()
if self._sendmapchanges and k.get("keymap"):
self._sendmapchanges(k.get("keymap"))
if newgeometry:
self.hide()
if kbdname != "_minimized":
self._previouskeyboard = n
self._previousgeometry = self.geometry()
self._kbdstack.setCurrentIndex(k.get("_stackindex", 0))
if self._kbd["views"].get(self._viewname):
self.setView(self._viewname, newgeometry)
else:
self.setView("default", newgeometry)
return True
return False
def setView(self, viewname, newgeometry=None):
# print ("setView", viewname)
if self._kbd["views"].get(viewname):
self._view = self._kbd["views"][viewname]
self._viewname = viewname
self._kbd["_QWidget"].layout().setCurrentIndex(self._view["_stackindex"])
if newgeometry:
self.setGeometry(newgeometry)
self.show()
else:
self.updateKeyboard()
return True
return False
def getRawKbds(self):
return self._kbds
#
# initKeyboards sets up a QStackedLayout holding QWidgets for each keyboard, which in turn have a
# QStackedlayout that holds a QWidget for each view within that keyboard. That has a QGridLayout with
# QHboxLayouts in it that hold the individual key QPushButton widgets. It also sets the captions and
# button actions for each key and figures out how many standard key widths and row vis there are in
# all the views, which is used by updateKeyboard() to dynamically figure out how big the fonts, margins
# and rounded corners need to be.
#
def initKeyboards(self):
# Helper to return placeholder "empty row" widget
def _makeEmptyRow(row):
er = QPushButton(self)
er.pressed.connect(partial(self._buttonhandler, er, PRESSED))
er.released.connect(partial(self._buttonhandler, er, RELEASED))
er.setMinimumSize(1, 1)
er.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
er.setProperty("class", "emptyrow")
row["_QWidget"] = er
row["type"] = "emptyrow"
er.data = row
return er
# Helper to return a QLayout to go in place of the QPushButton that contains it plus
# any extra labels stacked on top
def _makeCaptionLayout(k):
extracaptions = k.data.get("extracaptions", None)
if not extracaptions:
return False
# ecl = extra captions layout
ecl = QStackedLayout()
ecl.setStackingMode(QStackedLayout.StackAll)
ecl.addWidget(k)
for cssclass, txt in extracaptions.items():
ql = QLabel(txt)
ql.setProperty("class", cssclass)
ql.setAttribute(Qt.WA_TransparentForMouseEvents)
ecl.addWidget(ql)
return ecl
def _maxRowsInView(view):
maxrows = 0
for column in view.get("columns", []):
maxrows = max(len(column.get("rows")), maxrows)
return maxrows
# This stores the width and height in standard key widths for each view.
def _storeWidthsAndHeights(view):
total_height = 0
# Heights are only stored in first column
column = view["columns"][0]
for ri, row in enumerate(column.get("rows", [])):
total_height += row.get("height", 1)
total_width = 0
for ci, column in enumerate(view.get("columns", [])):
largest_width = 0
for ri, row in reversed(list(enumerate(column.get("rows", [])))):
if len(row.get("keys", [])):
totalweight = 0
for keydata in row.get("keys", []):
w = keydata.get("width", 1)
totalweight += w
# Not counting frst row if there are widths already (reversed order)
if totalweight > largest_width and (ri != 0 or totalweight == 0):
largest_width = totalweight
column["_widthInUnits"] = largest_width
total_width += largest_width
view["_widthInUnits"] = max(total_width, 1)
view["_heightInUnits"] = max(total_height, 1)
# Start of initKeyboards() itself
if self._kbdstack.itemAt(0):
self._clearLayout(self._kbdstack)
ki = 0
for kbdname, kbd in self._kbds.items():
viewstack = QStackedLayout()
vi = 0
for viewname, view in kbd.get("views", {}).items():
_storeWidthsAndHeights(view)
grid = QGridLayout()
grid.setSpacing(0)
grid.setContentsMargins(0, 0, 0, 0)
for ci, column in enumerate(view.get("columns", [])):
for ri in range(_maxRowsInView(view)):
if ri < len(column["rows"]):
row = column["rows"][ri]
else:
row = {"keys": []}
column["rows"].append(row)
keys = row.get("keys", [])
kl = QHBoxLayout()
kl.setContentsMargins(0, 0, 0, 0)
kl.setSpacing(0)
for keydata in keys:
stretch = keydata.get("width", 1) * 10
type = keydata.get("type", "key")
k = QPushButton(self)
k.setMinimumSize(1, 1)
keydata["_QWidget"] = k
keydata["_selected"] = False
k.data = keydata
k.pressed.connect(partial(self._buttonhandler, k, PRESSED))
k.released.connect(partial(self._buttonhandler, k, RELEASED))
k.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
k.setMinimumSize(1, 1)
if type == "key":
k.setText(keydata.get("caption", ""))
# Multiple captions? Create a QStackedWidget overlays them all
ecl = _makeCaptionLayout(k)
if ecl:
kl.addLayout(ecl, stretch)
else:
kl.addWidget(k, int(stretch))
else:
kl.addWidget(k, int(stretch))
if not len(keys):
er = _makeEmptyRow(row)
kl.addWidget(er)
else:
row["_QWidget"] = None
grid.addLayout(kl, ri, ci * 2)
if ci == 0:
grid.setRowStretch(ri, int(row.get("height", 1) * 10))
grid.setColumnStretch(ci * 2, int(column.get("_widthInUnits", 1) * 10))
if ci > 0:
spacercolumn = QHBoxLayout()
spacercolumn.addWidget(QWidget(None))
grid.setColumnStretch((ci * 2) - 1, int(COLUMN_MARGIN * 10))
grid.addLayout(spacercolumn, 0, (ci * 2) - 1)
# Create with self as parent, then reparent to prevent startup flicker
view["_QWidget"] = QWidget(self)
view["_QWidget"].setLayout(grid)
viewstack.addWidget(view["_QWidget"])
view["_stackindex"] = vi
vi += 1
kbd["_QWidget"] = QWidget(self)
kbd["_stackindex"] = ki
ki += 1
kbd["_QWidget"].setLayout(viewstack)
self._kbdstack.addWidget(kbd["_QWidget"])
self.setKeyboard(self._kbdname)
# Qt keeps coming up with minimum sizes that are way too wide
# Some sane number will have to go in at some point, I guess
self.setMaximumSize(16777215, 16777215)
self.setMinimumSize(1, 1)
def updateKeyboard(self):
# Helper function to dynamically recalculate some sizes in stylesheets
def fixStyle(stylesheet, fontsize, margin, radius):
if stylesheet == "":
return ""
# Replace the main calculated values
stylesheet = stylesheet.replace("_OSKB_FONTSIZE_", str(fontsize))
stylesheet = stylesheet.replace("_OSKB_MARGIN_", str(margin))
stylesheet = stylesheet.replace("_OSKB_RADIUS_", str(radius))
# And then all the percentages based thereon (Qt5 doesn't do percentages in fontsizes)
r = re.compile(r"font-size\s*:\s*(\d+)\%")
i = r.finditer(stylesheet)
for m in i:
stylesheet = stylesheet.replace(
m.group(0), "font-size: " + str(int((fontsize / 100) * int(m.group(1)))) + "px",
)
return stylesheet
if not self._view:
return False
# Calculate the font and margin sizes
kw = self.width() / self._view["_widthInUnits"]
kh = self.height() / self._view["_heightInUnits"]
fontsize = min(max(int(min(kw / 1.5, kh / 2)), 5), 50)
margin = int(fontsize / 15)
radius = margin * 3
# Dynamically change the default and keyboard stylesheets
all_sheets = self._stylesheet + "\n\n" + self._kbd.get("style", "")
super().setStyleSheet(fixStyle(all_sheets, fontsize, margin, radius))
# Then adjust the stylesheets and class properties of all keys
for ci, column in enumerate(self._view.get("columns", [])):
for ri, row in enumerate(column.get("rows", [])):
rowwidget = row.get("_QWidget")
if rowwidget:
if row.get("_selected", False):
rowwidget.setProperty("class", "emptyrow selected")
else:
rowwidget.setProperty("class", "emptyrow")
# It needs .setStyleSheet(""), not .repaint() to show the changes
rowwidget.setStyleSheet("")
else:
for keydata in row.get("keys", []):
k = keydata.get("_QWidget")
type = keydata.get("type", "key")
classes = [type]
classes.append(keydata.get("class", ""))
if keydata.get("single") and keydata["single"].get("modifier"):
modname = keydata["single"]["modifier"].get("name", "")
moddata = self._modifiers.get(modname, {})
modstate = moddata.get("state")
if modstate == 1:
classes.append("held")
elif modstate == 2:
classes.append("locked")
else:
classes.append("modifier")
if keydata.get("_selected", False):
classes.append("selected")
classes.append("view_" + self._viewname)
classes.append("row" + str(ri + 1))
classes.append("col" + str(ci + 1))
k.setProperty("class", " ".join(classes).strip())
keystyle = keydata.get("style", "")
k.setStyleSheet(fixStyle(keystyle, fontsize, margin, radius))
#
# The part here is the low-level button handling. It takes care of calling _doAction() with PRESSED and
# RELEASED with pointers to either the "single", "double" or "long" sub-dictionaries for that button,
# handling all the nitty-gritty. Bit involved.., Maybe only touch when wide awake and concentrated.
#
def _oskbButtonHandler(self, button, direction):
sng = button.data.get("single")
dbl = button.data.get("double")
lng = button.data.get("long")
if direction == PRESSED:
if self._doublebutton and self._doublebutton != button:
# Another key was pressed within the doubleclick timeout, so we must
# first process the previous key that was held back
self._doAction(self._doublebutton.data.get("single"), PRESSED)
self._doAction(self._doublebutton.data.get("single"), RELEASED)
self._doublebutton = None
self._doubletimer.stop()
self._stopsinglepress = False
if lng or dbl:
if lng:
self._longtimer = QTimer()
self._longtimer.setSingleShot(True)
self._longtimer.timeout.connect(partial(self._longPress, lng))
self._longtimer.start(LONGPRESS_TIMEOUT)
if dbl:
self._stopsinglepress = True
if self._doubletimer.isActive():
self._doubletimer.stop()
self._doAction(dbl, PRESSED)
self._doAction(dbl, RELEASED)
self._doublebutton = None
else:
self._doublebutton = button
self._doubletimer.start(DOUBLECLICK_TIMEOUT)
else:
self._doAction(sng, PRESSED)
else:
if not self._stopsinglepress:
if self._longtimer.isActive():
self._longtimer.stop()
self._doAction(sng, PRESSED)
self._doAction(sng, RELEASED)
else:
self._doAction(sng, RELEASED)
self._stopsinglepress = False
self._longtimer.stop()
def _longPress(self, lng):
self._stopsinglepress = True
self._doAction(lng, PRESSED)
self._doAction(lng, RELEASED)
def _doubleTimeout(self):
if not self._stopsinglepress:
actiondict = self._doublebutton.data.get("single")
self._doAction(actiondict, PRESSED)
self._doAction(actiondict, RELEASED)
self._doublebutton = None
#
# Higher level button handling: parses the actions from the action dictionary
#
def _doAction(self, actiondict, direction):
if not actiondict:
return
for cmd, argdict in actiondict.items():
if not argdict:
continue
if cmd == "send":
keycode = argdict.get("keycode", "")
keycodeplus = keycode
keyname = argdict.get("name", "")
printable = argdict.get("printable", True)
for modname, mod in self._modifiers.items():
if mod.get("state") > 0:
keyname = modname + " " + keyname
modkeycode = mod.get("keycode")
keycodeplus = modkeycode + "+" + keycode
if not mod.get("printable"):
printable = False
if direction == PRESSED and self._flashmodifiers:
self._injectKeys(modkeycode, PRESSED)
self._injectKeys(keycode, direction)
if direction == RELEASED:
self._releaseModifiers()
if self._viewuntil and re.fullmatch(self._viewuntil, keyname):
self.setView(self._thenview)
self.viewuntil, self._thenview = None, None
if cmd == "view" and direction == RELEASED:
viewname = argdict.get("name", "default")
self._viewuntil = argdict.get("until")
self._thenview = argdict.get("thenview")
self.setView(viewname)
addclass = "oneview" if self._viewuntil else "view"
self.setProperty("class", self._view.get("class", "") + addclass)
self.updateKeyboard()
if cmd == "modifier" and direction == RELEASED:
keycode = argdict.get("keycode", "")
modifier = argdict.get("name", "")
printable = argdict.get("printable", True)
modaction = argdict.get("action", "toggle")
m = self._modifiers.get(modifier)
if modaction == "toggle":
if not m or m["state"] == 0:
self._modifiers[modifier] = {
"state": 1,
"keycode": keycode,
"printable": printable,
}
if not self._flashmodifiers:
self._injectKeys(keycode, PRESSED)
else:
self._modifiers[modifier] = {
"state": 0,
"keycode": keycode,
"printable": printable,
}
if not self._flashmodifiers:
self._injectKeys(keycode, RELEASED)
if modaction == "lock":
if not m:
self._modifiers[modifier] = {}
s = self._modifiers[modifier].get("state", 0)
self._modifiers[modifier] = {
"state": 0 if s == 2 else 2,
"keycode": keycode,
"printable": printable,
}
if not self._flashmodifiers:
self._injectKeys(keycode, PRESSED if s == 0 else RELEASED)
self.updateKeyboard()
if cmd == "keyboard" and direction == RELEASED:
kbdname = argdict.get("name", "")
self.setKeyboard(kbdname)
# This is where the strings with keycodes to be pressed or released get turned into actual keypress
# events. There's two levels here: "42+2;57" (in the US layout) means we're first pressing and then
# releasing shift 2 (an exclamation point) and then a space.
def _injectKeys(self, keystr, direction):
keylist = keystr.split(";")
# If PRESSED, press and release all the ;-separated keycodes, releasing all but the last
if direction == PRESSED:
for keycodes in keylist:
keycodelist = keycodes.split("+")
for keycode in keycodelist:
self._sendKey(int(keycode), PRESSED)
if keycodes != keylist[-1]:
self._sendKey(int(keycode), RELEASED)
# If RELEASED, only need to release the last (set of) keys
if direction == RELEASED:
keycodelist = keylist[-1].split("+")
for keycode in reversed(keycodelist):
self._sendKey(int(keycode), RELEASED)
def _sendKey(self, keycode, keyevent):
if self._sendkeys:
self._sendkeys(keycode, keyevent)
def _releaseModifiers(self):
if self._view:
donestuff = False
for modinfo in self._modifiers.values():
if modinfo["state"] == 1:
donestuff = True
if not self._flashmodifiers:
self._injectKeys(modinfo["keycode"], RELEASED)
modinfo["state"] = 0
if self._flashmodifiers:
self._injectKeys(modinfo["keycode"], RELEASED)
if donestuff:
self.updateKeyboard()
# Helper
def _clearLayout(self, layout):
if layout != None:
while layout.count():
child = layout.takeAt(0)
if child.widget() is not None:
child.widget().deleteLater()
elif child.layout() is not None:
self._clearLayout(child.layout())
# oskbCopy() copies an oskb data structure (a dict with sub-dicts and sub-lists). If you specify two
# variables it will move from one to the other without breaking the reference. If you specify just one,
# it will return a new copy.
def oskbCopy(f, t=None):
if t == None:
t = {}
t.clear()
if type(f) == dict:
for fk, fv in f.items():
if fv != {} and fv != "" and not fk.startswith("_"):
if type(fv) == list or type(fv) == dict:
t[fk] = oskbCopy(fv)
else:
t[fk] = fv
elif type(f) == list:
t = []
for fi, fv in enumerate(f):
t.append(oskbCopy(fv))
return t
if __name__ == "__main__":
main()

48
src/lib/oskb/oskbedit.css Normal file
View File

@ -0,0 +1,48 @@
QWidget {
background-color: #cccccc;
margin:0px;
}
QLabel {
font-size: 50%;
background: transparent;
margin: 2px 5px;
qproperty-alignment: 'AlignBottom | AlignLeft';
}
.key {
background-color: #eeeeee;
border: 1px solid black;
font-size: _OSKB_FONTSIZE_px;
margin: _OSKB_MARGIN_px;
border-radius: _OSKB_RADIUS_px;
}
.spacer {
background-color: #cccccc;
border: 2px dotted white;
margin: _OSKB_MARGIN_px 0px;
border-radius: _OSKB_RADIUS_px;
}
.emptyrow {
background-color: #cccccc;
border: 2px dotted #aaaaaa;
margin: _OSKB_MARGIN_px 0px;
border-radius: _OSKB_RADIUS_px;
}
.key:pressed, .key.pseudomodifier, .key.held {
background-color: #999999;
}
.view .key.pseudomodifier, .key.locked {
background-color: #cc9999;
}
/* Below are used by oskbedit */
.key.selected, .spacer.selected, .emptyrow.selected {
background-color: #77bbff !important;
}

1025
src/lib/oskb/oskbedit.py Normal file

File diff suppressed because it is too large Load Diff

410
src/lib/oskb/ui/EditKey.ui Normal file
View File

@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditKey</class>
<widget class="QDialog" name="EditKey">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>444</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>632</width>
<height>444</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>632</width>
<height>444</height>
</size>
</property>
<property name="windowTitle">
<string>Edit key properties</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QDialogButtonBox" name="cancelsavebuttons">
<property name="geometry">
<rect>
<x>330</x>
<y>400</y>
<width>291</width>
<height>41</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
<widget class="QTabWidget" name="maintabs">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>611</width>
<height>381</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>611</width>
<height>381</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>611</width>
<height>381</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="appearance">
<attribute name="title">
<string>Appearance</string>
</attribute>
<widget class="QLabel" name="lbl_4">
<property name="geometry">
<rect>
<x>300</x>
<y>10</y>
<width>151</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Additional captions:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="caption">
<property name="geometry">
<rect>
<x>110</x>
<y>20</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="cssclass">
<property name="geometry">
<rect>
<x>40</x>
<y>130</y>
<width>221</width>
<height>19</height>
</rect>
</property>
</widget>
<widget class="QTableWidget" name="extracaptions">
<property name="geometry">
<rect>
<x>300</x>
<y>30</y>
<width>261</width>
<height>121</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="showGrid">
<bool>true</bool>
</property>
<property name="rowCount">
<number>0</number>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>CSS class</string>
</property>
</column>
<column>
<property name="text">
<string>Caption</string>
</property>
</column>
</widget>
<widget class="QLabel" name="lbl_3">
<property name="geometry">
<rect>
<x>40</x>
<y>90</y>
<width>191</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>Additional CSS classes, separated by spaces:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="lbl_1">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Caption:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QPlainTextEdit" name="style">
<property name="geometry">
<rect>
<x>20</x>
<y>190</y>
<width>541</width>
<height>131</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbl_5">
<property name="geometry">
<rect>
<x>40</x>
<y>170</y>
<width>291</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>CSS StyleSheet specific to this key:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbl_6">
<property name="geometry">
<rect>
<x>20</x>
<y>320</y>
<width>541</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>(Better to add CSS class and put style info in the keyboard stylesheet)</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbl_2">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Key width:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QDoubleSpinBox" name="width">
<property name="geometry">
<rect>
<x>110</x>
<y>50</y>
<width>81</width>
<height>24</height>
</rect>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
<widget class="QPushButton" name="deletecaption">
<property name="geometry">
<rect>
<x>530</x>
<y>150</y>
<width>31</width>
<height>21</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>-</string>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
<widget class="QPushButton" name="addcaption">
<property name="geometry">
<rect>
<x>500</x>
<y>150</y>
<width>31</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>+</string>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</widget>
<widget class="QWidget" name="action">
<attribute name="title">
<string>Action</string>
</attribute>
<widget class="QTabWidget" name="actiontabs">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>571</width>
<height>321</height>
</rect>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="single">
<attribute name="title">
<string>Single Tap</string>
</attribute>
</widget>
<widget class="QWidget" name="double">
<attribute name="title">
<string>Double Tap</string>
</attribute>
</widget>
<widget class="QWidget" name="long">
<attribute name="title">
<string>Press and hold</string>
</attribute>
</widget>
</widget>
</widget>
</widget>
</widget>
<tabstops>
<tabstop>maintabs</tabstop>
<tabstop>caption</tabstop>
<tabstop>width</tabstop>
<tabstop>cssclass</tabstop>
<tabstop>extracaptions</tabstop>
<tabstop>addcaption</tabstop>
<tabstop>deletecaption</tabstop>
<tabstop>style</tabstop>
<tabstop>actiontabs</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>cancelsavebuttons</sender>
<signal>accepted()</signal>
<receiver>EditKey</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>488</x>
<y>404</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>cancelsavebuttons</sender>
<signal>rejected()</signal>
<receiver>EditKey</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>550</x>
<y>410</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KbdProperties</class>
<widget class="QDialog" name="KbdProperties">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>515</width>
<height>401</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>515</width>
<height>401</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>515</width>
<height>401</height>
</size>
</property>
<property name="windowTitle">
<string>Keyboard Properties</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>150</x>
<y>360</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
<widget class="QTabWidget" name="maintabs">
<property name="geometry">
<rect>
<x>20</x>
<y>80</y>
<width>481</width>
<height>271</height>
</rect>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>default.css (readonly)</string>
</attribute>
<widget class="QTextEdit" name="defaultcss">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>451</width>
<height>221</height>
</rect>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Keyboard Stylesheet</string>
</attribute>
<widget class="QTextEdit" name="keyboardcss">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>451</width>
<height>221</height>
</rect>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</widget>
</widget>
<widget class="QLineEdit" name="description">
<property name="geometry">
<rect>
<x>120</x>
<y>10</y>
<width>201</width>
<height>21</height>
</rect>
</property>
<property name="maxLength">
<number>30</number>
</property>
</widget>
<widget class="QLineEdit" name="layout">
<property name="geometry">
<rect>
<x>120</x>
<y>40</y>
<width>113</width>
<height>21</height>
</rect>
</property>
<property name="maxLength">
<number>16</number>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>91</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Description:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>91</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Layout:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>KbdProperties</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>KbdProperties</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KeyActions</class>
<widget class="QWidget" name="KeyActions">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>560</width>
<height>290</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<widget class="QComboBox" name="view_thenview">
<property name="geometry">
<rect>
<x>180</x>
<y>220</y>
<width>131</width>
<height>26</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="lbl_16">
<property name="geometry">
<rect>
<x>330</x>
<y>210</y>
<width>61</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QCheckBox" name="send">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>151</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Send keypress</string>
</property>
</widget>
<widget class="QCheckBox" name="send_printable">
<property name="geometry">
<rect>
<x>120</x>
<y>110</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>printable</string>
</property>
</widget>
<widget class="QLineEdit" name="view_until">
<property name="geometry">
<rect>
<x>230</x>
<y>250</y>
<width>71</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QCheckBox" name="keyboard">
<property name="geometry">
<rect>
<x>310</x>
<y>180</y>
<width>211</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Jump to other keyboard</string>
</property>
</widget>
<widget class="QComboBox" name="view_name">
<property name="geometry">
<rect>
<x>50</x>
<y>190</y>
<width>131</width>
<height>26</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="lbl_12">
<property name="geometry">
<rect>
<x>30</x>
<y>250</y>
<width>191</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>After keyname matches:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLineEdit" name="modifier_keycode">
<property name="geometry">
<rect>
<x>400</x>
<y>50</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbl_13">
<property name="geometry">
<rect>
<x>310</x>
<y>50</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>keycode:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbl_15">
<property name="geometry">
<rect>
<x>310</x>
<y>110</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>action:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbl_10">
<property name="geometry">
<rect>
<x>30</x>
<y>50</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>keycode:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLineEdit" name="send_keycode">
<property name="geometry">
<rect>
<x>120</x>
<y>50</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="modifier_action">
<property name="geometry">
<rect>
<x>400</x>
<y>110</y>
<width>91</width>
<height>26</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<item>
<property name="text">
<string>toggle</string>
</property>
</item>
<item>
<property name="text">
<string>lock</string>
</property>
</item>
</widget>
<widget class="QCheckBox" name="modifier_printable">
<property name="geometry">
<rect>
<x>400</x>
<y>140</y>
<width>111</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>printable</string>
</property>
</widget>
<widget class="QCheckBox" name="view">
<property name="geometry">
<rect>
<x>30</x>
<y>160</y>
<width>181</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Jump to other view</string>
</property>
</widget>
<widget class="QLineEdit" name="keyboard_name">
<property name="geometry">
<rect>
<x>400</x>
<y>210</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="send_name">
<property name="geometry">
<rect>
<x>120</x>
<y>80</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QCheckBox" name="modifier">
<property name="geometry">
<rect>
<x>310</x>
<y>20</y>
<width>161</width>
<height>20</height>
</rect>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Be a modifier key</string>
</property>
</widget>
<widget class="QCheckBox" name="view_until_checkbox">
<property name="geometry">
<rect>
<x>50</x>
<y>220</y>
<width>131</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Then jump to</string>
</property>
</widget>
<widget class="QLineEdit" name="modifier_name">
<property name="geometry">
<rect>
<x>400</x>
<y>80</y>
<width>101</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="lbl_14">
<property name="geometry">
<rect>
<x>310</x>
<y>80</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="lbl_11">
<property name="geometry">
<rect>
<x>30</x>
<y>80</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QPushButton" name="send_wiz">
<property name="geometry">
<rect>
<x>220</x>
<y>50</y>
<width>41</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>wiz</string>
</property>
</widget>
<widget class="QPushButton" name="modifier_wiz">
<property name="geometry">
<rect>
<x>500</x>
<y>50</y>
<width>41</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>wiz</string>
</property>
</widget>
</widget>
<tabstops>
<tabstop>send</tabstop>
<tabstop>send_keycode</tabstop>
<tabstop>send_wiz</tabstop>
<tabstop>send_name</tabstop>
<tabstop>send_printable</tabstop>
<tabstop>view</tabstop>
<tabstop>view_name</tabstop>
<tabstop>view_until_checkbox</tabstop>
<tabstop>view_thenview</tabstop>
<tabstop>view_until</tabstop>
<tabstop>modifier</tabstop>
<tabstop>modifier_keycode</tabstop>
<tabstop>modifier_wiz</tabstop>
<tabstop>modifier_name</tabstop>
<tabstop>modifier_action</tabstop>
<tabstop>modifier_printable</tabstop>
<tabstop>keyboard</tabstop>
<tabstop>keyboard_name</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>KeyWizard</class>
<widget class="QDialog" name="KeyWizard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>189</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Key Wizard</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>341</width>
<height>141</height>
</rect>
</property>
<property name="text">
<string>This is the Key Wizard. Simply press the key you would like to assign, and the key caption and keycodes will be set automatically. If you want to add functions for long presses or doubleclicks, or if you would like to add more captions, you can always edit the key manually later by doubleclicking it.</string>
</property>
<property name="alignment">
<set>Qt::AlignJustify|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit">
<property name="geometry">
<rect>
<x>350</x>
<y>160</y>
<width>41</width>
<height>21</height>
</rect>
</property>
<property name="cursor">
<cursorShape>BlankCursor</cursorShape>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color: transparent; border:none</string>
</property>
<property name="clearButtonEnabled">
<bool>false</bool>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,5 @@
# User Interface design files
These files hold the types and positions of all the elements in the various dialog windows. These files are edited with a program called "Qt Designer" which is normally part of a much bigger package called QT Creator, which is a complete development environment and several gigabytes in size. You can also download it standalone [here](https://build-system.fman.io/qt-designer-download).
Once you have edited a file, you can compile it to its corresponding file starting with `ui_` in the directory above this one by entering (for example) `pyuic5 EditKey.ui > ../ui_editkey.py`. (`pyuic5` comes with PyQt5 that was installed when you installed oskb.) Do not edit these `ui_something.py` files manually, as they will be overwritten next time someone compiles `SomeThing.ui`.

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ValueEdit</class>
<widget class="QDialog" name="ValueEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>247</width>
<height>103</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>247</width>
<height>103</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>247</width>
<height>103</height>
</size>
</property>
<property name="windowTitle">
<string>Enter Value</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>60</x>
<y>60</y>
<width>171</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="geometry">
<rect>
<x>130</x>
<y>20</y>
<width>81</width>
<height>24</height>
</rect>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>111</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Width:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ValueEdit</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ValueEdit</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

165
src/lib/oskb/ui_editkey.py Normal file
View File

@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'EditKey.ui'
#
# Created by: PyQt5 UI code generator 5.14.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_EditKey(object):
def setupUi(self, EditKey):
EditKey.setObjectName("EditKey")
EditKey.resize(632, 444)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(EditKey.sizePolicy().hasHeightForWidth())
EditKey.setSizePolicy(sizePolicy)
EditKey.setMinimumSize(QtCore.QSize(632, 444))
EditKey.setMaximumSize(QtCore.QSize(632, 444))
EditKey.setModal(True)
self.cancelsavebuttons = QtWidgets.QDialogButtonBox(EditKey)
self.cancelsavebuttons.setGeometry(QtCore.QRect(330, 400, 291, 41))
self.cancelsavebuttons.setOrientation(QtCore.Qt.Horizontal)
self.cancelsavebuttons.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save)
self.cancelsavebuttons.setObjectName("cancelsavebuttons")
self.maintabs = QtWidgets.QTabWidget(EditKey)
self.maintabs.setGeometry(QtCore.QRect(10, 10, 611, 381))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.maintabs.sizePolicy().hasHeightForWidth())
self.maintabs.setSizePolicy(sizePolicy)
self.maintabs.setMinimumSize(QtCore.QSize(611, 381))
self.maintabs.setMaximumSize(QtCore.QSize(611, 381))
self.maintabs.setObjectName("maintabs")
self.appearance = QtWidgets.QWidget()
self.appearance.setObjectName("appearance")
self.lbl_4 = QtWidgets.QLabel(self.appearance)
self.lbl_4.setGeometry(QtCore.QRect(300, 10, 151, 16))
self.lbl_4.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.lbl_4.setObjectName("lbl_4")
self.caption = QtWidgets.QLineEdit(self.appearance)
self.caption.setGeometry(QtCore.QRect(110, 20, 101, 21))
self.caption.setObjectName("caption")
self.cssclass = QtWidgets.QLineEdit(self.appearance)
self.cssclass.setGeometry(QtCore.QRect(40, 130, 221, 19))
self.cssclass.setObjectName("cssclass")
self.extracaptions = QtWidgets.QTableWidget(self.appearance)
self.extracaptions.setGeometry(QtCore.QRect(300, 30, 261, 121))
font = QtGui.QFont()
font.setPointSize(11)
self.extracaptions.setFont(font)
self.extracaptions.setShowGrid(True)
self.extracaptions.setRowCount(0)
self.extracaptions.setColumnCount(2)
self.extracaptions.setObjectName("extracaptions")
item = QtWidgets.QTableWidgetItem()
self.extracaptions.setHorizontalHeaderItem(0, item)
item = QtWidgets.QTableWidgetItem()
self.extracaptions.setHorizontalHeaderItem(1, item)
self.extracaptions.horizontalHeader().setCascadingSectionResizes(False)
self.extracaptions.horizontalHeader().setStretchLastSection(True)
self.extracaptions.verticalHeader().setVisible(False)
self.extracaptions.verticalHeader().setStretchLastSection(False)
self.lbl_3 = QtWidgets.QLabel(self.appearance)
self.lbl_3.setGeometry(QtCore.QRect(40, 90, 191, 41))
self.lbl_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.lbl_3.setWordWrap(True)
self.lbl_3.setObjectName("lbl_3")
self.lbl_1 = QtWidgets.QLabel(self.appearance)
self.lbl_1.setGeometry(QtCore.QRect(20, 20, 81, 16))
self.lbl_1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_1.setObjectName("lbl_1")
self.style = QtWidgets.QPlainTextEdit(self.appearance)
self.style.setGeometry(QtCore.QRect(20, 190, 541, 131))
self.style.setObjectName("style")
self.lbl_5 = QtWidgets.QLabel(self.appearance)
self.lbl_5.setGeometry(QtCore.QRect(40, 170, 291, 16))
self.lbl_5.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.lbl_5.setObjectName("lbl_5")
self.lbl_6 = QtWidgets.QLabel(self.appearance)
self.lbl_6.setGeometry(QtCore.QRect(20, 320, 541, 16))
self.lbl_6.setAlignment(QtCore.Qt.AlignCenter)
self.lbl_6.setObjectName("lbl_6")
self.lbl_2 = QtWidgets.QLabel(self.appearance)
self.lbl_2.setGeometry(QtCore.QRect(20, 50, 81, 16))
self.lbl_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_2.setObjectName("lbl_2")
self.width = QtWidgets.QDoubleSpinBox(self.appearance)
self.width.setGeometry(QtCore.QRect(110, 50, 81, 24))
self.width.setDecimals(1)
self.width.setMinimum(0.1)
self.width.setSingleStep(0.1)
self.width.setProperty("value", 1.0)
self.width.setObjectName("width")
self.deletecaption = QtWidgets.QPushButton(self.appearance)
self.deletecaption.setGeometry(QtCore.QRect(530, 150, 31, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.deletecaption.setFont(font)
self.deletecaption.setDefault(False)
self.deletecaption.setFlat(False)
self.deletecaption.setObjectName("deletecaption")
self.addcaption = QtWidgets.QPushButton(self.appearance)
self.addcaption.setGeometry(QtCore.QRect(500, 150, 31, 21))
self.addcaption.setDefault(False)
self.addcaption.setFlat(False)
self.addcaption.setObjectName("addcaption")
self.maintabs.addTab(self.appearance, "")
self.action = QtWidgets.QWidget()
self.action.setObjectName("action")
self.actiontabs = QtWidgets.QTabWidget(self.action)
self.actiontabs.setGeometry(QtCore.QRect(20, 20, 571, 321))
self.actiontabs.setObjectName("actiontabs")
self.single = QtWidgets.QWidget()
self.single.setObjectName("single")
self.actiontabs.addTab(self.single, "")
self.double = QtWidgets.QWidget()
self.double.setObjectName("double")
self.actiontabs.addTab(self.double, "")
self.long = QtWidgets.QWidget()
self.long.setObjectName("long")
self.actiontabs.addTab(self.long, "")
self.maintabs.addTab(self.action, "")
self.retranslateUi(EditKey)
self.maintabs.setCurrentIndex(0)
self.actiontabs.setCurrentIndex(0)
self.cancelsavebuttons.accepted.connect(EditKey.accept)
self.cancelsavebuttons.rejected.connect(EditKey.reject)
QtCore.QMetaObject.connectSlotsByName(EditKey)
EditKey.setTabOrder(self.maintabs, self.caption)
EditKey.setTabOrder(self.caption, self.width)
EditKey.setTabOrder(self.width, self.cssclass)
EditKey.setTabOrder(self.cssclass, self.extracaptions)
EditKey.setTabOrder(self.extracaptions, self.addcaption)
EditKey.setTabOrder(self.addcaption, self.deletecaption)
EditKey.setTabOrder(self.deletecaption, self.style)
EditKey.setTabOrder(self.style, self.actiontabs)
def retranslateUi(self, EditKey):
_translate = QtCore.QCoreApplication.translate
EditKey.setWindowTitle(_translate("EditKey", "Edit key properties"))
self.lbl_4.setText(_translate("EditKey", "Additional captions:"))
item = self.extracaptions.horizontalHeaderItem(0)
item.setText(_translate("EditKey", "CSS class"))
item = self.extracaptions.horizontalHeaderItem(1)
item.setText(_translate("EditKey", "Caption"))
self.lbl_3.setText(_translate("EditKey", "Additional CSS classes, separated by spaces:"))
self.lbl_1.setText(_translate("EditKey", "Caption:"))
self.lbl_5.setText(_translate("EditKey", "CSS StyleSheet specific to this key:"))
self.lbl_6.setText(_translate("EditKey", "(Better to add CSS class and put style info in the keyboard stylesheet)"))
self.lbl_2.setText(_translate("EditKey", "Key width:"))
self.deletecaption.setText(_translate("EditKey", "-"))
self.addcaption.setText(_translate("EditKey", "+"))
self.maintabs.setTabText(self.maintabs.indexOf(self.appearance), _translate("EditKey", "Appearance"))
self.actiontabs.setTabText(self.actiontabs.indexOf(self.single), _translate("EditKey", "Single Tap"))
self.actiontabs.setTabText(self.actiontabs.indexOf(self.double), _translate("EditKey", "Double Tap"))
self.actiontabs.setTabText(self.actiontabs.indexOf(self.long), _translate("EditKey", "Press and hold"))
self.maintabs.setTabText(self.maintabs.indexOf(self.action), _translate("EditKey", "Action"))

View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'KbdProperties.ui'
#
# Created by: PyQt5 UI code generator 5.14.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_KbdProperties(object):
def setupUi(self, KbdProperties):
KbdProperties.setObjectName("KbdProperties")
KbdProperties.resize(515, 401)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(KbdProperties.sizePolicy().hasHeightForWidth())
KbdProperties.setSizePolicy(sizePolicy)
KbdProperties.setMinimumSize(QtCore.QSize(515, 401))
KbdProperties.setMaximumSize(QtCore.QSize(515, 401))
self.buttonBox = QtWidgets.QDialogButtonBox(KbdProperties)
self.buttonBox.setGeometry(QtCore.QRect(150, 360, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save)
self.buttonBox.setObjectName("buttonBox")
self.maintabs = QtWidgets.QTabWidget(KbdProperties)
self.maintabs.setGeometry(QtCore.QRect(20, 80, 481, 271))
self.maintabs.setObjectName("maintabs")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.defaultcss = QtWidgets.QTextEdit(self.tab)
self.defaultcss.setGeometry(QtCore.QRect(10, 10, 451, 221))
self.defaultcss.setAcceptDrops(False)
self.defaultcss.setReadOnly(True)
self.defaultcss.setAcceptRichText(False)
self.defaultcss.setObjectName("defaultcss")
self.maintabs.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.keyboardcss = QtWidgets.QTextEdit(self.tab_2)
self.keyboardcss.setGeometry(QtCore.QRect(10, 10, 451, 221))
self.keyboardcss.setAcceptDrops(False)
self.keyboardcss.setReadOnly(False)
self.keyboardcss.setAcceptRichText(False)
self.keyboardcss.setObjectName("keyboardcss")
self.maintabs.addTab(self.tab_2, "")
self.description = QtWidgets.QLineEdit(KbdProperties)
self.description.setGeometry(QtCore.QRect(120, 10, 201, 21))
self.description.setMaxLength(30)
self.description.setObjectName("description")
self.layout = QtWidgets.QLineEdit(KbdProperties)
self.layout.setGeometry(QtCore.QRect(120, 40, 113, 21))
self.layout.setMaxLength(16)
self.layout.setObjectName("layout")
self.label = QtWidgets.QLabel(KbdProperties)
self.label.setGeometry(QtCore.QRect(20, 10, 91, 21))
self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(KbdProperties)
self.label_2.setGeometry(QtCore.QRect(20, 40, 91, 20))
self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_2.setObjectName("label_2")
self.retranslateUi(KbdProperties)
self.maintabs.setCurrentIndex(1)
self.buttonBox.accepted.connect(KbdProperties.accept)
self.buttonBox.rejected.connect(KbdProperties.reject)
QtCore.QMetaObject.connectSlotsByName(KbdProperties)
def retranslateUi(self, KbdProperties):
_translate = QtCore.QCoreApplication.translate
KbdProperties.setWindowTitle(_translate("KbdProperties", "Keyboard Properties"))
self.maintabs.setTabText(self.maintabs.indexOf(self.tab), _translate("KbdProperties", "default.css (readonly)"))
self.maintabs.setTabText(self.maintabs.indexOf(self.tab_2), _translate("KbdProperties", "Keyboard Stylesheet"))
self.label.setText(_translate("KbdProperties", "Description:"))
self.label_2.setText(_translate("KbdProperties", "Layout:"))

View File

@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'KeyActions.ui'
#
# Created by: PyQt5 UI code generator 5.14.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_KeyActions(object):
def setupUi(self, KeyActions):
KeyActions.setObjectName("KeyActions")
KeyActions.resize(560, 290)
KeyActions.setWindowTitle("")
self.view_thenview = QtWidgets.QComboBox(KeyActions)
self.view_thenview.setGeometry(QtCore.QRect(180, 220, 131, 26))
font = QtGui.QFont()
font.setPointSize(10)
self.view_thenview.setFont(font)
self.view_thenview.setObjectName("view_thenview")
self.lbl_16 = QtWidgets.QLabel(KeyActions)
self.lbl_16.setGeometry(QtCore.QRect(330, 210, 61, 20))
self.lbl_16.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_16.setObjectName("lbl_16")
self.send = QtWidgets.QCheckBox(KeyActions)
self.send.setGeometry(QtCore.QRect(30, 20, 151, 20))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.send.setFont(font)
self.send.setObjectName("send")
self.send_printable = QtWidgets.QCheckBox(KeyActions)
self.send_printable.setGeometry(QtCore.QRect(120, 110, 101, 20))
self.send_printable.setObjectName("send_printable")
self.view_until = QtWidgets.QLineEdit(KeyActions)
self.view_until.setGeometry(QtCore.QRect(230, 250, 71, 21))
self.view_until.setObjectName("view_until")
self.keyboard = QtWidgets.QCheckBox(KeyActions)
self.keyboard.setGeometry(QtCore.QRect(310, 180, 211, 20))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.keyboard.setFont(font)
self.keyboard.setObjectName("keyboard")
self.view_name = QtWidgets.QComboBox(KeyActions)
self.view_name.setGeometry(QtCore.QRect(50, 190, 131, 26))
font = QtGui.QFont()
font.setPointSize(10)
self.view_name.setFont(font)
self.view_name.setObjectName("view_name")
self.lbl_12 = QtWidgets.QLabel(KeyActions)
self.lbl_12.setGeometry(QtCore.QRect(30, 250, 191, 21))
self.lbl_12.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_12.setWordWrap(True)
self.lbl_12.setObjectName("lbl_12")
self.modifier_keycode = QtWidgets.QLineEdit(KeyActions)
self.modifier_keycode.setGeometry(QtCore.QRect(400, 50, 101, 21))
self.modifier_keycode.setObjectName("modifier_keycode")
self.lbl_13 = QtWidgets.QLabel(KeyActions)
self.lbl_13.setGeometry(QtCore.QRect(310, 50, 81, 16))
self.lbl_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_13.setObjectName("lbl_13")
self.lbl_15 = QtWidgets.QLabel(KeyActions)
self.lbl_15.setGeometry(QtCore.QRect(310, 110, 81, 16))
self.lbl_15.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_15.setObjectName("lbl_15")
self.lbl_10 = QtWidgets.QLabel(KeyActions)
self.lbl_10.setGeometry(QtCore.QRect(30, 50, 81, 16))
self.lbl_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_10.setObjectName("lbl_10")
self.send_keycode = QtWidgets.QLineEdit(KeyActions)
self.send_keycode.setGeometry(QtCore.QRect(120, 50, 101, 21))
self.send_keycode.setObjectName("send_keycode")
self.modifier_action = QtWidgets.QComboBox(KeyActions)
self.modifier_action.setGeometry(QtCore.QRect(400, 110, 91, 26))
font = QtGui.QFont()
font.setPointSize(10)
self.modifier_action.setFont(font)
self.modifier_action.setObjectName("modifier_action")
self.modifier_action.addItem("")
self.modifier_action.addItem("")
self.modifier_printable = QtWidgets.QCheckBox(KeyActions)
self.modifier_printable.setGeometry(QtCore.QRect(400, 140, 111, 20))
self.modifier_printable.setObjectName("modifier_printable")
self.view = QtWidgets.QCheckBox(KeyActions)
self.view.setGeometry(QtCore.QRect(30, 160, 181, 20))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.view.setFont(font)
self.view.setObjectName("view")
self.keyboard_name = QtWidgets.QLineEdit(KeyActions)
self.keyboard_name.setGeometry(QtCore.QRect(400, 210, 101, 21))
self.keyboard_name.setObjectName("keyboard_name")
self.send_name = QtWidgets.QLineEdit(KeyActions)
self.send_name.setGeometry(QtCore.QRect(120, 80, 101, 21))
self.send_name.setObjectName("send_name")
self.modifier = QtWidgets.QCheckBox(KeyActions)
self.modifier.setGeometry(QtCore.QRect(310, 20, 161, 20))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.modifier.setFont(font)
self.modifier.setObjectName("modifier")
self.view_until_checkbox = QtWidgets.QCheckBox(KeyActions)
self.view_until_checkbox.setGeometry(QtCore.QRect(50, 220, 131, 20))
self.view_until_checkbox.setObjectName("view_until_checkbox")
self.modifier_name = QtWidgets.QLineEdit(KeyActions)
self.modifier_name.setGeometry(QtCore.QRect(400, 80, 101, 21))
self.modifier_name.setObjectName("modifier_name")
self.lbl_14 = QtWidgets.QLabel(KeyActions)
self.lbl_14.setGeometry(QtCore.QRect(310, 80, 81, 16))
self.lbl_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_14.setObjectName("lbl_14")
self.lbl_11 = QtWidgets.QLabel(KeyActions)
self.lbl_11.setGeometry(QtCore.QRect(30, 80, 81, 16))
self.lbl_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.lbl_11.setObjectName("lbl_11")
self.send_wiz = QtWidgets.QPushButton(KeyActions)
self.send_wiz.setGeometry(QtCore.QRect(220, 50, 41, 21))
self.send_wiz.setObjectName("send_wiz")
self.modifier_wiz = QtWidgets.QPushButton(KeyActions)
self.modifier_wiz.setGeometry(QtCore.QRect(500, 50, 41, 21))
self.modifier_wiz.setObjectName("modifier_wiz")
self.retranslateUi(KeyActions)
QtCore.QMetaObject.connectSlotsByName(KeyActions)
KeyActions.setTabOrder(self.send, self.send_keycode)
KeyActions.setTabOrder(self.send_keycode, self.send_wiz)
KeyActions.setTabOrder(self.send_wiz, self.send_name)
KeyActions.setTabOrder(self.send_name, self.send_printable)
KeyActions.setTabOrder(self.send_printable, self.view)
KeyActions.setTabOrder(self.view, self.view_name)
KeyActions.setTabOrder(self.view_name, self.view_until_checkbox)
KeyActions.setTabOrder(self.view_until_checkbox, self.view_thenview)
KeyActions.setTabOrder(self.view_thenview, self.view_until)
KeyActions.setTabOrder(self.view_until, self.modifier)
KeyActions.setTabOrder(self.modifier, self.modifier_keycode)
KeyActions.setTabOrder(self.modifier_keycode, self.modifier_wiz)
KeyActions.setTabOrder(self.modifier_wiz, self.modifier_name)
KeyActions.setTabOrder(self.modifier_name, self.modifier_action)
KeyActions.setTabOrder(self.modifier_action, self.modifier_printable)
KeyActions.setTabOrder(self.modifier_printable, self.keyboard)
KeyActions.setTabOrder(self.keyboard, self.keyboard_name)
def retranslateUi(self, KeyActions):
_translate = QtCore.QCoreApplication.translate
self.lbl_16.setText(_translate("KeyActions", "name:"))
self.send.setText(_translate("KeyActions", "Send keypress"))
self.send_printable.setText(_translate("KeyActions", "printable"))
self.keyboard.setText(_translate("KeyActions", "Jump to other keyboard"))
self.lbl_12.setText(_translate("KeyActions", "After keyname matches:"))
self.lbl_13.setText(_translate("KeyActions", "keycode:"))
self.lbl_15.setText(_translate("KeyActions", "action:"))
self.lbl_10.setText(_translate("KeyActions", "keycode:"))
self.modifier_action.setItemText(0, _translate("KeyActions", "toggle"))
self.modifier_action.setItemText(1, _translate("KeyActions", "lock"))
self.modifier_printable.setText(_translate("KeyActions", "printable"))
self.view.setText(_translate("KeyActions", "Jump to other view"))
self.modifier.setText(_translate("KeyActions", "Be a modifier key"))
self.view_until_checkbox.setText(_translate("KeyActions", "Then jump to"))
self.lbl_14.setText(_translate("KeyActions", "name:"))
self.lbl_11.setText(_translate("KeyActions", "name:"))
self.send_wiz.setText(_translate("KeyActions", "wiz"))
self.modifier_wiz.setText(_translate("KeyActions", "wiz"))

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'KeyWizard.ui'
#
# Created by: PyQt5 UI code generator 5.14.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_KeyWizard(object):
def setupUi(self, KeyWizard):
KeyWizard.setObjectName("KeyWizard")
KeyWizard.resize(400, 189)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(KeyWizard.sizePolicy().hasHeightForWidth())
KeyWizard.setSizePolicy(sizePolicy)
KeyWizard.setModal(True)
self.label = QtWidgets.QLabel(KeyWizard)
self.label.setGeometry(QtCore.QRect(30, 20, 341, 141))
self.label.setAlignment(QtCore.Qt.AlignJustify|QtCore.Qt.AlignTop)
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.lineEdit = QtWidgets.QLineEdit(KeyWizard)
self.lineEdit.setGeometry(QtCore.QRect(350, 160, 41, 21))
self.lineEdit.setCursor(QtGui.QCursor(QtCore.Qt.BlankCursor))
self.lineEdit.setAutoFillBackground(False)
self.lineEdit.setStyleSheet("background-color: transparent; border:none")
self.lineEdit.setClearButtonEnabled(False)
self.lineEdit.setObjectName("lineEdit")
self.retranslateUi(KeyWizard)
QtCore.QMetaObject.connectSlotsByName(KeyWizard)
def retranslateUi(self, KeyWizard):
_translate = QtCore.QCoreApplication.translate
KeyWizard.setWindowTitle(_translate("KeyWizard", "Key Wizard"))
self.label.setText(_translate("KeyWizard", "This is the Key Wizard. Simply press the key you would like to assign, and the key caption and keycodes will be set automatically. If you want to add functions for long presses or doubleclicks, or if you would like to add more captions, you can always edit the key manually later by doubleclicking it."))

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ValueEdit.ui'
#
# Created by: PyQt5 UI code generator 5.14.0
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_ValueEdit(object):
def setupUi(self, ValueEdit):
ValueEdit.setObjectName("ValueEdit")
ValueEdit.resize(247, 103)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(ValueEdit.sizePolicy().hasHeightForWidth())
ValueEdit.setSizePolicy(sizePolicy)
ValueEdit.setMinimumSize(QtCore.QSize(247, 103))
ValueEdit.setMaximumSize(QtCore.QSize(247, 103))
self.buttonBox = QtWidgets.QDialogButtonBox(ValueEdit)
self.buttonBox.setGeometry(QtCore.QRect(60, 60, 171, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save)
self.buttonBox.setObjectName("buttonBox")
self.doubleSpinBox = QtWidgets.QDoubleSpinBox(ValueEdit)
self.doubleSpinBox.setGeometry(QtCore.QRect(130, 20, 81, 24))
self.doubleSpinBox.setDecimals(1)
self.doubleSpinBox.setMinimum(0.1)
self.doubleSpinBox.setSingleStep(0.1)
self.doubleSpinBox.setProperty("value", 0.5)
self.doubleSpinBox.setObjectName("doubleSpinBox")
self.label = QtWidgets.QLabel(ValueEdit)
self.label.setGeometry(QtCore.QRect(10, 20, 111, 21))
self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label.setObjectName("label")
self.retranslateUi(ValueEdit)
self.buttonBox.accepted.connect(ValueEdit.accept)
self.buttonBox.rejected.connect(ValueEdit.reject)
QtCore.QMetaObject.connectSlotsByName(ValueEdit)
def retranslateUi(self, ValueEdit):
_translate = QtCore.QCoreApplication.translate
ValueEdit.setWindowTitle(_translate("ValueEdit", "Enter Value"))
self.label.setText(_translate("ValueEdit", "Width:"))

View File

@ -1,6 +1,8 @@
import platform
import subprocess
import sys
import time
from components import ArchiveSynchronizer
from lib.db import Session, Users
from PyQt5.QtCore import QTimer, pyqtSignal
@ -63,3 +65,6 @@ class Login(Widget):
self.password_le.setFocus()
if platform.system().lower() == 'windows':
subprocess.Popen(["open_osk.bat"]) # OPEN ON SCREEN KEYBOARD
else:
subprocess.Popen(["./open_oskb.sh"]) # OPEN ON SCREEN KEYBOARD
time.sleep(0.5)