Skip to content

Commit

Permalink
Some refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
rogama25 committed Jul 4, 2021
1 parent 6c7e572 commit 568e71a
Show file tree
Hide file tree
Showing 20 changed files with 364 additions and 98 deletions.
117 changes: 91 additions & 26 deletions src/UBUVoiceAssistant/GUI/chat_window.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Module for the chat window GUI
"""
from threading import Thread
import subprocess
import sys
Expand All @@ -16,11 +18,13 @@


class ChatWindow(QtWidgets.QMainWindow):
def __init__(self, bus: MessageBusClient, ws: WebService) -> None:
"""Class for the Chat window GUI
"""
def __init__(self, bus: MessageBusClient, webservice: WebService) -> None:
super().__init__(parent=None)
uic.loadUi("./UBUVoiceAssistant/GUI/forms/chat-window.ui", self)
self.bus = bus
self.ws = ws
self.webservice = webservice
self.user_utterance = ""
self.mycroft_response = ""
self.mic_muted = False
Expand All @@ -29,8 +33,10 @@ def __init__(self, bus: MessageBusClient, ws: WebService) -> None:
self.next_message = 0
self.intent_labels = []

self.mic_icon = QtGui.QIcon(QtGui.QPixmap("UBUVoiceAssistant/imgs/mic.svg"))
self.mic_muted_icon = QtGui.QIcon(QtGui.QPixmap("UBUVoiceAssistant/imgs/mic_muted.svg"))
self.mic_icon = QtGui.QIcon(
QtGui.QPixmap("UBUVoiceAssistant/imgs/mic.svg"))
self.mic_muted_icon = QtGui.QIcon(
QtGui.QPixmap("UBUVoiceAssistant/imgs/mic_muted.svg"))

self.color: List[Union[int, float]] = list(
self.palette().color(QtGui.QPalette.ColorRole.Background).getRgb())
Expand Down Expand Up @@ -73,6 +79,8 @@ def __init__(self, bus: MessageBusClient, ws: WebService) -> None:
# os.path.abspath(os.getcwd())+"/UBUVoiceAssistant/GUI/forms/chat_window_html"))

def set_elements_web(self):
"""Sets the initial elements for the HTML webview
"""
print(self.color)
js_color = "document.body.style.backgroundColor = 'rgba(" + ','.join(
map(str, self.color)) + ")';"
Expand All @@ -84,7 +92,8 @@ def set_elements_web(self):
message += "· " + _("Tell me about the forums of (course)\n")
message += "· " + _("Tell me my grades\n")
message += "· " + _("Tell me about the events of (course)\n")
message += "· " + _("Tell me about the events on (month) (day) (year)\n")
message += "· " + \
_("Tell me about the events on (month) (day) (year)\n")
message += "· " + _("Tell me about the changes of (course)\n")
message += "· " + _("Tell me the grades of (course)\n")
message += "· " + _("Read the latest messages\n")
Expand All @@ -99,11 +108,19 @@ def set_elements_web(self):
self.web.page().runJavaScript(js_string)

def update_texts(self):
"""Sets the localized texts for the UI
"""
self.btnConfig.setText(_("Manage skills"))
self.btnSend.setText(_("Send"))
self.tbxInput.setPlaceholderText(_("Type your command here..."))

def update_chat(self, source: str, message: str):
"""Adds the new message to the chat
Args:
source (str): "u" when coming from user, "r" when coming from Mycroft
message (str): The message text
"""
js_string = "var chat = document.getElementById('chat-window');\n"
js_string += "var msg = document.createElement('li');\n"
if source == "u":
Expand All @@ -116,12 +133,24 @@ def update_chat(self, source: str, message: str):
self.web.page().runJavaScript(js_string)

def handle_speak(self, message: Message):
"""Sets the Mycroft response variable when a message comes
Args:
message (Message): Mycroft bus message
"""
self.mycroft_response = message.data.get("utterance")

def handle_utterance(self, message: Message):
"""Sets the user message variable when you send a message
Args:
message (Message): User bus message
"""
self.user_utterance = message.data["utterances"][0]

def check_for_chat_update(self):
"""Checks if a new message arrives and updates the UI
"""
if self.user_utterance:
self.update_chat("u", self.user_utterance)
self.user_utterance = ""
Expand All @@ -130,6 +159,11 @@ def check_for_chat_update(self):
self.mycroft_response = ""

def closeEvent(self, event: QtGui.QCloseEvent) -> None:
"""Handles when the user tries to close the window
Args:
event (QtGui.QCloseEvent): Qt Close Event
"""
self.close_window = MessageBox(_("Are you sure?"))
self.close_window.setStandardButtons(
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
Expand All @@ -148,19 +182,33 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None:
event.ignore()

def finish_exit(self):
"""Exits the program
"""
sys.exit(0)

def keyPressEvent(self, event):
"""This is executed when the user press a key
Args:
event: Qt Keypress event
"""
if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
self.on_send_pressed()

def on_send_pressed(self):
"""This runs when the user press enter or clicks the button on the UI
"""
self.user_utterance = self.tbxInput.text()
self.bus.emit(Message('recognizer_loop:utterance', {
'utterances': [self.user_utterance]}))
self.tbxInput.setText('')

def on_mic_pressed(self, startup: bool = False):
"""This runs when the user clicks the mic button
Args:
startup (bool, optional): True if this is running at startup. Defaults to False.
"""
# Switch between muted and unmuted when the mic is pressed
if self.mic_muted or startup:
self.mic_muted = False
Expand All @@ -176,14 +224,19 @@ def on_mic_pressed(self, startup: bool = False):
self.bus.emit(Message('mycroft.mic.mute'))

def on_skills_pressed(self):
"""This runs when you press the "manage skills" button. Shows the new window
"""

self.skills_dialog = QtWidgets.QDialog(self)
self.skills_dialog.setWindowTitle('Mycroft Skills')
self.skills_dialog.resize(600, 600)

self.pushButton_manage_skills = QtWidgets.QPushButton(self.skills_dialog)
self.pushButton_manage_skills.setGeometry(QtCore.QRect(470, 10, 120, 40))
self.pushButton_manage_skills.clicked.connect(self.on_manage_skills_pressed)
self.pushButton_manage_skills = QtWidgets.QPushButton(
self.skills_dialog)
self.pushButton_manage_skills.setGeometry(
QtCore.QRect(470, 10, 120, 40))
self.pushButton_manage_skills.clicked.connect(
self.on_manage_skills_pressed)

self.pushButton_manage_skills.setText(_("Save"))

Expand All @@ -202,14 +255,17 @@ def on_skills_pressed(self):
# Create checkboxes for every skill in self.active_skills
for count, name in enumerate(self.active_skills):
check_box = QtWidgets.QCheckBox(scroll_area_widget_skills)
spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
spacer = QtWidgets.QSpacerItem(
40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
check_box.setText(name)
check_box.setChecked(True)
logo = QtWidgets.QLabel(scroll_area_widget_skills)
if 'ubu' in name:
logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20))
logo.setPixmap(QtGui.QPixmap(
'UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20))
else:
logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20))
logo.setPixmap(QtGui.QPixmap(
'UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20))
self.active_skills_checkBoxes.append(check_box)
skills_grid_layout.addWidget(logo, count, 0)
skills_grid_layout.addWidget(check_box, count, 1)
Expand All @@ -221,9 +277,11 @@ def on_skills_pressed(self):
check_box.setText(name)
logo = QtWidgets.QLabel(scroll_area_widget_skills)
if 'ubu' in name:
logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20))
logo.setPixmap(QtGui.QPixmap(
'UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20))
else:
logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20))
logo.setPixmap(QtGui.QPixmap(
'UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20))
self.inactive_skills_checkBoxes.append(check_box)
skills_grid_layout.addWidget(logo, count, 0)
skills_grid_layout.addWidget(check_box, count, 1)
Expand All @@ -233,31 +291,38 @@ def on_skills_pressed(self):

def on_manage_skills_pressed(self):
""" Adds the checked skills to self.active_skills and the unchecked to
self.inactive_skills and activates or deactivates those skills.
self.inactive_skills and activates or deactivates those skills when pressing save.
"""
deactivated = []
activated = []
for cb in self.active_skills_checkBoxes:
if not cb.isChecked():
self.bus.emit(Message('skillmanager.deactivate', {'skill': cb.text()}))
deactivated.append(cb.text())

for cb in self.inactive_skills_checkBoxes:
if cb.isChecked():
self.bus.emit(Message('skillmanager.activate', {'skill': cb.text()}))
activated.append(cb.text())

self.active_skills = [skill for skill in self.active_skills if skill not in deactivated]
for checkbox in self.active_skills_checkBoxes:
if not checkbox.isChecked():
self.bus.emit(
Message('skillmanager.deactivate', {'skill': checkbox.text()}))
deactivated.append(checkbox.text())

for checkbox in self.inactive_skills_checkBoxes:
if checkbox.isChecked():
self.bus.emit(
Message('skillmanager.activate', {'skill': checkbox.text()}))
activated.append(checkbox.text())

self.active_skills = [
skill for skill in self.active_skills if skill not in deactivated]
self.active_skills.extend(activated)

self.inactive_skills = [skill for skill in self.inactive_skills if skill not in activated]
self.inactive_skills = [
skill for skill in self.inactive_skills if skill not in activated]
self.inactive_skills.extend(deactivated)

self.skills_dialog.hide()
self.on_skills_pressed()


class CloseMycroft(QtCore.QThread):
"""Thread to close Mycroft"""
def run(self):
"""Closes Mycroft and emits a signal when finished
"""
subprocess.run("/usr/lib/mycroft-core/stop-mycroft.sh", shell=True)
self.finished.emit()
46 changes: 39 additions & 7 deletions src/UBUVoiceAssistant/GUI/link_mycroft.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
class LinkMycroft(QtWidgets.QMainWindow):
"""Class for the linking Mycroft to web UI
"""

closed_signal = pyqtSignal()

def __init__(self, bus: MessageBusClient) -> None:
Expand All @@ -45,9 +45,10 @@ def __init__(self, bus: MessageBusClient) -> None:

self.file = open("/var/log/mycroft/skills.log", "r")
self.file.seek(0, 2) # Goes to the end of the file
self.bus.emit(Message("recognizer_loop:utterance",
{'utterances': [_("pair my device")]}))
# On other languages different than English, we must send again the phrase for it to start pairing
self.bus.emit(Message("recognizer_loop:utterance",
{'utterances': [_("pair my device")]}))
# On other languages different than English, we must send again the phrase for it
# to start pairing
bus.on("configuration.updated", self.pairing_done)
self.timer = QTimer()
self.timer.timeout.connect(self.add_pairing_code) # type: ignore
Expand All @@ -58,30 +59,43 @@ def __init__(self, bus: MessageBusClient) -> None:
self.setFixedSize(self.size())

def pairing_done(self, event):
"""This function gets triggered when we finish pairing Mycroft.
Args:
event: Mycroft pairing event
"""
self.done = True
self.file.close()
self.close()

def add_pairing_code(self):
"""Sets the pairing code and shows it on the UI
"""
self.lblCode.setText(self.code)

def read_pairing_code(self):
"""Reads the skills file and sets the pairing code to a variable
"""
while not self.done:
print("Reading...")
line = self.file.readline()
if line:
print(line)
matches = re.findall(
"(?<=" + re.escape("PairingSkill | ") + ")[a-zA-Z0-9 ]*: [A-Z0-9]{6}(?=\n)", line)
"(?<=" + re.escape("PairingSkill | ") + ")[a-zA-Z0-9 ]*: [A-Z0-9]{6}(?=\n)",
line)
print(matches)
if matches:
self.code = matches[0][-6:]
else:
time.sleep(1)

def update_texts(self):
"""Sets the localized texts in the UI
"""
self.lblWelcome.setText(_("Welcome!"))
self.lblFirstRun.setText(_("It's your first time using Mycroft, so please follow these instructions"))
self.lblFirstRun.setText(
_("It's your first time using Mycroft, so please follow these instructions"))
self.btnPrev.setText(_("Previous"))
self.btnNext.setText(_("Next"))
self.lblRegisterAddDevice.setText(
Expand All @@ -90,6 +104,8 @@ def update_texts(self):
self.lblInfoCode.setText(_("Input this code on the website"))

def go_next(self):
"""This function gets executed when the user clicks the next button
"""
self.hide_all_elements()
self.page = min(2, self.page + 1)
if self.page == 1:
Expand All @@ -105,6 +121,8 @@ def go_next(self):
self.btnPrev.setEnabled(True)

def go_previous(self):
"""This function gets executed when the user click the previous version
"""
self.hide_all_elements()
self.page = max(0, self.page - 1)
if self.page == 1:
Expand All @@ -119,6 +137,8 @@ def go_previous(self):
self.btnNext.setEnabled(True)

def hide_all_elements(self):
"""Hides all images and texts when switching pages
"""
self.imgSelectVoice.setVisible(False)
self.imgAddDevice.setVisible(False)
self.picInputCode.setVisible(False)
Expand All @@ -132,6 +152,11 @@ def hide_all_elements(self):
self.btnNext.setEnabled(False)

def closeEvent(self, event: QtGui.QCloseEvent) -> None:
"""This get triggered when closing the window
Args:
event (QtGui.QCloseEvent): Qt Close event
"""
if self.done:
self.closed_signal.emit()
time.sleep(2) # type: ignore
Expand All @@ -144,7 +169,8 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None:
print(self.close_res)
if self.close_res == QtWidgets.QMessageBox.Yes:
self.timer.stop()
self.closing_window = ProgressBox(_("Closing Mycroft, please wait..."))
self.closing_window = ProgressBox(
_("Closing Mycroft, please wait..."))
self.closing_window.show()
self.closing_thread = CloseMycroft()
self.closing_thread.finished.connect( # type: ignore
Expand All @@ -154,10 +180,16 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None:
event.ignore()

def finish_exit(self):
"""Exits the program
"""
sys.exit(0)


class CloseMycroft(QThread):
"""A thread to close Mycroft when exiting
"""
def run(self):
"""Closes Mycroft and emits the event
"""
subprocess.run("/usr/lib/mycroft-core/stop-mycroft.sh", shell=True)
self.finished.emit()
Loading

0 comments on commit 568e71a

Please sign in to comment.