GUI Cleanup (#1939)

* Try to improve stability

- Remove sync_active
- Update the way variables are sent to the gui

* Do not show full path for pages

The cli now shows the basename of the current page to make it easier to
determine if the correct page is displayed.

* Make elements in loaded list named tuples

This makes the intent of the code a bit cleaner.
pull/1948/head
Åke 2019-01-10 03:09:21 +01:00 committed by Steve Penrod
parent 74ae24ec7e
commit fa5c9abd78
2 changed files with 44 additions and 67 deletions

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import sys
from collections import namedtuple
from mycroft.configuration import Configuration
from mycroft.messagebus.client.ws import WebsocketClient
@ -26,6 +26,9 @@ from tornado.websocket import WebSocketHandler
from mycroft.messagebus.message import Message
Namespace = namedtuple('Namespace', ['name', 'pages'])
def DEBUG(str):
print(str)
# pass # disable by default
@ -226,14 +229,19 @@ class GUIConnection():
self.current_pages = []
self.current_index = None
# self.loaded is a list, each row in the list has two columns:
# [Namespace, [List of loaded qml pages]]
# self.loaded is a list, each element consists of a namespace named
# tuple.
# The namespace namedtuple has the properties "name" and "pages"
# The name contains the namespace name as a string and pages is a
# mutable list of loaded pages.
#
# [Namespace name, [List of loaded qml pages]]
# [
# ["SKILL_NAME", ["page1.qml, "page2.qml", ... , "pageN.qml"]
# [...]
# ]
self.loaded = [] # list of lists in order.
self.explicit_move = True # Set to true to send reorder commands
# Each connection will run its own Tornado server. If the
# connection drops, the server is killed.
@ -283,17 +291,18 @@ class GUIConnection():
self.callback_disconnect(self.id)
def set(self, namespace, name, value):
self.sync_active()
if namespace not in self.datastore:
self.datastore[namespace] = {}
if self.datastore[namespace].get(name) != value:
msg = {"type": "mycroft.session.set",
"namespace": namespace,
"data": {name: value}}
self.socket.send(msg)
self.datastore[namespace][name] = value
# If the namespace is loaded send data to gui
if namespace in [l.name for l in self.loaded]:
msg = {"type": "mycroft.session.set",
"namespace": namespace,
"data": {name: value}}
self.socket.send(msg)
def __find_namespace(self, namespace):
for i, skill in enumerate(self.loaded):
if skill[0] == namespace:
@ -308,13 +317,13 @@ class GUIConnection():
self.socket.send({"type": "mycroft.gui.list.insert",
"namespace": namespace,
"position": len(self.loaded[0][1]),
"position": len(self.loaded[0].pages),
"data": [{"url": p} for p in pages]
})
# append pages to local representation
for p in pages:
self.loaded[0][1].append(p)
self.loaded[0].pages.append(p)
def __insert_new_namespace(self, namespace, pages):
""" Insert new namespace and pages.
@ -332,6 +341,15 @@ class GUIConnection():
"position": 0,
"data": [{"skill_id": namespace}]
})
# Load any already stored Data
data = self.datastore.get(namespace, {})
for key in data:
msg = {"type": "mycroft.session.set",
"namespace": namespace,
"data": {key: data[key]}}
self.socket.send(msg)
DEBUG("Inserting new page")
self.socket.send({"type": "mycroft.gui.list.insert",
"namespace": namespace,
@ -339,7 +357,7 @@ class GUIConnection():
"data": [{"url": p} for p in pages]
})
# Make sure the local copy is updated
self.loaded.insert(0, [namespace, pages])
self.loaded.insert(0, Namespace(namespace, pages))
def __move_namespace(self, from_pos, to_pos):
""" Move an existing namespace to a new position in the stack.
@ -349,9 +367,14 @@ class GUIConnection():
to_pos: Position to move to
"""
DEBUG("Activating existing namespace")
self.socket.send({"type": "mycroft.session.list.move",
"namespace": "mycroft.system.active_skills",
"from": from_pos, "to": to_pos})
# Seems like the namespace is moved to the top automatically when
# a page change is done. Deactivating this for now.
if self.explicit_move:
DEBUG("move {} to {}".format(from_pos, to_pos))
self.socket.send({"type": "mycroft.session.list.move",
"namespace": "mycroft.system.active_skills",
"from": from_pos, "to": to_pos,
"items_number": 1})
# Move the local representation of the skill from current
# position to position 0.
self.loaded.insert(to_pos, self.loaded.pop(from_pos))
@ -364,12 +387,13 @@ class GUIConnection():
namespace: skill namespace
"""
try:
num = self.loaded[0][1].index(pages[0])
num = self.loaded[0].pages.index(pages[0])
except Exception as e:
DEBUG(e)
num = 0
DEBUG("Switching to already loaded page at index {}".format(num))
DEBUG("Switching to already loaded page at "
"index {} in namespace {}".format(num, namespace))
self.socket.send({"type": "mycroft.events.triggered",
"namespace": namespace,
"event_name": "page_gained_focus",
@ -385,7 +409,6 @@ class GUIConnection():
DEBUG("GUIConnection activating: " + namespace)
pages = page if isinstance(page, list) else [page]
self.sync_active()
# find namespace among loaded namespaces
try:
index = self.__find_namespace(namespace)
@ -401,7 +424,7 @@ class GUIConnection():
self.__move_namespace(index, 0)
# Find if any new pages needs to be inserted
new_pages = [p for p in pages if p not in self.loaded[0][1]]
new_pages = [p for p in pages if p not in self.loaded[0].pages]
if new_pages:
self.__insert_pages(namespace, new_pages)
else:
@ -415,53 +438,6 @@ class GUIConnection():
self.current_pages = pages
self.current_index = index
def sync_active(self):
# The main Enclosure keeps a list of active skills. Each GUI also
# has a list. Synchronize when appropriate.
if self.enclosure.active_namespaces != self._active_namespaces:
# First, zap any namespace not in the list anymore
if self._active_namespaces:
pos = len(self._active_namespaces) - 1
for ns in reversed(self._active_namespaces):
if ns not in self.enclosure.active_namespaces:
msg = {"type": "mycroft.session.list.remove",
"namespace": "mycroft.system.active_skills",
"position": pos,
"items_number": 1
}
self.socket.send(msg)
del self._active_namespaces[pos]
pos -= 1
# Next, insert any missing items
if not self._active_namespaces:
self._active_namespaces = []
for ns in self.enclosure.active_namespaces:
if ns not in self._active_namespaces:
msg = {"type": "mycroft.session.list.insert",
"namespace": "mycroft.system.active_skills",
"position": 0,
"data": [{'skill_id': ns}]
}
self.socket.send(msg)
self._active_namespaces.insert(0, ns)
# Finally, adjust orders to match
for idx in range(0, len(self.enclosure.active_namespaces)):
ns = self.enclosure.active_namespaces[idx]
idx_old = self._active_namespaces.index(ns)
if idx != idx_old:
msg = {"type": "mycroft.session.list.move",
"namespace": "mycroft.system.active_skills",
"from": idx_old,
"to": idx,
"items_number": 1
}
self.socket.send(msg)
del self._active_namespaces[idx_old]
self._active_namespaces.insert(idx, ns)
class GUIWebsocketHandler(WebSocketHandler):
"""

View File

@ -14,6 +14,7 @@
#
from os import getpid
from os.path import basename
import json
import websocket
from threading import Thread, Lock
@ -61,7 +62,7 @@ def build_output_buffer():
try:
if skill:
buffer.append("Active Skill: {}".format(skill))
buffer.append("Page: {}".format(page))
buffer.append("Page: {}".format(basename(page)))
buffer.append("vars: ")
for v in vars[skill]:
buffer.append(" {}: {}".format(v, vars[skill][v]))