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:
June Tate-Gans 2021-04-28 18:25:47 -05:00
parent 91e62addb7
commit 476b43ed5f
20 changed files with 702 additions and 357 deletions

View File

@ -0,0 +1,3 @@
import g13gui.model as model
import g13gui.ui as ui
import g13gui.observer as observer

View File

@ -9,11 +9,10 @@ import traceback
import xdg.BaseDirectory as basedir import xdg.BaseDirectory as basedir
import json import json
from common import PROFILES_CONFIG_PATH from g13gui.common import PROFILES_CONFIG_PATH
from common import VERSION from g13gui.common import VERSION
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import GObject from gi.repository import GObject
@ -32,24 +31,12 @@ class UploadTask():
class SaveTask(): class SaveTask():
def __init__(self, profiles, defaultProfileName): def __init__(self, prefsDict):
self._profiles = profiles self._prefsDict = prefsDict
self._defaultProfileName = defaultProfileName
def run(self, outfp, infp, callback): 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: with open(PROFILES_CONFIG_PATH, 'w') as f:
f.write(encoder.encode(config)) f.write(json.dumps(self._prefsDict, default=str))
f.flush() f.flush()

View File

@ -3,20 +3,23 @@
import queue import queue
import gi 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 gi.repository import Gtk, GObject
from mainwindow import MainWindow
from g13d import G13DWorker
VERSION = '0.1.0' VERSION = '0.1.0'
if __name__ == '__main__': if __name__ == '__main__':
prefs = model.Preferences()
queue = queue.Queue() queue = queue.Queue()
win = MainWindow(queue) win = ui.MainWindow(queue, prefs)
win.connect("destroy", Gtk.main_quit) win.connect("destroy", Gtk.main_quit)
win.show_all() win.show_all()

View File

@ -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)

View File

@ -0,0 +1,2 @@
from g13gui.model.bindingprofile import BindingProfile
from g13gui.model.prefs import Preferences

View File

@ -1,9 +1,9 @@
#!/usr/bin/python #!/usr/bin/python
import bindings import g13gui.model.bindings as bindings
from observer import Subject from g13gui.observer import Subject
from observer import ChangeType from g13gui.observer import ChangeType
class BindingProfile(Subject): class BindingProfile(Subject):
@ -17,6 +17,10 @@ class BindingProfile(Subject):
self._stickRegions = bindings.DEFAULT_STICK_REGIONS self._stickRegions = bindings.DEFAULT_STICK_REGIONS
self._stickRegionBindings = bindings.DEFAULT_STICK_REGION_BINDINGS self._stickRegionBindings = bindings.DEFAULT_STICK_REGION_BINDINGS
self._keyBindings = bindings.DEFAULT_KEY_BINDINGS self._keyBindings = bindings.DEFAULT_KEY_BINDINGS
self._lcdColor = bindings.DEFAULT_LCD_COLOR
def lcdColor(self):
return self._lcdColor
def stickMode(self): def stickMode(self):
return self._stickMode return self._stickMode
@ -36,6 +40,14 @@ class BindingProfile(Subject):
return [] 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): def _bindKey(self, gkey, keybinding):
if gkey in self._stickRegions.keys(): if gkey in self._stickRegions.keys():
self._stickRegionBindings[gkey] = keybinding self._stickRegionBindings[gkey] = keybinding
@ -59,15 +71,24 @@ class BindingProfile(Subject):
self._setStickMode(stickmode) self._setStickMode(stickmode)
self.notifyChanged() 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): def toCommandString(self):
commands = [] commands = []
for gkey, kbdkey in self._keyBindings.items(): commands.append(self._lcdColorToCommandString())
if len(kbdkey) > 0:
keys = '+'.join(['KEY_' + key for key in kbdkey]) for gkey in self._keyBindings.keys():
commands.append("bind %s %s" % (gkey, keys)) commands.append(self._keyBindingToCommandString(gkey))
else:
commands.append("unbind %s" % (gkey))
if self._stickMode == bindings.StickMode.KEYS: if self._stickMode == bindings.StickMode.KEYS:
for region, bounds in self._stickRegions.items(): for region, bounds in self._stickRegions.items():
@ -80,6 +101,7 @@ class BindingProfile(Subject):
return '\n'.join(commands) return '\n'.join(commands)
def loadFromDict(self, dict): def loadFromDict(self, dict):
self._lcdColor = dict['lcdcolor']
self._stickMode = dict['stickMode'] self._stickMode = dict['stickMode']
self._stickRegions = dict['stickRegions'] self._stickRegions = dict['stickRegions']
self._stickRegionBindings = dict['stickRegionBindings'] self._stickRegionBindings = dict['stickRegionBindings']
@ -87,6 +109,7 @@ class BindingProfile(Subject):
def saveToDict(self): def saveToDict(self):
return { return {
'lcdcolor': self._lcdColor,
'stickMode': self._stickMode, 'stickMode': self._stickMode,
'stickRegions': self._stickRegions, 'stickRegions': self._stickRegions,
'stickRegionBindings': self._stickRegionBindings, 'stickRegionBindings': self._stickRegionBindings,

View File

@ -2,10 +2,10 @@
import unittest import unittest
import bindings import g13gui.model.bindings as bindings
from bindingprofile import BindingProfile from g13gui.model.bindingprofile import BindingProfile
from observer import ChangeType from g13gui.observer import ChangeType
from observer import ObserverTestCase from g13gui.observer import ObserverTestCase
class PrefsTestCase(ObserverTestCase): class PrefsTestCase(ObserverTestCase):
@ -16,14 +16,18 @@ class PrefsTestCase(ObserverTestCase):
bp = BindingProfile() bp = BindingProfile()
self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS) self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS)
self.assertEqual(bp.stickRegions(), bindings.DEFAULT_STICK_REGIONS) 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._keyBindings, bindings.DEFAULT_KEY_BINDINGS)
self.assertEqual(bp._lcdColor, bindings.DEFAULT_LCD_COLOR)
def testInvalidDict(self): def testInvalidDict(self):
bp = BindingProfile({}) bp = BindingProfile({})
self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS) self.assertEqual(bp.stickMode(), bindings.StickMode.KEYS)
self.assertEqual(bp.stickRegions(), bindings.DEFAULT_STICK_REGIONS) 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) self.assertEqual(bp._keyBindings, bindings.DEFAULT_KEY_BINDINGS)
def testDictLoadSave(self): def testDictLoadSave(self):
@ -72,6 +76,21 @@ class PrefsTestCase(ObserverTestCase):
else: else:
self.fail('Expected ValueError from setStickMode') 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -1,7 +1,9 @@
#!/usr/bin/python3 #!/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 = { G13D_TO_GDK_KEYBINDS = {
'0': '0', '0': '0',
@ -147,42 +149,35 @@ G13_KEYS = [
'LEFT', 'DOWN', 'TOP', '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 = { DEFAULT_KEY_BINDINGS = {
'G1': ['ESC'], 'G1': ['GRAVE'],
'G2': ['1'], 'G2': ['1'],
'G3': ['2'], 'G3': ['2'],
'G4': ['3'], 'G4': ['3'],
'G5': ['4'], 'G5': ['4'],
'G6': ['5'], 'G6': ['5'],
'G7': ['Y'], 'G7': ['6'],
'G8': ['Q'], 'G8': ['TAB'],
'G9': ['Z'], 'G9': ['Q'],
'G10': ['V'], 'G10': ['W'],
'G11': ['SPACE'], 'G11': ['E'],
'G12': ['E'], 'G12': ['R'],
'G13': ['R'], 'G13': ['T'],
'G14': ['U'], 'G14': ['Y'],
'G15': ['LEFTSHIFT'], 'G15': ['A'],
'G16': ['F'], 'G16': ['S'],
'G17': ['X'], 'G17': ['D'],
'G18': ['C'], 'G18': ['F'],
'G19': ['H'], 'G19': ['G'],
'G20': ['LEFTCTRL'], 'G20': ['X'],
'G21': ['B'], 'G21': ['C'],
'G22': ['T'], 'G22': ['V'],
'LEFT': ['TAB'], 'LEFT': ['B'],
'DOWN': ['M'], 'DOWN': ['N'],
} }
class StickRegion(enum.Enum): class StickRegion():
UP = 'STICK_UP' UP = 'STICK_UP'
DOWN = 'STICK_DOWN' DOWN = 'STICK_DOWN'
LEFT = 'STICK_LEFT' LEFT = 'STICK_LEFT'
@ -190,35 +185,54 @@ class StickRegion(enum.Enum):
ALL_STICK_REGIONS = frozenset({ ALL_STICK_REGIONS = frozenset({
StickRegion.UP, StickRegion.UP, StickRegion.DOWN,
StickRegion.DOWN, StickRegion.LEFT, StickRegion.RIGHT
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 = { DEFAULT_STICK_REGION_BINDINGS = {
StickRegion.UP: ['W'], StickRegion.UP: ['W'],
StickRegion.DOWN: ['S'], StickRegion.DOWN: ['S'],
StickRegion.LEFT: ['A'], StickRegion.LEFT: ['A'],
StickRegion.RIGHT: ['D'] StickRegion.RIGHT: ['D']
} }
class StickMode(enum.Enum): class StickMode():
ABSOLUTE = 'ABSOLUTE' ABSOLUTE = 'ABSOLUTE'
RELATIVE = 'RELATIVE' RELATIVE = 'RELATIVE'
KEYS = 'KEYS' KEYS = 'KEYS'
ALL_STICK_MODES = frozenset({ ALL_STICK_MODES = frozenset({
StickMode.ABSOLUTE, StickMode.ABSOLUTE, StickMode.RELATIVE, StickMode.KEYS
StickMode.RELATIVE,
StickMode.KEYS
}) })
def G13DKeyIsModifier(key): def G13DKeyIsModifier(key):
key = key.upper() key = key.upper()
return (key == 'LEFTSHIFT' or key == 'RIGHTSHIFT' or return (key == 'LEFTSHIFT' or key == 'RIGHTSHIFT' or
key == 'LEFTALT' or key == 'RIGHTALT' or key == 'LEFTALT' or key == 'RIGHTALT' or
key == 'LEFTCTRL' or key == 'RIGHTCTRL') 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)

View File

@ -2,10 +2,10 @@
import traceback import traceback
from common import VERSION from g13gui.common import VERSION
from bindingprofile import BindingProfile from g13gui.model.bindingprofile import BindingProfile
from observer import Subject from g13gui.observer import Subject
from observer import ChangeType from g13gui.observer import ChangeType
DEFAULT_PROFILE_NAME = 'Default Profile' DEFAULT_PROFILE_NAME = 'Default Profile'
@ -21,7 +21,9 @@ class Preferences(Subject):
else: else:
self.initDefaultProfile() self.initDefaultProfile()
def profiles(self): def profiles(self, profileName=None):
if profileName:
return self._profiles[profileName]
return self._profiles return self._profiles
def profileNames(self): def profileNames(self):
@ -83,7 +85,7 @@ class Preferences(Subject):
def saveToDict(self): def saveToDict(self):
return { return {
'version': VERSION, '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 'selectedProfile': self._selectedProfile
} }

View File

@ -1,11 +1,11 @@
#!/usr/bin/python #!/usr/bin/python
import unittest import unittest
import prefs import g13gui.model.prefs as prefs
from common import VERSION from g13gui.common import VERSION
from observer import ChangeType from g13gui.observer import ChangeType
from observer import ObserverTestCase from g13gui.observer import ObserverTestCase
class PrefsTestCase(ObserverTestCase): class PrefsTestCase(ObserverTestCase):

View 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

View 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)))

View 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

View 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()

View File

@ -1,43 +1,55 @@
#!/usr/bin/python3
import gi 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('Gtk', '3.0')
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, GObject, Gdk
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
MAX_DELAY_BETWEEN_PRESSES_MILLIS = 250 MAX_DELAY_BETWEEN_PRESSES_MILLIS = 250
class ButtonMenu(Gtk.Popover): class G13ButtonPopover(Gtk.Popover, GtkObserver):
def __init__(self, profile, buttonName): def __init__(self, buttonOwner, prefs, keyName):
Gtk.Popover.__init__(self) Gtk.Popover.__init__(self)
GtkObserver.__init__(self)
self._profile = profile self._prefs = prefs
self._buttonName = buttonName self._prefs.registerObserver(self, {'selectedProfile'})
self._currentBindings = self._profile.getBoundKey(buttonName) self._keyName = keyName
self._bindingBox = None
self._modifiers = {} self._modifiers = set()
self._consonantKey = None self._consonantKey = None
self._lastPressTime = 0 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 = Gtk.Box(spacing=6, orientation=Gtk.Orientation.VERTICAL)
self._box.set_border_width(6)
self.add(self._box) self.add(self._box)
label = Gtk.Label() label = Gtk.Label()
label.set_markup("<b>" + buttonName + "</b>") label.set_markup("<b>" + self._keyName + "</b>")
self._box.pack_start(label, True, True, 6) self._box.pack_start(label, True, True, 6)
button = Gtk.Button(label="Clear Binding") button = Gtk.Button(label="Clear Binding")
button.set_can_focus(False)
self._box.pack_start(button, True, True, 6) self._box.pack_start(button, True, True, 6)
self._box.show_all() self._box.show_all()
self.connect("key-press-event", self.keypress) self.connect("key-press-event", self.keypress)
@ -46,21 +58,25 @@ class ButtonMenu(Gtk.Popover):
self.connect("closed", self.closed) self.connect("closed", self.closed)
button.connect("pressed", self.clear) button.connect("pressed", self.clear)
self.rebuildBindingDisplay() self.buildBindingDisplay()
def shown(self, widget): def shown(self, widget):
Gdk.keyboard_grab(self.get_window(), False, Gdk.CURRENT_TIME) self.grab_add()
def rebuildBindingDisplay(self): def rebuildBindingDisplay(self):
if self._bindingBox: if self._bindingBox:
self._box.remove(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.pack_start(self._bindingBox, True, True, 6)
self._box.reorder_child(self._bindingBox, 1) self._box.reorder_child(self._bindingBox, 1)
if len(self._currentBindings) > 0: if len(self._currentBindings) > 0:
keybinds = [G13D_TO_GDK_KEYBINDS[binding] for binding in self._currentBindings] keybinds = G13ToGDK(self._currentBindings)
accelerator = '+'.join(keybinds) accelerator = '+'.join(keybinds)
shortcut = Gtk.ShortcutsShortcut( shortcut = Gtk.ShortcutsShortcut(
shortcut_type=Gtk.ShortcutType.ACCELERATOR, shortcut_type=Gtk.ShortcutType.ACCELERATOR,
@ -75,43 +91,44 @@ class ButtonMenu(Gtk.Popover):
self._bindingBox.show_all() self._bindingBox.show_all()
def keypress(self, buttonMenu, eventKey): def keypress(self, buttonMenu, eventKey):
print("Keypressed! %s, %s" % (eventKey.keyval, Gdk.keyval_name(eventKey.keyval))) pressDelta = eventKey.time - self._lastPressTime
if pressDelta > MAX_DELAY_BETWEEN_PRESSES_MILLIS:
if eventKey.time - self._lastPressTime > MAX_DELAY_BETWEEN_PRESSES_MILLIS: self._modifiers = set()
self._modifiers = {}
self._consonantKey = None self._consonantKey = None
binding = Gdk.keyval_name(eventKey.keyval) binding = Gdk.keyval_name(eventKey.keyval)
if len(binding) == 1: if len(binding) == 1:
binding = binding.upper() binding = binding.upper()
if binding == 'Meta_L': if binding == 'Meta_L':
binding = 'Alt_L' binding = 'Alt_L'
if binding == 'Meta_R': if binding == 'Meta_R':
binding = 'Alt_R' binding = 'Alt_R'
binding = GDK_TO_G13D_KEYBINDS[binding]
print('Binding is %s' % (binding)) binding = GDKToG13(binding)
if G13DKeyIsModifier(binding): if G13DKeyIsModifier(binding):
self._modifiers[binding] = True self._modifiers.add(binding)
print("Modifiers are now %s" % (repr(self._modifiers.keys())))
else: else:
self._consonantKey = binding self._consonantKey = binding
self._lastPressTime = eventKey.time self._lastPressTime = eventKey.time
return True
def keyrelease(self, buttonMenu, eventKey): def keyrelease(self, buttonMenu, eventKey):
self._currentBindings = [modifier for modifier in self._modifiers.keys()] self._currentBindings = sorted(list(self._modifiers))
if self._consonantKey: if self._consonantKey:
self._currentBindings = self._currentBindings + [self._consonantKey] self._currentBindings += [self._consonantKey]
self.rebuildBindingDisplay() self.rebuildBindingDisplay()
print("Bindings are now %s" % (self._currentBindings)) self.hide()
return True
def clear(self, button): def clear(self, button):
self._currentBindings = [] self._currentBindings = []
self.rebuildBindingDisplay() self.rebuildBindingDisplay()
self.hide()
def closed(self, buttonMenu): def closed(self, buttonMenu):
self._profile.bindKey(self._buttonName, self._currentBindings) self.grab_remove()
self._prefs.selectedProfile().bindKey(self._keyName,
self._currentBindings)
self.hide() self.hide()

View 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

View 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])

View 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()