#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import socket
import json
import time
import getopt
import sip
import PyQt4.QtGui
from PyQt4.QtCore import Qt, pyqtSignal
from PyQt4.QtGui import (QApplication, QMainWindow, QMessageBox, QFileDialog, QDialog, QColor,
QBrush, QComboBox, QTreeWidgetItem, QAbstractItemView, QFont, QHeaderView,
QTableWidgetItem, QDialogButtonBox, QProgressDialog)
from obci.control.gui.obci_window import Ui_OBCILauncher
from obci.control.gui.connect_dialog import Ui_ConnectToMachine
from obci.control.gui.select_amplifier_dialog import Ui_SelectAmplifier
from obci.utils.filesystem import checkpidfile, removepidfile
from obci.utils.openbci_logging import get_logger, log_crash
from obci.drivers.eeg.driver_discovery import driver_discovery
from obci.control.gui.obci_launcher_engine import OBCILauncherEngine, USER_CATEGORY
from obci.control.gui.obci_launcher_constants import STATUS_COLORS
from obci.control.gui.experiment_engine_info import MODE_ADVANCED, MODES
import obci.control.launcher.obci_script_utils as obci_script_utils
from obci.control.launcher.launcher_tools import READY_TO_LAUNCH, LAUNCHING, \
RUNNING, FAILED, TERMINATED
import obci.control.common.obci_control_settings as settings
from obci.control.common.message import LauncherMessage
import obci.control.common.net_tools as net
import obci.control.gui.obci_log_engine as obci_log_engine
# import obci.control.gui.obci_log_model_dummy as obci_log_model_dummy
import obci.control.gui.obci_log_model_real as obci_log_model_real
[docs]class ObciLauncherWindow(QMainWindow, Ui_OBCILauncher):
start = pyqtSignal(str, object)
# amp_select = pyqtSignal(object)
stop = pyqtSignal(str, bool)
reset = pyqtSignal(str)
save_as = pyqtSignal(object)
remove_user_preset = pyqtSignal(object)
import_scenario = pyqtSignal(str)
engine_reinit = pyqtSignal(object)
@log_crash
def __init__(self, presets=None):
'''
Constructor
'''
QMainWindow.__init__(self)
self.presets = presets
self.logger = get_logger('launcherGUI', obci_peer=self)
self.setupUi(self)
self.basic_title = self.windowTitle()
self.exp_states = {}
self.exp_widgets = {}
self.scenarioTab.setTabText(0, "Scenario")
self.scenarioTab.setTabsClosable(True)
self.log_engine = obci_log_engine.LogEngine(self.scenarioTab)
self.engine_server_setup()
self._nearby_machines = self.engine.nearby_machines()
self.scenarios.setSelectionBehavior(QAbstractItemView.SelectRows)
self.scenarios.setColumnCount(2)
self.scenarios.setHeaderLabels(["Scenario", "Status"])
self.scenarios.setColumnWidth(0, 300)
self.scenarios.itemClicked.connect(self._setInfo)
self.scenarios.currentItemChanged.connect(self._setInfo)
self.details_mode.currentIndexChanged.connect(self.update_user_interface)
self.parameters.setHeaderLabels(["Name", 'Value', 'Info'])
self.parameters.itemClicked.connect(self._itemClicked)
self.parameters.itemChanged.connect(self._itemChanged)
self.parameters.itemDoubleClicked.connect(self._itemDoubleClicked)
self.parameters.setColumnWidth(0, 200)
self.parameters.setColumnWidth(1, 400)
self.machines_dialog = ConnectToMachine(self)
self.select_amplifier_dialog = SelectAmplifierDialog(self)
self.start_button.clicked.connect(self._start)
self.stop_button.clicked.connect(self._stop)
# self.reset_button.clicked.connect(self._reset)
# self.ampselect_pushButton.clicked.connect(self._amp_select)
self.store_container.hide()
self.store_checkBox.stateChanged.connect(self._update_store)
self.store_dir_chooser.clicked.connect(self._choose_dir)
self._params = []
self._scenarios = []
self.details_mode.addItems(MODES)
self.engine_reinit.connect(self.engine_server_setup)
self.setupMenus()
self.setupActions()
self.update_user_interface(None)
self.showMaximized()
if os.environ.get('OBCI_INSTALL_DIR') is not None:
PyQt4.QtGui.QMessageBox.information(
self,
"Non standard OpenBCI directory",
"OpenBCI is launched from local directory: " +
os.environ.get('OBCI_INSTALL_DIR') +
', to start default package version launch "obci_local_remove" in terminal.')
@log_crash
[docs] def closeEvent(self, e):
progress = QProgressDialog(self, Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
progress.setLabelText("Finishing...")
progress.setRange(0, 5)
progress.setCancelButton(None)
progress.show()
self.stop_logs()
removepidfile('gui.pid')
for i in range(5):
time.sleep(0.4)
progress.setValue(i + 1)
e.accept()
def _crash_extra_tags(self, exception=None):
return {'obci_part': 'launcher'}
[docs] def stop_logs(self):
for i, st in self.exp_states.items():
st.log_model.stop_running()
@log_crash
[docs] def engine_server_setup(self, server_ip_host=None):
server_ip, server_name = server_ip_host or (None, None)
old_ip = None
old_hostname = None
if hasattr(self, 'server_ip'):
old_ip = self.server_ip
old_hostname = self.server_hostname
self.server_ip = str(server_ip)
self.server_hostname = str(server_name)
ctx = None
if server_ip is None:
self.server_ip = '127.0.0.1'
self.server_hostname = socket.gethostname()
if hasattr(self, 'engine'):
client = self.engine.client
ctx = client.ctx
else:
self.engine = None
if old_ip != self.server_ip and old_hostname != self.server_hostname:
if self.engine is not None:
self.engine.cleanup()
self._disconnect_signals()
# self.engine.deleteLater()
sip.delete(self.engine)
del self.engine
self.engine = None
client = obci_script_utils.client_server_prep(server_ip=self.server_ip,
zmq_ctx=ctx,
start_srv=True)
if client is None:
self.quit()
self.exp_states = {}
self.engine = OBCILauncherEngine(client, self.server_ip, self.presets)
self._connect_signals()
if self.server_ip and self.server_hostname != socket.gethostname():
self.setWindowTitle(self.basic_title + ' - ' + 'remote connection ' +
' (' + self.server_ip + ' - ' + self.server_hostname + ')')
else:
self.setWindowTitle(self.basic_title + ' - ' + 'local connection (' +
self.server_hostname + ')')
if old_ip is not None:
self.engine.update_ui.emit(None)
def _connect_signals(self):
self.engine.update_ui.connect(self.update_user_interface)
self.engine.update_ui.connect(self.log_engine.update_user_interface)
self.engine.rq_error.connect(self.launcher_error)
self.engine.saver_msg.connect(self._saver_msg)
self.reset.connect(self.engine.reset_launcher)
self.start.connect(self.engine.start_experiment)
self.start.connect(self.log_engine.experiment_started)
self.stop.connect(self.engine.stop_experiment)
self.stop.connect(self.log_engine.experiment_stopped)
self.save_as.connect(self.engine.save_scenario_as)
self.import_scenario.connect(self.engine.import_scenario)
self.remove_user_preset.connect(self.engine.remove_preset)
def _disconnect_signals(self):
self.engine.update_ui.disconnect()
self.engine.rq_error.disconnect()
self.engine.obci_state_change.disconnect()
self.engine.saver_msg.disconnect()
self.reset.disconnect()
self.start.disconnect()
self.stop.disconnect()
self.save_as.disconnect()
self.import_scenario.disconnect()
self.remove_user_preset.disconnect()
[docs] def setupActions(self):
self.actionExit.triggered.connect(PyQt4.QtGui.qApp.quit)
self.actionConnect.triggered.connect(self._connect_to_machine)
self.actionSelectAmplifier.triggered.connect(self._select_amplifier)
self.actionSave_as.triggered.connect(self._save_current_as)
self.actionOpen.triggered.connect(self._import)
self.actionRemove_from_sidebar.triggered.connect(self._remove_from_sidebar)
@log_crash
[docs] def setScenarios(self, scenarios):
scenarios.sort(key=lambda a: a.name, reverse=True)
self._scenarios = scenarios
self.scenarios.setSortingEnabled(True)
self.scenarios.clear()
self.categories = []
self.exp_widgets = {}
for i, s in enumerate(scenarios):
cat = s.category
treecat = None
names = [c.text(0) for c in self.categories]
if cat not in names:
treecat = ObciTreeWidgetItem([cat], None)
treecat.setText(0, cat)
self.categories.append(treecat)
self.scenarios.addTopLevelItem(treecat)
treecat.setExpanded(False)
else:
treecat = self.categories[names.index(cat)]
name = ObciTreeWidgetItem([s.name, s.status.status_name], s.uuid)
self.exp_widgets[s.uuid] = name
if s.status.status_name:
name.setBackground(0, QColor(STATUS_COLORS[s.status.status_name]))
name.setBackground(1, QColor(STATUS_COLORS[s.status.status_name]))
treecat.addChild(name)
name.setToolTip(0, s.launch_file)
self.scenarios.sortItems(0, Qt.AscendingOrder)
[docs] def getScenarios(self):
return self._scenarios
@log_crash
def _setParams(self, experiment):
expanded = self.exp_states[experiment.exp.uuid].expanded_peers
self.parameters.clear()
self._params = experiment
experiment = experiment.exp
print("********************")
print("Machine/peer from current experiment " + str(experiment.uuid) + ":")
for peer_id, peer in experiment.exp_config.peers.items():
st = experiment.status.peer_status(peer_id).status_name
mch = str(peer.machine)
if mch not in self._nearby_machines.values():
mch = self.server_hostname
print(mch, peer_id)
parent = QTreeWidgetItem([peer_id, st])
parent.setFirstColumnSpanned(True)
parent.setBackground(0, QBrush(QColor(STATUS_COLORS[st])))
parent.setBackground(1, QBrush(QColor(STATUS_COLORS[st])))
parent.setBackground(2, QBrush(QColor(STATUS_COLORS[st])))
parent.setToolTip(0, str(peer.path))
combo = QComboBox()
combo.addItems(list(self._nearby_machines.values()))
if mch in self._nearby_machines.values():
index = list(self._nearby_machines.values()).index(mch)
combo.setCurrentIndex(index)
self.parameters.addTopLevelItem(parent)
self.parameters.setItemWidget(parent, 2, combo)
if peer_id == 'mx':
combo.setDisabled(True)
if parent is not None:
combo.currentIndexChanged['QString'].connect(self.makeComboHandler(parent, 2))
if parent.text(0) in expanded:
parent.setExpanded(True)
parent.setToolTip(0, peer.path)
params = experiment.parameters(peer_id, self.details_mode.currentText())
for param, (value, src) in params.items():
val = str(value) # if not src else value + " ["+src + ']'
src = src if src else ''
child = QTreeWidgetItem([param, val, src])
if src:
child.setDisabled(True)
parent.addChild(child)
print("********************")
[docs] def makeComboHandler(self, item, column):
def handler(string):
self.parameters.itemChanged.emit(item, column)
return handler
@log_crash
def _getParams(self):
uid = self._params.exp.exp_config.uuid
old_uid = self._params.exp.old_uid
if uid not in self.exp_states and old_uid not in self.exp_states:
print("stale experiment descriptor")
return self._params
state = self.exp_states.get(uid, self.exp_states.get(old_uid, None))
if state is None:
print("_getParams - experiment not found")
return self._params
expanded = set()
for i, peer in enumerate(self._params.exp.exp_config.peers.values()):
parent = self.parameters.topLevelItem(i)
if parent is None:
print("***** _getParams: ", i, peer, "parent none")
continue
if parent.isExpanded():
expanded.add(parent.text(0))
# for j, param in enumerate(peer.config.local_params.keys()):
# child = parent.child(j)
state.expanded_peers = expanded
return self._params
def _itemDoubleClicked(self, item, column):
if item.parent() is None and column != 2:
uid = str(self.scenarios.currentItem().uuid)
# self.exp_states[uid].exp.exp_config.peers[
self.log_engine.show_log(item.text(0), uid)
def _itemClicked(self, item, column):
if item.columnCount() > 1 and column > 0:
if not item.isDisabled():
item.setFlags(item.flags() | Qt.ItemIsEditable)
else:
item.setFlags(Qt.ItemIsSelectable)
else:
if not item.isDisabled():
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
else:
item.setFlags(Qt.ItemIsSelectable)
def _itemChanged(self, item, column):
changed = False
if item.parent() is None:
peer_id = item.text(0)
if column == 2:
combo_box = self.parameters.itemWidget(item, column)
machine = combo_box.currentText()
old_ma = self._params.exp.exp_config.peer_machine(peer_id)
if old_ma != machine:
self._params.exp.exp_config.update_peer_machine(peer_id, machine)
changed = True
else:
exp_state = self._params
peer_id = item.parent().text(0)
param = item.text(0)
val = item.text(1)
old_val = exp_state.exp.exp_config.param_value(peer_id, param)
if old_val != item.text(1):
exp_state.exp.update_peer_param(peer_id, param, val)
changed = True
if changed:
self._getParams()
self._setParams(self._params)
@log_crash
def _setInfo(self, scenario_item, column):
if scenario_item is None:
pass
elif scenario_item.uuid not in self.exp_states:
self._params = None
self.parameters.clear()
self.parameters_of.setTitle("Parameters")
else:
self.info.setText(self.exp_states[scenario_item.uuid].exp.info)
if self._params:
self._getParams()
self._setParams(self.exp_states[scenario_item.uuid])
self.parameters_of.setTitle("Parameters of " + self.exp_states[scenario_item.uuid].exp.name)
self._store_update_info(self.exp_states[scenario_item.uuid].store_options)
self.log_engine.show(self.exp_states[scenario_item.uuid])
self._manage_actions(scenario_item)
# @log_crash
def _start(self):
uid = str(self.scenarios.currentItem().uuid)
if self.store_checkBox.isChecked():
store_options = {'save_file_name': self.store_file.text(),
'save_file_path': self.store_dir.text(),
'append_timestamp': str(int(self.store_ts_checkBox.isChecked())),
'store_locally': str(int(self.store_local_checkBox.isChecked()))
}
self.exp_states[uid].store_options = store_options
else:
store_options = None
self.log_engine.on_experiment_start(self.exp_states[uid].exp)
print("obciLauncher - _start(), exp: ", self.exp_states[uid].exp)
self.start.emit(uid, store_options)
def _stop(self):
uid = str(self.scenarios.currentItem().uuid)
print("obciLauncher._stop - begin uid: " + str(uid))
state = self.exp_states[uid]
if state.stopping:
print(
"Warning!!! - tried to perform stop action again on the same experiment ....................... Ignore")
return
state.stopping = True
state.log_model.stop_running()
self.stop.emit(uid, state.store_options is not None)
state.store_options = None
print("obciLauncher._stop - end")
def _amp_select(self):
p = {'path': 'drivers/eeg/cpp_amplifiers/amplifier_tmsi.py'}
d = {'usb_device': '/dev/tmsi0', 'driver_executable': 'tmsi_amplifier'}
uuid = str(self.scenarios.currentItem().uuid)
# si = self.scenarios.currentItem()
# exp = self.exp_states[uuid].exp
exp = self.exp_states[uuid]
peers = exp.exp.exp_config.peers
print(exp)
print(dir(exp))
print(exp.exp)
print(dir(exp.exp))
print(exp.exp.exp_config)
print(dir(exp.exp.exp_config))
# cfg = exp.exp.exp_config
# cfg.get_pee
a = peers['amplifier']
print(peers['amplifier'])
print(dir(peers['amplifier']))
print(a.public_params)
peers['amplifier'].path = p['path']
for k, v in d.items():
print(k, v)
a.config.update_local_param(k, v)
self._setParams(exp)
print("amp select")
@log_crash
def _saver_msg(self, killer_proc):
print("GUI SAVER MSG")
reply = QMessageBox.question(
self,
'Signal saving',
"Signal saving is taking quite some time. This is normal for longer EEG sessions.\n"
"Continue saving?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
killer_proc.poll()
if reply == QMessageBox.No and killer_proc.returncode is None:
print("KILLING")
killer_proc.kill()
killer_proc.wait()
print(killer_proc.returncode, "^^^")
def _update_store(self, state):
if int(state):
self.store_container.show()
else:
self.store_container.hide()
def _reset(self):
self.reset.emit(str(self.server_ip))
def _select_amplifier(self):
if self.select_amplifier_dialog.exec_() is None:
return
path = self.select_amplifier_dialog.path
params = self.select_amplifier_dialog.params
uuid = str(self.scenarios.currentItem().uuid)
exp = self.exp_states[uuid]
peers = exp.exp.exp_config.peers
amp = peers['amplifier']
amp.path = path
for k, v in params.items():
print(k, v)
amp.config.update_local_param(k, v)
# self._setParams(exp)
self._setInfo(self.scenarios.currentItem(), 1)
print("AMPLIFIER SELECTED")
def _connect_to_machine(self):
self.machines_dialog.set_nearby_machines(self._nearby_machines,
self.server_hostname, self.server_ip)
if self.machines_dialog.exec_():
self.stop_logs()
new_ip, new_name = self.machines_dialog.chosen_machine
self.engine_reinit.emit((new_ip, new_name))
self.update_user_interface(None)
def _save_current_as(self):
filename = QFileDialog.getSaveFileName(self, "Save scenario as..,",
os.path.join(settings.DEFAULT_SCENARIO_DIR),
'INI files (*.ini)')
if not filename:
return
filename = str(filename)
if not filename.endswith('.ini'):
filename += '.ini'
uid = self.scenarios.currentItem().uuid
exp = self.exp_states[uid].exp
self.save_as.emit((filename, exp))
def _choose_dir(self):
curr = str(self.store_dir.text())
if len(curr) == 0:
curr = '~'
curr = os.path.expanduser(curr)
if self.server_hostname != socket.gethostname():
PyQt4.QtGui.QMessageBox.information(self, "This is a remote connection",
"Enter the directory path by hand.")
direc = curr
else:
direc = QFileDialog.getExistingDirectory(self, "Choose directory..,", curr)
if not direc:
return
self.store_dir.setText(direc)
def _import(self):
filename = QFileDialog.getOpenFileName(self, "Import scenario...",
os.path.join(settings.DEFAULT_SCENARIO_DIR),
'INI files (*.ini)')
if not filename:
return
self.import_scenario.emit(filename)
def _remove_from_sidebar(self):
uid = self.scenarios.currentItem().uuid
exp = self.exp_states[uid].exp
self.remove_user_preset.emit(exp)
[docs] def exp_destroyed(self, *args, **kwargs):
print(args, kwargs)
print("DESTROYED")
[docs] def launcher_error(self, error_msg):
if isinstance(error_msg.details, dict):
str_details = str(json.dumps(error_msg.details, sort_keys=True, indent=4))
else:
str_details = str(error_msg.details)
QMessageBox.critical(self, "Request error", "Error: " + str(error_msg.err_code) +
"\nDetails: " + str_details,
QMessageBox.Ok)
@log_crash
[docs] def update_user_interface(self, update_msg):
user_imported_uuid = None
if isinstance(update_msg, LauncherMessage):
if update_msg.type == 'nearby_machines':
self._nearby_machines = self.engine.nearby_machines()
elif update_msg.type == '_user_set_scenario':
user_imported_uuid = update_msg.uuid
scenarios = self.engine.list_experiments()
current_sc = self.scenarios.currentItem()
curr_uid = current_sc.uuid if current_sc is not None else None
curr_exp_state = self.exp_states.get(curr_uid, None)
curr_exp = curr_exp_state.exp if curr_exp_state is not None else None
if curr_exp is not None:
if curr_exp in scenarios:
curr_uid = curr_exp.exp_config.uuid
else:
print("not found", curr_uid, curr_exp)
current_sc = None
new_states = {}
for exp in scenarios:
if exp.uuid not in self.exp_states:
st = new_states[exp.uuid] = ExperimentGuiState(
self.engine.client,
exp,
self.log_engine,
self.exp_states.get(exp.old_uid, None)
)
st.exp.destroyed.connect(self.exp_destroyed)
else:
new_states[exp.uuid] = self.exp_states[exp.uuid]
self.exp_states = new_states
mode = self.details_mode.currentText()
if mode not in MODES:
mode = MODE_ADVANCED
self.engine.details_mode = mode
self.setScenarios(scenarios)
if user_imported_uuid is not None:
current_sc = self.exp_widgets.get(user_imported_uuid,
self._first_exp(self.scenarios))
elif current_sc is None:
current_sc = self._first_exp(self.scenarios)
else:
uid = curr_exp.exp_config.uuid
old_uid = curr_exp.old_uid
current_sc = self.exp_widgets.get(uid,
self.exp_widgets.get(old_uid, self._first_exp(self.scenarios)))
self.scenarios.setCurrentItem(current_sc)
self._manage_actions(current_sc)
def _first_exp(self, scenarios):
exp = None
for index in range(scenarios.topLevelItemCount()):
item = scenarios.topLevelItem(index)
for ich in range(item.childCount()):
exp = item.child(ich)
break
if exp:
break
return exp
def _manage_actions(self, current_sc):
if current_sc is None:
self.start_button.setEnabled(False)
self._store_set_enabled(False)
self.actionSelectAmplifier.setEnabled(False)
self.stop_button.setEnabled(False)
return
if current_sc.uuid not in self.exp_states:
self.start_button.setEnabled(False)
self._store_set_enabled(False)
self.actionSelectAmplifier.setEnabled(False)
self.stop_button.setEnabled(False)
return
current_exp = self.exp_states[current_sc.uuid].exp
if current_exp.launcher_data is not None:
self.start_button.setEnabled(False)
self._store_set_enabled(False)
self.actionSelectAmplifier.setEnabled(False)
self.stop_button.setEnabled(True)
self.parameters.setEditTriggers(QAbstractItemView.NoEditTriggers)
else:
enable = (current_exp.status.status_name == READY_TO_LAUNCH)
self.start_button.setEnabled(enable)
is_amp = enable and "amplifier" in current_exp.exp_config.peers
self._store_set_enabled(is_amp)
self.actionSelectAmplifier.setEnabled(is_amp)
self.stop_button.setEnabled(False)
self.parameters.setEditTriggers(QAbstractItemView.DoubleClicked |
QAbstractItemView.EditKeyPressed)
launched = current_exp.status.status_name not in [LAUNCHING, RUNNING, FAILED, TERMINATED]
self.actionOpen.setEnabled(True)
self.actionSave_as.setEnabled(launched)
if current_exp.preset_data is not None:
remove_enabled = current_exp.preset_data["category"] == USER_CATEGORY
self.actionRemove_from_sidebar.setEnabled(remove_enabled)
self.actionExit.setEnabled(True)
self.actionConnect.setEnabled(self._nearby_machines != {})
def _store_set_enabled(self, enable):
self.store_checkBox.setEnabled(enable)
self.store_file.setEnabled(enable)
self.store_dir.setEnabled(enable)
self.store_dir_chooser.setEnabled(enable)
self.store_ts_checkBox.setEnabled(enable)
# self.store_local_checkBox.setEnabled(enable)
# self.ampselect_pushButton.setEnabled(enable)
def _store_update_info(self, store_options):
if store_options is not None:
self.store_file.setText(store_options['save_file_name'])
self.store_dir.setText(store_options['save_file_path'])
self.store_ts_checkBox.setChecked(int(store_options['append_timestamp']))
# self.store_local_checkBox.setChecked(store_options[u'store_locally'])
self.store_checkBox.setChecked(True)
self.store_container.show()
else:
self.store_checkBox.setChecked(False)
self.store_container.hide()
[docs]class ExperimentGuiState(object):
def __init__(self, srv_client, engine_experiment, log_engine, old_exp=None):
self.exp = engine_experiment
self.expanded_peers = set()
if old_exp is None:
self.store_options = None
self.stopping = False
self.log_model = obci_log_model_real.RealLogModel(srv_client)
self.log_model.update_log.connect(log_engine.update_log)
else:
self.store_options = old_exp.store_options
self.stopping = old_exp.stopping
self.log_model = old_exp.log_model
[docs]class ConnectToMachine(QDialog, Ui_ConnectToMachine):
def __init__(self, parent):
super(ConnectToMachine, self).__init__(parent)
self.setupUi(self)
self.nearby_machines.horizontalHeader().setResizeMode(QHeaderView.Stretch)
self.nearby_machines.setSelectionBehavior(QAbstractItemView.SelectRows)
self.nearby_machines.setColumnCount(3)
self.nearby_machines.setHorizontalHeaderLabels(["IP", "Hostname", "Status"])
self.nearby_machines.horizontalHeader().setVisible(True)
self.nearby_machines.setColumnWidth(0, 200)
self.nearby_machines.itemDoubleClicked.connect(self.accept)
self.chosen_machine = None
[docs] def set_nearby_machines(self, machines, current_hostname, current_ip):
self.nearby_machines.clearContents()
self.nearby_machines.setRowCount(len(machines))
for i, (ip, hostname) in enumerate(machines.items()):
ip_w = QTableWidgetItem(ip)
ip_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.nearby_machines.setItem(i, 0, ip_w)
hn_w = QTableWidgetItem(hostname)
hn_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.nearby_machines.setItem(i, 1, hn_w)
st_w = QTableWidgetItem()
st_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if (str(ip) == str(current_ip) or str(current_ip) == net.lo_ip())\
and str(hostname) == str(current_hostname):
font = QFont()
font.setWeight(QFont.Black)
hn_w.setFont(font)
ip_w.setFont(font)
st_w.setText('Connected')
st_w.setFont(font)
self.nearby_machines.setItem(i, 2, st_w)
if machines:
self.nearby_machines.setCurrentItem(self.nearby_machines.item(0, 0))
else:
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
[docs] def accept(self):
row = self.nearby_machines.currentRow()
cur_ip = self.nearby_machines.item(row, 0)
cur_host = self.nearby_machines.item(row, 1)
self.chosen_machine = (cur_ip.text(), cur_host.text())
super(ConnectToMachine, self).accept()
[docs]class SelectAmplifierDialog(QDialog, Ui_SelectAmplifier):
def __init__(self, parent):
super(SelectAmplifierDialog, self).__init__(parent)
self.setupUi(self)
self.amplifiers.horizontalHeader().setResizeMode(QHeaderView.Stretch)
self.amplifiers.setSelectionBehavior(QAbstractItemView.SelectRows)
self.amplifiers.setColumnCount(3)
self.amplifiers.setColumnWidth(0, 300)
self.amplifiers.setHorizontalHeaderLabels(["Experiment", "Amplifier", "Device"])
self.amplifiers.horizontalHeader().setVisible(True)
self.amplifiers.itemDoubleClicked.connect(self.accept)
self.refreshButton.clicked.connect(self.refresh)
self.amps = []
self.path = ''
self.params = {}
[docs] def refresh(self):
# self.amps = []
# self.update()
amps = driver_discovery.find_drivers()
self.amps = amps
self.update()
def _update_amplifiers(self):
print("SHOW")
amps = self.amps
self.amplifiers.clearContents()
self.amplifiers.setRowCount(len(amps))
for i, amp in enumerate(amps):
exp = amp['amplifier_peer_info']['path']
amp_name = amp['amplifier_params']['channels_info']['name']
device = amp['amplifier_params']['additional_params'].get('bluetooth_device', '')
if len(device) == 0:
device = amp['amplifier_params']['additional_params'].get('usb_device', '')
ip_w = QTableWidgetItem(exp)
ip_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.amplifiers.setItem(i, 0, ip_w)
hn_w = QTableWidgetItem(amp_name)
hn_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.amplifiers.setItem(i, 1, hn_w)
st_w = QTableWidgetItem(device)
st_w.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
self.amplifiers.setItem(i, 2, st_w)
if len(amps) > 0:
self.amplifiers.setCurrentItem(self.amplifiers.item(0, 0))
else:
self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
[docs] def update(self):
self._update_amplifiers()
super(SelectAmplifierDialog, self).update()
[docs] def accept(self):
row = self.amplifiers.currentRow()
amp = self.amps[row]
path = amp['amplifier_peer_info']['path']
params = {}
params['driver_executable'] = amp['amplifier_peer_info']['driver_executable']
params['bluetooth_device'] = amp['amplifier_params']['additional_params'].get('bluetooth_device', '')
params['usb_device'] = amp['amplifier_params']['additional_params'].get('usb_device', '')
self.path = path
self.params = params
super(SelectAmplifierDialog, self).accept()
[docs]def run_obci_gui():
opts, args = getopt.getopt(sys.argv[1:], '', ['tray', 'presets='])
presets = None
for opt, arg in opts:
if opt in ('--tray'):
os.system('obci_x_tray &')
elif opt == '--presets':
presets = arg
if checkpidfile('gui.pid'):
sys.exit(0)
app = QApplication([])
# p = QPixmap(os.path.join(settings.INSTALL_DIR, "gui/ugm/resources/obci+svarog.png"))
# s = QSplashScreen(p)
# s.show()
# s.showMessage("OpenBCI - free as in freedom.")
# s.finish(dialog)
# commented by now - couses sometimes app.exec_ to hang forever ...
dialog = ObciLauncherWindow(presets)
dialog.start.connect(lambda name: sys.stderr.write('Start %s \n' % name))
dialog.stop.connect(lambda name: sys.stderr.write('Stop %s \n' % name))
sys.exit(app.exec_())