use a single separate thread for all possibly long-running stuff in GUI
This commit is contained in:
parent
cd437c3809
commit
edce56cd20
|
@ -127,7 +127,7 @@ def _print_device(dev, verbose=False):
|
|||
print (" Polling rate :", dev.polling_rate, "ms")
|
||||
print (" Serial number:", dev.serial)
|
||||
for fw in dev.firmware:
|
||||
print (" %-11s:" % fw.kind, (fw.name + ' ' + fw.version).strip())
|
||||
print (" %11s:" % fw.kind, (fw.name + ' ' + fw.version).strip())
|
||||
|
||||
if dev.power_switch_location:
|
||||
print (" The power switch is located on the %s." % dev.power_switch_location)
|
||||
|
|
|
@ -42,9 +42,45 @@ def error_dialog(reason, object):
|
|||
GLib.idle_add(_error_dialog, reason, object)
|
||||
|
||||
#
|
||||
#
|
||||
# A separate thread is used to read/write from the device
|
||||
# so as not to block the main (GUI) thread.
|
||||
#
|
||||
|
||||
try:
|
||||
from Queue import Queue
|
||||
except ImportError:
|
||||
from queue import Queue
|
||||
_task_queue = Queue(16)
|
||||
del Queue
|
||||
|
||||
|
||||
from threading import Thread, current_thread as _current_thread
|
||||
|
||||
def _process_async_queue():
|
||||
t = _current_thread()
|
||||
t.alive = True
|
||||
while t.alive:
|
||||
function, args, kwargs = _task_queue.get()
|
||||
if function:
|
||||
function(*args, **kwargs)
|
||||
if _log.isEnabledFor(_DEBUG):
|
||||
_log.debug("stopped %s", t.name)
|
||||
|
||||
_queue_processor = Thread(name='AsyncUI', target=_process_async_queue)
|
||||
_queue_processor.daemon = True
|
||||
_queue_processor.alive = False
|
||||
_queue_processor.start()
|
||||
|
||||
del Thread
|
||||
|
||||
def async(function, *args, **kwargs):
|
||||
task = (function, args, kwargs)
|
||||
_task_queue.put(task)
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from . import notify, tray, window
|
||||
|
||||
|
@ -57,6 +93,10 @@ def run_loop():
|
|||
Gtk.main()
|
||||
|
||||
def destroy():
|
||||
# stop the async UI processor
|
||||
_queue_processor.alive = False
|
||||
async(None)
|
||||
|
||||
tray.destroy()
|
||||
window.destroy()
|
||||
notify.uninit()
|
||||
|
|
|
@ -6,45 +6,34 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
|||
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
|
||||
from solaar.ui import async as _ui_async
|
||||
from logitech.unifying_receiver.settings import KIND as _SETTING_KIND
|
||||
|
||||
#
|
||||
# a separate thread is used to read/write from the device
|
||||
# so as not to block the main (GUI) thread
|
||||
#
|
||||
#
|
||||
|
||||
try:
|
||||
from Queue import Queue as _Queue
|
||||
except ImportError:
|
||||
from queue import Queue as _Queue
|
||||
_apply_queue = _Queue(8)
|
||||
def _read_async(setting, force_read, sbox, device_is_online):
|
||||
def _do_read(s, force, sb, online):
|
||||
v = s.read(not force)
|
||||
GLib.idle_add(_update_setting_item, sb, v, online, priority=99)
|
||||
|
||||
def _process_apply_queue():
|
||||
def _write_start(sbox):
|
||||
_, failed, spinner, control = sbox.get_children()
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
_ui_async(_do_read, setting, force_read, sbox, device_is_online)
|
||||
|
||||
while True:
|
||||
task = _apply_queue.get()
|
||||
assert isinstance(task, tuple)
|
||||
device_is_online = True
|
||||
# print ("task", *task)
|
||||
if task[0] == 'write':
|
||||
_, setting, value, sbox = task
|
||||
GLib.idle_add(_write_start, sbox, priority=0)
|
||||
value = setting.write(value)
|
||||
elif task[0] == 'read':
|
||||
_, setting, force_read, sbox, device_is_online = task
|
||||
value = setting.read(not force_read)
|
||||
GLib.idle_add(_update_setting_item, sbox, value, device_is_online, priority=99)
|
||||
|
||||
from threading import Thread as _Thread
|
||||
_queue_processor = _Thread(name='SettingsProcessor', target=_process_apply_queue)
|
||||
_queue_processor.daemon = True
|
||||
_queue_processor.start()
|
||||
def _write_async(setting, value, sbox):
|
||||
_, failed, spinner, control = sbox.get_children()
|
||||
control.set_sensitive(False)
|
||||
failed.set_visible(False)
|
||||
spinner.set_visible(True)
|
||||
spinner.start()
|
||||
|
||||
def _do_write(s, v, sb):
|
||||
v = setting.write(v)
|
||||
GLib.idle_add(_update_setting_item, sb, v, True, priority=99)
|
||||
|
||||
_ui_async(_do_write, setting, value, sbox)
|
||||
|
||||
#
|
||||
#
|
||||
|
@ -53,7 +42,7 @@ _queue_processor.start()
|
|||
def _create_toggle_control(setting):
|
||||
def _switch_notify(switch, _, s):
|
||||
if switch.get_sensitive():
|
||||
_apply_queue.put(('write', s, switch.get_active() == True, switch.get_parent()))
|
||||
_write_async(s, switch.get_active() == True, switch.get_parent())
|
||||
|
||||
c = Gtk.Switch()
|
||||
c.connect('notify::active', _switch_notify, setting)
|
||||
|
@ -62,7 +51,7 @@ def _create_toggle_control(setting):
|
|||
def _create_choice_control(setting):
|
||||
def _combo_notify(cbbox, s):
|
||||
if cbbox.get_sensitive():
|
||||
_apply_queue.put(('write', s, cbbox.get_active_id(), cbbox.get_parent()))
|
||||
_write_async(s, cbbox.get_active_id(), cbbox.get_parent())
|
||||
|
||||
c = Gtk.ComboBoxText()
|
||||
for entry in setting.choices:
|
||||
|
@ -181,7 +170,7 @@ def update(device, is_online=None):
|
|||
sbox = _items[k] = _create_sbox(s)
|
||||
_box.pack_start(sbox, False, False, 0)
|
||||
|
||||
_apply_queue.put(('read', s, False, sbox, is_online))
|
||||
_read_async(s, False, sbox, is_online)
|
||||
|
||||
_box.set_visible(True)
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ from logging import getLogger, DEBUG as _DEBUG
|
|||
_log = getLogger(__name__)
|
||||
del getLogger
|
||||
|
||||
from gi.repository import Gtk, Gdk
|
||||
from gi.repository import Gtk, Gdk, GLib
|
||||
from gi.repository.GObject import TYPE_PYOBJECT
|
||||
|
||||
from solaar import NAME
|
||||
# from solaar import __version__ as VERSION
|
||||
from solaar.ui import async as _ui_async
|
||||
from logitech.unifying_receiver import hidpp10 as _hidpp10
|
||||
from logitech.unifying_receiver.common import NamedInts as _NamedInts, NamedInt as _NamedInt
|
||||
from logitech.unifying_receiver.status import KEYS as _K
|
||||
|
@ -20,7 +21,6 @@ from . import config_panel as _config_panel
|
|||
from . import action as _action, icons as _icons
|
||||
from .about import show_window as _show_about_window
|
||||
|
||||
|
||||
#
|
||||
# constants
|
||||
#
|
||||
|
@ -480,7 +480,10 @@ def _update_details(button):
|
|||
if visible:
|
||||
# _details._text.set_markup('<small>reading...</small>')
|
||||
|
||||
def _details_items(device):
|
||||
def _details_items(device, read_all=False):
|
||||
# If read_all is False, only return stuff that is ~100% already
|
||||
# cached, and involves no HID++ calls.
|
||||
|
||||
if device.kind is None:
|
||||
yield ('Path', device.path)
|
||||
# 046d is the Logitech vendor id
|
||||
|
@ -491,13 +494,15 @@ def _update_details(button):
|
|||
yield ('Wireless PID', device.wpid)
|
||||
hid_version = device.protocol
|
||||
yield ('Protocol', 'HID++ %1.1f' % hid_version if hid_version else 'unknown')
|
||||
if device.polling_rate:
|
||||
if read_all and device.polling_rate:
|
||||
yield ('Polling rate', '%d ms (%dHz)' % (device.polling_rate, 1000 // device.polling_rate))
|
||||
|
||||
yield ('Serial', device.serial)
|
||||
if read_all or (device.kind is not None and device.online):
|
||||
yield ('Serial', device.serial)
|
||||
|
||||
for fw in list(device.firmware):
|
||||
yield (fw.kind, (fw.name + ' ' + fw.version).strip())
|
||||
if read_all:
|
||||
for fw in list(device.firmware):
|
||||
yield (' ' + str(fw.kind), (fw.name + ' ' + fw.version).strip())
|
||||
|
||||
flag_bits = device.status.get(_K.NOTIFICATION_FLAGS)
|
||||
if flag_bits is None and device.kind is not None:
|
||||
|
@ -506,11 +511,31 @@ def _update_details(button):
|
|||
flag_names = ('(none)',) if flag_bits == 0 else _hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits)
|
||||
yield ('Notifications', ('\n%15s' % ' ').join(flag_names))
|
||||
|
||||
def _set_details(text):
|
||||
_details._text.set_markup(text)
|
||||
|
||||
def _make_text(items):
|
||||
text = '\n'.join('%-13s: %s' % i for i in items)
|
||||
return '<small><tt>' + text + '</tt></small>'
|
||||
|
||||
def _read_slow(device):
|
||||
items = _details_items(selected_device, True)
|
||||
text = _make_text(items)
|
||||
if device == _details._current_device:
|
||||
GLib.idle_add(_set_details, text)
|
||||
|
||||
selected_device = _find_selected_device()
|
||||
assert selected_device
|
||||
items = _details_items(selected_device)
|
||||
text = '\n'.join('%-13s: %s' % i for i in items if i)
|
||||
_details._text.set_markup('<small><tt>' + text + '</tt></small>')
|
||||
_details._current_device = selected_device
|
||||
|
||||
read_all = not (selected_device.kind is None or selected_device.online)
|
||||
items = _details_items(selected_device, read_all)
|
||||
_set_details(_make_text(items))
|
||||
|
||||
if read_all:
|
||||
_details._current_device = None
|
||||
else:
|
||||
_ui_async(_read_slow, selected_device)
|
||||
|
||||
_details.set_visible(visible)
|
||||
|
||||
|
|
Loading…
Reference in New Issue