Target now an object, locking for sensor readouts, re-organized controller menu, ...
Added shutdown and reboot actions, added keyboard shortcut for stop_programs, ... Implemented buffered std_stream output for multiple controllers, sensor readout toggle now toggle view instead of window, ... Sensor phantoms now deactivated themselves when they aren't in focus
This commit is contained in:
parent
f9ccbd9ae2
commit
278caced99
1 changed files with 217 additions and 56 deletions
|
@ -22,6 +22,7 @@ from SublimeMenu import *
|
||||||
import Logging
|
import Logging
|
||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import threading
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -44,12 +45,19 @@ ERROR_OPEN = "<body><style>code { color: var(--redish); }</style><code>"
|
||||||
ERROR_CLOSE = "</code></body>"
|
ERROR_CLOSE = "</code></body>"
|
||||||
|
|
||||||
windows = []
|
windows = []
|
||||||
|
views = []
|
||||||
sensor_phantoms = []
|
sensor_phantoms = []
|
||||||
|
|
||||||
def set_status(status, window):
|
def set_status(status, window):
|
||||||
window.active_view().set_status(FL0W_STATUS,
|
window.active_view().set_status(FL0W_STATUS,
|
||||||
"fl0w: %s" % status)
|
"fl0w: %s" % status)
|
||||||
|
|
||||||
|
class Target:
|
||||||
|
def __init__(self, id_, name):
|
||||||
|
self.id = id_
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
class Fl0wClient(Client):
|
class Fl0wClient(Client):
|
||||||
def setup(self, routes, fl0w, debug=False):
|
def setup(self, routes, fl0w, debug=False):
|
||||||
super().setup(routes, debug=debug)
|
super().setup(routes, debug=debug)
|
||||||
|
@ -67,13 +75,15 @@ class Fl0wClient(Client):
|
||||||
|
|
||||||
|
|
||||||
def closed(self, code, reason):
|
def closed(self, code, reason):
|
||||||
self.fl0w.connected = False
|
self.fl0w.invoke_disconnect()
|
||||||
if self.fl0w.debug:
|
if self.fl0w.debug:
|
||||||
Logging.info("Connection closed: %s (%s)" % (reason, code))
|
Logging.info("Connection closed: %s (%s)" % (reason, code))
|
||||||
|
|
||||||
|
|
||||||
def peer_unavaliable(self, peer):
|
def peer_unavaliable(self, peer):
|
||||||
sublime.error_message("The specifed controller is not connected anymore.")
|
sublime.error_message("The specifed controller is not connected anymore.")
|
||||||
|
if self.fl0w.target.id == peer:
|
||||||
|
self.fl0w.target = None
|
||||||
|
|
||||||
|
|
||||||
class Info(Route):
|
class Info(Route):
|
||||||
|
@ -87,8 +97,10 @@ class Fl0wClient(Client):
|
||||||
|
|
||||||
class Sensor(Pipe):
|
class Sensor(Pipe):
|
||||||
def run(self, data, peer, handler):
|
def run(self, data, peer, handler):
|
||||||
|
handler.fl0w.subscriptions_lock.acquire()
|
||||||
for sensor_phantom in handler.fl0w.subscriptions:
|
for sensor_phantom in handler.fl0w.subscriptions:
|
||||||
sensor_phantom.update_sensor_values(data)
|
sensor_phantom.update_sensor_values(data)
|
||||||
|
handler.fl0w.subscriptions_lock.release()
|
||||||
|
|
||||||
|
|
||||||
class Peers(Route):
|
class Peers(Route):
|
||||||
|
@ -97,38 +109,61 @@ class Fl0wClient(Client):
|
||||||
|
|
||||||
def run(self, data, handler):
|
def run(self, data, handler):
|
||||||
handler.fl0w.controller_menu.clear()
|
handler.fl0w.controller_menu.clear()
|
||||||
|
if handler.fl0w.target != None:
|
||||||
|
if not handler.fl0w.target.id in data:
|
||||||
|
handler.fl0w.target = None
|
||||||
for id_ in data:
|
for id_ in data:
|
||||||
action_menu = Menu()
|
action_menu = Menu()
|
||||||
|
power_menu = Menu()
|
||||||
|
utilities_menu = Menu()
|
||||||
action_menu.id_ = id_
|
action_menu.id_ = id_
|
||||||
|
action_menu.name = data[id_]["name"]
|
||||||
action_menu += Entry("Set Target",
|
action_menu += Entry("Set Target",
|
||||||
"Set controller as target for program execution and sensor readouts.",
|
"Set controller as target for program execution and sensor readouts",
|
||||||
action=partial(lambda handler, id_: self.set_target(handler, id_),
|
action=partial(self.set_target,
|
||||||
handler, id_))
|
handler, id_, data[id_]["name"]))
|
||||||
action_menu += Entry("Programs",
|
action_menu += Entry("Run program",
|
||||||
"Lists all executable programs on the controller.",
|
"Run a botball program on the controller",
|
||||||
action=partial(lambda handler, id_: handler.pipe(None, "list_programs", id_),
|
action=partial(lambda handler, id_: handler.pipe(None, "list_programs", id_),
|
||||||
|
handler, id_))
|
||||||
|
action_menu += Entry("Stop programs",
|
||||||
|
"Stop all currently running botball programs",
|
||||||
|
action=partial(lambda handler, id_: handler.pipe(None, "stop_programs", id_),
|
||||||
handler, id_))
|
handler, id_))
|
||||||
action_menu += Entry("Set Name",
|
utilities_menu += Entry("Set Name",
|
||||||
"Sets the hostname of the selected controller",
|
"Sets the hostname of the selected controller",
|
||||||
action=partial(lambda handler, id_: Input("New Hostname:", initial_text=data[id_]["name"],
|
action=partial(lambda handler, id_: Input("New Hostname:",
|
||||||
|
initial_text=data[id_]["name"],
|
||||||
on_done=lambda hostname: handler.pipe(
|
on_done=lambda hostname: handler.pipe(
|
||||||
{"set" : hostname}, "hostname", id_)).invoke(handler.fl0w.window), handler, id_))
|
{"set" : hostname},
|
||||||
action_menu += Entry("Processes",
|
"hostname", id_)).invoke(handler.fl0w.window), handler, id_))
|
||||||
"Lists processes currently running on controller.",
|
utilities_menu += Entry("Processes",
|
||||||
|
"Lists processes currently running on controller",
|
||||||
action=partial(lambda handler, id_: handler.pipe(None, "processes", id_),
|
action=partial(lambda handler, id_: handler.pipe(None, "processes", id_),
|
||||||
handler, id_))
|
handler, id_))
|
||||||
action_menu += Entry("Identify",
|
utilities_menu += Entry("Identify",
|
||||||
"Plays an identification sound on the controller.",
|
"Plays an identification sound on the controller.",
|
||||||
action=partial(lambda handler, id_: handler.pipe(None, "identify", id_),
|
action=partial(lambda handler, id_: handler.pipe(None, "identify", id_),
|
||||||
handler, id_))
|
handler, id_))
|
||||||
|
action_menu += Entry("Utilities", "Stuff you might need but probably won't",
|
||||||
|
sub_menu=utilities_menu)
|
||||||
|
power_menu += Entry("Shutdown",
|
||||||
|
"Shutdown the controller",
|
||||||
|
action=partial(lambda handler, id_: handler.pipe(None, "shutdown", id_),
|
||||||
|
handler, id_))
|
||||||
|
power_menu += Entry("Reboot",
|
||||||
|
"Reboot the controller",
|
||||||
|
action=partial(lambda handler, id_: handler.pipe(None, "reboot", id_),
|
||||||
|
handler, id_))
|
||||||
|
action_menu += Entry("Power", "Power related actions", sub_menu=power_menu)
|
||||||
action_menu.back = handler.fl0w.controller_menu
|
action_menu.back = handler.fl0w.controller_menu
|
||||||
handler.fl0w.controller_menu += Entry(data[id_]["name"], id_, sub_menu=action_menu,
|
handler.fl0w.controller_menu += Entry(data[id_]["name"], id_, sub_menu=action_menu,
|
||||||
action=self.set_selected_action_menu,
|
action=self.set_selected_action_menu,
|
||||||
kwargs={"selected_action_menu" : action_menu})
|
kwargs={"selected_action_menu" : action_menu})
|
||||||
|
|
||||||
|
|
||||||
def set_target(self, handler, peer):
|
def set_target(self, handler, peer, name):
|
||||||
handler.fl0w.target = peer
|
handler.fl0w.target = Target(peer, name)
|
||||||
if handler.fl0w.debug:
|
if handler.fl0w.debug:
|
||||||
set_status("Target: %s" % peer, handler.fl0w.window)
|
set_status("Target: %s" % peer, handler.fl0w.window)
|
||||||
|
|
||||||
|
@ -152,18 +187,99 @@ class Fl0wClient(Client):
|
||||||
program_menu = Menu(subtitles=False)
|
program_menu = Menu(subtitles=False)
|
||||||
for program in data:
|
for program in data:
|
||||||
program_menu += Entry(program,
|
program_menu += Entry(program,
|
||||||
action=lambda handler: handler.pipe(program,
|
action=partial(self.run_program,
|
||||||
"run_program",
|
handler, handler.routes["peers"].selected_action_menu.id_,
|
||||||
handler.routes["peers"].selected_action_menu.id_),
|
program))
|
||||||
kwargs={"handler" : handler})
|
|
||||||
program_menu.invoke(handler.fl0w.window,
|
program_menu.invoke(handler.fl0w.window,
|
||||||
back=handler.routes["peers"].selected_action_menu)
|
back=handler.routes["peers"].selected_action_menu)
|
||||||
|
|
||||||
class StdStream(Pipe):
|
def run_program(self, handler, id_, program):
|
||||||
def run(data, peer, handler):
|
handler.pipe(program, "run_program", id_)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
class StdStream(Pipe):
|
||||||
|
def start(self, handler):
|
||||||
|
self.output_panels = {}
|
||||||
|
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.buffer = {}
|
||||||
|
|
||||||
|
self.handler = None
|
||||||
|
|
||||||
|
self.fetcher = self.Fetcher(self.buffer, self.write_to_panel,
|
||||||
|
self.lock)
|
||||||
|
self.fetcher.start()
|
||||||
|
|
||||||
|
|
||||||
|
def create_output_panel(self, window, peer):
|
||||||
|
view = window.create_output_panel(peer)
|
||||||
|
view.settings().set("draw_white_space", False)
|
||||||
|
view.settings().set("draw_indent_guides", False)
|
||||||
|
view.settings().set("gutter", False)
|
||||||
|
view.settings().set("line_numbers", False)
|
||||||
|
view.set_read_only(True)
|
||||||
|
return view
|
||||||
|
|
||||||
|
|
||||||
|
def run(self, data, peer, handler):
|
||||||
|
self.handler = handler
|
||||||
|
if type(data) is str:
|
||||||
|
self.lock.acquire()
|
||||||
|
# try/except is faster than an explicit if as long as the
|
||||||
|
# condition is not met
|
||||||
|
try:
|
||||||
|
self.buffer[peer].append(data)
|
||||||
|
except KeyError:
|
||||||
|
self.buffer[peer] = []
|
||||||
|
self.create_output_panel(handler.fl0w.window, peer)
|
||||||
|
self.output_panels[peer] = self.create_output_panel(handler.fl0w.window, peer)
|
||||||
|
self.buffer[peer].append(data)
|
||||||
|
self.lock.release()
|
||||||
|
"""
|
||||||
|
elif type(data) is dict:
|
||||||
|
# Bad solution, should instead be treated seperately
|
||||||
|
# while still being timed correctly.
|
||||||
|
meta_text = ""
|
||||||
|
if "exit_code" in data:
|
||||||
|
meta_text += "Program finished with exit code: %d\n" % data["exit_code"]
|
||||||
|
self.lock.acquire()
|
||||||
|
self.buffer.append(meta_text)
|
||||||
|
self.lock.release()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def write_to_panel(self, text, peer):
|
||||||
|
self.output_panels[peer].set_read_only(False)
|
||||||
|
self.output_panels[peer].run_command("append", {"characters": text, "scroll_to_end" : True})
|
||||||
|
self.output_panels[peer].set_read_only(True)
|
||||||
|
self.handler.fl0w.window.run_command("show_panel", {"panel": "output.%s" % peer})
|
||||||
|
|
||||||
|
|
||||||
|
# Sublime gets quite overwhelmed when faced with typical
|
||||||
|
# "while (1) { printf(...)}" output.
|
||||||
|
# That's why instead of directly writing to the view all received text
|
||||||
|
# is bundled together after a fixed period of time.
|
||||||
|
class Fetcher(threading.Thread):
|
||||||
|
def __init__(self, buffer, write_to_panel, lock, push_rate=0.2):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.buffer = buffer
|
||||||
|
self.write_to_panel = write_to_panel
|
||||||
|
self.lock = lock
|
||||||
|
self.push_rate = push_rate
|
||||||
|
self.daemon = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
self.lock.acquire()
|
||||||
|
for peer in self.buffer:
|
||||||
|
if len(self.buffer[peer]) > 0:
|
||||||
|
self.write_to_panel(
|
||||||
|
"".join(self.buffer[peer]),
|
||||||
|
peer)
|
||||||
|
self.buffer[peer] = []
|
||||||
|
self.lock.release()
|
||||||
|
sleep(self.push_rate)
|
||||||
|
|
||||||
|
|
||||||
class Fl0w:
|
class Fl0w:
|
||||||
def __init__(self, window, debug=False):
|
def __init__(self, window, debug=False):
|
||||||
|
@ -175,6 +291,7 @@ class Fl0w:
|
||||||
self.connected = False
|
self.connected = False
|
||||||
|
|
||||||
self.subscriptions = {}
|
self.subscriptions = {}
|
||||||
|
self.subscriptions_lock = threading.Lock()
|
||||||
self._combined_subscriptions = {"analog" : [], "digital" : []}
|
self._combined_subscriptions = {"analog" : [], "digital" : []}
|
||||||
|
|
||||||
self._target = None
|
self._target = None
|
||||||
|
@ -234,11 +351,14 @@ class Fl0w:
|
||||||
@target.setter
|
@target.setter
|
||||||
def target(self, target):
|
def target(self, target):
|
||||||
if self.target != None:
|
if self.target != None:
|
||||||
self.ws.pipe("unsubscribe", "sensor", self.target)
|
self.ws.pipe("unsubscribe", "sensor", self.target.id)
|
||||||
self._target = target
|
self._target = target
|
||||||
set_status("Set target: %s" % target, self.window)
|
if target != None:
|
||||||
if self.combined_subscriptions != {"analog" : [], "digital" : []}:
|
set_status("Target: %s (%s)" % (target.name, target.id), self.window)
|
||||||
self.ws.pipe({"subscribe" : self.combined_subscriptions_}, "sensor", target)
|
if self.combined_subscriptions != {"analog" : [], "digital" : []}:
|
||||||
|
self.ws.pipe({"subscribe" : self.combined_subscriptions}, "sensor", target.id)
|
||||||
|
else:
|
||||||
|
set_status("The target has become unavaliable.", self.window)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -262,6 +382,9 @@ class Fl0w:
|
||||||
set_status("Debug set to %s" % self._debug, self.window)
|
set_status("Debug set to %s" % self._debug, self.window)
|
||||||
|
|
||||||
|
|
||||||
|
# Could be simplified because only one view can be active at any time.
|
||||||
|
# This would definetly lead to some major performace improvements on
|
||||||
|
# view switching and less unnecessary unsubscribes.
|
||||||
@property
|
@property
|
||||||
def combined_subscriptions(self):
|
def combined_subscriptions(self):
|
||||||
return self._combined_subscriptions
|
return self._combined_subscriptions
|
||||||
|
@ -272,20 +395,25 @@ class Fl0w:
|
||||||
if self.combined_subscriptions != combined_subscriptions_:
|
if self.combined_subscriptions != combined_subscriptions_:
|
||||||
self._combined_subscriptions = combined_subscriptions_
|
self._combined_subscriptions = combined_subscriptions_
|
||||||
if self.connected and self.target != None:
|
if self.connected and self.target != None:
|
||||||
self.ws.pipe("unsubscribe", "sensor", self.target)
|
self.ws.pipe("unsubscribe", "sensor", self.target.id)
|
||||||
self.ws.pipe({"subscribe" : combined_subscriptions_}, "sensor",
|
if combined_subscriptions_ != {"analog" : [], "digital" : []}:
|
||||||
self.target)
|
self.ws.pipe({"subscribe" : combined_subscriptions_}, "sensor",
|
||||||
|
self.target.id)
|
||||||
|
|
||||||
|
|
||||||
def subscribe(self, sensor_phatom, subscriptions):
|
def subscribe(self, sensor_phatom, subscriptions):
|
||||||
|
self.subscriptions_lock.acquire()
|
||||||
self.subscriptions[sensor_phatom] = subscriptions
|
self.subscriptions[sensor_phatom] = subscriptions
|
||||||
|
self.subscriptions_lock.release()
|
||||||
self.make_subscriptions()
|
self.make_subscriptions()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def unsubscribe(self, sensor_phantom):
|
def unsubscribe(self, sensor_phantom):
|
||||||
if sensor_phantom in self.subscriptions:
|
if sensor_phantom in self.subscriptions:
|
||||||
|
self.subscriptions_lock.acquire()
|
||||||
del self.subscriptions[sensor_phantom]
|
del self.subscriptions[sensor_phantom]
|
||||||
|
self.subscriptions_lock.release()
|
||||||
self.make_subscriptions()
|
self.make_subscriptions()
|
||||||
|
|
||||||
|
|
||||||
|
@ -306,7 +434,12 @@ class Fl0w:
|
||||||
if os.path.isfile(self.folder + relpath):
|
if os.path.isfile(self.folder + relpath):
|
||||||
if self.debug:
|
if self.debug:
|
||||||
Logging.info("Running program '%s'" % relpath)
|
Logging.info("Running program '%s'" % relpath)
|
||||||
self.ws.pipe(relpath, "run_program", self.target)
|
self.ws.pipe(relpath.rstrip(".c"), "run_program", self.target.id)
|
||||||
|
|
||||||
|
|
||||||
|
def stop_programs(self):
|
||||||
|
if self.connected and self.target != None:
|
||||||
|
self.ws.pipe(None, "stop_programs", self.target.id)
|
||||||
|
|
||||||
|
|
||||||
def invoke_start_menu(self):
|
def invoke_start_menu(self):
|
||||||
|
@ -328,7 +461,8 @@ class Fl0w:
|
||||||
self.ws = Fl0wClient('ws://%s' % connect_details)
|
self.ws = Fl0wClient('ws://%s' % connect_details)
|
||||||
self.ws.setup({"info" : Fl0wClient.Info(), "peers" : Fl0wClient.Peers(),
|
self.ws.setup({"info" : Fl0wClient.Info(), "peers" : Fl0wClient.Peers(),
|
||||||
"processes" : Fl0wClient.Processes(),
|
"processes" : Fl0wClient.Processes(),
|
||||||
"list_programs" : Fl0wClient.ListPrograms(), "sensor" : Fl0wClient.Sensor()},
|
"list_programs" : Fl0wClient.ListPrograms(), "sensor" : Fl0wClient.Sensor(),
|
||||||
|
"std_stream" : Fl0wClient.StdStream()},
|
||||||
self, debug=True)
|
self, debug=True)
|
||||||
self.ws.connect()
|
self.ws.connect()
|
||||||
sublime.set_timeout_async(self.ws.run_forever, 0)
|
sublime.set_timeout_async(self.ws.run_forever, 0)
|
||||||
|
@ -349,6 +483,7 @@ class Fl0w:
|
||||||
for sensor_phantom in sensor_phantoms:
|
for sensor_phantom in sensor_phantoms:
|
||||||
if sensor_phantom.window.id() == self.window.id():
|
if sensor_phantom.window.id() == self.window.id():
|
||||||
sensor_phantom.enabled = False
|
sensor_phantom.enabled = False
|
||||||
|
self.target = None
|
||||||
self.ws.close()
|
self.ws.close()
|
||||||
set_status("Connection closed '%s'" % self.folder, self.window)
|
set_status("Connection closed '%s'" % self.folder, self.window)
|
||||||
self.connected = False
|
self.connected = False
|
||||||
|
@ -407,47 +542,61 @@ class RunCommand(sublime_plugin.WindowCommand):
|
||||||
else:
|
else:
|
||||||
sublime.error_message("fl0w is not running in your current window.")
|
sublime.error_message("fl0w is not running in your current window.")
|
||||||
|
|
||||||
|
class StopCommand(sublime_plugin.WindowCommand):
|
||||||
|
def run(self):
|
||||||
|
if hasattr(self.window, "fl0w"):
|
||||||
|
if self.window.fl0w.connected:
|
||||||
|
if self.window.fl0w.target == None:
|
||||||
|
sublime.error_message("A target controller has to be set to "
|
||||||
|
"stop programs.")
|
||||||
|
else:
|
||||||
|
self.window.fl0w.stop_programs()
|
||||||
|
else:
|
||||||
|
sublime.error_message("fl0w is not connected.")
|
||||||
|
else:
|
||||||
|
sublime.error_message("fl0w is not running in your current window.")
|
||||||
|
|
||||||
|
|
||||||
class SensorCommand(sublime_plugin.WindowCommand):
|
class SensorCommand(sublime_plugin.WindowCommand):
|
||||||
def __init__(self, window):
|
|
||||||
super().__init__(window)
|
|
||||||
self.enabled = False
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not self.enabled:
|
if hasattr(self.window, "fl0w"):
|
||||||
if hasattr(self.window, "fl0w"):
|
if self.window.fl0w.connected:
|
||||||
if self.window.fl0w.connected:
|
if self.window.fl0w.target == None:
|
||||||
if self.window.fl0w.target == None:
|
sublime.error_message("A target controller has to be set to "
|
||||||
sublime.error_message("A target controller has to be set to "
|
"enable inline sensor readouts.")
|
||||||
"enable inline sensor readouts.")
|
|
||||||
else:
|
|
||||||
self.enabled = not self.enabled
|
|
||||||
for sensor_phantom in sensor_phantoms:
|
|
||||||
if sensor_phantom.window.id() == self.window.id():
|
|
||||||
sensor_phantom.enabled = self.enabled
|
|
||||||
set_status("Enabled sensor phantoms.", self.window)
|
|
||||||
else:
|
else:
|
||||||
sublime.error_message("fl0w is not connected.")
|
view_id = self.window.active_view().id()
|
||||||
|
for view in views:
|
||||||
|
if view.id() == view_id:
|
||||||
|
view.sensor_phantom.enabled = not view.sensor_phantom.enabled
|
||||||
|
view_file_name = view.file_name()
|
||||||
|
if view_file_name == None:
|
||||||
|
view_file_name = "untitled"
|
||||||
|
set_status("%s sensor phantoms for '%s'." % (
|
||||||
|
"Enabled" if view.sensor_phantom.enabled else "Disabled",
|
||||||
|
view_file_name),
|
||||||
|
self.window)
|
||||||
else:
|
else:
|
||||||
sublime.error_message("fl0w is not running in your current window.")
|
sublime.error_message("fl0w is not connected.")
|
||||||
else:
|
else:
|
||||||
self.enabled = not self.enabled
|
sublime.error_message("fl0w is not running in your current window.")
|
||||||
for sensor_phantom in sensor_phantoms:
|
|
||||||
if sensor_phantom.window.id() == self.window.id():
|
|
||||||
sensor_phantom.enabled = self.enabled
|
|
||||||
set_status("Disabled sensor phantoms.", self.window)
|
|
||||||
|
|
||||||
|
|
||||||
class SensorPhantom(sublime_plugin.ViewEventListener):
|
class SensorPhantom(sublime_plugin.ViewEventListener):
|
||||||
def __init__(self, view):
|
def __init__(self, view):
|
||||||
self.view = view
|
self.view = view
|
||||||
|
self.view.sensor_phantom = self
|
||||||
|
if not view in views:
|
||||||
|
views.append(view)
|
||||||
self.window = view.window()
|
self.window = view.window()
|
||||||
|
|
||||||
# Is patched by the fl0w instance that is in control of the same window
|
# Is patched by the fl0w instance that is in control of the same window
|
||||||
self.fl0w = None
|
self.fl0w = None
|
||||||
self._enabled = False
|
self._enabled = False
|
||||||
|
|
||||||
|
self.previously_enabled = False
|
||||||
|
|
||||||
self._matches = {"analog" : [], "digital" : []}
|
self._matches = {"analog" : [], "digital" : []}
|
||||||
|
|
||||||
self.timeout_scheduled = False
|
self.timeout_scheduled = False
|
||||||
|
@ -467,16 +616,17 @@ class SensorPhantom(sublime_plugin.ViewEventListener):
|
||||||
|
|
||||||
@enabled.setter
|
@enabled.setter
|
||||||
def enabled(self, enabled_):
|
def enabled(self, enabled_):
|
||||||
self._enabled = enabled_
|
|
||||||
if enabled_:
|
if enabled_:
|
||||||
if self.fl0w != None:
|
if self.fl0w != None:
|
||||||
self.find_matches()
|
self.find_matches()
|
||||||
self.fl0w.subscribe(self, self.subscriptions)
|
self.fl0w.subscribe(self, self.subscriptions)
|
||||||
|
self._enabled = True
|
||||||
else:
|
else:
|
||||||
if self.fl0w != None:
|
if self.fl0w != None:
|
||||||
self.fl0w.unsubscribe(self)
|
self.fl0w.unsubscribe(self)
|
||||||
for sensor_type in ("analog", "digital"):
|
for sensor_type in ("analog", "digital"):
|
||||||
self.window.active_view().erase_phantoms(sensor_type)
|
self.view.erase_phantoms(sensor_type)
|
||||||
|
self._enabled = False
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -547,6 +697,17 @@ class SensorPhantom(sublime_plugin.ViewEventListener):
|
||||||
self.find_matches()
|
self.find_matches()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_deactivated(self):
|
||||||
|
self.previously_enabled = self.enabled
|
||||||
|
if self.enabled:
|
||||||
|
self.enabled = False
|
||||||
|
|
||||||
|
def on_activated(self):
|
||||||
|
if not self.enabled and self.previously_enabled:
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
if self in sensor_phantoms:
|
if self in sensor_phantoms:
|
||||||
|
|
Reference in a new issue