Source code for ccpn.ui.gui.popups.ShortcutsPopup

#=========================================================================================
# Licence, Reference and Credits
#=========================================================================================
__copyright__ = "Copyright (C) CCPN project (http://www.ccpn.ac.uk) 2014 - 2021"
__credits__ = ("Ed Brooksbank, Luca Mureddu, Timothy J Ragan & Geerten W Vuister")
__licence__ = ("CCPN licence. See http://www.ccpn.ac.uk/v3-software/downloads/license")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124, http://doi.org/10.1007/s10858-016-0060-y")
#=========================================================================================
# Last code modification
#=========================================================================================
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$dateModified: 2021-02-04 12:07:36 +0000 (Thu, February 04, 2021) $"
__version__ = "$Revision: 3.0.3 $"
#=========================================================================================
# Created
#=========================================================================================
__author__ = "$Author: CCPN $"
__date__ = "$Date: 2017-04-07 10:28:41 +0000 (Fri, April 07, 2017) $"
#=========================================================================================
# Start of code
#=========================================================================================

import os
from functools import reduce, partial
from PyQt5 import QtWidgets
from ccpn.ui.gui.widgets.Frame import ScrollableFrame, Frame
from ccpn.ui.gui.widgets.Button import Button
from ccpn.ui.gui.widgets.ButtonList import ButtonList
from ccpn.ui.gui.widgets.FileDialog import MacrosFileDialog
from ccpn.ui.gui.widgets.Label import Label
from ccpn.ui.gui.widgets.LineEdit import LineEdit
from ccpn.ui.gui.widgets.MessageDialog import showWarning
from ccpn.ui.gui.widgets.ScrollArea import ScrollArea
from ccpn.ui.gui.popups.Dialog import CcpnDialog, CcpnDialogMainWidget
from ccpn.util.Logging import getLogger


[docs]class ShortcutsPopup(CcpnDialogMainWidget): USESCROLLWIDGET = True def __init__(self, parent=None, mainWindow=None, title='Define User Shortcuts', **kwds): super().__init__(parent, setLayout=True, windowTitle=title, **kwds) # define the common attributes self.mainWindow = mainWindow self.application = self.mainWindow.application self.preferences = self.application.preferences self.shortcutWidget = ShortcutWidget(self.mainWidget, mainWindow=mainWindow, setLayout=True, grid=(0, 0)) # ejb self.setMinimumSize(400, 400) # ButtonList(self, grid=(1, 1), # texts=['Cancel', 'Save', 'Save and Close'], # callbacks=[self.close, self.save, self.saveAndQuit]) # add close/save/saveAndClose buttons (close/apply/ok) self.setCloseButton(callback=self.close) self.setApplyButton(callback=self.save, text='Save') self.setOkButton(callback=self.saveAndQuit, text='Save and Close') self.setDefaultButton(CcpnDialogMainWidget.CLOSEBUTTON) # make the buttons appear self._setButtons()
[docs] def save(self): newShortcuts = self.shortcutWidget.getShortcuts() self.preferences.shortcuts = newShortcuts if hasattr(self.application, '_userShortcuts') and self.application._userShortcuts: for shortcut in newShortcuts: self.application._userShortcuts.addUserShortcut(shortcut, newShortcuts[shortcut])
[docs] def saveAndQuit(self): self.save() self.close()
[docs]class ShortcutWidget(Frame): def __init__(self, parent, mainWindow=None, setLayout=True, **kwds): super().__init__(parent, setLayout=setLayout, **kwds) from functools import partial self.mainWindow = mainWindow self.application = self.mainWindow.application self.preferences = self.application.preferences i = 0 self.widgets = [] for shortcut in sorted(self.preferences.shortcuts): shortcutLabel = Label(self, grid=(i, 0), text=shortcut) self.shortcutLineEdit = LineEdit(self, grid=(i, 1)) self.shortcutLineEdit.setText(self.preferences.shortcuts[shortcut]) self.shortcutLineEdit.editingFinished.connect(partial(self.validateFunction, i)) pathButton = Button(self, grid=(i, 2), icon='icons/applications-system', callback=partial(self._getMacroFile, i)) self.widgets.append([shortcutLabel, self.shortcutLineEdit, pathButton]) i += 1
[docs] def getShortcuts(self): shortcutDict = {} layout = self.layout() for i in range(layout.rowCount()): shortcut = layout.itemAtPosition(i, 0).widget().text() function = layout.itemAtPosition(i, 1).widget().text() shortcutDict[shortcut] = function return shortcutDict
def _addToFirstAvailableShortCut(self, filePath): command = 'runMacro("%s")' %filePath layout = self.layout() if command not in self.getShortcuts().values(): for i in range(layout.rowCount()): functionWidget = layout.itemAtPosition(i, 1).widget() if functionWidget.get() == '': functionWidget.set(command) return def _getMacroFile(self, index): shortcutLineEdit = self.widgets[index][1] if os.path.exists('/'.join(shortcutLineEdit.text().split('/')[:-1])): currentDirectory = '/'.join(shortcutLineEdit.text().split('/')[:-1]) else: currentDirectory = os.path.expanduser(self.preferences.general.userMacroPath) dialog = MacrosFileDialog(parent=self, acceptMode='select', directory=currentDirectory) dialog._show() directory = dialog.selectedFiles() if directory and len(directory) > 0: shortcutLineEdit.setText('runMacro("%s")' % directory[0])
[docs] def validateFunction(self, i): # check if function for shortcut is a .py file or exists in the CcpNmr V3 namespace path = self.widgets[i][1].text() namespace = self.mainWindow.namespace if path == '': return if path.startswith('/'): if os.path.exists(path) and path.split('/')[-1].endswith('.py'): print('pathValid') return True elif not os.path.exists(path) or not path.split('/')[-1].endswith('.py'): if not os.path.exists(path): showWarning('Invalid macro path', 'Macro path: %s is not a valid path' % path) return False if not path.split('/')[-1].endswith('.py'): showWarning('Invalid macro file', 'Macro files must be valid python files and end in .py' % path) return False else: stub = namespace.get(path.split('.')[0]) if not stub: showWarning('Invalid function', 'Function: %s is not a valid CcpNmr function' % path) return False else: try: reduce(getattr, path.split('.')[1:], stub) return True except: showWarning('Invalid function', 'Function: %s is not a valid CcpNmr function' % path) return False
[docs]class UserShortcuts(): def __init__(self, mainWindow=None): self.mainWindow = mainWindow self.namespace = self.mainWindow.namespace self._userShortcutFunctions = {} self._numUserShortcutFunctions = 0
[docs] def addUserShortcut(self, funcName, funcStr): self._userShortcutFunctions[funcName] = funcStr
[docs] def runUserShortcut(self, funcStr): if funcStr in self._userShortcutFunctions: function = self._userShortcutFunctions[funcStr] if funcStr and function: if function.split('(')[0] == 'runMacro': func = partial(self.namespace['runMacro'], function.split('(')[1].split(')')[0]) if func: getLogger().info(function) try: func() except: getLogger().warning('Error executing macro: %s ' % function) # QtWidgets.QShortcut(QtGui.QKeySequence("%s, %s" % (shortcut[0], shortcut[1])), # self, partial(self.namespace['runMacro'], function.split('(')[1].split(')')[0]), # context=context) else: stub = self.namespace.get(function.split('.')[0]) func = reduce(getattr, function.split('.')[1:], stub) if func: getLogger().info(function) try: func() except: getLogger().warning('Error executing user shortcut: %s ' % function)
# QtWidgets.QShortcut(QtGui.QKeySequence("%s, %s" % (shortcut[0], shortcut[1])), self, # reduce(getattr, function.split('.')[1:], stub), context=context)