From 3c82abc806ba9a4d93e9608e9cf1d801058fec1f Mon Sep 17 00:00:00 2001 From: Lukasz Migas Date: Mon, 20 Nov 2017 01:28:38 +0000 Subject: [PATCH] Updated to 1.0.5 - fixed issues where you couldn't load multiple manual CIU dataset simultaneously - fixed some issues with paths - fixed issues where you couldn't change the header/footnote information for individual plots in the interactive output panel - added colorbar option to the interactive output panel --- ORIGAMI_ANALYSE/source/NoZoomSpan.py | 17 + ORIGAMI_ANALYSE/source/ORIGAMI.py | 51 ++- ORIGAMI_ANALYSE/source/ZoomBox.py | 17 + ORIGAMI_ANALYSE/source/_compileScript.py | 44 +- ORIGAMI_ANALYSE/source/analysisFcns.py | 17 + ORIGAMI_ANALYSE/source/bokeh3D.py | 137 ++++++ ORIGAMI_ANALYSE/source/ids.py | 2 + ORIGAMI_ANALYSE/source/mainWindow.py | 4 +- ORIGAMI_ANALYSE/source/origamiConfig.py | 63 ++- ORIGAMI_ANALYSE/source/panelControls.py | 21 +- ORIGAMI_ANALYSE/source/panelDocumentTree.py | 12 +- ORIGAMI_ANALYSE/source/panelMultipleML.py | 16 +- ORIGAMI_ANALYSE/source/panelOutput.py | 457 ++++++++++++++++---- ORIGAMI_ANALYSE/source/plot1d.py | 17 + ORIGAMI_ANALYSE/source/plot2d.py | 17 + ORIGAMI_ANALYSE/source/plot3d.py | 17 + ORIGAMI_ANALYSE/source/plot_MSandCIU.py | 17 + ORIGAMI_ANALYSE/source/plottingWindow.py | 17 + ORIGAMI_ANALYSE/source/toolbox.py | 24 + 19 files changed, 839 insertions(+), 128 deletions(-) create mode 100644 ORIGAMI_ANALYSE/source/bokeh3D.py diff --git a/ORIGAMI_ANALYSE/source/NoZoomSpan.py b/ORIGAMI_ANALYSE/source/NoZoomSpan.py index 8f3d9f5d..32cbe6be 100644 --- a/ORIGAMI_ANALYSE/source/NoZoomSpan.py +++ b/ORIGAMI_ANALYSE/source/NoZoomSpan.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from matplotlib.transforms import blended_transform_factory from ZoomBox import * diff --git a/ORIGAMI_ANALYSE/source/ORIGAMI.py b/ORIGAMI_ANALYSE/source/ORIGAMI.py index 8effc57c..40ef9e47 100644 --- a/ORIGAMI_ANALYSE/source/ORIGAMI.py +++ b/ORIGAMI_ANALYSE/source/ORIGAMI.py @@ -1,4 +1,20 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + # Import libraries import wx.py as py import wx @@ -110,6 +126,7 @@ def init(self, *args, **kwargs): # Load protein/CCS database self.onImportCCSDatabase(evt=None, onStart=True) self.onCheckVersion(evt=None) + # Log all events to if self.logging == True: sys.stdin = self.view.panelPlots.log @@ -381,14 +398,19 @@ def onThreading(self, evt, args, action='loadOrigami'): # Setup thread if action == 'loadOrigami': th = threading.Thread(target=self.onLoadOrigamiDataThreaded, args=(args,evt)) + elif action == 'saveFigs': target, path, kwargs = args th = threading.Thread(target=target.saveFigure2, args=(path,), kwargs=kwargs) + elif action == 'extractIons': th = threading.Thread(target=self.onExtract2DimsOverMZrangeMultipleThreaded, args=(evt,)) # Start thread - th.start() + try: + th.start() + except: + print('exception') def onOrigamiRawDirectory(self, evt): self.config.ciuMode = 'ORIGAMI' @@ -798,9 +820,16 @@ def onExtract2DimsOverMZrangeMultipleThreaded(self, evt): # Check if manual dataset elif self.docs.dataType == 'Type: MANUAL': # Shortcut to the file list - nameList = self.view.panelMML.topP.filelist # List with MassLynx file information + nameList = self.view.panelMML.topP.filelist # List with MassLynx file information + # Sort data regardless of what user did + self.view.panelMML.topP.OnSortByColumn(column=1, overrideReverse=True) tempDict = {} for item in xrange(nameList.GetItemCount()): + # Determine whether the title of the document matches the title of the item in the table + # if it does not, skip the row + docValue = nameList.GetItem(item,2).GetText() + if docValue != self.docs.title: continue + nameValue = nameList.GetItem(item,0).GetText() pathValue = self.docs.multipleMassSpectrum[nameValue]['path'] @@ -834,8 +863,11 @@ def onExtract2DimsOverMZrangeMultipleThreaded(self, evt): counter = 0 # needed to start off xlabelsActual = [] for item in xrange(nameList.GetItemCount()): + # Determine whether the title of the document matches the title of the item in the table + # if it does not, skip the row + docValue = nameList.GetItem(item,2).GetText() + if docValue != self.docs.title: continue key = nameList.GetItem(item,0).GetText() -# print(key, self.docs.multipleMassSpectrum[key]['trap']) if counter == 0: tempArray = tempDict[key][0] xLabelLow = self.docs.multipleMassSpectrum[key]['trap'] # first iteration so first value @@ -5154,7 +5186,8 @@ def onPlot2DIMS2(self, zvals, xvals, yvals, xlabel, ylabel, xlabel=xlabel, labelsY=yvals, interpolation=self.config.interpolation, ylabel=ylabel, title="", - cmap=cmap, cmapNorm=cmapNorm, + cmap=cmap, + cmapNorm=cmapNorm, colorbar=self.config.colorbar, axesSize=self.config.plotSizes['2D'], plotName='2D') @@ -6333,6 +6366,14 @@ def onCheckVersion(self, evt): except: print('Could not check version number') + def onOpenUserGuide(self, evt): + """ + Opens PDF viewer + """ + try: + os.startfile('UserGuide_ANALYSE.pdf') + except: + return if __name__ == '__main__': diff --git a/ORIGAMI_ANALYSE/source/ZoomBox.py b/ORIGAMI_ANALYSE/source/ZoomBox.py index 398ccef2..e9347afc 100644 --- a/ORIGAMI_ANALYSE/source/ZoomBox.py +++ b/ORIGAMI_ANALYSE/source/ZoomBox.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from matplotlib.patches import Rectangle from wx.lib.pubsub import setupkwargs from wx.lib.pubsub import pub diff --git a/ORIGAMI_ANALYSE/source/_compileScript.py b/ORIGAMI_ANALYSE/source/_compileScript.py index 2857c800..a8816457 100644 --- a/ORIGAMI_ANALYSE/source/_compileScript.py +++ b/ORIGAMI_ANALYSE/source/_compileScript.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + import sys import zipfile from distutils.core import setup @@ -16,7 +33,7 @@ from shutil import copy sys.setrecursionlimit(5000) -version = '1.0.4' +version = '1.0.5' # Compile program using: # python compileScript.py py2exe @@ -35,7 +52,7 @@ def numpy_dll_paths_fix(): numpy_dll_paths_fix() current_dir = os.path.dirname(os.path.realpath(__file__)) -dist_dir = os.path.join(current_dir, "ORIGAMI_ANALYSE_v1.0.4") +dist_dir = os.path.join(current_dir, "ORIGAMI_ANALYSE_v1.0.5") # py2exe options @@ -45,13 +62,22 @@ def numpy_dll_paths_fix(): excludes = ["Tkconstants","Tkinter","tcl","Qt","PyQt4", "pywin", "pywin.debugger", "pywin.debugger.dbgcon", "pywin.dialogs", "pywin.dialogs.list","h5py","babel", - "pdb", '_ssl','Qt5','nbconvert','IPython'], + "pdb", '_ssl','Qt5','nbconvert','IPython', + #'scipy.sparse.linalg.eigen.arpack', + #'scipy.sparse.linalg.dsolve', + ], includes = ["matplotlib.backends.backend_qt5agg", "PyQt5", - "scipy.sparse.csgraph._validation", "scipy.linalg.cython_blas", - "scipy.linalg.*","scipy.integrate", "scipy.special", "scipy", - "scipy.special._ufuncs_cxx", "scipy.special.*", - "scipy.special._ufuncs","scipy.stats", - "email.mime.*", "jinja2", + "scipy.sparse.csgraph._validation", + "scipy.linalg.cython_blas", + "scipy.linalg.*","scipy.integrate", + "scipy.special", + "scipy", + "scipy.special._ufuncs_cxx", + "scipy.special.*", + "scipy.special._ufuncs", + "scipy.stats", + "email.mime.*", + "jinja2", "bokeh.core", "bokeh.events"], packages = ["wx.lib.pubsub", "pkg_resources"], dll_excludes=['msvcr71.dll','hdf5.dll'], @@ -62,7 +88,7 @@ def numpy_dll_paths_fix(): # main setup setup(name = "ORIGAMI", - version = '1.0.4', + version = '1.0.5', description = "ORIGAMI - A Software Suite for Activated Ion Mobility Mass Spectrometry ", author = "Lukasz G. Migas", url = 'https://www.click2go.umip.com/i/s_w/ORIGAMI.html', diff --git a/ORIGAMI_ANALYSE/source/analysisFcns.py b/ORIGAMI_ANALYSE/source/analysisFcns.py index 85a0fce1..85ffc69f 100644 --- a/ORIGAMI_ANALYSE/source/analysisFcns.py +++ b/ORIGAMI_ANALYSE/source/analysisFcns.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + import os.path from subprocess import call,Popen import numpy as np diff --git a/ORIGAMI_ANALYSE/source/bokeh3D.py b/ORIGAMI_ANALYSE/source/bokeh3D.py new file mode 100644 index 00000000..4d0db6e2 --- /dev/null +++ b/ORIGAMI_ANALYSE/source/bokeh3D.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + +# -*- encoding: utf-8 -*- + +JS_CODE = """ +# This file contains the JavaScript (CoffeeScript) implementation +# for a Bokeh custom extension. The "surface3d.py" contains the +# python counterpart. +# +# This custom model wraps one part of the third-party vis.js library: +# +# http://visjs.org/index.html +# +# Making it easy to hook up python data analytics tools (NumPy, SciPy, +# Pandas, etc.) to web presentations using the Bokeh server. + +# These "require" lines are similar to python "import" statements +import * as p from "core/properties" +import {LayoutDOM, LayoutDOMView} from "models/layouts/layout_dom" + +# This defines some default options for the Graph3d feature of vis.js +# See: http://visjs.org/graph3d_examples.html for more details. +OPTIONS = + width: '800px' + height: '800px' + style: 'surface' + showPerspective: true + showGrid: true + keepAspectRatio: true + verticalRatio: 1.0 + legendLabel: 'stuff' + cameraPosition: + horizontal: -0.35 + vertical: 0.22 + distance: 1.8 + +# To create custom model extensions that will render on to the HTML canvas +# or into the DOM, we must create a View subclass for the model. Currently +# Bokeh models and views are based on BackBone. More information about +# using Backbone can be found here: +# +# http://backbonejs.org/ +# +# In this case we will subclass from the existing BokehJS ``LayoutDOMView``, +# corresponding to our +export class Surface3dView extends LayoutDOMView + + initialize: (options) -> + super(options) + + url = "https://cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.js" + + script = document.createElement('script') + script.src = url + script.async = false + script.onreadystatechange = script.onload = () => @_init() + document.querySelector("head").appendChild(script) + + _init: () -> + # Create a new Graph3s using the vis.js API. This assumes the vis.js has + # already been loaded (e.g. in a custom app template). In the future Bokeh + # models will be able to specify and load external scripts automatically. + # + # Backbone Views create
elements by default, accessible as @el. Many + # Bokeh views ignore this default
, and instead do things like draw + # to the HTML canvas. In this case though, we use the
to attach a + # Graph3d to the DOM. + @_graph = new vis.Graph3d(@el, @get_data(), OPTIONS) + + # Set Backbone listener so that when the Bokeh data source has a change + # event, we can process the new data + @connect(@model.data_source.change, () => + @_graph.setData(@get_data()) + ) + + # This is the callback executed when the Bokeh data has an change. Its basic + # function is to adapt the Bokeh data source to the vis.js DataSet format. + get_data: () -> + data = new vis.DataSet() + source = @model.data_source + for i in [0...source.get_length()] + data.add({ + x: source.get_column(@model.x)[i] + y: source.get_column(@model.y)[i] + z: source.get_column(@model.z)[i] + style: source.get_column(@model.color)[i] + }) + return data + +# We must also create a corresponding JavaScript Backbone model sublcass to +# correspond to the python Bokeh model subclass. In this case, since we want +# an element that can position itself in the DOM according to a Bokeh layout, +# we subclass from ``LayoutDOM`` +export class Surface3d extends LayoutDOM + + # This is usually boilerplate. In some cases there may not be a view. + default_view: Surface3dView + + # The ``type`` class attribute should generally match exactly the name + # of the corresponding Python class. + type: "Surface3d" + + # The @define block adds corresponding "properties" to the JS model. These + # should basically line up 1-1 with the Python model class. Most property + # types have counterparts, e.g. ``bokeh.core.properties.String`` will be + # ``p.String`` in the JS implementatin. Where the JS type system is not yet + # as rich, you can use ``p.Any`` as a "wildcard" property type. + @define { + x: [ p.String ] + y: [ p.String ] + z: [ p.String ] + color: [ p.String ] + data_source: [ p.Instance ] + } +""" + +def prepJSCODE(self, parameters): + """ + This function produces a specific JS file for generation of 3D files + """ + print() + diff --git a/ORIGAMI_ANALYSE/source/ids.py b/ORIGAMI_ANALYSE/source/ids.py index 6d80cb3f..36dcc2bb 100644 --- a/ORIGAMI_ANALYSE/source/ids.py +++ b/ORIGAMI_ANALYSE/source/ids.py @@ -7,6 +7,8 @@ ID_about = ID_ABOUT ID_quickDisplayDocument = NewId() +ID_helpGuideLocal = NewId() + ID_saveAllDocuments = NewId() ID_selectCalibrant = NewId() ID_selectProtein = NewId() diff --git a/ORIGAMI_ANALYSE/source/mainWindow.py b/ORIGAMI_ANALYSE/source/mainWindow.py index d9db7d5b..3f374735 100644 --- a/ORIGAMI_ANALYSE/source/mainWindow.py +++ b/ORIGAMI_ANALYSE/source/mainWindow.py @@ -296,7 +296,8 @@ def makeMenubar(self): # menuHelp.Append(ID_RESET_ORIGAMI, 'Reinitilise ORIGAMI') menuHelp.Append(ID_RESET_PLOT_ZOOM, 'Reset Zoom Tool\tF12') menuHelp.AppendSeparator() - menuHelp.Append(ID_helpGuide, 'User Guide... (web)\tF1') + menuHelp.Append(ID_helpGuide, 'Open User Guide... (web)\tF1') + menuHelp.Append(ID_helpGuideLocal, 'Open User Guide... (local)\tF2') menuHelp.AppendItem(makeMenuItem(parent=menuHelp, id=ID_helpYoutube, text='Check out video guides... ', bitmap=self.icons.iconsLib['youtube16'])) @@ -317,6 +318,7 @@ def makeMenubar(self): # HELP MENU self.Bind(wx.EVT_MENU, self.onHelpAbout, id=ID_SHOW_ABOUT) self.Bind(wx.EVT_MENU, self.presenter.onLibraryLink, id=ID_helpGuide) + self.Bind(wx.EVT_MENU, self.presenter.onOpenUserGuide, id=ID_helpGuideLocal) self.Bind(wx.EVT_MENU, self.presenter.onLibraryLink, id=ID_helpCite) self.Bind(wx.EVT_MENU, self.presenter.onLibraryLink, id=ID_helpYoutube) self.Bind(wx.EVT_MENU, self.presenter.onLibraryLink, id=ID_helpNewVersion) diff --git a/ORIGAMI_ANALYSE/source/origamiConfig.py b/ORIGAMI_ANALYSE/source/origamiConfig.py index 815ec8b5..01ea1871 100644 --- a/ORIGAMI_ANALYSE/source/origamiConfig.py +++ b/ORIGAMI_ANALYSE/source/origamiConfig.py @@ -1,4 +1,20 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + import os import numpy as np import matplotlib.cm as cm @@ -132,7 +148,6 @@ def __init__(self): self.plot2Dtype = 'contour' # contour/imshow self.visible = True - class OrigamiConfig: def __init__(self): @@ -140,7 +155,7 @@ def __init__(self): Initilize config """ - self.version = "1.0.4" + self.version = "1.0.5" self.links = {'home' : 'https://www.click2go.umip.com/i/s_w/ORIGAMI.html', 'github' : 'https://github.com/lukasz-migas/ORIGAMI', 'cite' : 'https://doi.org/10.1016/j.ijms.2017.08.014', @@ -501,7 +516,9 @@ def initilizeConfig(self): #=============================================================================== # # Interactive parameters #=============================================================================== - + + self.saveInteractiveOverride = True + self.figHeight = 600 self.figWidth = 600 self.figHeight1D = 300 @@ -513,12 +530,31 @@ def initilizeConfig(self): self.defNumColumns = '' self.linkXYaxes = True self.hoverVline = True + self.colorbarInteractive = False self.toolsLocation = 'right' self.activeDrag = 'Box Zoom' self.activeWheel = 'None' self.activeInspect = 'Hover' + + # Colorbar self.colorbarInteractive = False + self.colorbarPrecision = 1 + self.colorbarLabelOffset = 15 + self.colorbarUseScientific = False + self.colorbarLocation = 'right' + self.colorbarOrientation = 'vertical' + self.colorbarInteractiveX = 20 + self.colorbarInteractiveY = 0 + self.colorbarWidthInteractive = 25 + self.colorbarPaddingInteractive = 25 + + self.outlineWidthInteractive = 2 + self.outlineAlphaInteractive = 1 + self.minBorderRightInteractive = 60 + self.minBorderLeftInteractive = 60 + self.minBorderTopInteractive = 20 + self.minBorderBottomInteractive = 60 self.titleFontSizeVal = 16 self.titleFontWeightInteractive = False @@ -875,7 +911,6 @@ def saveConfigXML(self, path, evt=None): buff += ' \n' % (str(self.activeDrag)) buff += ' \n' % (str(self.activeWheel)) buff += ' \n' % (str(self.activeInspect)) - buff += ' \n' % (bool(self.colorbarInteractive)) buff += ' \n' % (int(self.titleFontSizeVal)) buff += ' \n' % (bool(self.titleFontWeightInteractive)) buff += ' \n' % (int(self.labelFontSizeVal)) @@ -886,6 +921,22 @@ def saveConfigXML(self, path, evt=None): buff += ' \n' % (str(self.notationColor)) buff += ' \n' % (str(self.notationBackgroundColor)) buff += ' \n' % (bool(self.openInteractiveOnSave)) + buff += ' \n' % (bool(self.colorbarInteractive)) + buff += ' \n' % (bool(self.colorbarUseScientific)) + buff += ' \n' % (int(self.colorbarLabelOffset)) + buff += ' \n' % (int(self.colorbarPrecision)) + buff += ' \n' % (int(self.colorbarInteractiveX)) + buff += ' \n' % (int(self.colorbarInteractiveY)) + buff += ' \n' % (int(self.colorbarWidthInteractive)) + buff += ' \n' % (int(self.colorbarPaddingInteractive)) + buff += ' \n' % (str(self.colorbarLocation)) + buff += ' \n' % (str(self.colorbarOrientation)) + buff += ' \n' % (float(self.outlineAlphaInteractive)) + buff += ' \n' % (float(self.outlineWidthInteractive)) + buff += ' \n' % (int(self.minBorderRightInteractive)) + buff += ' \n' % (int(self.minBorderLeftInteractive)) + buff += ' \n' % (int(self.minBorderTopInteractive)) + buff += ' \n' % (int(self.minBorderBottomInteractive)) buff += ' \n\n' @@ -1185,7 +1236,6 @@ def setProperType(self, value, type): # Return value return value - class IconContainer: def __init__(self): @@ -2041,5 +2091,4 @@ def loadIcons(self): self.iconsLib['bgrToolbar'] = self.getBgrToolbarBitmap() - \ No newline at end of file diff --git a/ORIGAMI_ANALYSE/source/panelControls.py b/ORIGAMI_ANALYSE/source/panelControls.py index 4be58d5f..9370a460 100644 --- a/ORIGAMI_ANALYSE/source/panelControls.py +++ b/ORIGAMI_ANALYSE/source/panelControls.py @@ -25,6 +25,7 @@ from wx import EVT_BUTTON, ID_ANY from ast import literal_eval import sys +from dialogs import dlgBox # import wx.lib.agw.foldpanelbar as fpb import wx.lib.scrolledpanel @@ -1209,6 +1210,7 @@ def documentTreeSubPanel(self): self.quickDisplayCheck.Bind(wx.EVT_CHECKBOX, self.onUpdateDocumentTree) self.quickDisplayCheck.Bind(wx.EVT_CHECKBOX, self.parent.panelDocuments.topP.documents.onNotUseQuickDisplay) self.threadingCheck.Bind(wx.EVT_CHECKBOX, self.exportToConfig) + self.threadingCheck.Bind(wx.EVT_CHECKBOX, self.onEnableDisableThreading) self.loggingCheck.Bind(wx.EVT_CHECKBOX, self.exportToConfig) self.loggingCheck.Bind(wx.EVT_CHECKBOX, self.onEnableDisableLogging) @@ -1949,7 +1951,6 @@ def importFromConfig(self, evt): self.binMSfromRT.SetValue(self.config.binMSfromRT) self.loggingCheck.SetValue(self.config.logging) self.threadingCheck.SetValue(self.config.threading) -# self.quickDisplayCheck.SetValue(self.config.quickDisplay) self.normalizeTgl.SetValue(self.config.normalize) self.colorbarTgl.SetValue(self.config.colorbar) self.colorbarWidth_value.SetValue(self.config.colorbarWidth) @@ -1972,7 +1973,7 @@ def importFromConfig(self, evt): self.onEnableDisableLogging(evt=None) # self.onUpdateDocumentTree() self.parent.panelDocuments.topP.documents.onNotUseQuickDisplay(evt=None) - + self.onEnableDisableThreading(evt=None) self.importEvent = False self.notebookSettings_paneML.Layout() @@ -2144,7 +2145,7 @@ def onChangeRMSDColor(self, evt): def onEnableDisableLogging(self, evt): self.config.logging = self.loggingCheck.GetValue() - if self.config.logging == True: + if self.config.logging: sys.stdin = self.parent.panelPlots.log sys.stdout = self.parent.panelPlots.log sys.stderr = self.parent.panelPlots.log @@ -2157,6 +2158,20 @@ def onEnableDisableLogging(self, evt): if evt != None: evt.Skip() + + def onEnableDisableThreading(self, evt): + + self.config.threading = self.threadingCheck.GetValue() + if self.config.threading: + print('Multi-threading was enabled') + dlgBox(exceptionTitle="Warning", + exceptionMsg="Multi-threading is only an experimental feature for now! It might occasionally crash ORIGAMI, in which case you will lose your processed data!", + type="Warning") + else: + print('Multi-threading was disabled') + + if evt != None: + evt.Skip() def __del__( self ): pass diff --git a/ORIGAMI_ANALYSE/source/panelDocumentTree.py b/ORIGAMI_ANALYSE/source/panelDocumentTree.py index 6a4d9047..28155422 100644 --- a/ORIGAMI_ANALYSE/source/panelDocumentTree.py +++ b/ORIGAMI_ANALYSE/source/panelDocumentTree.py @@ -128,9 +128,7 @@ def onNotUseQuickDisplay(self, evt): Function to either allow or disallow quick plotting selection of datasets """ - self.config.quickDisplay = self.presenter.view.panelControls.quickDisplayCheck.GetValue() - print(self.config.quickDisplay) - + self.config.quickDisplay = self.presenter.view.panelControls.quickDisplayCheck.GetValue() if self.config.quickDisplay: self.Bind(wx.EVT_TREE_SEL_CHANGED, self.onChangePlot, id=wx.ID_ANY) @@ -138,13 +136,7 @@ def onNotUseQuickDisplay(self, evt): else: self.Unbind(wx.EVT_TREE_SEL_CHANGED, id=wx.ID_ANY) print('Quick display is OFF') - - -# if self.config.quickDisplay == False: -# self.Bind(wx.EVT_TREE_SEL_CHANGED, self.onChangePlot, id=wx.ID_ANY) -# elif self.config.quickDisplay == True: -# self.Unbind(wx.EVT_TREE_SEL_CHANGED, id=wx.ID_ANY) - + if evt != None: evt.Skip() diff --git a/ORIGAMI_ANALYSE/source/panelMultipleML.py b/ORIGAMI_ANALYSE/source/panelMultipleML.py index 496b6643..2b422dc7 100644 --- a/ORIGAMI_ANALYSE/source/panelMultipleML.py +++ b/ORIGAMI_ANALYSE/source/panelMultipleML.py @@ -326,10 +326,15 @@ def OnCheckAllItems(self, evt, check=True, override=False): if evt != None: evt.Skip() - def OnSortByColumn(self, column): + def OnSortByColumn(self, column, overrideReverse=False): """ Sort data in peaklist based on pressed column """ + + # Override reverse + if overrideReverse: + self.reverse = True + # Check if it should be reversed if self.lastColumn == None: self.lastColumn = column @@ -358,17 +363,15 @@ def OnSortByColumn(self, column): else: tempRow.append(item.GetText()) tempData.append(tempRow) - - # Sort data - tempData.sort(key = itemgetter(column), reverse=self.reverse) + + # Sort data (always by document + another variable + tempData.sort(key = itemgetter(2, column), reverse=self.reverse) # Clear table and reinsert data self.filelist.DeleteAllItems() for row in range(rows): self.filelist.Append(tempData[row]) # Now insert it into the document -# document = self.getCurrentDocument() -# if document == None: return for row in range(rows): itemName = self.filelist.GetItem(itemId=row, col=self.config.multipleMLColNames['filename']).GetText() @@ -379,6 +382,7 @@ def OnSortByColumn(self, column): self.presenter.documentsDict[docName].multipleMassSpectrum[itemName]['trap'] = trapCV + def getCurrentDocument(self, docNameOnly=False): """ Determines what is the currently selected document diff --git a/ORIGAMI_ANALYSE/source/panelOutput.py b/ORIGAMI_ANALYSE/source/panelOutput.py index 52f0be76..9bc76cb3 100644 --- a/ORIGAMI_ANALYSE/source/panelOutput.py +++ b/ORIGAMI_ANALYSE/source/panelOutput.py @@ -21,16 +21,17 @@ import wx.lib.scrolledpanel from ids import * from wx import ID_ANY -from toolbox import str2int, isempty, str2num +from toolbox import str2int, isempty, str2num, int2float import numpy as np from origamiStyles import * -# from origamiConfig import IconContainer as icons from os import getcwd from operator import itemgetter import dialogs as dialogs from bokeh.plotting import figure, show, save, ColumnDataSource, Column, gridplot -from bokeh.models import HoverTool, LinearColorMapper, Label, ColorBar +from bokeh.models import (HoverTool, LinearColorMapper, Label, ColorBar, + AdaptiveTicker, LogColorMapper, LogTicker, + BasicTickFormatter) from bokeh.layouts import column, widgetbox, layout, row, gridplot from bokeh.models.widgets import Panel, Tabs, Div from bokeh import events @@ -58,6 +59,7 @@ def __init__(self, parent, icons, presenter, config): self.currentPath = self.presenter.currentPath self.currentItem = None + self.loading = False self.listOfPlots = [] self.SetBackgroundColour(wx.WHITE) @@ -326,7 +328,9 @@ def makeHTMLView(self): order_label = makeStaticText(self.htmlView, u"Order") self.order_value = wx.TextCtrl(self.htmlView, -1, "", size=(50, -1)) - + colorbar_label = makeStaticText(self.htmlView, u"Colorbar") + self.colorbarCheck = wx.CheckBox(self.htmlView, -1 ,u'', (15, 30)) + self.colorbarCheck.SetValue(self.config.colorbarInteractive) page_label = makeStaticText(self.htmlView, u"Assign page") self.pageLayoutSelect_htmlView = wx.ComboBox(self.htmlView, -1, choices=[], value="None", style=wx.CB_READONLY) @@ -364,7 +368,8 @@ def makeHTMLView(self): self.comboCmapSelect.Bind(wx.EVT_COMBOBOX, self.onChangeColour, id=ID_changeColormapInteractive) self.pageLayoutSelect_htmlView.Bind(wx.EVT_COMBOBOX, self.onChangePageForItem) self.plotTypeToolsSelect_htmlView.Bind(wx.EVT_COMBOBOX, self.onChangeToolsForItem) - +# self.colorbarCheck.Bind(wx.EVT_CHECKBOX, self.onChangeSettings) + self.colorbarCheck.Bind(wx.EVT_CHECKBOX, self.onAnnotateItems) # Disable all elements when nothing is selected itemList = [self.itemName_value, self.itemHeader_value, self.itemFootnote_value, @@ -402,6 +407,9 @@ def makeHTMLView(self): grid.Add(ymax_label, (n,14), wx.GBSpan(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) grid.Add(self.ymax_value, (n,15), wx.GBSpan(1,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) n = n+1 + grid.Add(colorbar_label, (n,0), wx.GBSpan(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + grid.Add(self.colorbarCheck, (n,1), wx.GBSpan(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) + n = n+1 grid.Add(tools_label, (n,0), wx.GBSpan(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) grid.Add(self.plotTypeToolsSelect_htmlView, (n,1), wx.GBSpan(1,2), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) n = n+1 @@ -428,19 +436,22 @@ def makePropertiesView(self): toolsSizer = self.makeInteractiveToolsSubPanel() plot1Dsizer = self.make1DplotSubPanel() overlaySizer = self.makeOverLaySubPanel() + colorbarSizer = self.makeColorbarSubPanel() + plotSettingsSizer = self.makePlotSettingsSubPanel() mainGrid = wx.GridBagSizer(2,2) # x = 1 - mainGrid.Add(toolsSizer, (0,0), wx.GBSpan(6,1), flag=wx.EXPAND) - mainGrid.Add(plot1Dsizer, (6,0), wx.GBSpan(1,1), flag=wx.EXPAND) + mainGrid.Add(toolsSizer, (0,0), wx.GBSpan(5,1), flag=wx.EXPAND) + mainGrid.Add(plot1Dsizer, (5,0), wx.GBSpan(1,1), flag=wx.EXPAND) + mainGrid.Add(overlaySizer, (6,0), wx.GBSpan(1,1), flag=wx.EXPAND) # x = 2 mainGrid.Add(fontSizer, (0,1), wx.GBSpan(2,1), flag=wx.EXPAND) mainGrid.Add(imageSizer, (2,1), wx.GBSpan(2,1), flag=wx.EXPAND) - mainGrid.Add(overlaySizer, (4,1), wx.GBSpan(1,1), flag=wx.EXPAND) + mainGrid.Add(plotSettingsSizer, (4,1), wx.GBSpan(4,1), flag=wx.EXPAND) # x = 3 mainGrid.Add(pageLayoutSizer, (0,2), wx.GBSpan(2,1), flag=wx.EXPAND) mainGrid.Add(rmsdSizer, (2,2), wx.GBSpan(2,1), flag=wx.EXPAND) - + mainGrid.Add(colorbarSizer, (4,2), wx.GBSpan(2,1), flag=wx.EXPAND) viewSizer.Add(mainGrid, 0, wx.EXPAND, 5) @@ -525,7 +536,7 @@ def makeImageSubPanel(self): self.figHeight1D_value.Bind(wx.EVT_TEXT, self.onChangeSettings) self.figWidth1D_value.Bind(wx.EVT_TEXT, self.onChangeSettings) - gridFigure = wx.GridBagSizer(2,10) + gridFigure = wx.GridBagSizer(2,2) n = 0 gridFigure.Add(figHeight1D_label, (n,0)) gridFigure.Add(self.figHeight1D_value, (n,1)) @@ -539,6 +550,77 @@ def makeImageSubPanel(self): figSizer.Add(gridFigure, 0, wx.EXPAND|wx.ALL, 2) return figSizer + def makePlotSettingsSubPanel(self): + imageBox = makeStaticBox(self.propertiesView, "Plot properties", (270,-1), wx.BLUE) + figSizer = wx.StaticBoxSizer(imageBox, wx.HORIZONTAL) + + borderRight_label = makeStaticText(self.propertiesView, u"Border \nright") + self.minBorderRightInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.minBorderRightInteractive),min=0, max=100, + initial=float(self.config.minBorderRightInteractive), + inc=5, size=(50,-1)) + self.minBorderRightInteractive.SetToolTip(wx.ToolTip("Set minimum border size (pixels)")) + + borderLeft_label = makeStaticText(self.propertiesView, u"Border \nleft") + self.minBorderLeftInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.minBorderLeftInteractive),min=0, max=100, + initial=float(self.config.minBorderLeftInteractive), inc=5, size=(50,-1)) + self.minBorderLeftInteractive.SetToolTip(wx.ToolTip("Set minimum border size (pixels)")) + + borderTop_label = makeStaticText(self.propertiesView, u"Border \ntop") + self.minBorderTopInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.minBorderTopInteractive),min=0, max=100, + initial=float(self.config.minBorderTopInteractive), inc=5, size=(50,-1)) + self.minBorderTopInteractive.SetToolTip(wx.ToolTip("Set minimum border size (pixels)")) + + borderBottom_label = makeStaticText(self.propertiesView, u"Border \nbottom") + self.minBorderBottomInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.minBorderBottomInteractive),min=0, max=100, + initial=float(self.config.minBorderBottomInteractive), inc=5, size=(50,-1)) + self.minBorderBottomInteractive.SetToolTip(wx.ToolTip("Set minimum border size (pixels)")) + + outlineWidth_label = makeStaticText(self.propertiesView, u"Outline \nwidth") + self.outlineWidthInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.outlineWidthInteractive),min=0, max=5, + initial=self.config.outlineWidthInteractive, inc=0.5, size=(50,-1)) + self.outlineWidthInteractive.SetToolTip(wx.ToolTip("Plot outline line thickness")) + + outlineTransparency_label = makeStaticText(self.propertiesView, u"Outline \nalpha") + self.outlineAlphaInteractive = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.outlineAlphaInteractive),min=0, max=1, + initial=self.config.outlineAlphaInteractive, inc=0.05, size=(50,-1)) + self.outlineAlphaInteractive.SetToolTip(wx.ToolTip("Plot outline line transparency value")) + + # bind + self.minBorderRightInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.minBorderLeftInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.minBorderTopInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.minBorderBottomInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.outlineWidthInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.outlineAlphaInteractive.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + + + gridFigure = wx.GridBagSizer(5,2) + n = 0 + gridFigure.Add(borderRight_label, (n,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(borderLeft_label, (n,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(borderTop_label, (n,2), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(borderBottom_label, (n,3), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + n = n+1 + gridFigure.Add(self.minBorderRightInteractive, (n,0)) + gridFigure.Add(self.minBorderLeftInteractive, (n,1)) + gridFigure.Add(self.minBorderTopInteractive, (n,2)) + gridFigure.Add(self.minBorderBottomInteractive, (n,3)) + n = n+1 + gridFigure.Add(outlineWidth_label, (n,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(outlineTransparency_label, (n,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + n = n+1 + gridFigure.Add(self.outlineWidthInteractive, (n,0)) + gridFigure.Add(self.outlineAlphaInteractive, (n,1)) + + figSizer.Add(gridFigure, 0, wx.EXPAND|wx.ALL, 2) + return figSizer + def makePageLayoutSubPanel(self): layoutBox = makeStaticBox(self.propertiesView, "Page properties", (200,-1), wx.BLUE) layoutSizer = wx.StaticBoxSizer(layoutBox, wx.HORIZONTAL) @@ -777,7 +859,7 @@ def make1DplotSubPanel(self): # bind self.hoverVlineCheck.Bind(wx.EVT_CHECKBOX, self.onChangeSettings) - gridFigure = wx.GridBagSizer(2,10) + gridFigure = wx.GridBagSizer(2,2) n = 0 gridFigure.Add(self.hoverVlineCheck, (n,0)) @@ -801,23 +883,107 @@ def makeOverLaySubPanel(self): self.layout_combo.Bind(wx.EVT_COMBOBOX, self.onChangeSettings) self.XYaxisLinkCheck.Bind(wx.EVT_CHECKBOX, self.onChangeSettings) - gridFigure = wx.GridBagSizer(2,10) + gridFigure = wx.GridBagSizer(2,2) n = 0 - gridFigure.Add(layout_label, (n,0)) - gridFigure.Add(self.layout_combo, (n,1)) - gridFigure.Add(self.XYaxisLinkCheck, (n,2)) - n = n+1 - + gridFigure.Add(layout_label, (n,0), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.layout_combo, (n,1), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.XYaxisLinkCheck, (n,2), flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL) figSizer.Add(gridFigure, 0, wx.ALIGN_CENTER|wx.ALL, 5) + return figSizer + + def makeColorbarSubPanel(self): + mainBox = makeStaticBox(self.propertiesView, "Colorbar properties", (270,-1), wx.BLUE) + figSizer = wx.StaticBoxSizer(mainBox, wx.HORIZONTAL) +# + precision_label = makeStaticText(self.propertiesView, u"Precision") + self.colorbarPrecision = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarPrecision),min=0, max=5, + initial=self.config.colorbarPrecision, inc=1, size=(50,-1)) + self.colorbarPrecision.SetToolTip(wx.ToolTip("Number of decimal places in the colorbar tickers")) + + self.colorbarUseScientific = wx.CheckBox(self.propertiesView, -1 ,u'Scientific\nnotation', (15, 30)) + self.colorbarUseScientific.SetValue(self.config.colorbarUseScientific) + self.colorbarUseScientific.SetToolTip(wx.ToolTip("Enable/disable scientific notation of colorbar tickers")) + + labelOffset_label = makeStaticText(self.propertiesView, u"Label\noffset") + self.colorbarLabelOffset = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarLabelOffset),min=0, max=100, + initial=self.config.colorbarLabelOffset, inc=5, size=(50,-1)) + self.colorbarLabelOffset.SetToolTip(wx.ToolTip("Distance between the colorbar and labels")) + + + location_label = makeStaticText(self.propertiesView, u"Position") + self.colorbarLocation = wx.ComboBox(self.propertiesView, -1, + choices=['left', 'right', 'above', 'below'], + value=self.config.colorbarLocation, style=wx.CB_READONLY) + self.colorbarLocation.SetToolTip(wx.ToolTip("Colorbar position next to the plot. The colorbar orientation changes automatically")) + + offsetX_label = makeStaticText(self.propertiesView, u"Offset X") + self.colorbarInteractiveX = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarInteractiveX),min=0, max=100, + initial=self.config.colorbarInteractiveX, inc=5, size=(50,-1)) + self.colorbarInteractiveX.SetToolTip(wx.ToolTip("Colorbar position offset in the X axis. Adjust if colorbar is too close or too far away from the plot")) + + offsetY_label = makeStaticText(self.propertiesView, u"Offset Y") + self.colorbarInteractiveY = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarInteractiveY),min=0, max=100, + initial=self.config.colorbarInteractiveY, inc=5, size=(50,-1)) + self.colorbarInteractiveY.SetToolTip(wx.ToolTip("Colorbar position offset in the Y axis. Adjust if colorbar is too close or too far away from the plot")) + + padding_label = makeStaticText(self.propertiesView, u"Pad") + self.colorbarPadding = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarPaddingInteractive),min=0, max=100, + initial=self.config.colorbarPaddingInteractive, inc=5, size=(50,-1)) + self.colorbarPadding.SetToolTip(wx.ToolTip("")) + + margin_label = makeStaticText(self.propertiesView, u"Width") + self.colorbarWidth = wx.SpinCtrlDouble(self.propertiesView, wx.ID_ANY, + value=str(self.config.colorbarWidthInteractive), min=0, max=100, + initial=self.config.colorbarWidthInteractive, inc=5, size=(50,-1)) + self.colorbarWidth.SetToolTip(wx.ToolTip("")) + + + # bind + self.colorbarPrecision.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.colorbarUseScientific.Bind(wx.EVT_CHECKBOX, self.onChangeSettings) + self.colorbarLocation.Bind(wx.EVT_COMBOBOX, self.onChangeSettings) + self.colorbarInteractiveX.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.colorbarInteractiveY.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.colorbarPadding.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.colorbarWidth.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + self.colorbarLabelOffset.Bind(wx.EVT_SPINCTRLDOUBLE, self.onChangeSettings) + + gridFigure = wx.GridBagSizer(2,2) + n = 0 + gridFigure.Add(precision_label, (n,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(self.colorbarPrecision, (n,1), flag=wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.colorbarUseScientific, (n,2), wx.GBSpan(1,2), flag=wx.ALIGN_CENTER_VERTICAL) + n = n+1 + gridFigure.Add(labelOffset_label, (n,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(self.colorbarLabelOffset, (n,1), flag=wx.ALIGN_CENTER_VERTICAL) + n = n+1 + gridFigure.Add(location_label, (n,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(offsetX_label, (n,1), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(offsetY_label, (n,2), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(padding_label, (n,3), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + gridFigure.Add(margin_label, (n,4), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT) + n = n+1 + gridFigure.Add(self.colorbarLocation, (n,0), flag=wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.colorbarInteractiveX, (n,1), flag=wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.colorbarInteractiveY, (n,2), flag=wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.colorbarPadding, (n,3), flag=wx.ALIGN_CENTER_VERTICAL) + gridFigure.Add(self.colorbarWidth, (n,4), flag=wx.ALIGN_CENTER_VERTICAL) + figSizer.Add(gridFigure, 0, wx.ALIGN_CENTER|wx.ALL, 5) + return figSizer def makeDlgButtons(self): mainSizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, ""), wx.VERTICAL) pathBtn = wx.Button(self, -1, "Set Path", size=(80,-1)) - saveBtn = wx.Button(self, -1, "Save", size=(80,-1)) + saveBtn = wx.Button(self, -1, "Export HTML", size=(80,-1)) cancelBtn = wx.Button(self, -1, "Cancel", size=(80,-1)) openHTMLWebBtn = wx.Button(self, ID_helpHTMLEditor, "HTML Editor", size=(80,-1)) openHTMLWebBtn.SetToolTip(wx.ToolTip("Opens a web-based HTML editor")) @@ -1000,7 +1166,31 @@ def onChangeSettings(self, evt): # Tools self.config.toolsLocation = self.location_combo.GetValue() - + + # Colorbar + self.config.colorbarPrecision = str2int(self.colorbarPrecision.GetValue()) + self.config.colorbarUseScientific = self.colorbarUseScientific.GetValue() + self.config.colorbarInteractiveX = str2int(self.colorbarInteractiveX.GetValue()) + self.config.colorbarInteractiveY = str2int(self.colorbarInteractiveY.GetValue()) + self.config.colorbarLocation = self.colorbarLocation.GetStringSelection() + if self.config.colorbarLocation in ('right', 'left'): + self.config.colorbarOrientation = 'vertical' + else: + self.config.colorbarOrientation = 'horizontal' + + self.config.colorbarPaddingInteractive = str2int(self.colorbarPadding.GetValue()) + self.config.colorbarWidthInteractive = str2int(self.colorbarWidth.GetValue()) + + self.config.colorbarLabelOffset = str2int(self.colorbarLabelOffset.GetValue()) + + # Plot parameters + self.config.outlineWidthInteractive = str2num(self.outlineWidthInteractive.GetValue()) + self.config.outlineAlphaInteractive = str2num(self.outlineAlphaInteractive.GetValue()) + self.config.minBorderRightInteractive = str2int(self.minBorderRightInteractive.GetValue()) + self.config.minBorderLeftInteractive = str2int(self.minBorderLeftInteractive.GetValue()) + self.config.minBorderTopInteractive = str2int(self.minBorderTopInteractive.GetValue()) + self.config.minBorderBottomInteractive = str2int(self.minBorderBottomInteractive.GetValue()) + if evt != None: evt.Skip() @@ -1183,6 +1373,9 @@ def checkIfHasHTMLkeys(self, dictionary): if 'cmap' in dictionary: pass else: dictionary['cmap'] = '' + + if 'colorbar' in dictionary: pass + else: dictionary['colorbar'] = False if 'page' in dictionary: # If it has page information in dictionary, add it to the config object @@ -1482,12 +1675,16 @@ def onItemSelected(self, evt): # Disable all elements when nothing is selected itemList = [self.itemName_value, self.itemHeader_value, self.itemFootnote_value, self.order_value, self.pageLayoutSelect_htmlView, - self.plotTypeToolsSelect_htmlView + self.plotTypeToolsSelect_htmlView, self.colorbarCheck ] for item in itemList: item.Enable() + # When selecting new item, it automatically updates each field in the GUI, + # however, as it does that, it also updates the data dictionary. By enabling + # loading mode, only the GUI is updated and nothing else. + self.loading = True self.currentItem = evt.m_itemIndex name = self.itemsList.GetItem(self.currentItem,0).GetText() key = self.itemsList.GetItem(self.currentItem,1).GetText() @@ -1523,7 +1720,8 @@ def onItemSelected(self, evt): order = docData['order'] page = docData['page']['name'] tool = docData['tools']['name'] - + colorbar = docData['colorbar'] + # Update item editor self.itemName_value.SetValue(title) self.itemHeader_value.SetValue(header) @@ -1531,7 +1729,8 @@ def onItemSelected(self, evt): self.order_value.SetValue(order) self.pageLayoutSelect_htmlView.SetStringSelection(page) self.plotTypeToolsSelect_htmlView.SetStringSelection(tool) - + self.colorbarCheck.SetValue(colorbar) + if any(key in method for method in ["MS", "RT", "1D", "RT, Combined", 'MS, Multiple', 'DT-IMS']): color = (eval(color)) self.colorBtn.SetBackgroundColour(tuple([np.int(color[0]*255), @@ -1540,25 +1739,35 @@ def onItemSelected(self, evt): self.colorBtn.Enable() self.comboCmapSelect.Disable() self.layout_combo.Disable() + self.colorbarCheck.Disable() self.pageLayoutSelect_htmlView.Enable() - elif any(key in method for method in ["2D", "Statistical","2D, Combined","2D, Processed"]): + self.plotTypeToolsSelect_htmlView.Enable() + elif (any(key in method for method in ["2D", "Statistical", "2D, Combined", "2D, Processed"]) or + (key == 'Overlay' and overlayMethod[0] in ('RMSD','RMSF'))): self.comboCmapSelect.SetValue(color) self.comboCmapSelect.Enable() self.colorBtn.Disable() self.layout_combo.Disable() self.pageLayoutSelect_htmlView.Enable() + self.colorbarCheck.Enable() + self.plotTypeToolsSelect_htmlView.Enable() elif key == 'Overlay' and (overlayMethod[0] == 'Mask' or overlayMethod[0] == 'Transparent'): self.layout_combo.Enable() self.comboCmapSelect.Disable() self.colorBtn.Disable() -# self.pageLayoutSelect_htmlView.SetStringSelection('None') self.pageLayoutSelect_htmlView.Disable() + self.colorbarCheck.Enable() + self.plotTypeToolsSelect_htmlView.Enable() else: self.comboCmapSelect.Enable() self.colorBtn.Enable() self.layout_combo.Disable() + self.colorbarCheck.Disable() self.pageLayoutSelect_htmlView.Enable() - + self.plotTypeToolsSelect_htmlView.Enable() + + self.loading = False + def getItemData(self, name, key, innerKey): # Determine which document was selected document = self.documentsDict[name] @@ -1685,7 +1894,6 @@ def onAnnotateItems(self, evt=None, itemID=None): tools = self.getToolSet(preset=tool) toolSet = {'name':tool, 'tools':tools} # dictionary object - if any(key in method for method in ["MS", "RT", "1D", "RT, Combined", 'MS, Multiple']): color = (eval(color)) @@ -1693,12 +1901,14 @@ def onAnnotateItems(self, evt=None, itemID=None): header = self.itemHeader_value.GetValue() footnote = self.itemFootnote_value.GetValue() orderNum = self.order_value.GetValue() - + colorbar = self.colorbarCheck.GetValue() + if key == 'Overlay': overlayMethod = innerKey.split('__') if overlayMethod[0] == 'Mask' or overlayMethod[0] == 'Transparent': pageData = self.config.pageDict['None'] + if self.loading: return # Retrieve and add data to dictionary document = self.documentsDict[name] @@ -1714,18 +1924,6 @@ def onAnnotateItems(self, evt=None, itemID=None): if key == '1D' and innerKey == '': document.DT = self.addHTMLtagsToDictionary(document.DT, title, header, footnote, orderNum, color, pageData, toolSet) - if key == '2D' and innerKey == '': document.IMS2D = self.addHTMLtagsToDictionary(document.IMS2D, - title, header, footnote, orderNum, color, pageData, toolSet) - - if key == '2D, Processed' and innerKey == '': document.IMS2Dprocess = self.addHTMLtagsToDictionary(document.IMS2Dprocess, - title, header, footnote, orderNum, color, pageData, toolSet) - - if key == '2D, Processed' and innerKey == '': document.IMS2Dprocess = self.addHTMLtagsToDictionary(document.multipleMassSpectrum, - title, header, footnote, orderNum, color, pageData, toolSet) - - if key == '2D' and innerKey != '': document.IMS2Dions[innerKey] = self.addHTMLtagsToDictionary(document.IMS2Dions[innerKey], - title, header, footnote, orderNum, color, pageData, toolSet) - if key == 'MS, Multiple' and innerKey != '': document.multipleMassSpectrum[innerKey] = self.addHTMLtagsToDictionary(document.multipleMassSpectrum[innerKey], title, header, footnote, orderNum, color, pageData, toolSet) @@ -1735,17 +1933,29 @@ def onAnnotateItems(self, evt=None, itemID=None): if key == 'RT, Combined' and innerKey != '': document.IMSRTCombIons[innerKey] = self.addHTMLtagsToDictionary(document.IMSRTCombIons[innerKey], title, header, footnote, orderNum, color, pageData, toolSet) + if key == '2D' and innerKey == '': document.IMS2D = self.addHTMLtagsToDictionary(document.IMS2D, + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) + + if key == '2D, Processed' and innerKey == '': document.IMS2Dprocess = self.addHTMLtagsToDictionary(document.IMS2Dprocess, + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) + + if key == '2D, Processed' and innerKey == '': document.IMS2Dprocess = self.addHTMLtagsToDictionary(document.multipleMassSpectrum, + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) + + if key == '2D' and innerKey != '': document.IMS2Dions[innerKey] = self.addHTMLtagsToDictionary(document.IMS2Dions[innerKey], + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) + if key == '2D, Combined' and innerKey != '': document.IMS2DCombIons[innerKey] = self.addHTMLtagsToDictionary(document.IMS2DCombIons[innerKey], - title, header, footnote, orderNum, color, pageData, toolSet) + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) if key == '2D, Processed' and innerKey != '': document.IMS2DionsProcess[innerKey] = self.addHTMLtagsToDictionary(document.IMS2DionsProcess[innerKey], - title, header, footnote, orderNum, color, pageData, toolSet) + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) if key == 'Overlay' and innerKey != '': document.IMS2DoverlayData[innerKey] = self.addHTMLtagsToDictionary(document.IMS2DoverlayData[innerKey], - title, header, footnote, orderNum, color, pageData, toolSet) + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) if key == 'Statistical' and innerKey != '': document.IMS2DstatsData[innerKey] = self.addHTMLtagsToDictionary(document.IMS2DstatsData[innerKey], - title, header, footnote, orderNum, color, pageData, toolSet) + title, header, footnote, orderNum, color, pageData, toolSet, colorbar) # Set new text for labels @@ -1767,7 +1977,7 @@ def onAnnotateItems(self, evt=None, itemID=None): self.presenter.documentsDict[document.title] = document def addHTMLtagsToDictionary(self, dictionary, title, header, footnote, - orderNumber, color, page, toolSet): + orderNumber, color, page, toolSet, colorbar=False): """ Helper function to add title,header,footnote data to dictionary @@ -1779,12 +1989,13 @@ def addHTMLtagsToDictionary(self, dictionary, title, header, footnote, dictionary['cmap'] = color dictionary['page'] = page dictionary['tools'] = toolSet + dictionary['colorbar'] = colorbar return dictionary def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, title=None, xlabelIn=None, ylabelIn=None, linkXYAxes=False, - layout='Rows'): + layout='Rows', addColorbar=None): """ Prepare data to be in an appropriate format """ @@ -1829,9 +2040,9 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit # Add only tools TOOLS = data['tools']['tools']['tools'] elif self.hover_check.GetValue(): - print('generic tools') TOOLS = [hoverTool,self.tools] else: TOOLS = self.tools + bokehPlot = figure(x_range=(min(xvals), max(xvals)), y_range=(min(yvals), max(yvals)), tools=TOOLS, @@ -1840,13 +2051,14 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit plot_width=self.config.figWidth1D, plot_height=(self.config.figHeight1D), toolbar_location=self.config.toolsLocation) + cmap = tuple([np.int(cmap[0]*255), np.int(cmap[1]*255), np.int(cmap[2]*255)]) bokehPlot.line(xvals, yvals, line_color=cmap) # Add border - bokehPlot.outline_line_width = 2 - bokehPlot.outline_line_alpha = 1 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive bokehPlot.outline_line_color = "black" # X-axis bokehPlot.xaxis.axis_label = xlabelIn @@ -1865,6 +2077,7 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit dataType='plot', plotType='2D', compact=False) + colormap = cm.get_cmap(cmap) # choose any matplotlib colormap here bokehpalette = [colors.rgb2hex(m) for m in colormap(np.arange(colormap.N))] hoverTool = HoverTool(tooltips = [(xlabel, '$x{0}'), @@ -1883,6 +2096,24 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit TOOLS = data['tools']['tools']['tools'] elif self.hover_check.GetValue(): TOOLS = [hoverTool,self.tools] else: TOOLS = self.tools + + colorbar, colorMapper = None, None + if addColorbar: + colorMapper = LinearColorMapper(palette=bokehpalette, + low=np.min(zvals), + high=np.max(zvals)) + colorbar = ColorBar(color_mapper = colorMapper, + ticker=AdaptiveTicker(), + label_standoff = self.config.colorbarLabelOffset, + border_line_color = None, + location = (self.config.colorbarInteractiveX, + self.config.colorbarInteractiveY), + formatter=BasicTickFormatter(precision=self.config.colorbarPrecision, + use_scientific=self.config.colorbarUseScientific), + orientation=self.config.colorbarOrientation, + width=self.config.colorbarWidthInteractive, + padding=self.config.colorbarPaddingInteractive) + bokehPlot = figure(x_range=(min(xvals), max(xvals)), y_range=(min(yvals), max(yvals)), tools=TOOLS, @@ -1900,14 +2131,17 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit bokehPlot.quad(top=max(yvals), bottom=min(yvals), left=min(xvals), right=max(xvals), alpha=0) + if addColorbar: + bokehPlot.add_layout(colorbar, self.config.colorbarLocation) + # Add border - bokehPlot.outline_line_width = 2 - bokehPlot.outline_line_alpha = 1 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive bokehPlot.outline_line_color = "black" - bokehPlot.min_border_right = 60 - bokehPlot.min_border_left = 60 - bokehPlot.min_border_top = 20 - bokehPlot.min_border_bottom = 60 + bokehPlot.min_border_right = self.config.minBorderRightInteractive + bokehPlot.min_border_left = self.config.minBorderLeftInteractive + bokehPlot.min_border_top = self.config.minBorderTopInteractive + bokehPlot.min_border_bottom = self.config.minBorderBottomInteractive # X-axis bokehPlot.xaxis.axis_label = xlabel bokehPlot.xaxis.axis_label_text_font_size = self.labelFontSize @@ -1987,13 +2221,13 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit color='colors') # Add border - bokehPlot.outline_line_width = 2 - bokehPlot.outline_line_alpha = 1 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive bokehPlot.outline_line_color = "black" - bokehPlot.min_border_right = 60 - bokehPlot.min_border_left = 60 - bokehPlot.min_border_top = 20 - bokehPlot.min_border_bottom = 60 + bokehPlot.min_border_right = self.config.minBorderRightInteractive + bokehPlot.min_border_left = self.config.minBorderLeftInteractive + bokehPlot.min_border_top = self.config.minBorderTopInteractive + bokehPlot.min_border_bottom = self.config.minBorderBottomInteractive # X-axis bokehPlot.xaxis.axis_label_text_font_size = self.labelFontSize bokehPlot.xaxis.axis_label_text_font_style = self.labelFontWeightInteractive @@ -2021,6 +2255,23 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit hoverTool = HoverTool(tooltips = [(xlabel, '$x{0}'), (ylabel, '$y{0}')], point_policy = 'follow_mouse') + colorbar, colorMapper = None, None + if addColorbar: + colorMapper = LinearColorMapper(palette=bokehpalette, + low=np.min(zvals), + high=np.max(zvals)) + colorbar = ColorBar(color_mapper = colorMapper, + ticker=AdaptiveTicker(), + label_standoff = self.config.colorbarLabelOffset, + border_line_color = None, + location = (self.config.colorbarInteractiveX, + self.config.colorbarInteractiveY), + formatter=BasicTickFormatter(precision=self.config.colorbarPrecision, + use_scientific=self.config.colorbarUseScientific), + orientation=self.config.colorbarOrientation, + width=self.config.colorbarWidthInteractive, + padding=self.config.colorbarPaddingInteractive) + # Add tools if len(data['tools']) > 0: # Enable current active tools @@ -2069,15 +2320,17 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit background_fill_color=backgroundColor, background_fill_alpha=1.0) bokehPlot.add_layout(rmsdAnnot) + if addColorbar: + bokehPlot.add_layout(colorbar, self.config.colorbarLocation) # Add border - bokehPlot.outline_line_width = 2 - bokehPlot.outline_line_alpha = 1 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive bokehPlot.outline_line_color = "black" - bokehPlot.min_border_right = 60 - bokehPlot.min_border_left = 60 - bokehPlot.min_border_top = 20 - bokehPlot.min_border_bottom = 60 + bokehPlot.min_border_right = self.config.minBorderRightInteractive + bokehPlot.min_border_left = self.config.minBorderLeftInteractive + bokehPlot.min_border_top = self.config.minBorderTopInteractive + bokehPlot.min_border_bottom = self.config.minBorderBottomInteractive # X-axis bokehPlot.xaxis.axis_label = xlabel bokehPlot.xaxis.axis_label_text_font_size = self.labelFontSize @@ -2133,8 +2386,8 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit np.int(color[2]*255)]) bokehPlotRMSF.line(xvals, yvalsRMSF, line_color=color) # Add border - bokehPlotRMSF.outline_line_width = 2 - bokehPlotRMSF.outline_line_alpha = 1 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive bokehPlotRMSF.outline_line_color = "black" # Y-axis bokehPlotRMSF.yaxis.axis_label = ylabelRMSF @@ -2150,6 +2403,23 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit hoverTool = HoverTool(tooltips = [(xlabelRMSD, '$x{0}'), (ylabelRMSD, '$y{0}')], point_policy = 'follow_mouse') + colorbar, colorMapper = None, None + if addColorbar: + colorMapper = LinearColorMapper(palette=bokehpalette, + low=np.min(zvals), + high=np.max(zvals)) + colorbar = ColorBar(color_mapper = colorMapper, + ticker=AdaptiveTicker(), + label_standoff = self.config.colorbarLabelOffset, + border_line_color = None, + location = (self.config.colorbarInteractiveX, + self.config.colorbarInteractiveY), + formatter=BasicTickFormatter(precision=self.config.colorbarPrecision, + use_scientific=self.config.colorbarUseScientific), + orientation=self.config.colorbarOrientation, + width=self.config.colorbarWidthInteractive, + padding=self.config.colorbarPaddingInteractive) + # Add tools if len(data['tools']) > 0: # Enable current active tools @@ -2209,15 +2479,17 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit background_fill_color=backgroundColor, background_fill_alpha=1.0) bokehPlotRMSD.add_layout(rmsdAnnot) + if addColorbar: + bokehPlotRMSD.add_layout(colorbar, self.config.colorbarLocation) # Add border - bokehPlotRMSD.outline_line_width = 2 - bokehPlotRMSD.outline_line_alpha = 1 - bokehPlotRMSD.outline_line_color = "black" - bokehPlotRMSD.min_border_right = 60 - bokehPlotRMSD.min_border_left = 60 - bokehPlotRMSD.min_border_top = 20 - bokehPlotRMSD.min_border_bottom = 60 + bokehPlot.outline_line_width = self.config.outlineWidthInteractive + bokehPlot.outline_line_alpha = self.config.outlineAlphaInteractive + bokehPlot.outline_line_color = "black" + bokehPlot.min_border_right = self.config.minBorderRightInteractive + bokehPlot.min_border_left = self.config.minBorderLeftInteractive + bokehPlot.min_border_top = self.config.minBorderTopInteractive + bokehPlot.min_border_bottom = self.config.minBorderBottomInteractive # X-axis bokehPlotRMSD.xaxis.axis_label = xlabelRMSD bokehPlotRMSD.xaxis.axis_label_text_font_size = self.labelFontSize @@ -2322,13 +2594,13 @@ def prepareDataForInteractivePlots(self, data=None, type=None, subtype=None, tit alpha=0) for plot in [leftPlot, rightPlot]: - plot.outline_line_width = 2 - plot.outline_line_alpha = 1 + plot.outline_line_width = self.config.outlineWidthInteractive + plot.outline_line_alpha = self.config.outlineAlphaInteractive plot.outline_line_color = "black" - plot.min_border_right = 60 - plot.min_border_left = 60 - plot.min_border_top = 20 - plot.min_border_bottom = 60 + plot.min_border_right = self.config.minBorderRightInteractive + plot.min_border_left = self.config.minBorderLeftInteractive + plot.min_border_top = self.config.minBorderTopInteractive + plot.min_border_bottom = self.config.minBorderBottomInteractive if layout=='Rows': for plot in [leftPlot, rightPlot]: @@ -2404,7 +2676,8 @@ def onGenerateHTML(self, evt): for item in xrange(self.itemsList.GetItemCount()): if self.itemsList.IsChecked(index=item): - self.onAnnotateItems(evt=None, itemID=item) + # Disabled in v1.0.5 as it was always overriding title, header and other information = should have known better... +# self.onAnnotateItems(evt=None, itemID=item) name = self.itemsList.GetItem(item,0).GetText() key = self.itemsList.GetItem(item,1).GetText() innerKey = self.itemsList.GetItem(item,2).GetText() @@ -2414,6 +2687,8 @@ def onGenerateHTML(self, evt): header = data['header'] footnote = data['footnote'] page = data['page'] + colorbar = data['colorbar'] + # Check that the page names agree. If they don't, the table page # name takes priority as it has been pre-emptively added to the # plotDict dictionary @@ -2451,7 +2726,8 @@ def onGenerateHTML(self, evt): elif key =='2D' or key == '2D, Combined' or key == '2D, Processed': bokehPlot = self.prepareDataForInteractivePlots(data=data, type='2D', - title=title) + title=title, + addColorbar=colorbar) elif key == 'RT': bokehPlot = self.prepareDataForInteractivePlots(data=data, @@ -2477,11 +2753,13 @@ def onGenerateHTML(self, evt): bokehPlot = self.prepareDataForInteractivePlots(data=data, type='RMSF', linkXYAxes=self.config.linkXYaxes, - title=title) + title=title, + addColorbar=colorbar) elif overlayMethod[0] == 'RMSD': bokehPlot = self.prepareDataForInteractivePlots(data=data, type='RMSD', - title=title) + title=title, + addColorbar=colorbar) elif overlayMethod[0] == '1D' or overlayMethod[0] == 'RT': msg = "Cannot export '%s - %s (%s)' in an interactive format yet - it will be available in the future updates. For now, please deselect it in the table. LM" % (overlayMethod[0], key, innerKey) dialogs.dlgBox(exceptionTitle='Not supported yet', @@ -2497,7 +2775,8 @@ def onGenerateHTML(self, evt): overlayMethod[0] == 'Standard Deviation'): bokehPlot = self.prepareDataForInteractivePlots(data=data, type='2D', - title=title) + title=title, + addColorbar=colorbar) elif overlayMethod[0] == 'RMSD Matrix': bokehPlot = self.prepareDataForInteractivePlots(data=data, type='Matrix', @@ -2597,7 +2876,11 @@ def onGetSavePath(self, evt): """ fileType = "HTML file|*.html" - dlg = wx.FileDialog(self, "Save interactive document to file...", "", "", fileType, + if self.config.saveInteractiveOverride: + dlg = wx.FileDialog(self, "Save interactive document to file...", "", "", fileType, + wx.FD_SAVE) + else: + dlg = wx.FileDialog(self, "Save interactive document to file...", "", "", fileType, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) dlg.SetFilename('Interactive Output') if dlg.ShowModal() == wx.ID_OK: diff --git a/ORIGAMI_ANALYSE/source/plot1d.py b/ORIGAMI_ANALYSE/source/plot1d.py index bc845304..6a5f50e7 100644 --- a/ORIGAMI_ANALYSE/source/plot1d.py +++ b/ORIGAMI_ANALYSE/source/plot1d.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from plottingWindow import plottingWindow from numpy import arange, sin, pi, min, max, arange, linspace from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg diff --git a/ORIGAMI_ANALYSE/source/plot2d.py b/ORIGAMI_ANALYSE/source/plot2d.py index 4067aa61..3f780330 100644 --- a/ORIGAMI_ANALYSE/source/plot2d.py +++ b/ORIGAMI_ANALYSE/source/plot2d.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from __future__ import division from plottingWindow import plottingWindow from numpy import arange, sin, pi, min, max, random, amax diff --git a/ORIGAMI_ANALYSE/source/plot3d.py b/ORIGAMI_ANALYSE/source/plot3d.py index fc4deb61..87dd20f0 100644 --- a/ORIGAMI_ANALYSE/source/plot3d.py +++ b/ORIGAMI_ANALYSE/source/plot3d.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from plottingWindow import plottingWindow from numpy import arange, sin, pi, meshgrid, sqrt, shape, ravel, zeros_like, divide from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg diff --git a/ORIGAMI_ANALYSE/source/plot_MSandCIU.py b/ORIGAMI_ANALYSE/source/plot_MSandCIU.py index 0cff54e9..7f55f6be 100644 --- a/ORIGAMI_ANALYSE/source/plot_MSandCIU.py +++ b/ORIGAMI_ANALYSE/source/plot_MSandCIU.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + from plottingWindow import plottingWindow from numpy import arange, sin, pi import numpy as np diff --git a/ORIGAMI_ANALYSE/source/plottingWindow.py b/ORIGAMI_ANALYSE/source/plottingWindow.py index 401323b8..bf13a4ce 100644 --- a/ORIGAMI_ANALYSE/source/plottingWindow.py +++ b/ORIGAMI_ANALYSE/source/plottingWindow.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + # IMPORT LIBS import wx from matplotlib import interactive diff --git a/ORIGAMI_ANALYSE/source/toolbox.py b/ORIGAMI_ANALYSE/source/toolbox.py index 0abe1ce5..0ffd61d9 100644 --- a/ORIGAMI_ANALYSE/source/toolbox.py +++ b/ORIGAMI_ANALYSE/source/toolbox.py @@ -1,3 +1,20 @@ +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------------------- +# Copyright (C) 2017 Lukasz G. Migas +# +# GitHub : https://github.com/lukasz-migas/ORIGAMI +# University of Manchester IP : https://www.click2go.umip.com/i/s_w/ORIGAMI.html +# Cite : 10.1016/j.ijms.2017.08.014 +# +# This program is free software. Feel free to redistribute it and/or +# modify it under the condition you cite and credit the authors whenever +# appropriate. +# The program is distributed in the hope that it will be useful but is +# provided WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE +# ------------------------------------------------------------------------- + # This file contains a number of useful functions from __future__ import division, print_function from __builtin__ import str @@ -92,6 +109,13 @@ def float2int(num): except (ValueError, TypeError): return num +def int2float(num): + try: + val = float(num) + return val + except (ValueError, TypeError): + return num + def isempty(input): try: if asarray(input).size == 0 or input is None: