mirror of
https://github.com/jtgans/g13gui.git
synced 2025-06-20 00:14:09 -04:00
g13gui: Got a bit carried away, totally reworked things
So at this point, g13gui has quite a lot of functionality built into it now. We can bind keys, we can unbind keys, we can upload whole profiles to g13d, we can create and edit profiles, and we can even set LCD colors per-profile. We're not loading the configuration from disk, sadly, since I had to rework quite a lot of the UI infrastructure to get observer notifications to work with GTK. Lots of simplifications in here, though, which reduces the complexity of the code considerably. - Migrated all model related classes into the model module. - Migrated UI classes into the ui module. - Migrated observer-related stuff into observer. - Created the GtkObserver adaptor for GTK UI threading. This makes heavy use of GObject signals and queuing to dispatch to the UI thread. - Migrated all of the G13 buttons into their own class. - Renamed ButtonMenu into G13ButtonPopover. - Setup the profile combo box as its own class. - Created the profile popover so we can add/remove/edit profiles.
This commit is contained in:
parent
91e62addb7
commit
476b43ed5f
@ -0,0 +1,3 @@
|
||||
import g13gui.model as model
|
||||
import g13gui.ui as ui
|
||||
import g13gui.observer as observer
|
@ -9,11 +9,10 @@ import traceback
|
||||
import xdg.BaseDirectory as basedir
|
||||
import json
|
||||
|
||||
from common import PROFILES_CONFIG_PATH
|
||||
from common import VERSION
|
||||
from g13gui.common import PROFILES_CONFIG_PATH
|
||||
from g13gui.common import VERSION
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import GObject
|
||||
|
||||
|
||||
@ -32,24 +31,12 @@ class UploadTask():
|
||||
|
||||
|
||||
class SaveTask():
|
||||
def __init__(self, profiles, defaultProfileName):
|
||||
self._profiles = profiles
|
||||
self._defaultProfileName = defaultProfileName
|
||||
def __init__(self, prefsDict):
|
||||
self._prefsDict = prefsDict
|
||||
|
||||
def run(self, outfp, infp, callback):
|
||||
profiles = {}
|
||||
for key, profile in self._profiles.items():
|
||||
profiles[key] = profile.toDict()
|
||||
|
||||
config = {
|
||||
'version': VERSION,
|
||||
'defaultProfileName': self._defaultProfileName,
|
||||
'profiles': profiles
|
||||
}
|
||||
|
||||
encoder = json.JSONEncoder()
|
||||
with open(PROFILES_CONFIG_PATH, 'w') as f:
|
||||
f.write(encoder.encode(config))
|
||||
f.write(json.dumps(self._prefsDict, default=str))
|
||||
f.flush()
|
||||
|
||||
|
||||
|
@ -3,20 +3,23 @@
|
||||
import queue
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
import g13gui.model as model
|
||||
import g13gui.ui as ui
|
||||
|
||||
from g13gui.g13d import G13DWorker
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, GObject
|
||||
from mainwindow import MainWindow
|
||||
from g13d import G13DWorker
|
||||
|
||||
|
||||
VERSION = '0.1.0'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
prefs = model.Preferences()
|
||||
queue = queue.Queue()
|
||||
|
||||
win = MainWindow(queue)
|
||||
win = ui.MainWindow(queue, prefs)
|
||||
win.connect("destroy", Gtk.main_quit)
|
||||
win.show_all()
|
||||
|
||||
|
@ -1,234 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import gi
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from common import VERSION
|
||||
from common import PROFILES_CONFIG_PATH
|
||||
|
||||
from g13d import UploadTask
|
||||
from g13d import SaveTask
|
||||
from bindings import G13D_TO_GDK_KEYBINDS
|
||||
from bindings import G13_KEYS
|
||||
from bindingprofile import BindingProfile
|
||||
from buttonmenu import ButtonMenu
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
|
||||
class MainWindow(Gtk.Window):
|
||||
def __init__(self, workerQueue):
|
||||
Gtk.Window.__init__(self)
|
||||
|
||||
self._workerQueue = workerQueue
|
||||
|
||||
GObject.signal_new("uploading", self, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_FLOAT,))
|
||||
self.connect("uploading", self.uploadStatusChanged)
|
||||
GObject.signal_new("daemon-connection-changed", self, GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,))
|
||||
self.connect("daemon-connection-changed", self.daemonConnectionChanged)
|
||||
|
||||
default_profile = BindingProfile()
|
||||
default_profile.registerObserver(self)
|
||||
self._profiles = {'Default Profile': default_profile}
|
||||
self._currentProfile = self._profiles['Default Profile']
|
||||
|
||||
self.loadProfiles()
|
||||
|
||||
self.headerBar = Gtk.HeaderBar()
|
||||
self.headerBar.set_title("G13 Configurator")
|
||||
self.headerBar.set_show_close_button(True)
|
||||
|
||||
self.profileComboBox = Gtk.ComboBoxText()
|
||||
self.profileComboBox.connect("changed", self.profileChanged)
|
||||
self.headerBar.add(self.profileComboBox)
|
||||
|
||||
addProfileButton = Gtk.Button.new_from_icon_name("add", 1)
|
||||
addProfileButton.connect("clicked", self.addProfileClicked)
|
||||
self.headerBar.add(addProfileButton)
|
||||
|
||||
self._uploadButton = Gtk.Button.new_from_icon_name("up", 1)
|
||||
self._uploadButton.connect("clicked", self.uploadClicked)
|
||||
self.headerBar.add(self._uploadButton)
|
||||
|
||||
Gtk.Window.set_default_size(self, 640, 480)
|
||||
Gtk.Window.set_titlebar(self, self.headerBar)
|
||||
|
||||
self.box = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
|
||||
self.add(self.box)
|
||||
|
||||
self.setupG13ButtonGrid()
|
||||
self.updateProfileBox()
|
||||
|
||||
def loadProfiles(self):
|
||||
result = {}
|
||||
currentProfile = None
|
||||
|
||||
try:
|
||||
with open(PROFILES_CONFIG_PATH, 'r') as f:
|
||||
serializedConfig = json.load(f)
|
||||
|
||||
if serializedConfig['version'] != VERSION:
|
||||
print('WARNING: This profile config is from a different version (wanted %s got %s)!' %
|
||||
(VERSION, result['version']))
|
||||
print('This configuration may not load properly!')
|
||||
|
||||
print("Loaded dict: %s" % (serializedConfig))
|
||||
|
||||
for name, dict in serializedConfig['profiles'].items():
|
||||
result[name] = BindingProfile(dict=dict)
|
||||
|
||||
for name, dict in result.items():
|
||||
if name == serializedConfig['defaultProfileName']:
|
||||
currentProfile = result[name]
|
||||
|
||||
except (OSError, json.JSONDecodeError, KeyError, ValueError) as e:
|
||||
print("Failed to read profiles from disk: %s" % (e))
|
||||
traceback.print_exc()
|
||||
else:
|
||||
self._profiles = result
|
||||
self._currentProfile = currentProfile
|
||||
|
||||
def daemonConnectionChanged(self, widget, connected):
|
||||
self._connected = connected
|
||||
if connected:
|
||||
self._uploadButton.set_state_flags(Gtk.StateFlags.NORMAL, True)
|
||||
else:
|
||||
self._uploadButton.set_state_flags(Gtk.StateFlags.INSENSITIVE, True)
|
||||
|
||||
def uploadStatusChanged(self, widget, percentage):
|
||||
print("Upload in progress: %f" % (percentage * 100))
|
||||
|
||||
def profileChanged(self, widget):
|
||||
pass
|
||||
|
||||
def addProfileClicked(self, widget):
|
||||
pass
|
||||
|
||||
def uploadClicked(self, widget):
|
||||
config = self._currentProfile.generateConfigString()
|
||||
task = UploadTask(config)
|
||||
self._workerQueue.put(task)
|
||||
|
||||
currentProfileName = None
|
||||
for name, profile in self._profiles.items():
|
||||
if self._currentProfile == profile:
|
||||
currentProfileName = name
|
||||
break
|
||||
|
||||
task = SaveTask(self._profiles, currentProfileName)
|
||||
self._workerQueue.put(task)
|
||||
|
||||
def updateProfileBox(self):
|
||||
self.profileComboBox.remove_all()
|
||||
row = 0
|
||||
for profileName in self._profiles.keys():
|
||||
self.profileComboBox.append_text(profileName)
|
||||
|
||||
if self._profiles[profileName] == self._currentProfile:
|
||||
print("Set active profile to %d (%s)" % (row, profileName))
|
||||
self.profileComboBox.set_active(row)
|
||||
|
||||
row = row + 1
|
||||
|
||||
def setupG13ButtonGrid(self):
|
||||
self.lcdButtons = Gtk.Box(spacing=3, orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.box.pack_start(self.lcdButtons, True, True, 6)
|
||||
|
||||
self.mButtons = Gtk.Box(spacing=3, orientation=Gtk.Orientation.HORIZONTAL)
|
||||
self.box.pack_start(self.mButtons, True, True, 6)
|
||||
|
||||
self.keyGrid = Gtk.Grid()
|
||||
self.keyGrid.set_row_spacing(3)
|
||||
self.keyGrid.set_column_spacing(3)
|
||||
self.box.pack_start(self.keyGrid, True, True, 6)
|
||||
|
||||
self.stickGrid = Gtk.Grid()
|
||||
self.stickGrid.set_row_spacing(3)
|
||||
self.stickGrid.set_column_spacing(3)
|
||||
self.box.pack_start(self.stickGrid, False, False, 6)
|
||||
|
||||
self.g13Buttons = {}
|
||||
|
||||
self.lcdButtons.pack_start(self.newG13Button('BD'), True, True, 6)
|
||||
self.lcdButtons.pack_start(self.newG13Button('L1'), True, True, 6)
|
||||
self.lcdButtons.pack_start(self.newG13Button('L2'), True, True, 6)
|
||||
self.lcdButtons.pack_start(self.newG13Button('L3'), True, True, 6)
|
||||
self.lcdButtons.pack_start(self.newG13Button('L4'), True, True, 6)
|
||||
self.lcdButtons.pack_start(self.newG13Button('LIGHT'), True, True, 6)
|
||||
|
||||
self.mButtons.pack_start(self.newG13Button('M1'), True, True, 6)
|
||||
self.mButtons.pack_start(self.newG13Button('M2'), True, True, 6)
|
||||
self.mButtons.pack_start(self.newG13Button('M3'), True, True, 6)
|
||||
self.mButtons.pack_start(self.newG13Button('MR'), True, True, 6)
|
||||
|
||||
# G1 to G14
|
||||
self._buttonNum = 1
|
||||
for row in range(0, 2):
|
||||
for col in range(0, 7):
|
||||
self.keyGrid.attach(self.newG13NumberedButton(),
|
||||
col, row, 1, 1)
|
||||
|
||||
# G15 to G19
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 1, 3, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 2, 3, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 3, 3, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 4, 3, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 5, 3, 1, 1)
|
||||
|
||||
# G20 to G22
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 2, 4, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 3, 4, 1, 1)
|
||||
self.keyGrid.attach(self.newG13NumberedButton(), 4, 4, 1, 1)
|
||||
|
||||
self.stickGrid.attach(self.newG13Button("STICK_UP"), 4, 0, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("LEFT"), 2, 1, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("STICK_LEFT"), 3, 1, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("TOP"), 4, 1, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("STICK_RIGHT"), 5, 1, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("STICK_DOWN"), 4, 2, 1, 1)
|
||||
self.stickGrid.attach(self.newG13Button("DOWN"), 4, 3, 1, 1)
|
||||
|
||||
def newG13NumberedButton(self):
|
||||
button = self.newG13Button('G' + str(self._buttonNum))
|
||||
self._buttonNum = self._buttonNum + 1
|
||||
return button
|
||||
|
||||
def newG13Button(self, name):
|
||||
popover = ButtonMenu(self._currentProfile, name)
|
||||
button = Gtk.MenuButton(popover=popover)
|
||||
self.g13Buttons[name] = button
|
||||
self.updateG13Button(name)
|
||||
|
||||
return button
|
||||
|
||||
def updateG13Button(self, name):
|
||||
button = self.g13Buttons[name]
|
||||
children = button.get_children()
|
||||
|
||||
if len(children) > 0:
|
||||
button.remove(children[0])
|
||||
|
||||
bindings = self._currentProfile.getBoundKey(name)
|
||||
|
||||
if len(bindings) > 0:
|
||||
keybinds = [G13D_TO_GDK_KEYBINDS[binding] for binding in bindings]
|
||||
accelerator = '+'.join(keybinds)
|
||||
shortcut = Gtk.ShortcutsShortcut(
|
||||
shortcut_type=Gtk.ShortcutType.ACCELERATOR,
|
||||
accelerator=accelerator)
|
||||
shortcut.set_halign(Gtk.Align.CENTER)
|
||||
button.add(shortcut)
|
||||
else:
|
||||
label = Gtk.Label(name)
|
||||
button.add(label)
|
||||
|
||||
button.show_all()
|
||||
|
||||
def on_changed(self, profile):
|
||||
for key in G13_KEYS:
|
||||
self.updateG13Button(key)
|
||||
|
||||
self.uploadClicked(self)
|
2
g13gui/g13gui/model/__init__.py
Normal file
2
g13gui/g13gui/model/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from g13gui.model.bindingprofile import BindingProfile
|
||||
from g13gui.model.prefs import Preferences
|
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import bindings
|
||||
import g13gui.model.bindings as bindings
|
||||
|
||||
from observer import Subject
|
||||
from observer import ChangeType
|
||||
from g13gui.observer import Subject
|
||||
from g13gui.observer import ChangeType
|
||||
|
||||
|
||||
class BindingProfile(Subject):
|
||||
@ -17,6 +17,10 @@ class BindingProfile(Subject):
|
||||
self._stickRegions = bindings.DEFAULT_STICK_REGIONS
|
||||
self._stickRegionBindings = bindings.DEFAULT_STICK_REGION_BINDINGS
|
||||
self._keyBindings = bindings.DEFAULT_KEY_BINDINGS
|
||||
self._lcdColor = bindings.DEFAULT_LCD_COLOR
|
||||
|
||||
def lcdColor(self):
|
||||
return self._lcdColor
|
||||
|
||||
def stickMode(self):
|
||||
return self._stickMode
|
||||
@ -36,6 +40,14 @@ class BindingProfile(Subject):
|
||||
|
||||
return []
|
||||
|
||||
def _setLCDColor(self, red, green, blue):
|
||||
self._lcdColor = (red, green, blue)
|
||||
self.addChange(ChangeType.MODIFY, 'lcdcolor', self._lcdColor)
|
||||
|
||||
def setLCDColor(self, red, green, blue):
|
||||
self._setLCDColor(red, green, blue)
|
||||
self.notifyChanged()
|
||||
|
||||
def _bindKey(self, gkey, keybinding):
|
||||
if gkey in self._stickRegions.keys():
|
||||
self._stickRegionBindings[gkey] = keybinding
|
||||
@ -59,15 +71,24 @@ class BindingProfile(Subject):
|
||||
self._setStickMode(stickmode)
|
||||
self.notifyChanged()
|
||||
|
||||
def _lcdColorToCommandString(self):
|
||||
return 'rgb %d %d %d' % tuple([int(x * 255) for x in self._lcdColor])
|
||||
|
||||
def _keyBindingToCommandString(self, gkey):
|
||||
kbdkey = self._keyBindings[gkey]
|
||||
if len(kbdkey) > 0:
|
||||
keys = '+'.join(['KEY_' + key for key in kbdkey])
|
||||
return "bind %s %s" % (gkey, keys)
|
||||
else:
|
||||
return "unbind %s" % (gkey)
|
||||
|
||||
def toCommandString(self):
|
||||
commands = []
|
||||
|
||||
for gkey, kbdkey in self._keyBindings.items():
|
||||
if len(kbdkey) > 0:
|
||||
keys = '+'.join(['KEY_' + key for key in kbdkey])
|
||||
commands.append("bind %s %s" % (gkey, keys))
|
||||
else:
|
||||
commands.append("unbind %s" % (gkey))
|
||||
commands.append(self._lcdColorToCommandString())
|
||||
|
||||
for gkey in self._keyBindings.keys():
|
||||
commands.append(self._keyBindingToCommandString(gkey))
|
||||
|
||||
if self._stickMode == bindings.StickMode.KEYS:
|
||||
for region, bounds in self._stickRegions.items():
|
||||
@ -80,6 +101,7 @@ class BindingProfile(Subject):
|
||||
return '\n'.join(commands)
|
||||
|
||||
def loadFromDict(self, dict):
|
||||
self._lcdColor = dict['lcdcolor']
|
||||
self._stickMode = dict['stickMode']
|
||||
self._stickRegions = dict['stickRegions']
|
||||
self._stickRegionBindings = dict['stickRegionBindings']
|
||||
@ -87,6 +109,7 @@ class BindingProfile(Subject):
|
||||
|
||||
def saveToDict(self):
|
||||
return {
|
||||
'lcdcolor': self._lcdColor,
|
||||
'stickMode': self._stickMode,
|
||||
'stickRegions': self._stickRegions,
|
||||
'stickRegionBindings': self._stickRegionBindings,
|
@ -2,10 +2,10 @@
|
||||
|
||||
import unittest
|
||||
|
||||
import bindings
|
||||
from bindingprofile import BindingProfile
|
||||
from observer import ChangeType
|
||||
from observer import ObserverTestCase
|
||||
import g13gui.model.bindings as bindings
|
||||
from g13gui.model.bindingprofile import BindingProfile
|
||||
from g13gui.observer import ChangeType
|
||||
from g13gui.observer import ObserverTestCase
|
||||
|
||||
|
||||
class PrefsTestCase(ObserverTestCase):
|
||||
@ -16,14 +16,18 @@ class PrefsTestCase(ObserverTestCase):
|
||||
bp = BindingProfile()
|
||||
self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS)
|
||||
self.assertEqual(bp.stickRegions(), bindings.DEFAULT_STICK_REGIONS)
|
||||
self.assertEqual(bp._stickRegionBindings, bindings.DEFAULT_STICK_REGION_BINDINGS)
|
||||
self.assertEqual(bp.lcdColor(), bindings.DEFAULT_LCD_COLOR)
|
||||
self.assertEqual(bp._stickRegionBindings,
|
||||
bindings.DEFAULT_STICK_REGION_BINDINGS)
|
||||
self.assertEqual(bp._keyBindings, bindings.DEFAULT_KEY_BINDINGS)
|
||||
self.assertEqual(bp._lcdColor, bindings.DEFAULT_LCD_COLOR)
|
||||
|
||||
def testInvalidDict(self):
|
||||
bp = BindingProfile({})
|
||||
self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS)
|
||||
self.assertEqual(bp.stickRegions(), bindings.DEFAULT_STICK_REGIONS)
|
||||
self.assertEqual(bp._stickRegionBindings, bindings.DEFAULT_STICK_REGION_BINDINGS)
|
||||
self.assertEqual(bp._stickRegionBindings,
|
||||
bindings.DEFAULT_STICK_REGION_BINDINGS)
|
||||
self.assertEqual(bp._keyBindings, bindings.DEFAULT_KEY_BINDINGS)
|
||||
|
||||
def testDictLoadSave(self):
|
||||
@ -72,6 +76,21 @@ class PrefsTestCase(ObserverTestCase):
|
||||
else:
|
||||
self.fail('Expected ValueError from setStickMode')
|
||||
|
||||
def testLCDColor(self):
|
||||
bp = BindingProfile()
|
||||
bp.registerObserver(self)
|
||||
bp.setLCDColor(1.0, 0.5, 0.1)
|
||||
self.assertEqual(bp._lcdColor, (1.0, 0.5, 0.1))
|
||||
self.assertEqual(bp.lcdColor(), (1.0, 0.5, 0.1))
|
||||
self.assertChangeCount(1)
|
||||
self.assertChangeNotified(bp, ChangeType.MODIFY, 'lcdcolor')
|
||||
self.assertChangeDataEquals((1.0, 0.5, 0.1))
|
||||
|
||||
def testToCommandString(self):
|
||||
bp = BindingProfile()
|
||||
result = bp.toCommandString()
|
||||
self.assertIsNotNone(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,7 +1,9 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import enum
|
||||
|
||||
"""
|
||||
Defines a whole bunch of constants relating to mapping G13D key names to GDK key
|
||||
names, as well as the symbols that g13d natively supports.
|
||||
"""
|
||||
|
||||
G13D_TO_GDK_KEYBINDS = {
|
||||
'0': '0',
|
||||
@ -147,42 +149,35 @@ G13_KEYS = [
|
||||
'LEFT', 'DOWN', 'TOP',
|
||||
]
|
||||
|
||||
DEFAULT_STICK_REGIONS = {
|
||||
'STICK_UP': [0.0, 0.0, 1.0, 0.2],
|
||||
'STICK_DOWN': [0.0, 0.8, 1.0, 1.0],
|
||||
'STICK_LEFT': [0.0, 0.0, 0.2, 1.0],
|
||||
'STICK_RIGHT': [0.8, 0.0, 1.0, 1.0]
|
||||
}
|
||||
|
||||
DEFAULT_KEY_BINDINGS = {
|
||||
'G1': ['ESC'],
|
||||
'G1': ['GRAVE'],
|
||||
'G2': ['1'],
|
||||
'G3': ['2'],
|
||||
'G4': ['3'],
|
||||
'G5': ['4'],
|
||||
'G6': ['5'],
|
||||
'G7': ['Y'],
|
||||
'G8': ['Q'],
|
||||
'G9': ['Z'],
|
||||
'G10': ['V'],
|
||||
'G11': ['SPACE'],
|
||||
'G12': ['E'],
|
||||
'G13': ['R'],
|
||||
'G14': ['U'],
|
||||
'G15': ['LEFTSHIFT'],
|
||||
'G16': ['F'],
|
||||
'G17': ['X'],
|
||||
'G18': ['C'],
|
||||
'G19': ['H'],
|
||||
'G20': ['LEFTCTRL'],
|
||||
'G21': ['B'],
|
||||
'G22': ['T'],
|
||||
'LEFT': ['TAB'],
|
||||
'DOWN': ['M'],
|
||||
'G7': ['6'],
|
||||
'G8': ['TAB'],
|
||||
'G9': ['Q'],
|
||||
'G10': ['W'],
|
||||
'G11': ['E'],
|
||||
'G12': ['R'],
|
||||
'G13': ['T'],
|
||||
'G14': ['Y'],
|
||||
'G15': ['A'],
|
||||
'G16': ['S'],
|
||||
'G17': ['D'],
|
||||
'G18': ['F'],
|
||||
'G19': ['G'],
|
||||
'G20': ['X'],
|
||||
'G21': ['C'],
|
||||
'G22': ['V'],
|
||||
'LEFT': ['B'],
|
||||
'DOWN': ['N'],
|
||||
}
|
||||
|
||||
|
||||
class StickRegion(enum.Enum):
|
||||
class StickRegion():
|
||||
UP = 'STICK_UP'
|
||||
DOWN = 'STICK_DOWN'
|
||||
LEFT = 'STICK_LEFT'
|
||||
@ -190,13 +185,18 @@ class StickRegion(enum.Enum):
|
||||
|
||||
|
||||
ALL_STICK_REGIONS = frozenset({
|
||||
StickRegion.UP,
|
||||
StickRegion.DOWN,
|
||||
StickRegion.LEFT,
|
||||
StickRegion.RIGHT
|
||||
StickRegion.UP, StickRegion.DOWN,
|
||||
StickRegion.LEFT, StickRegion.RIGHT
|
||||
})
|
||||
|
||||
|
||||
DEFAULT_STICK_REGIONS = {
|
||||
StickRegion.UP: [0.0, 0.0, 1.0, 0.2],
|
||||
StickRegion.DOWN: [0.0, 0.8, 1.0, 1.0],
|
||||
StickRegion.LEFT: [0.0, 0.0, 0.2, 1.0],
|
||||
StickRegion.RIGHT: [0.8, 0.0, 1.0, 1.0]
|
||||
}
|
||||
|
||||
DEFAULT_STICK_REGION_BINDINGS = {
|
||||
StickRegion.UP: ['W'],
|
||||
StickRegion.DOWN: ['S'],
|
||||
@ -205,20 +205,34 @@ DEFAULT_STICK_REGION_BINDINGS = {
|
||||
}
|
||||
|
||||
|
||||
class StickMode(enum.Enum):
|
||||
class StickMode():
|
||||
ABSOLUTE = 'ABSOLUTE'
|
||||
RELATIVE = 'RELATIVE'
|
||||
KEYS = 'KEYS'
|
||||
|
||||
|
||||
ALL_STICK_MODES = frozenset({
|
||||
StickMode.ABSOLUTE,
|
||||
StickMode.RELATIVE,
|
||||
StickMode.KEYS
|
||||
StickMode.ABSOLUTE, StickMode.RELATIVE, StickMode.KEYS
|
||||
})
|
||||
|
||||
|
||||
def G13DKeyIsModifier(key):
|
||||
key = key.upper()
|
||||
return (key == 'LEFTSHIFT' or key == 'RIGHTSHIFT' or
|
||||
key == 'LEFTALT' or key == 'RIGHTALT' or
|
||||
key == 'LEFTCTRL' or key == 'RIGHTCTRL')
|
||||
|
||||
|
||||
def G13ToGDK(keybinds):
|
||||
if type(keybinds) == list:
|
||||
return [G13D_TO_GDK_KEYBINDS[binding] for binding in keybinds]
|
||||
return G13D_TO_GDK_KEYBINDS[keybinds]
|
||||
|
||||
|
||||
def GDKToG13(keybinds):
|
||||
if type(keybinds) == list:
|
||||
return [GDK_TO_G13D_KEYBINDS[binding] for binding in keybinds]
|
||||
return GDK_TO_G13D_KEYBINDS[keybinds]
|
||||
|
||||
|
||||
DEFAULT_LCD_COLOR = (1.0, 0.0, 0.0)
|
@ -2,10 +2,10 @@
|
||||
|
||||
import traceback
|
||||
|
||||
from common import VERSION
|
||||
from bindingprofile import BindingProfile
|
||||
from observer import Subject
|
||||
from observer import ChangeType
|
||||
from g13gui.common import VERSION
|
||||
from g13gui.model.bindingprofile import BindingProfile
|
||||
from g13gui.observer import Subject
|
||||
from g13gui.observer import ChangeType
|
||||
|
||||
|
||||
DEFAULT_PROFILE_NAME = 'Default Profile'
|
||||
@ -21,7 +21,9 @@ class Preferences(Subject):
|
||||
else:
|
||||
self.initDefaultProfile()
|
||||
|
||||
def profiles(self):
|
||||
def profiles(self, profileName=None):
|
||||
if profileName:
|
||||
return self._profiles[profileName]
|
||||
return self._profiles
|
||||
|
||||
def profileNames(self):
|
||||
@ -83,7 +85,7 @@ class Preferences(Subject):
|
||||
def saveToDict(self):
|
||||
return {
|
||||
'version': VERSION,
|
||||
'profiles': dict([(name, profile.toDict()) for name, profile in self._profiles.items()]),
|
||||
'profiles': dict([(name, profile.saveToDict()) for name, profile in self._profiles.items()]),
|
||||
'selectedProfile': self._selectedProfile
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import unittest
|
||||
import prefs
|
||||
import g13gui.model.prefs as prefs
|
||||
|
||||
from common import VERSION
|
||||
from observer import ChangeType
|
||||
from observer import ObserverTestCase
|
||||
from g13gui.common import VERSION
|
||||
from g13gui.observer import ChangeType
|
||||
from g13gui.observer import ObserverTestCase
|
||||
|
||||
|
||||
class PrefsTestCase(ObserverTestCase):
|
5
g13gui/g13gui/observer/__init__.py
Normal file
5
g13gui/g13gui/observer/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from g13gui.observer.observer import Observer
|
||||
from g13gui.observer.observer import Subject
|
||||
from g13gui.observer.observer import ObserverTestCase
|
||||
from g13gui.observer.observer import ChangeType
|
||||
from g13gui.observer.gtkobserver import GtkObserver
|
53
g13gui/g13gui/observer/gtkobserver.py
Normal file
53
g13gui/g13gui/observer/gtkobserver.py
Normal file
@ -0,0 +1,53 @@
|
||||
import gi
|
||||
import queue
|
||||
|
||||
from g13gui.observer import Observer
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
|
||||
class GObjectObserverProxy(GObject.Object):
|
||||
def __init__(self, owner):
|
||||
GObject.Object.__init__(self)
|
||||
self._owner = owner
|
||||
|
||||
@GObject.Signal(name='subject-changed')
|
||||
def subjectChanged(self):
|
||||
self._owner._gtkSubjectChanged(self)
|
||||
|
||||
|
||||
class GtkObserver(Observer):
|
||||
def __init__(self):
|
||||
"""Constructor. Must be called by a subclass' constructor."""
|
||||
self._observerQueue = queue.Queue()
|
||||
self._gobjectProxy = GObjectObserverProxy(self)
|
||||
|
||||
def onSubjectChanged(self, subject, changeType, key, data=None):
|
||||
"""Original Observer signal handler.
|
||||
|
||||
Runs on a (possibly) background thread to put the notification into a
|
||||
queue, then signal to trampoline to the UI thread before handling the
|
||||
notification.
|
||||
"""
|
||||
self._observerQueue.put((subject, changeType, key, data))
|
||||
self._gobjectProxy.emit("subject-changed")
|
||||
|
||||
def _gtkSubjectChanged(self, widget):
|
||||
"""GObject 'subject-changed' signal handler.
|
||||
|
||||
Runs on the UI thread, and pops the change notification off the queue
|
||||
and processes it by way of the gtkSubjectChanged method that must be
|
||||
overridden.
|
||||
"""
|
||||
(subject, changeType, key, data) = self._observerQueue.get()
|
||||
self.gtkSubjectChanged(subject, changeType, key, data)
|
||||
self._observerQueue.task_done()
|
||||
|
||||
def gtkSubjectChanged(self, subject, changeType, key, data=None):
|
||||
"""Subject notification handler.
|
||||
|
||||
Runs on the UI thread, and must be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"%s did not override Observer#gtkSubjectChanged" % (type(self)))
|
6
g13gui/g13gui/ui/__init__.py
Normal file
6
g13gui/g13gui/ui/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from g13gui.ui.profilecombobox import ProfileComboBox
|
||||
from g13gui.ui.g13button import G13Button
|
||||
from g13gui.ui.g13buttonpopover import G13ButtonPopover
|
||||
from g13gui.ui.mainwindow import MainWindow
|
||||
from g13gui.ui.profilepopover import ProfilePopover
|
||||
from g13gui.ui.profilepopover import ProfilePopoverMode
|
71
g13gui/g13gui/ui/g13button.py
Normal file
71
g13gui/g13gui/ui/g13button.py
Normal file
@ -0,0 +1,71 @@
|
||||
import gi
|
||||
|
||||
import g13gui.ui as ui
|
||||
from g13gui.observer import GtkObserver
|
||||
from g13gui.model.bindings import G13ToGDK
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, GObject, Gdk
|
||||
|
||||
|
||||
class G13Button(Gtk.MenuButton, GtkObserver):
|
||||
def __init__(self, prefs, g13KeyName):
|
||||
Gtk.MenuButton.__init__(self)
|
||||
GtkObserver.__init__(self)
|
||||
|
||||
self._prefs = prefs
|
||||
self._prefs.registerObserver(self, {'selectedProfile'})
|
||||
self._keyName = g13KeyName
|
||||
self._lastProfileName = None
|
||||
|
||||
self._popover = ui.G13ButtonPopover(self, self._prefs, self._keyName)
|
||||
self.set_popover(self._popover)
|
||||
|
||||
_image = Gtk.Image.new_from_file(g13KeyName + '.png')
|
||||
self.get_style_context().add_class('flat')
|
||||
|
||||
self.set_can_default(False)
|
||||
self.updateProfileRegistration()
|
||||
self.updateBindingDisplay()
|
||||
|
||||
def updateProfileRegistration(self):
|
||||
if self._lastProfileName:
|
||||
if self._lastProfileName in self._prefs.profileNames():
|
||||
lastProfile = self._prefs.profiles(self._lastProfileName)
|
||||
lastProfile.removeObserver(self)
|
||||
|
||||
self._prefs.selectedProfile().registerObserver(self, {self._keyName})
|
||||
|
||||
def _removeChild(self):
|
||||
child = self.get_child()
|
||||
if child:
|
||||
self.remove(child)
|
||||
|
||||
def updateBindingDisplay(self):
|
||||
self._removeChild()
|
||||
bindings = self._prefs.selectedProfile().keyBinding(self._keyName)
|
||||
print('[%s %s] %s: %s' % (self._prefs.selectedProfileName(),
|
||||
self._prefs.selectedProfile(),
|
||||
self._keyName, bindings))
|
||||
|
||||
if len(bindings) > 0:
|
||||
keybinds = G13ToGDK(bindings)
|
||||
accelerator = '+'.join(keybinds)
|
||||
shortcut = Gtk.ShortcutsShortcut(
|
||||
shortcut_type=Gtk.ShortcutType.ACCELERATOR,
|
||||
accelerator=accelerator)
|
||||
shortcut.set_halign(Gtk.Align.CENTER)
|
||||
self.add(shortcut)
|
||||
else:
|
||||
label = Gtk.Label(self._keyName)
|
||||
self.add(label)
|
||||
|
||||
self.show_all()
|
||||
|
||||
def gtkSubjectChanged(self, subject, changeType, key, data=None):
|
||||
if key == 'selectedProfile':
|
||||
self.updateProfileRegistration()
|
||||
self.updateBindingDisplay()
|
||||
elif key == self._keyName:
|
||||
self.updateBindingDisplay()
|
@ -1,43 +1,55 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import gi
|
||||
|
||||
from g13gui.observer import GtkObserver
|
||||
|
||||
from g13gui.model.bindings import G13ToGDK
|
||||
from g13gui.model.bindings import GDKToG13
|
||||
from g13gui.model.bindings import G13DKeyIsModifier
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
from bindings import GDK_TO_G13D_KEYBINDS
|
||||
from bindings import G13D_TO_GDK_KEYBINDS
|
||||
from bindings import G13DKeyIsModifier
|
||||
from gi.repository import Gtk, GObject, Gdk
|
||||
|
||||
|
||||
MAX_DELAY_BETWEEN_PRESSES_MILLIS = 250
|
||||
|
||||
|
||||
class ButtonMenu(Gtk.Popover):
|
||||
def __init__(self, profile, buttonName):
|
||||
class G13ButtonPopover(Gtk.Popover, GtkObserver):
|
||||
def __init__(self, buttonOwner, prefs, keyName):
|
||||
Gtk.Popover.__init__(self)
|
||||
GtkObserver.__init__(self)
|
||||
|
||||
self._profile = profile
|
||||
self._buttonName = buttonName
|
||||
self._currentBindings = self._profile.getBoundKey(buttonName)
|
||||
self._bindingBox = None
|
||||
self._modifiers = {}
|
||||
self._prefs = prefs
|
||||
self._prefs.registerObserver(self, {'selectedProfile'})
|
||||
self._keyName = keyName
|
||||
|
||||
self._modifiers = set()
|
||||
self._consonantKey = None
|
||||
self._lastPressTime = 0
|
||||
|
||||
self.set_relative_to(buttonOwner)
|
||||
self.updateBinding()
|
||||
self.build()
|
||||
|
||||
def updateBinding(self):
|
||||
selectedProfile = self._prefs.selectedProfile()
|
||||
self._currentBindings = selectedProfile.keyBinding(self._keyName)
|
||||
|
||||
def gtkSubjectChanged(self, subject, changeType, key, data=None):
|
||||
self.updateBinding()
|
||||
|
||||
def build(self):
|
||||
self._box = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
|
||||
self._box.set_border_width(6)
|
||||
self.add(self._box)
|
||||
|
||||
label = Gtk.Label()
|
||||
label.set_markup("<b>" + buttonName + "</b>")
|
||||
label.set_markup("<b>" + self._keyName + "</b>")
|
||||
self._box.pack_start(label, True, True, 6)
|
||||
|
||||
button = Gtk.Button(label="Clear Binding")
|
||||
button.set_can_focus(False)
|
||||
self._box.pack_start(button, True, True, 6)
|
||||
|
||||
self._box.show_all()
|
||||
|
||||
self.connect("key-press-event", self.keypress)
|
||||
@ -46,21 +58,25 @@ class ButtonMenu(Gtk.Popover):
|
||||
self.connect("closed", self.closed)
|
||||
button.connect("pressed", self.clear)
|
||||
|
||||
self.rebuildBindingDisplay()
|
||||
self.buildBindingDisplay()
|
||||
|
||||
def shown(self, widget):
|
||||
Gdk.keyboard_grab(self.get_window(), False, Gdk.CURRENT_TIME)
|
||||
self.grab_add()
|
||||
|
||||
def rebuildBindingDisplay(self):
|
||||
if self._bindingBox:
|
||||
self._box.remove(self._bindingBox)
|
||||
|
||||
self._bindingBox = Gtk.Box(spacing=0, orientation=Gtk.Orientation.VERTICAL)
|
||||
self.buildBindingDisplay()
|
||||
|
||||
def buildBindingDisplay(self):
|
||||
self._bindingBox = Gtk.Box(spacing=0,
|
||||
orientation=Gtk.Orientation.VERTICAL)
|
||||
self._box.pack_start(self._bindingBox, True, True, 6)
|
||||
self._box.reorder_child(self._bindingBox, 1)
|
||||
|
||||
if len(self._currentBindings) > 0:
|
||||
keybinds = [G13D_TO_GDK_KEYBINDS[binding] for binding in self._currentBindings]
|
||||
keybinds = G13ToGDK(self._currentBindings)
|
||||
accelerator = '+'.join(keybinds)
|
||||
shortcut = Gtk.ShortcutsShortcut(
|
||||
shortcut_type=Gtk.ShortcutType.ACCELERATOR,
|
||||
@ -75,43 +91,44 @@ class ButtonMenu(Gtk.Popover):
|
||||
self._bindingBox.show_all()
|
||||
|
||||
def keypress(self, buttonMenu, eventKey):
|
||||
print("Keypressed! %s, %s" % (eventKey.keyval, Gdk.keyval_name(eventKey.keyval)))
|
||||
|
||||
if eventKey.time - self._lastPressTime > MAX_DELAY_BETWEEN_PRESSES_MILLIS:
|
||||
self._modifiers = {}
|
||||
pressDelta = eventKey.time - self._lastPressTime
|
||||
if pressDelta > MAX_DELAY_BETWEEN_PRESSES_MILLIS:
|
||||
self._modifiers = set()
|
||||
self._consonantKey = None
|
||||
|
||||
binding = Gdk.keyval_name(eventKey.keyval)
|
||||
if len(binding) == 1:
|
||||
binding = binding.upper()
|
||||
|
||||
if binding == 'Meta_L':
|
||||
binding = 'Alt_L'
|
||||
if binding == 'Meta_R':
|
||||
binding = 'Alt_R'
|
||||
binding = GDK_TO_G13D_KEYBINDS[binding]
|
||||
print('Binding is %s' % (binding))
|
||||
|
||||
binding = GDKToG13(binding)
|
||||
|
||||
if G13DKeyIsModifier(binding):
|
||||
self._modifiers[binding] = True
|
||||
print("Modifiers are now %s" % (repr(self._modifiers.keys())))
|
||||
self._modifiers.add(binding)
|
||||
else:
|
||||
self._consonantKey = binding
|
||||
|
||||
self._lastPressTime = eventKey.time
|
||||
return True
|
||||
|
||||
def keyrelease(self, buttonMenu, eventKey):
|
||||
self._currentBindings = [modifier for modifier in self._modifiers.keys()]
|
||||
self._currentBindings = sorted(list(self._modifiers))
|
||||
if self._consonantKey:
|
||||
self._currentBindings = self._currentBindings + [self._consonantKey]
|
||||
|
||||
self._currentBindings += [self._consonantKey]
|
||||
self.rebuildBindingDisplay()
|
||||
print("Bindings are now %s" % (self._currentBindings))
|
||||
self.hide()
|
||||
return True
|
||||
|
||||
def clear(self, button):
|
||||
self._currentBindings = []
|
||||
self.rebuildBindingDisplay()
|
||||
self.hide()
|
||||
|
||||
def closed(self, buttonMenu):
|
||||
self._profile.bindKey(self._buttonName, self._currentBindings)
|
||||
self.grab_remove()
|
||||
self._prefs.selectedProfile().bindKey(self._keyName,
|
||||
self._currentBindings)
|
||||
self.hide()
|
186
g13gui/g13gui/ui/mainwindow.py
Normal file
186
g13gui/g13gui/ui/mainwindow.py
Normal file
@ -0,0 +1,186 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import gi
|
||||
|
||||
import g13gui.ui as ui
|
||||
|
||||
from g13gui.g13d import SaveTask
|
||||
from g13gui.g13d import UploadTask
|
||||
from g13gui.observer import GtkObserver
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, Gdk, GObject
|
||||
|
||||
|
||||
class MainWindow(Gtk.Window, GtkObserver):
|
||||
def __init__(self, workerQueue, prefs):
|
||||
Gtk.Window.__init__(self)
|
||||
GtkObserver.__init__(self)
|
||||
|
||||
self.set_default_size(640, 480)
|
||||
geometry = Gdk.Geometry()
|
||||
geometry.max_width = 640
|
||||
geometry.max_height = 480
|
||||
self.set_geometry_hints(None, geometry, Gdk.WindowHints.MAX_SIZE)
|
||||
|
||||
self._workerQueue = workerQueue
|
||||
self._prefs = prefs
|
||||
self._prefs.registerObserver(self, 'selectedProfile')
|
||||
self._prefs.selectedProfile().registerObserver(self)
|
||||
self._lastProfileName = self._prefs.selectedProfileName()
|
||||
|
||||
self.setupHeaderBar()
|
||||
|
||||
self._box = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
|
||||
self._box.set_border_width(6)
|
||||
self.add(self._box)
|
||||
|
||||
self._infoBar = Gtk.InfoBar()
|
||||
self._infoBar.set_no_show_all(True)
|
||||
self._infoBarLabel = Gtk.Label()
|
||||
self._infoBar.get_content_area().add(self._infoBarLabel)
|
||||
self._infoBarLabel.show()
|
||||
self._box.add(self._infoBar)
|
||||
|
||||
self.setupG13ButtonGrid()
|
||||
|
||||
def gtkSubjectChanged(self, subject, changeType, key, data=None):
|
||||
self._doUpload()
|
||||
self._doSave()
|
||||
|
||||
def setupHeaderBar(self):
|
||||
self._headerBar = Gtk.HeaderBar()
|
||||
self._headerBar.set_title("G13 Configurator")
|
||||
self._headerBar.set_show_close_button(True)
|
||||
|
||||
self._profileComboBox = ui.ProfileComboBox(self._prefs)
|
||||
self._profileComboBox.connect('changed', self._profileChanged)
|
||||
self._headerBar.add(self._profileComboBox)
|
||||
|
||||
addProfileButton = Gtk.MenuButton.new()
|
||||
addProfileButton.add(Gtk.Image.new_from_icon_name(
|
||||
"document-new-symbolic", 1))
|
||||
addProfilePopover = ui.ProfilePopover(self._prefs,
|
||||
mode=ui.ProfilePopoverMode.ADD)
|
||||
addProfileButton.set_popover(addProfilePopover)
|
||||
self._headerBar.add(addProfileButton)
|
||||
|
||||
editProfileButton = Gtk.MenuButton.new()
|
||||
editProfileButton.add(
|
||||
Gtk.Image.new_from_icon_name('document-edit-symbolic', 1))
|
||||
editProfilePopover = ui.ProfilePopover(self._prefs,
|
||||
mode=ui.ProfilePopoverMode.EDIT)
|
||||
editProfileButton.set_popover(editProfilePopover)
|
||||
self._headerBar.add(editProfileButton)
|
||||
|
||||
self._uploadButton = Gtk.Button.new_from_icon_name(
|
||||
"document-send-symbolic", 1)
|
||||
self._uploadButton.connect("clicked", self.uploadClicked)
|
||||
self._headerBar.add(self._uploadButton)
|
||||
|
||||
Gtk.Window.set_titlebar(self, self._headerBar)
|
||||
|
||||
@GObject.Signal(name='daemon-connection-changed', arg_types=(bool,))
|
||||
def daemonConnectionChanged(self, connected):
|
||||
self._connected = connected
|
||||
if connected:
|
||||
self._uploadButton.set_state_flags(Gtk.StateFlags.NORMAL, True)
|
||||
self._infoBar.hide()
|
||||
self._doUpload()
|
||||
else:
|
||||
self._uploadButton.set_state_flags(Gtk.StateFlags.INSENSITIVE,
|
||||
True)
|
||||
self._infoBar.set_message_type(Gtk.MessageType.WARNING)
|
||||
self._infoBarLabel.set_text(
|
||||
'The G13 user space driver is not running. '
|
||||
'Attempting to reconnect.')
|
||||
self._infoBar.show()
|
||||
|
||||
@GObject.Signal(name='uploading', arg_types=(float,))
|
||||
def uploadStatusChanged(self, percentage):
|
||||
if percentage < 1.0:
|
||||
self._infoBar.set_message_type(Gtk.MessageType.INFO)
|
||||
self._infoBarLabel.set_text('Uploading to the G13...')
|
||||
self._infoBar.show()
|
||||
else:
|
||||
self._infoBar.hide()
|
||||
|
||||
def _profileChanged(self, widget):
|
||||
self._doUpload()
|
||||
|
||||
def _doUpload(self):
|
||||
config = self._prefs.selectedProfile().toCommandString()
|
||||
task = UploadTask(config)
|
||||
self._workerQueue.put(task)
|
||||
|
||||
def _doSave(self):
|
||||
task = SaveTask(self._prefs.saveToDict())
|
||||
self._workerQueue.put(task)
|
||||
|
||||
def uploadClicked(self, widget):
|
||||
self._doUpload()
|
||||
self._doSave()
|
||||
|
||||
def setupG13ButtonGrid(self):
|
||||
self._mButtons = Gtk.ButtonBox(
|
||||
spacing=3,
|
||||
orientation=Gtk.Orientation.HORIZONTAL,
|
||||
baseline_position=Gtk.BaselinePosition.CENTER)
|
||||
self._mButtons.set_layout(Gtk.ButtonBoxStyle.CENTER)
|
||||
self._box.pack_start(self._mButtons, False, False, 6)
|
||||
|
||||
self._keyGrid = Gtk.Grid()
|
||||
self._keyGrid.set_hexpand(False)
|
||||
self._keyGrid.set_vexpand(False)
|
||||
self._keyGrid.set_row_spacing(3)
|
||||
self._keyGrid.set_column_spacing(3)
|
||||
self._box.pack_start(self._keyGrid, False, False, 6)
|
||||
|
||||
self._stickGrid = Gtk.Grid()
|
||||
self._stickGrid.set_row_spacing(3)
|
||||
self._stickGrid.set_column_spacing(3)
|
||||
self._box.pack_start(self._stickGrid, False, False, 6)
|
||||
|
||||
self._g13Buttons = {}
|
||||
|
||||
self._mButtons.pack_start(self.newG13Button('M1'), False, False, 6)
|
||||
self._mButtons.pack_start(self.newG13Button('M2'), False, False, 6)
|
||||
self._mButtons.pack_start(self.newG13Button('M3'), False, False, 6)
|
||||
|
||||
# G1 to G14
|
||||
self._buttonNum = 1
|
||||
for row in range(0, 2):
|
||||
for col in range(0, 7):
|
||||
self._keyGrid.attach(self.newG13NumberedButton(),
|
||||
col, row, 1, 1)
|
||||
|
||||
# G15 to G19
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 1, 3, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 2, 3, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 3, 3, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 4, 3, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 5, 3, 1, 1)
|
||||
|
||||
# G20 to G22
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 2, 4, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 3, 4, 1, 1)
|
||||
self._keyGrid.attach(self.newG13NumberedButton(), 4, 4, 1, 1)
|
||||
|
||||
self._stickGrid.attach(self.newG13Button("STICK_UP"), 4, 0, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("LEFT"), 2, 1, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("STICK_LEFT"), 3, 1, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("TOP"), 4, 1, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("STICK_RIGHT"), 5, 1, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("STICK_DOWN"), 4, 2, 1, 1)
|
||||
self._stickGrid.attach(self.newG13Button("DOWN"), 4, 3, 1, 1)
|
||||
|
||||
def newG13NumberedButton(self):
|
||||
button = self.newG13Button('G' + str(self._buttonNum))
|
||||
self._buttonNum = self._buttonNum + 1
|
||||
return button
|
||||
|
||||
def newG13Button(self, name):
|
||||
button = ui.G13Button(self._prefs, name)
|
||||
self._g13Buttons[name] = button
|
||||
return button
|
59
g13gui/g13gui/ui/profilecombobox.py
Normal file
59
g13gui/g13gui/ui/profilecombobox.py
Normal file
@ -0,0 +1,59 @@
|
||||
import gi
|
||||
|
||||
from g13gui.observer import GtkObserver
|
||||
from g13gui.observer import ChangeType
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, GObject, Gdk
|
||||
|
||||
|
||||
def AlphabeticalSort(model, a, b, userData):
|
||||
print('AlphabeticalSort %s <=> %s' % (a, b))
|
||||
if a == b:
|
||||
return 0
|
||||
if a < b:
|
||||
return -1
|
||||
return 1
|
||||
|
||||
|
||||
class ProfileComboBox(Gtk.ComboBoxText, GtkObserver):
|
||||
def __init__(self, prefs):
|
||||
Gtk.ComboBoxText.__init__(self)
|
||||
GtkObserver.__init__(self)
|
||||
|
||||
self._prefs = prefs
|
||||
self._prefs.registerObserver(self, {'profile'})
|
||||
self._isUpdating = False
|
||||
|
||||
self._model = self.get_model()
|
||||
self._model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
|
||||
self._model.set_default_sort_func(AlphabeticalSort)
|
||||
self.connect("changed", self._profileChanged)
|
||||
|
||||
self.update()
|
||||
|
||||
def _profileChanged(self, widget):
|
||||
selectedProfile = self.get_active_text()
|
||||
print('Profile changed to %s' % selectedProfile)
|
||||
if selectedProfile:
|
||||
self._prefs.setSelectedProfile(selectedProfile)
|
||||
|
||||
def update(self):
|
||||
profiles = self._prefs.profileNames()
|
||||
selected = self._prefs.selectedProfileName()
|
||||
|
||||
self._model.clear()
|
||||
row = 0
|
||||
|
||||
for name in profiles:
|
||||
self._model.append([name, name])
|
||||
if name == selected:
|
||||
self.set_active(row)
|
||||
row = row + 1
|
||||
|
||||
def gtkSubjectChanged(self, subject, changeType, key, data=None):
|
||||
name = list(data.keys())[0]
|
||||
|
||||
if changeType == ChangeType.ADD:
|
||||
self._model.append([name, name])
|
129
g13gui/g13gui/ui/profilepopover.py
Normal file
129
g13gui/g13gui/ui/profilepopover.py
Normal file
@ -0,0 +1,129 @@
|
||||
import gi
|
||||
import enum
|
||||
|
||||
import g13gui.model.bindings as bindings
|
||||
from g13gui.observer import GtkObserver
|
||||
from g13gui.model import BindingProfile
|
||||
from g13gui.model.bindings import G13ToGDK
|
||||
from g13gui.model.bindings import GDKToG13
|
||||
from g13gui.model.bindings import G13DKeyIsModifier
|
||||
from g13gui.model.bindings import StickMode
|
||||
from g13gui.model.bindings import ALL_STICK_MODES
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk, GObject, Gdk
|
||||
|
||||
|
||||
class ProfilePopoverMode(enum.Enum):
|
||||
EDIT = 'edit'
|
||||
ADD = 'add'
|
||||
|
||||
|
||||
class ProfilePopover(Gtk.Popover, GtkObserver):
|
||||
def __init__(self, prefs, mode=ProfilePopoverMode.EDIT):
|
||||
Gtk.Popover.__init__(self)
|
||||
GtkObserver.__init__(self)
|
||||
|
||||
self._prefs = prefs
|
||||
self._mode = mode
|
||||
self._lastRow = 0
|
||||
|
||||
self.build()
|
||||
self.connect('show', self.shown)
|
||||
|
||||
def updateFromPrefs(self):
|
||||
self._profileName.set_text(self._prefs.selectedProfileName())
|
||||
|
||||
profile = self._prefs.selectedProfile()
|
||||
lcdColor = profile.lcdColor()
|
||||
self._lcdColorButton.set_rgba(Gdk.RGBA(*lcdColor, alpha=1.0))
|
||||
|
||||
stickMode = profile.stickMode()
|
||||
activeIndex = sorted(list(ALL_STICK_MODES)).index(stickMode)
|
||||
self._stickModeCombo.set_active(activeIndex)
|
||||
|
||||
def commitToPrefs(self):
|
||||
pass
|
||||
|
||||
def addRow(self, widget, labelText=None):
|
||||
if labelText:
|
||||
label = Gtk.Label()
|
||||
label.set_text(labelText)
|
||||
self._grid.attach(label, 1, self._lastRow, 1, 1)
|
||||
self._grid.attach(widget, 2, self._lastRow, 1, 1)
|
||||
else:
|
||||
self._grid.attach(widget, 1, self._lastRow, 2, 1)
|
||||
self._lastRow += 1
|
||||
|
||||
def build(self):
|
||||
self._grid = Gtk.Grid()
|
||||
self._grid.set_row_spacing(6)
|
||||
self._grid.set_column_spacing(10)
|
||||
self._grid.set_border_width(6)
|
||||
self.add(self._grid)
|
||||
|
||||
self._profileName = Gtk.Entry()
|
||||
self._profileName.set_can_focus(True)
|
||||
self._profileName.set_activates_default(True)
|
||||
self.addRow(self._profileName, 'Profile Name')
|
||||
|
||||
self._lcdColorButton = Gtk.ColorButton()
|
||||
self._lcdColorButton.set_use_alpha(False)
|
||||
self._lcdColorButton.set_rgba(Gdk.RGBA(*bindings.DEFAULT_LCD_COLOR))
|
||||
self._lcdColorButton.set_title('LCD Color')
|
||||
self.addRow(self._lcdColorButton, 'LCD Color')
|
||||
|
||||
self._stickModeCombo = Gtk.ComboBoxText()
|
||||
for mode in sorted(list(ALL_STICK_MODES)):
|
||||
self._stickModeCombo.append_text(mode.capitalize())
|
||||
self._stickModeCombo.set_active(1)
|
||||
self.addRow(self._stickModeCombo, 'Joystick Mode')
|
||||
|
||||
commitButton = Gtk.Button()
|
||||
commitButton.set_receives_default(True)
|
||||
commitButton.set_can_default(True)
|
||||
commitButton.connect('clicked', self.commitClicked)
|
||||
|
||||
if self._mode == ProfilePopoverMode.EDIT:
|
||||
commitButton.set_label('Update')
|
||||
commitButton.get_style_context().add_class('suggested-action')
|
||||
self.addRow(commitButton)
|
||||
|
||||
removeButton = Gtk.Button()
|
||||
removeButton.set_label('Remove')
|
||||
removeButton.connect('clicked', self.removeClicked)
|
||||
removeButton.get_style_context().add_class('destructive-action')
|
||||
self.addRow(removeButton)
|
||||
else:
|
||||
commitButton.set_label('Add')
|
||||
commitButton.get_style_context().add_class('suggested-action')
|
||||
self.addRow(commitButton)
|
||||
|
||||
self._grid.show_all()
|
||||
|
||||
def commitClicked(self, widget):
|
||||
lcdColor = self._lcdColorButton.get_rgba()
|
||||
lcdColor = (lcdColor.red, lcdColor.green, lcdColor.blue)
|
||||
profileName = self._profileName.get_text()
|
||||
stickMode = self._stickModeCombo.get_active_text()
|
||||
|
||||
profile = None
|
||||
if self._mode == ProfilePopoverMode.ADD:
|
||||
profile = BindingProfile()
|
||||
self._prefs.addProfile(profileName, profile)
|
||||
else:
|
||||
profile = self._prefs.selectedProfile()
|
||||
|
||||
profile.setLCDColor(*lcdColor)
|
||||
profile.setStickMode(stickMode.upper())
|
||||
|
||||
self.hide()
|
||||
|
||||
def removeClicked(self, widget):
|
||||
pass
|
||||
|
||||
def shown(self, widget):
|
||||
self._profileName.grab_focus()
|
||||
if self._mode == ProfilePopoverMode.EDIT:
|
||||
self.updateFromPrefs()
|
Loading…
Reference in New Issue
Block a user