#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# mesycontrol - Remote control for mesytec devices.
# Copyright (C) 2015-2016 mesytec GmbH & Co. KG <info@mesytec.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
__author__ = 'Florian Lüke'
__email__ = 'f.lueke@mesytec.com'
from functools import partial
import collections
import copy
import logging
import sys
import weakref
import pyqtgraph.console
pg = pyqtgraph
from qt import Qt
from qt import QtCore
from qt import QtGui
from basic_model import IDCConflict
from gui_util import is_setup, is_registry, is_mrc, is_bus, is_device, is_device_cfg, is_device_hw, get_mrc
from gui_util import is_config, is_hardware
from model_util import add_mrc_connection
from util import make_icon
import app_model as am
import async_util
import config_gui
import config_util
import device_tableview
import future
import gui_tutorial
import gui_util
import hardware_util
import resources
import util
log = logging.getLogger(__name__)
[docs]class GUIApplication(QtCore.QObject):
"""GUI logic"""
TOOLBAR_ICON_SIZE = QtCore.QSize(12, 12)
TOOLBAR_FONT_SIZE = 10
def __init__(self, context, mainwindow):
super(GUIApplication, self).__init__()
self.log = util.make_logging_source_adapter(__name__, self)
self._mainwindow = weakref.ref(mainwindow)
self.context = context
self._linked_mode = False
self._device_window_map = dict() # app_model.Device -> list of QMdiSubWindow
self._previous_tree_node = None # The previously selected tree node
self._selected_tree_node = None # The currently selected tree node
self._selected_device = None # The currently selected device or None if no device is selected
self._previous_subwindow = None
self._current_subwindow = None
self._subwindow_toolbar = None
self.mainwindow.installEventFilter(self)
self.mainwindow.mdiArea.subWindowActivated.connect(self._on_subwindow_activated)
self.mainwindow.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.mainwindow.toolbar.setIconSize(GUIApplication.TOOLBAR_ICON_SIZE)
self.mainwindow.actionQuickstart.triggered.connect(self._show_quickstart)
font = self.mainwindow.toolbar.font()
font.setPixelSize(GUIApplication.TOOLBAR_FONT_SIZE)
self.mainwindow.toolbar.setFont(font)
# Treeview
self.treeview = self.mainwindow.treeview
self.treeview.cfg_context_menu_requested.connect(self._cfg_context_menu)
self.treeview.hw_context_menu_requested.connect(self._hw_context_menu)
self.treeview.node_activated.connect(self._tree_node_activated)
self.treeview.node_selected.connect(self._tree_node_selected)
# Logview setup: show logged and unhandled exceptions in the log view
self.logview = self.mainwindow.logview
sys.excepthook.register_handler(self.logview.handle_exception)
callback_handler = util.CallbackHandler()
callback_handler.addFilter(util.MinimumLevelFilter(logging.WARNING))
#callback_handler.addFilter(util.HasExceptionFilter())
callback_handler.add_callback(self.logview.handle_log_record)
logging.getLogger().addHandler(callback_handler)
# Load device modules
context.init_device_registry()
for mod in context.device_registry.modules.itervalues():
self.logview.append("Loaded device module '%s' (idc=%d, name=%s)" %
(mod.__name__, mod.idc, mod.profile.name))
# Clean resources on exit
context.add_shutdown_callback(resources.qCleanupResources)
# Actions
self._create_actions()
self._populate_menus()
self._populate_toolbar()
self._populate_treeview()
# Model changes
self.app_registry.hw.mrc_added.connect(self._hw_mrc_added)
self.app_registry.config_set.connect(self._setup_changed)
self.app_registry.mrc_added.connect(self._app_mrc_added)
self.app_registry.mrc_about_to_be_removed.connect(self._app_mrc_about_to_be_removed)
# Init
self._setup_changed(self.app_registry, None, self.app_registry.cfg)
self._update_actions()
settings = self.context.make_qsettings()
if settings.value('MainWindow/first_run', QtCore.QVariant(True)).toBool():
settings.setValue('MainWindow/first_run', False)
self._show_quickstart()
def _setup_changed(self, app_registry, old, new):
if old:
old.modified_changed.disconnect(self._update_actions)
if new:
new.modified_changed.connect(self._update_actions)
self._update_actions()
def _app_mrc_added(self, mrc):
mrc.device_added.connect(self._app_device_added)
mrc.device_about_to_be_removed.connect(self._app_device_about_to_be_removed)
for device in mrc:
self._app_device_added(device)
def _app_mrc_about_to_be_removed(self, mrc):
mrc.device_added.disconnect(self._app_device_added)
mrc.device_about_to_be_removed.disconnect(self._app_device_about_to_be_removed)
def _app_device_added(self, device):
device.hardware_set.connect(self._app_device_hardware_set)
device.config_set.connect(self._app_device_config_set)
def _app_device_about_to_be_removed(self, device):
device.hardware_set.disconnect(self._app_device_hardware_set)
device.config_set.disconnect(self._app_device_config_set)
for window in list(self._device_window_map.get(device, list())):
window.close()
def _app_device_hardware_set(self, device, old, new):
if device.idc_conflict:
for window in list(self._device_window_map.get(device, list())):
try:
if util.COMBINED in (window.display_mode, window.write_mode):
window.close()
except AttributeError:
pass
def _app_device_config_set(self, device, old, new):
if device.idc_conflict:
for window in list(self._device_window_map.get(device, list())):
try:
if util.COMBINED in (window.display_mode, window.write_mode):
window.close()
except AttributeError:
pass
def _create_actions(self):
# Actions will be added to toolbars in dictionary insertion order.
self.actions = collections.OrderedDict()
# ===== Config ===== #
# Open setup
action = QtGui.QAction(make_icon(":/open-setup.png"),
"&Open setup", self, triggered=self._open_setup)
action.setToolTip("Open a setup file")
action.setStatusTip(action.toolTip())
action.setShortcut(QtGui.QKeySequence.Open)
action.cfg_toolbar = True
self.actions['open_setup'] = action
# Save setup
action = QtGui.QAction(make_icon(":/save-setup.png"),
"&Save setup", self, triggered=self._save_setup)
action.setToolTip("Save setup")
action.setStatusTip(action.toolTip())
action.setShortcut(QtGui.QKeySequence.Save)
action.cfg_toolbar = True
self.actions['save_setup'] = action
# Save setup as
action = QtGui.QAction(make_icon(":/save-setup-as.png"),
"S&ave setup as", self, triggered=self._save_setup_as)
action.setToolTip("Save setup as")
action.setStatusTip(action.toolTip())
action.setShortcut(QtGui.QKeySequence.SaveAs)
action.cfg_toolbar = True
self.actions['save_setup_as'] = action
# Close setup
action = QtGui.QAction(make_icon(":/close-setup.png"),
"&Close setup", self, triggered=self._close_setup)
action.setToolTip("Close setup")
action.setStatusTip(action.toolTip())
action.cfg_toolbar = True
self.actions['close_setup'] = action
# Add config
action = QtGui.QAction(make_icon(":/add-config.png"), "Add config", self,
triggered=self._add_config)
action.cfg_toolbar = True
self.actions['add_config'] = action
# Remove config
action = QtGui.QAction(make_icon(":/remove-config.png"), "Remove config", self,
triggered=self._remove_config)
action.cfg_toolbar = True
self.actions['remove_config'] = action
# Rename
action = QtGui.QAction("Rename", self,
triggered=self._rename_config)
self.actions['rename_config'] = action
# Open device config
action = QtGui.QAction(QtGui.QIcon.fromTheme("document-open"),
"Load device config from file", self, triggered=self._open_device_config)
self.actions['open_device_config'] = action
# Save device config
action = QtGui.QAction(QtGui.QIcon.fromTheme("document-save"),
"Save device config to file", self, triggered=self._save_device_config)
self.actions['save_device_config'] = action
# Edit MRC config / MRC Properties
action = QtGui.QAction(QtGui.QIcon.fromTheme("document-properties"),
"Properties", self, triggered=self._edit_mrc_config)
self.actions['edit_mrc_config'] = action
# Edit device config / device properties
action = QtGui.QAction(QtGui.QIcon.fromTheme("document-properties"),
"Properties", self, triggered=self._edit_device_config)
self.actions['edit_device_config'] = action
# ===== Hardware ===== #
# Connect/Disconnect
icons = {
'connect': make_icon(":/connect.png"),
'disconnect': make_icon(":/disconnect.png")
}
action = QtGui.QAction(icons['connect'], "&Connect", self,
triggered=self._connect_or_disconnect)
action.icons = icons
action.hw_toolbar = True
self.actions['connect_disconnect'] = action
# Write access
action = QtGui.QAction(make_icon(":/write-access.png"), "Toggle write access", self,
checkable=True, triggered=self._toggle_write_access)
action.hw_toolbar = True
self.actions['toggle_write_access'] = action
# Silent mode
icons = {
True: make_icon(":/silent-mode-on.png"),
False: make_icon(":/silent-mode-off.png")
}
action = QtGui.QAction(icons[False], "Toggle silent mode", self,
checkable=True, triggered=self._toggle_silent_mode)
action.icons = icons
action.hw_toolbar = True
self.actions['toggle_silent_mode'] = action
# Toggle RC
action = QtGui.QAction(make_icon(":/remote-control.png"), "Toggle RC", self,
checkable=True, triggered=self._toggle_rc)
action.hw_toolbar = True
self.actions['toggle_rc'] = action
# Refresh device memory
action = QtGui.QAction(make_icon(":/refresh.png"), "&Refresh memory", self,
triggered=self._refresh)
action.hw_toolbar = True
self.actions['refresh'] = action
# Add connection
action = QtGui.QAction(make_icon(":/add-mrc.png"), "Add MRC connection", self,
triggered=self._add_mrc_connection)
action.hw_toolbar = True
self.actions['add_mrc_connection'] = action
# Remove connection
action = QtGui.QAction(make_icon(":/remove-mrc.png"), "Remove MRC connection", self,
triggered=self._remove_mrc_connection)
action.hw_toolbar = True
self.actions['remove_mrc_connection'] = action
# Show server output
action = QtGui.QAction("View server log", self,
triggered=self._view_server_log)
self.actions['view_server_log'] = action
# ===== Splitter =====
# Linked Mode
link_icons = {
True: make_icon(":/linked.png"),
False: make_icon(":/unlinked.png")
}
action = QtGui.QAction(link_icons[self.linked_mode], "Toggle linked mode",
self, toggled=self.set_linked_mode)
action.icons = link_icons
action.setToolTip("Link Hardware & Config Views")
action.setStatusTip(action.toolTip())
action.setCheckable(True)
action.setChecked(self.linked_mode)
action.splitter_toolbar = True
self.actions['toggle_linked_mode'] = action
# Check config
action = QtGui.QAction(make_icon(":/check-config.png"), "Compare config and hardware", self,
triggered=self._check_config)
action.splitter_toolbar = True
self.actions['check_config'] = action
# Config to Hardware
action = QtGui.QAction(make_icon(":/apply-config-to-hardware.png"),
"Apply config to hardware", self, triggered=self._apply_config_to_hardware)
action.splitter_toolbar = True
self.actions['apply_config_to_hardware'] = action
# Hardware to Config
action = QtGui.QAction(make_icon(":/apply-hardware-to-config.png"),
"Copy hardware values to config", self, triggered=self._apply_hardware_to_config)
action.splitter_toolbar = True
self.actions['apply_hardware_to_config'] = action
# Widget window
action = QtGui.QAction(make_icon(":/open-device-widget.png"),
"Open device widget", self, triggered=self._open_device_widget)
action.setToolTip("Open device widget")
action.setStatusTip(action.toolTip())
action.splitter_toolbar = True
self.actions['open_device_widget'] = action
# Table window
action = QtGui.QAction(make_icon(":/open-device-table.png"),
"Open device table", self, triggered=self._open_device_table)
action.setToolTip("Open device table")
action.setStatusTip(action.toolTip())
action.splitter_toolbar = True
self.actions['open_device_table'] = action
# ===== Mainwindow toolbar =====
# Display mode
group = QtGui.QActionGroup(self)
self.actions['display_hw'] = QtGui.QAction("Hardware", group,
checkable=True, enabled=False, triggered=self._on_display_hw_triggered)
self.actions['display_cfg'] = QtGui.QAction("Config", group,
checkable=True, enabled=False, triggered=self._on_display_cfg_triggered)
self.actions['display_combined'] = QtGui.QAction("Combined", group,
checkable=True, enabled=False, triggered=self._on_display_combined_triggered)
action = QtGui.QAction(make_icon(":/select-display-mode.png"),
"Display mode", self, enabled=False)
action.setToolTip("Select display mode")
action.setStatusTip(action.toolTip())
action.toolbar = True
action.setMenu(QtGui.QMenu())
action.menu().addActions(group.actions())
self.actions['select_display_mode'] = action
# Write mode
group = QtGui.QActionGroup(self)
self.actions['write_hw'] = QtGui.QAction("Hardware", group,
checkable=True, enabled=False, triggered=self._on_write_hw_triggered)
self.actions['write_cfg'] = QtGui.QAction("Config", group,
checkable=True, enabled=False, triggered=self._on_write_cfg_triggered)
self.actions['write_combined'] = QtGui.QAction("Combined", group,
checkable=True, enabled=False, triggered=self._on_write_combined_triggered)
action = QtGui.QAction(make_icon(":/select-write-mode.png"),
"Write mode", self, enabled=False)
action.setToolTip("Select write mode")
action.setStatusTip(action.toolTip())
action.toolbar = True
action.setMenu(QtGui.QMenu())
action.menu().addActions(group.actions())
self.actions['select_write_mode'] = action
# Quit
action = QtGui.QAction("&Quit", self, triggered=self.mainwindow.close)
action.setShortcut(QtGui.QKeySequence.Quit)
action.setShortcutContext(Qt.ApplicationShortcut)
self.actions['quit'] = action
# Next Window
action = QtGui.QAction("&Next Window", self,
triggered=self.mainwindow.mdiArea.activateNextSubWindow)
action.setShortcut(QtGui.QKeySequence.NextChild)
self.actions['next_window'] = action
# Previous Window
action = QtGui.QAction("&Previous Window", self,
triggered=self.mainwindow.mdiArea.activatePreviousSubWindow)
action.setShortcut(QtGui.QKeySequence.PreviousChild)
self.actions['previous_window'] = action
# Cascade Windows
action = QtGui.QAction("&Cascade Windows", self,
triggered=self.mainwindow.mdiArea.cascadeSubWindows)
self.actions['cascade_windows'] = action
# Tile Windows
action = QtGui.QAction("&Tile Windows", self,
triggered=self.mainwindow.mdiArea.tileSubWindows)
self.actions['tile_windows'] = action
# Close all windows
action = QtGui.QAction("Cl&ose all Windows", self,
triggered=self.mainwindow.mdiArea.closeAllSubWindows)
self.actions['close_all_windows'] = action
# Edit extensions
action = QtGui.QAction("Show device extensions", self,
triggered=self._show_device_extensions)
self.actions['show_device_extensions'] = action
def _populate_menus(self):
menu_file = self.mainwindow.menu_file
menu_file.addAction(self.actions['open_setup'])
menu_file.addAction(self.actions['save_setup'])
menu_file.addAction(self.actions['save_setup_as'])
menu_file.addAction(self.actions['close_setup'])
menu_file.addSeparator()
menu_file.addAction(self.actions['quit'])
menu_window = self.mainwindow.menu_window
menu_window.addAction(self.actions['next_window'])
menu_window.addAction(self.actions['previous_window'])
menu_window.addSeparator()
menu_window.addAction(self.actions['cascade_windows'])
menu_window.addAction(self.actions['tile_windows'])
menu_window.addSeparator()
menu_window.addAction(self.actions['close_all_windows'])
def _populate_toolbar(self):
tb = self.mainwindow.toolbar
f = lambda a: getattr(a, 'toolbar', False)
for action in filter(f, self.actions.values()):
tb.addAction(action)
if action.menu() is not None:
tb.widgetForAction(action).setPopupMode(QtGui.QToolButton.InstantPopup)
def _populate_treeview(self):
# Splitter
f = lambda a: getattr(a, 'splitter_toolbar', False)
for action in filter(f, self.actions.values()):
self.treeview.splitter_toolbar.addAction(action)
# Config
f = lambda a: getattr(a, 'cfg_toolbar', False)
for action in filter(f, self.actions.values()):
self.treeview.cfg_toolbar.addAction(action)
# Hardware
f = lambda a: getattr(a, 'hw_toolbar', False)
for action in filter(f, self.actions.values()):
self.treeview.hw_toolbar.addAction(action)
def _update_actions_cb(self, *args, **kwargs):
"""Calls _update_actions(), ignoring args and kwargs. Usable as a
Future callback."""
return self._update_actions()
def _update_actions(self):
node = self._selected_tree_node
self.log.debug("update actions: selected=%s", node)
setup = self.app_registry.cfg
self.actions['save_setup'].setEnabled(setup.modified and len(setup))
self.actions['save_setup_as'].setEnabled(len(setup))
self.actions['close_setup'].setEnabled(len(setup))
a = self.actions['add_config']
a.setEnabled(node is None or is_config(node))
if is_setup(node):
a.setText("Add MRC")
else:
a.setText("Add Device")
a = self.actions['remove_config']
a.setEnabled((is_mrc(node) or is_device(node)) and node.ref.has_cfg
and (self.linked_mode or is_config(node)))
if a.isEnabled() and is_mrc(node):
a.setText("Remove MRC config")
if a.isEnabled() and is_device(node):
a.setText("Remove Device config")
self.actions['rename_config'].setEnabled(
(is_mrc(node) or is_device(node)) and node.ref.has_cfg)
self.actions['open_device_config'].setEnabled(is_device(node))
self.actions['save_device_config'].setEnabled(is_device(node))
a = self.actions['connect_disconnect']
a.setEnabled((is_registry(node) and len(node.children)) or is_mrc(node))
if a.isEnabled() and is_registry(node):
if all((mrc.has_hw and mrc.hw.is_connected()) for mrc in node.ref):
a.setIcon(a.icons['disconnect'])
a.setToolTip("Disconnect all MRCs")
else:
a.setIcon(a.icons['connect'])
a.setToolTip("Connect all MRCs")
a.setText(a.toolTip())
a.setStatusTip(a.toolTip())
if a.isEnabled() and is_mrc(node):
if node.ref.has_hw and node.ref.hw.is_connected():
a.setIcon(a.icons['disconnect'])
a.setToolTip("Disconnect")
else:
a.setIcon(a.icons['connect'])
a.setToolTip("Connect")
a.setText(a.toolTip())
a.setStatusTip(a.toolTip())
# Toggle RC
a = self.actions['toggle_rc']
a.setEnabled(is_device(node) and node.ref.has_hw and not node.ref.hw.address_conflict
and (is_hardware(node) or self.linked_mode))
a.setEnabled(is_device(node)
and (is_hardware(node) or self.linked_mode)
and node.ref.has_hw
and node.ref.hw.is_connected()
and node.ref.hw.mrc.write_access
and not node.ref.hw.address_conflict)
if a.isEnabled():
a.setChecked(node.ref.hw.rc)
a.setToolTip("Disable RC" if a.isChecked() else "Enable RC")
a.setText(a.toolTip())
a.setStatusTip(a.toolTip())
# Refresh device memory
a = self.actions['refresh']
a.setEnabled(is_hardware(node))
# Write access
mrc = get_mrc(node)
a = self.actions['toggle_write_access']
a.setEnabled(mrc is not None
and mrc.has_hw
and (is_hardware(node) or self.linked_mode)
and mrc.hw.is_connected())
a.setChecked(a.isEnabled() and mrc.hw.write_access)
if a.isChecked():
a.setToolTip("Release write access")
else:
a.setToolTip("Acquire write access")
a.setText(a.toolTip())
a.setStatusTip(a.toolTip())
# Silent mode
enabled = (mrc is not None
and mrc.has_hw
and (is_hardware(node) or self.linked_mode)
and mrc.hw.is_connected()
and mrc.hw.write_access)
checked = (mrc is not None
and mrc.has_hw
and mrc.hw.silenced)
a = self.actions['toggle_silent_mode']
a.setEnabled(enabled)
a.setChecked(checked)
a.setIcon(a.icons[a.isChecked()])
if a.isChecked():
a.setToolTip("Disable silent mode")
else:
a.setToolTip("Enable silent mode")
a.setText(a.toolTip())
a.setStatusTip(a.toolTip())
# Remove connection
a = self.actions['remove_mrc_connection']
a.setEnabled(is_mrc(node)
and (is_hardware(node) or self.linked_mode)
and node.ref.has_hw)
a = self.actions['toggle_linked_mode']
a.setChecked(self.linked_mode)
a.setIcon(a.icons[self.linked_mode])
# Open device widget
self.actions['open_device_widget'].setEnabled(
((is_device_cfg(node) and node.ref.cfg_module.has_widget_class())
or (is_device_hw(node) and node.ref.hw_module.has_widget_class()
and (not node.ref.has_hw or not node.ref.hw.address_conflict))))
# Open device table
self.actions['open_device_table'].setEnabled(
is_device_cfg(node)
or (is_device_hw(node)
and (not node.ref.has_hw or not node.ref.hw.address_conflict)))
self.actions['apply_config_to_hardware'].setEnabled(
((is_setup(node) and node.ref.has_cfg)
or (is_mrc(node) and node.ref.has_cfg)
or (is_bus(node)
and node.parent is not None
and node.parent.ref.has_cfg)
or (is_device(node)
and not node.ref.idc_conflict
and not node.ref.address_conflict
and node.ref.has_cfg
and node.ref.has_hw)))
self.actions['apply_hardware_to_config'].setEnabled(
(is_setup(node) and node.ref.has_hw and node.ref.hw.contains_devices())
or (is_mrc(node) and node.ref.has_hw and len(node.ref.hw))
or (is_bus(node)
and node.parent is not None
and node.parent.ref.has_hw
and len(node.parent.ref.hw.get_devices(node.bus_number)))
or (is_device(node)
and not node.ref.idc_conflict
and not node.ref.address_conflict
and node.ref.has_hw))
win = self._current_subwindow
act_display = self.actions['select_display_mode']
act_write = self.actions['select_write_mode']
if isinstance(win, gui_util.DeviceSubWindow):
try:
device = win.device
except RuntimeError:
device = None # c++ subwin might've been deleted
display_mode = win.display_mode
write_mode = win.write_mode
# Enable the parent actions
act_display.setEnabled(True)
act_write.setEnabled(True)
act_display.setText(util.RW_MODE_NAMES[win.display_mode].capitalize())
act_write.setText(util.RW_MODE_NAMES[win.write_mode].capitalize())
if display_mode == util.COMBINED:
self.actions['display_combined'].setChecked(True)
elif display_mode == util.HARDWARE:
self.actions['display_hw'].setChecked(True)
else:
self.actions['display_cfg'].setChecked(True)
if write_mode == util.COMBINED:
self.actions['write_combined'].setChecked(True)
elif write_mode == util.HARDWARE:
self.actions['write_hw'].setChecked(True)
else:
self.actions['write_cfg'].setChecked(True)
self.actions['display_combined'].setEnabled(win.has_combined_display()
and device.has_hw and device.has_cfg and not device.idc_conflict)
self.actions['write_combined'].setEnabled(device.has_hw and device.has_cfg
and not device.idc_conflict)
self.actions['display_hw'].setEnabled(device.has_hw
and (not device.idc_conflict or display_mode == util.HARDWARE))
self.actions['write_hw'].setEnabled(device.has_hw
and (not device.idc_conflict or display_mode == util.HARDWARE))
self.actions['display_cfg'].setEnabled(device.has_cfg
and (not device.idc_conflict or display_mode == util.CONFIG))
self.actions['write_cfg'].setEnabled(device.has_cfg
and (not device.idc_conflict or display_mode == util.CONFIG))
else:
# Disable the parent actions
act_display.setEnabled(False)
act_write.setEnabled(False)
for a in self.actions.values():
if len(a.toolTip()) and not len(a.statusTip()):
a.setStatusTip(a.toolTip())
def _tree_node_selected(self, node):
self.log.debug("_tree_node_selected: %s", node)
self._previous_tree_node = prev_node = self._selected_tree_node
self._selected_tree_node = node
self._selected_device = node.ref if is_device(node) else None
self._update_actions()
hw_signals = [
'address_conflict_changed',
'connected',
'connecting',
'disconnected',
'connection_error'
]
mrc_hw_signals = [
'write_access_changed',
'silenced_changed'
]
device_signals = [
'idc_conflict_changed',
'idc_changed',
'cfg_idc_changed',
'hw_idc_changed'
]
device_hw_signals = [
'rc_changed',
'address_conflict_changed'
]
app_signals = [
'hardware_set',
'config_set'
]
def disconnect_signals(obj, signals):
self.log.debug("_tree_node_selected: disconnecting '%s' from '%s'", signals, obj)
for sig in signals:
try:
getattr(obj, sig).disconnect(self._update_actions)
except TypeError:
pass
def connect_signals(obj, signals):
self.log.debug("_tree_node_selected: connecting '%s' to '%s'", signals, obj)
for sig in signals:
getattr(obj, sig).connect(self._update_actions)
if prev_node is not None:
if (is_mrc(prev_node) or is_device(prev_node)) and prev_node.ref.has_hw:
disconnect_signals(prev_node.ref.hw, hw_signals)
if (is_mrc(prev_node) and prev_node.ref.has_hw):
disconnect_signals(prev_node.ref.hw, mrc_hw_signals)
if is_device(prev_node):
disconnect_signals(prev_node.ref, device_signals)
if is_device(prev_node) and prev_node.ref.has_hw:
disconnect_signals(prev_node.ref.hw, device_hw_signals)
if isinstance(prev_node.ref, am.AppObject):
disconnect_signals(prev_node.ref, app_signals)
mrc = get_mrc(prev_node)
if mrc is not None and mrc.has_hw:
disconnect_signals(mrc.hw, mrc_hw_signals)
if node is not None:
if (is_mrc(node) or is_device(node)) and node.ref.has_hw:
connect_signals(node.ref.hw, hw_signals)
if (is_mrc(node) and node.ref.has_hw):
connect_signals(node.ref.hw, mrc_hw_signals)
if is_device(node):
connect_signals(node.ref, device_signals)
if is_device(node) and node.ref.has_hw:
connect_signals(node.ref.hw, device_hw_signals)
if isinstance(node.ref, am.AppObject):
connect_signals(node.ref, app_signals)
mrc = get_mrc(node)
if mrc is not None and mrc.has_hw:
connect_signals(mrc.hw, mrc_hw_signals)
if is_device(node) and not self._show_device_windows(
node.ref, is_device_cfg(node), is_device_hw(node)):
# No window for the selected node: make no window active in the mdi area
self.mainwindow.mdiArea.setActiveSubWindow(None)
# ===== Action implementations =====
def _open_setup(self):
gui_util.run_open_setup_dialog(context=self.context, parent_widget=self.mainwindow)
def _save_setup(self):
gui_util.run_save_setup(context=self.context, parent_widget=self.mainwindow)
def _save_setup_as(self):
gui_util.run_save_setup_as_dialog(context=self.context, parent_widget=self.mainwindow)
def _close_setup(self):
gui_util.run_close_setup(context=self.context, parent_widget=self.mainwindow)
self.context.make_qsettings().remove('Files/last_setup_file')
def _add_config(self):
node = self._selected_tree_node
if node is None or is_setup(node):
gui_util.run_add_mrc_config_dialog(
registry=self.app_registry,
parent_widget=self.mainwindow)
if is_mrc(node):
gui_util.run_add_device_config_dialog(
registry=self.app_registry,
device_registry=self.context.device_registry,
mrc=node.ref,
parent_widget=self.mainwindow)
if is_bus(node):
assert node.parent is not None
gui_util.run_add_device_config_dialog(
registry=self.app_registry,
device_registry=self.context.device_registry,
mrc=node.parent.ref,
bus=node.bus_number,
parent_widget=self.mainwindow)
if is_device(node):
gui_util.run_add_device_config_dialog(
registry=self.app_registry,
device_registry=self.context.device_registry,
mrc=node.ref.mrc,
address=None if node.ref.has_cfg else node.ref.address,
parent_widget=self.mainwindow)
def _remove_config(self):
node = self._selected_tree_node
if is_mrc(node):
self.app_registry.cfg.remove_mrc(node.ref.cfg)
if is_device(node):
device = node.ref
self._close_device_windows(device)
device.mrc.cfg.remove_device(device.cfg)
def _rename_config(self):
node = self._selected_tree_node
if ((is_mrc(node) or is_device(node)) and
node.ref.has_cfg):
self.treeview.cfg_view.edit(
self.treeview.cfg_model.index_for_ref(node.ref))
def _connect_or_disconnect(self):
node = self._selected_tree_node
a = self.actions['connect_disconnect']
if is_registry(node):
if all((mrc.has_hw and mrc.hw.is_connected()) for mrc in node.ref):
futures = [mrc.hw.disconnect() for mrc in node.ref]
else:
futures = list()
for mrc in node.ref:
if not mrc.has_hw:
futures.append(add_mrc_connection(self.app_registry.hw, mrc.url, True))
elif not mrc.hw.is_connected() and not mrc.hw.is_connecting():
futures.append(mrc.hw.connect())
if len(futures):
future.all_done(*futures).add_done_callback(
self._update_actions_cb)
if is_mrc(node):
if not node.ref.has_hw:
add_mrc_connection(self.app_registry.hw, node.ref.url, True)
elif node.ref.hw.is_disconnected():
node.ref.hw.connect()
a.setIcon(a.icons['disconnect'])
else:
node.ref.hw.disconnect()
a.setIcon(a.icons['connect'])
def _refresh(self):
node = self._selected_tree_node
assert is_hardware(node)
devices = None
if is_registry(node):
devices = [d for mrc in node.ref for d in mrc if d.has_hw]
elif is_mrc(node):
devices = [d for d in node.ref if d.has_hw]
elif is_bus(node):
devices = [d for d in node.parent.ref.get_devices(bus=node.bus_number) if d.has_hw]
elif is_device(node):
devices = [node.ref]
if not len(devices):
self.logview.append("Refresh: no devices present")
return
gen = hardware_util.refresh_device_memory(devices)
runner = async_util.DefaultGeneratorRunner(gen, self.mainwindow)
dialog = config_gui.SubProgressDialog(title="Refreshing device memory")
dialog.canceled.connect(runner.close)
runner.progress_changed.connect(dialog.set_progress)
f = runner.start()
fo = future.FutureObserver(f)
fo.done.connect(dialog.close)
dialog.exec_()
if f.done() and f.exception() is not None:
log.error("Refresh: %s", f.exception())
QtGui.QMessageBox.critical(self.mainwindow, "Error", str(f.exception()))
def _toggle_rc(self):
node = self._selected_tree_node
if is_device(node) and node.ref.has_hw:
f = node.ref.hw.set_rc(not node.ref.hw.rc)
f.add_done_callback(self._update_actions_cb)
def _toggle_write_access(self):
mrc = get_mrc(self._selected_tree_node).hw
if not mrc.is_connected():
return
f = None
if mrc.write_access:
f = mrc.release_write_access()
else:
force = False
if not mrc.can_acquire_write_access():
answer = QtGui.QMessageBox.question(
self.mainwindow,
"Acquire write access",
"Write access is currently taken by another client.\nForcibly acquire write access?",
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.No)
if answer != QtGui.QMessageBox.Yes:
self._update_actions()
return
force = True
f = mrc.acquire_write_access(force)
if f is not None:
f.add_done_callback(self._update_actions_cb)
def _toggle_silent_mode(self):
mrc = get_mrc(self._selected_tree_node).hw
if not mrc.is_connected():
return
f = mrc.set_silenced(not mrc.silenced)
f.add_done_callback(self._update_actions_cb)
def _add_mrc_connection(self):
gui_util.run_add_mrc_connection_dialog(
registry=self.app_registry,
parent_widget=self.mainwindow)
def _remove_mrc_connection(self):
node = self._selected_tree_node
def do_remove(f_ignored):
self.app_registry.hw.remove_mrc(node.ref.hw)
node.ref.hw.disconnect().add_done_callback(do_remove)
def _view_server_log(self):
node = self._selected_tree_node
mrc = node.ref.hw
server = mrc.connection.server
url = mrc.connection.url
view = gui_util.ServerLogView(server, parent=self.mainwindow)
sub = QtGui.QMdiSubWindow()
sub.setWidget(view)
sub.setAttribute(Qt.WA_DeleteOnClose)
sub.setWindowIcon(util.make_icon(":/window-icon.png"))
sub.setWindowTitle("Server log for %s" % url)
self.mainwindow.mdiArea.addSubWindow(sub)
sub.show()
def _open_device_widget(self):
node = self._selected_tree_node
self.log.debug("_open_device_widget: node=%s, is_cfg=%s, is_hw=%s",
node, is_device_cfg(node), is_device_hw(node))
self._create_device_widget_window(self._selected_device,
is_device_cfg(node), is_device_hw(node))
def _open_device_table(self):
node = self._selected_tree_node
self.log.debug("_open_device_table: node=%s, is_cfg=%s, is_hw=%s",
node, is_device_cfg(node), is_device_hw(node))
self._create_device_table_window(self._selected_device,
is_device_cfg(node), is_device_hw(node))
def _check_config(self):
node = self._selected_tree_node
if is_registry(node):
gen = (d for mrc in node.ref for d in mrc)
elif is_mrc(node):
gen = (d for d in node.ref)
elif is_bus(node):
assert node.parent is not None
gen = (d for d in node.parent.ref if d.bus == node.bus_number)
elif is_device(node):
gen = (d for d in (node.ref,))
elif node is None:
gen = (d for mrc in self.app_registry.mrcs for d in mrc)
else:
self.log.warning("check config: unsupported node type %s", node)
return
predicate = lambda d: not d.idc_conflict and d.has_cfg
devices = filter(predicate, gen)
self.log.info("check config: node=%s, devices=%s", node, devices)
self.set_linked_mode(True)
runner = config_gui.ReadConfigParametersRunner(
devices=devices,
parent_widget=self.mainwindow)
progress_dialog = config_gui.SubProgressDialog(title="Reading hardware values")
runner.progress_changed.connect(progress_dialog.set_progress)
progress_dialog.canceled.connect(runner.close)
f = runner.start()
fo = future.FutureObserver(f)
fo.done.connect(progress_dialog.close)
progress_dialog.exec_()
if f.done() and f.exception() is not None:
log.error("Check config: %s", f.exception())
QtGui.QMessageBox.critical(self.mainwindow, "Error", str(f.exception()))
def _run_config_creation_prompt(self, device):
QMB = QtGui.QMessageBox
mb = QMB(QMB.Question,
"Create device config",
"""
Config for %s at (%s, %d, %X) does not exist yet.
Initialize using the current hardware values or the device defaults?
""" % (device.get_device_name(), device.mrc.get_display_url(), device.bus, device.address),
buttons=QMB.Yes | QMB.No | QMB.Cancel,
parent=self.mainwindow)
mb.button(QMB.Yes).setText("Hardware values")
mb.button(QMB.No).setText("Device defaults")
res = mb.exec_()
d = { QMB.Yes: 'hardware', QMB.No: 'defaults' }
return d.get(res, False)
def _run_create_config(self, device):
source = self._run_config_creation_prompt(device)
if not source:
return False
device.create_config()
if source == 'hardware':
progress_dialog = config_gui.SubProgressDialog(title="Copying from hardware to config")
runner = config_gui.FillDeviceConfigsRunner([device], self.mainwindow)
progress_dialog.canceled.connect(runner.close)
f = runner.start()
fo = future.FutureObserver(f)
fo.done.connect(progress_dialog.close)
progress_dialog.exec_()
if f.done() and f.exception() is not None:
log.error("Check config: %s", f.exception())
QtGui.QMessageBox.critical(self.mainwindow, "Error", str(f.exception()))
return True
def _apply_config_to_hardware(self):
node = self._selected_tree_node
devices = None
if is_setup(node):
devices = [d for mrc in node.ref for d in mrc if d.has_cfg]
elif is_mrc(node):
devices = [d for d in node.ref if d.has_cfg]
elif is_bus(node):
devices = [d for d in node.parent.ref.get_devices(bus=node.bus_number) if d.has_cfg]
elif is_device(node):
devices = [node.ref]
if not len(devices):
return
runner = config_gui.ApplyDeviceConfigsRunner(
devices=devices,
parent_widget=self.mainwindow)
progress_dialog = config_gui.SubProgressDialog(title="Applying config to hardware")
runner.progress_changed.connect(progress_dialog.set_progress)
progress_dialog.canceled.connect(runner.close)
f = runner.start()
fo = future.FutureObserver(f)
fo.done.connect(progress_dialog.close)
progress_dialog.exec_()
if f.done() and f.exception() is not None:
log.error("Apply config: %s", f.exception())
QtGui.QMessageBox.critical(self.mainwindow, "Error", str(f.exception()))
def _apply_hardware_to_config(self):
# FIXME: this does not work for MRCs that have never been connected as
# the device list will be empty which will cause the runners generator
# to do nothing. Instead of specifying the devices here a list of MRCs
# could be passed and the list of devices would be built dynamically.
node = self._selected_tree_node
devices = None
if is_registry(node):
devices = [d for mrc in node.ref for d in mrc if d.has_hw]
elif is_mrc(node):
devices = [d for d in node.ref if d.has_hw]
elif is_bus(node):
devices = [d for d in node.parent.ref.get_devices(bus=node.bus_number) if d.has_hw]
elif is_device(node):
devices = [node.ref]
assert len(devices)
runner = config_gui.FillDeviceConfigsRunner(
devices=devices, parent_widget=self.mainwindow)
progress_dialog = config_gui.SubProgressDialog(title="Copying from hardware to config")
runner.progress_changed.connect(progress_dialog.set_progress)
progress_dialog.canceled.connect(runner.close)
f = runner.start()
fo = future.FutureObserver(f)
fo.done.connect(progress_dialog.close)
progress_dialog.exec_()
if f.done() and f.exception() is not None:
log.error("Fill config: %s", f.exception())
QtGui.QMessageBox.critical(self.mainwindow, "Error", str(f.exception()))
def _open_device_config(self):
device = self._selected_tree_node.ref
mrc = device.mrc
if gui_util.run_load_device_config(device=device, context=self.context,
parent_widget=self.mainwindow):
# If the device did not have a hardware model prior to loading the
# config it will have been completely removed and a new
# app_model.Device will have been created. Query the mrc to get the
# newly created Device.
device = mrc.get_device(device.bus, device.address)
# Select the hardware node before the config node to end up
# with focus in the config tree.
if self.linked_mode and not device.idc_conflict:
self.treeview.select_hardware_node_by_ref(device)
self.treeview.select_config_node_by_ref(device)
def _save_device_config(self):
device = self._selected_tree_node.ref
gui_util.run_save_device_config(device=device, context=self.context,
parent_widget=self.mainwindow)
def _edit_mrc_config(self):
gui_util.run_edit_mrc_config(mrc=self._selected_tree_node.ref,
registry=self.app_registry, parent_widget=self.mainwindow)
def _edit_device_config(self):
gui_util.run_edit_device_config(
device=self._selected_tree_node.ref,
registry=self.app_registry,
device_registry=self.context.device_registry,
parent_widget=self.mainwindow)
[docs] def quit(self):
"""Non-blocking method to quit the application. Needs a running event
loop."""
QtCore.QMetaObject.invokeMethod(self.mainwindow, "close", Qt.QueuedConnection)
def _on_subwindow_activated(self, window):
self._current_subwindow = window
self._update_actions()
if self._subwindow_toolbar is not None:
self.mainwindow.removeToolBar(self._subwindow_toolbar)
self._subwindow_toolbar = None
if isinstance(window, gui_util.DeviceSubWindow):
device = window.device
display_mode = window.display_mode
write_mode = window.write_mode
self.log.debug("_on_subwindow_activated: d=%s, has_hw=%s, has_cfg=%s, display_mode=%s, write_mode=%s",
device, device.has_hw, device.has_cfg,
util.RW_MODE_NAMES[display_mode], util.RW_MODE_NAMES[write_mode])
if display_mode & util.CONFIG:
self.treeview.select_config_node_by_ref(device)
elif display_mode & util.HARDWARE:
self.treeview.select_hardware_node_by_ref(device)
if hasattr(window, 'has_toolbar') and window.has_toolbar():
self._subwindow_toolbar = tb = window.get_toolbar()
tb.setIconSize(GUIApplication.TOOLBAR_ICON_SIZE)
font = tb.font()
font.setPixelSize(GUIApplication.TOOLBAR_FONT_SIZE)
tb.setFont(font)
tb.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
tb.setWindowTitle("Subwindow Toolbar")
tb.setObjectName("subwindow_toolbar")
self.mainwindow.addToolBar(tb)
tb.show()
[docs] def active_subwindow(self):
return self.mainwindow.mdiArea.activeSubWindow()
# Note: The toggled() signal is emitted on user action _and_ on
# setChecked() and similar calls. In contrast triggered() is only emitted
# on user action.
def _on_display_hw_triggered(self, b):
if b:
w = self.active_subwindow()
w.display_mode = util.HARDWARE
self._update_actions()
def _on_display_cfg_triggered(self, b):
if b:
w = self.active_subwindow()
w.display_mode = util.CONFIG
self._update_actions()
def _on_display_combined_triggered(self, b):
if b:
w = self.active_subwindow()
w.display_mode = util.COMBINED
self._update_actions()
def _on_write_hw_triggered(self, b):
if b:
w = self.active_subwindow()
w.write_mode = util.HARDWARE
self._update_actions()
def _on_write_cfg_triggered(self, b):
if b:
w = self.active_subwindow()
w.write_mode = util.CONFIG
self._update_actions()
def _on_write_combined_triggered(self, b):
if b:
w = self.active_subwindow()
w.write_mode = util.COMBINED
self._update_actions()
def _show_device_windows(self, device, show_cfg, show_hw):
"""Shows existing device windows. Return True if at least one window
was shown, False otherwise."""
if device not in self._device_window_map:
self.log.debug("No window for %s", device)
return False
def window_filter(window):
return ((show_cfg and window.display_mode & util.CONFIG)
or (show_hw and window.display_mode & util.HARDWARE))
windows = filter(window_filter, self._device_window_map[device])
self.log.debug("Found %d windows for %s", len(windows), device)
for subwin in windows:
if subwin.isMinimized():
subwin.showNormal()
self.mainwindow.mdiArea.setActiveSubWindow(subwin)
return len(windows) > 0
def _close_device_windows(self, device):
for window in copy.copy(self._device_window_map.get(device, set())):
window.close()
def _tree_node_activated(self, node):
if is_device_hw(node) and node.ref.has_hw and node.ref.address_conflict:
return
if is_device(node):
device = node.ref
self._show_or_create_device_window(device,
is_device_cfg(node), is_device_hw(node))
def _show_or_create_device_window(self, device, from_config_side, from_hw_side):
self.log.debug("_show_or_create_device_window: device=%s, cfg_side=%s, hw_side=%s",
device, from_config_side, from_hw_side)
if self._show_device_windows(device, from_config_side, from_hw_side):
return
try:
module = device.module
except IDCConflict:
module = device.cfg_module if from_config_side else device.hw_module
self.log.debug("_show_or_create_device_window: using module %s", module)
if module.has_widget_class():
self._create_device_widget_window(device, from_config_side, from_hw_side)
else:
self._create_device_table_window(device, from_config_side, from_hw_side)
def _create_device_table_window(self, app_device, from_config_side, from_hw_side):
self.log.debug("_create_device_table_window: device=%s, cfg_side=%s, hw_side=%s, linked_mode=%s",
app_device, from_config_side, from_hw_side, self.linked_mode)
if self.linked_mode and not app_device.has_cfg:
if not self._run_create_config(app_device):
return
if self.linked_mode and not app_device.idc_conflict:
display_mode = write_mode = util.COMBINED
elif from_config_side:
display_mode = write_mode = util.CONFIG
elif from_hw_side:
display_mode = write_mode = util.HARDWARE
subwin = self._add_device_table_window(app_device, display_mode, write_mode)
if subwin.isMinimized():
subwin.showNormal()
def _create_device_widget_window(self, app_device, from_config_side, from_hw_side):
self.log.debug("_create_device_widget_window: device=%s, cfg_side=%s, hw_side=%s, linked_mode=%s",
app_device, from_config_side, from_hw_side, self.linked_mode)
if self.linked_mode and not app_device.has_cfg:
if not self._run_create_config(app_device):
return
if self.linked_mode and not app_device.idc_conflict:
if app_device.has_hw and app_device.has_cfg:
write_mode = util.COMBINED
read_mode = util.CONFIG if from_config_side else util.HARDWARE
elif app_device.has_hw:
write_mode = read_mode = util.HARDWARE
else:
write_mode = read_mode = util.CONFIG
else:
write_mode = read_mode = util.CONFIG if from_config_side else util.HARDWARE
subwin = self._add_device_widget_window(app_device, read_mode, write_mode)
if subwin.isMinimized():
subwin.showNormal()
[docs] def get_mainwindow(self):
return self._mainwindow()
[docs] def set_linked_mode(self, linked_mode):
if self._linked_mode == linked_mode:
return
self._linked_mode = bool(linked_mode)
self.treeview.linked_mode = self.linked_mode
self._previous_tree_node = self._selected_tree_node = self._selected_device = None
self._update_actions()
for device, window_list in self._device_window_map.iteritems():
# Use a copy of window_list here as closing windows will modify the
# original list.
for window in list(window_list):
try:
if linked_mode and window.has_combined_display() and not device.idc_conflict:
window.display_mode = util.COMBINED
elif not linked_mode:
if util.COMBINED in (window.display_mode, window.write_mode):
window.close()
window.linked_mode = linked_mode
except AttributeError:
pass
[docs] def get_linked_mode(self):
return self._linked_mode
mainwindow = property(get_mainwindow)
app_registry = property(lambda self: self.context.app_registry)
device_registry = property(lambda self: self.context.device_registry)
linked_mode = property(get_linked_mode, set_linked_mode)
def _hw_mrc_added(self, mrc):
self.log.debug("hw mrc added: %s", mrc.url)
mrc.connecting.connect(partial(self._hw_mrc_connecting, mrc=mrc))
mrc.connected.connect(self._update_actions)
mrc.disconnected.connect(partial(self._hw_mrc_disconnected, mrc=mrc))
def _hw_mrc_connecting(self, f, mrc):
self.logview.append("Connecting to %s" % mrc.get_display_url())
fo = future.FutureObserver()
def done(f, fo=fo):
try:
f.result()
self.logview.append("Connected to %s" % mrc.get_display_url())
except Exception as e:
self.logview.append("Error connecting to %s: %s" % (mrc.get_display_url(), e))
fo.deleteLater()
self._update_actions()
def progress_text_changed(txt):
self.logview.append("%s: %s" % (mrc.get_display_url(), txt))
f.add_done_callback(done)
fo.set_future(f)
fo.progress_text_changed.connect(progress_text_changed)
def _hw_mrc_disconnected(self, mrc):
self.logview.append("Disconnected from %s" % mrc.get_display_url())
self._update_actions()
# Device table window creation
def _add_device_table_window(self, device, display_mode, write_mode):
self.log.debug("Adding device table for %s with display_mode=%s, write_mode=%s",
device,
util.RW_MODE_NAMES[display_mode],
util.RW_MODE_NAMES[write_mode])
widget = device_tableview.DeviceTableWidget(device, display_mode, write_mode)
subwin = gui_util.DeviceTableSubWindow(widget=widget)
subwin.set_linked_mode(self.linked_mode)
return self._register_device_subwindow(subwin)
def _add_device_widget_window(self, app_device, display_mode, write_mode):
self.log.debug("Adding device widget for %s with display_mode=%s, write_mode=%s",
app_device,
util.RW_MODE_NAMES[display_mode],
util.RW_MODE_NAMES[write_mode])
widget = app_device.make_device_widget(display_mode, write_mode,
make_settings=self.context.make_qsettings)
subwin = gui_util.DeviceWidgetSubWindow(widget=widget)
subwin.set_linked_mode(self.linked_mode)
return self._register_device_subwindow(subwin)
def _register_device_subwindow(self, subwin):
self.log.debug("registering subwin %s for device %s", subwin, subwin.device)
self.mainwindow.mdiArea.addSubWindow(subwin)
subwin.installEventFilter(self)
gui_util.restore_subwindow_state(subwin, self.context.make_qsettings())
subwin.show()
self._device_window_map.setdefault(subwin.device, set()).add(subwin)
return subwin
def _cfg_context_menu(self, node, idx, pos, view):
menu = QtGui.QMenu()
def add_action(action):
if action.isEnabled():
menu.addAction(action)
if is_setup(node):
add_action(self.actions['open_setup'])
add_action(self.actions['save_setup'])
add_action(self.actions['save_setup_as'])
add_action(self.actions['close_setup'])
menu.addSeparator()
add_action(self.actions['add_config'])
if is_mrc(node):
add_action(self.actions['rename_config'])
add_action(self.actions['add_config'])
add_action(self.actions['remove_config'])
menu.addSeparator()
add_action(self.actions['edit_mrc_config'])
if is_bus(node):
add_action(self.actions['add_config'])
if is_device(node):
add_action(self.actions['open_device_widget'])
add_action(self.actions['open_device_table'])
add_action(self.actions['rename_config'])
menu.addSeparator()
add_action(self.actions['open_device_config'])
if node.ref.has_cfg:
add_action(self.actions['save_device_config'])
add_action(self.actions['remove_config'])
if self.actions['add_config'].isEnabled():
add_action(self.actions['add_config'])
#add_action(self.actions['show_device_extensions'])
menu.addSeparator()
add_action(self.actions['edit_device_config'])
if not menu.isEmpty():
menu.exec_(view.mapToGlobal(pos))
def _hw_context_menu(self, node, idx, pos, view):
menu = QtGui.QMenu()
def add_action(action):
if action.isEnabled():
menu.addAction(action)
if is_registry(node):
add_action(self.actions['add_mrc_connection'])
add_action(self.actions['refresh'])
if is_mrc(node):
add_action(self.actions['connect_disconnect'])
add_action(self.actions['refresh'])
menu.addSeparator()
mrc = node.ref
if mrc.hw is not None and hasattr(mrc.hw.connection, 'server'):
add_action(self.actions['view_server_log'])
add_action(self.actions['remove_mrc_connection'])
if is_bus(node):
add_action(self.actions['refresh'])
if is_device(node):
add_action(self.actions['open_device_widget'])
add_action(self.actions['open_device_table'])
add_action(self.actions['toggle_rc'])
add_action(self.actions['refresh'])
#add_action(self.actions['show_device_extensions'])
if not menu.isEmpty():
menu.exec_(view.mapToGlobal(pos))
[docs] def eventFilter(self, watched_object, event):
if (event.type() == QtCore.QEvent.Close
and isinstance(watched_object, QtGui.QMdiSubWindow)):
self.log.debug("CloseEvent for %s", watched_object)
if self._current_subwindow is watched_object:
self._current_subwindow = None
gui_util.store_subwindow_state(watched_object, self.context.make_qsettings())
if (hasattr(watched_object, 'device')
and watched_object.device in self._device_window_map):
# Remove the subwindow from the set of device windows
self.log.debug("removing subwin %s for device %s", watched_object, watched_object.device)
self._device_window_map[watched_object.device].remove(watched_object)
elif (event.type() == QtCore.QEvent.Close
and watched_object is self.mainwindow):
self.log.debug("CloseEvent for mainwindow")
if not gui_util.run_close_setup(self.context, self.mainwindow):
event.ignore()
return True
return False
def _show_device_extensions(self):
node = self._selected_tree_node
self._create_device_extension_window(
self._selected_device,
is_device_cfg(node),
is_device_hw(node))
def _create_device_extension_window(self, app_device, from_config_side, from_hw_side):
from pyqtgraph import parametertree as pt
tree = widget = pt.ParameterTree()
subwin = QtGui.QMdiSubWindow()
subwin.setWidget(widget)
self.mainwindow.mdiArea.addSubWindow(subwin)
subwin.show()
device = app_device.cfg if from_config_side else app_device.hw
profile = app_device.cfg_profile if from_config_side else app_device.hw_profile
extensions = device.get_extensions()
print "extensions:", extensions
extensions_param = extensions_to_ptree(extensions, profile)
extensions_param.sigTreeStateChanged.connect(on_tree_state_changed)
tree.setParameters(extensions_param, showTop=False)
def _show_quickstart(self):
subwin = self.mainwindow.findChild(QtGui.QMdiSubWindow, "quickstart")
if subwin:
subwin.widget().show()
subwin.raise_()
subwin.showNormal()
return
subwin = QtGui.QMdiSubWindow()
subwin.setWidget(gui_tutorial.TutorialWidget(self))
subwin.setWindowTitle("Quickstart")
subwin.setObjectName("quickstart")
subwin.setWindowIcon(util.make_icon(":/window-icon.png"))
subwin.resize(QtCore.QSize(600, 400))
self.mainwindow.mdiArea.addSubWindow(subwin)
subwin.show()
[docs]def on_tree_state_changed(emitting_param, changes):
print "on_tree_state_changed"
print "changes:", changes
for param, change, value in changes:
print param
print change
print value
print emitting_param.childPath(param)
print "=" * 15
[docs]def extensions_to_ptree(extensions, device_profile):
from pyqtgraph import parametertree as pt
def list2param(name, value):
ret = pt.Parameter.create(name=name, type='group')
ret.type = type(list())
for idx, val in enumerate(value):
ret.addChild(value2param(name=str(idx), value=val))
return ret
def dict2param(name, value):
ret = pt.Parameter.create(name=name, type='group')
ret.type = type(dict())
for k, v in value.iteritems():
ret.addChild(value2param(name=str(k), value=v))
return ret
def value2param(name, value):
log = logging.getLogger(__name__)
log.warning("value2param n=%s, v=%s", name, value)
try:
ext_profile = device_profile.get_extension(name)
log.warning("ext_profile from device_profile")
except KeyError:
ext_profile = dict(name=name)
log.warning("ext_profile from dict")
log.warning("ext_profile=%s", ext_profile)
print ext_profile
#if 'values' in ext_profile:
# log.warning(value)
# log.warning(ext_profile)
# return pt.Parameter.create(value=value, type='list', **ext_profile)
if isinstance(value, str):
log.warning("str")
return pt.Parameter.create(type='str', **ext_profile)
elif isinstance(value, int):
log.warning("int")
return pt.Parameter.create(type='int', **ext_profile)
elif isinstance(value, float):
log.warning("float")
return pt.Parameter.create(type='float', **ext_profile)
elif isinstance(value, list):
log.warning("list2param %s=%s", name, value)
return list2param(name, value)
elif isinstance(value, dict):
log.warning("dict2param %s=%s", name, value)
return dict2param(name, value)
else:
raise TypeError("value2xml: unhandled value type '%s'" % type(value).__name__)
ret = pt.Parameter.create(name='root', type='group')
ret.sigTreeStateChanged.connect(on_tree_state_changed)
log.warning("value2param: exts=%s %s", type(extensions), extensions)
for name, value in extensions.iteritems():
log.warning("value2param iteration: n=%s, v=%s", name, value)
param = value2param(name, value)
ret.addChild(param)
return ret