Source code for cbmpy.CBWx

"""
CBMPy: CBWx module
===================
PySCeS Constraint Based Modelling (http://cbmpy.sourceforge.net)
Copyright (C) 2009-2017 Brett G. Olivier, VU University Amsterdam, Amsterdam, The Netherlands

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 3 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, see <http://www.gnu.org/licenses/>

Author: Brett G. Olivier
Contact email: bgoli@users.sourceforge.net
Last edit: $Author: bgoli $ ($Id: CBWx.py 575 2017-04-13 12:18:44Z bgoli $)

"""

# preparing for Python 3 port
from __future__ import division, print_function
from __future__ import absolute_import
#from __future__ import unicode_literals

import os, time, random, math, re, webbrowser, locale, sys
# this is a hack that needs to be streamlined a bit
try:
    import cStringIO as csio
except ImportError:
    import io as csio
HAVE_WX = False
try:
    import wx
    import wx.grid
    import wx.html
    import wx.lib.ClickableHtmlWindow
    import wx.aui
    import wx.lib.colourdb
    from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources
    from wx.lib import scrolledpanel
    import  wx.lib.mixins.listctrl  as  listmix
    from wx import ImageFromStream, BitmapFromImage
    HAVE_WX = True
    print('WX GUI tools available.')
except ImportError as ex:
    print('\nWX GUI tools not available.')
    print(ex)

HAVE_URLLIB2 = True
try:
    import urllib2
except:
    HAVE_URLLIB2 = False

if HAVE_WX:
    class ModelEditor(wx.Frame):
        PanelSize = (1100,600)
        RPwidth = 0.5
        RPheight = 1.0
        LPwidth = 0.5
        LPheight = 1.0
        mainSizer = None
        RGridCol = None
        RGridRow = None
        rlabels = ('Reaction','Name','Flux','d','LB','UB','RCost','Exch','Balanced')
        NOVAL = (None, '', 'None')
        RGrid = None
        pybox = None
        NoteB1 = None
        NoteB1_Psession = None
        NoteB1_Preaction = None
        NoteB1_Prelate = None
        NoteB1_Pgene = None
        FCanvas_met = None
        FCanvas_gen = None
        Colour = None
        Rinfbox = None
        _cmod_ = None
        _cbm_ = None
        MainPanel = None
        StatusBar = None
        OUT_dir = None
        OUT_file = None
        STS_OBJ_FUNC = None
        STS_OBJ_SENSE = None
        STS_OBJ_COND = None
        BUT_minsum = None
        BUT_optimise = None
        RGridOK = True
        __ActiveReaction = None
        __ScaledReducedCost = False
        __BoundaryDetection = True
        WX_COLOURS = None
        FC_LineStyles = None
        __C_BUFF__ = None
        RGdict = None
        SGdict = None
        GGdict = None
        Rmap = None
        Smap = None
        GPRmap = None
        PRGmap = None
        OnlyActiveReactions = False
        ZERO_TOL = 1.0e-11
        SEARCH_MODE = 0
        RTreeCache = None
        ReactionBalanceInfo = None
        FontCache = None
        SemanticSBMLClient = None

        def __init__(self, cmod):
            mystyle = wx.DEFAULT_DIALOG_STYLE
            mystyle = wx.DEFAULT_FRAME_STYLE
            fSize = wx.DisplaySize()
            fSize = fSize[0], fSize[1]-50
            wx.Frame.__init__(self, None, style=mystyle, name='frame1', pos=(0, 0), size=fSize)
            #self.SetSize(self.PanelSize)
            #self.SetMinSize(self.PanelSize)
            #self.ShowFullScreen(True)
            self.SetTitle('PySCeS-CBM Model Editor - editing: %s (%s)' % (cmod.getId(), cmod.getName()))
            fSize = self.GetSize()
            fSize = fSize[0], fSize[1]-20
            self.MainPanel = wx.Panel(self, -1, size=fSize)
            self.MainPanel.SetSize(fSize)
            self.MainPanel.SetMinSize(fSize)

            self.PanelSize = self.MainPanel.GetSize()
            self._cmod_ = cmod
            self.__C_BUFF__ = []
            self.RGridRow = cmod.getReactionIds()
            self.RGdict = {}
            self.SGdict = {}
            self.GGdict = {}
            self.Rmap = {}
            self.Smap = {}
            self.GPRmap = {}
            self.PRGmap = {}
            self.RTreeCache = []
            self.ReactionBalanceInfo = {}
            self.FontCache = {}

            self.BuildMainPanel()
            self.CreateRGrid()
            #self.LeftPanel.Refresh()
            #self.MainPanel.UpdateWindowUI()

            import cbmpy as cbm
            self._cbm_ = cbm
            self.OUT_dir = os.getcwd()
            self.UpdateModelStatus()
            self.CreateMaps()
            try:
                #self.RESTClient = cbm.CBNetDB.RESTClient()
                self.SemanticSBMLClient = cbm.CBNetDB.SemanticSBML()
            except:
                print('REST web serices not available')

        def EVT_SEARCH_MODE_SELECT(self, event):
            if event.GetId() == 211:
                self.SEARCH_MODE = 0
                self.searchSelectmenu.Check(211, True)
                self.searchSelectmenu.Check(212, False)
                self.SEARCH_OBJ.Enable(True)
            elif event.GetId() == 212:
                self.SEARCH_MODE = 1
                self.searchSelectmenu.Check(211, False)
                self.searchSelectmenu.Check(212, True)
                self.SEARCH_OBJ.Enable(False)
            print('Search mode selected:', self.SEARCH_MODE, event.GetId())

        def BuildMainPanel(self):
            # build some frame stuff
            # Setting up the menu.
            filemenu= wx.Menu()
            menuSave = filemenu.Append(101, "&Save Model"," Save to SBML", kind=wx.ID_SAVE)
            menuExport = filemenu.Append(102, "Save S&ession"," Export session as Python script", kind=wx.ID_SAVE)
            menuAbout= filemenu.Append(103, "&About"," Information about this program", kind=wx.ID_ABOUT)
            menuExit = filemenu.Append(104,"E&xit"," Terminate the program", kind=wx.ID_EXIT)

            self.searchSelectmenu = wx.Menu()
            self.CHECK_AUTOCOMP = self.searchSelectmenu.Append(211, 'AutoComplete', kind=wx.ITEM_CHECK)
            self.CHECK_SUBSEARCH = self.searchSelectmenu.Append(212, 'Subsearch', kind=wx.ITEM_CHECK)

            self.Bind(wx.EVT_MENU, self.EVT_SEARCH_MODE_SELECT, self.CHECK_AUTOCOMP)
            self.Bind(wx.EVT_MENU, self.EVT_SEARCH_MODE_SELECT, self.CHECK_SUBSEARCH)
            self.searchSelectmenu.Check(self.CHECK_AUTOCOMP.GetId(), True)

            optionsMenu = wx.Menu()
            optionsMenu.AppendMenu(201, '&Search mode', self.searchSelectmenu)

            actionMenu = wx.Menu()
            menuAnalyseBalances = actionMenu.Append(301, 'Run &Balance Checker', kind=wx.ID_DEFAULT)
            SemSBMLMenu = wx.Menu()
            menuSemSBMLMenu_name = SemSBMLMenu.Append(311, 'Name', kind=wx.ID_DEFAULT)
            menuSemSBMLMenu_id = SemSBMLMenu.Append(312, 'Id', kind=wx.ID_DEFAULT)
            actionMenu.AppendMenu(302, 'Run Semantic &SBML', SemSBMLMenu)
            self.Bind(wx.EVT_MENU, self.MENUAnalyseBalances, menuAnalyseBalances)
            self.Bind(wx.EVT_MENU, self.SemSBML_name, menuSemSBMLMenu_name)
            self.Bind(wx.EVT_MENU, self.SemSBML_id, menuSemSBMLMenu_id)



            # Creating the menubar and statusbar
            menuBar = wx.MenuBar()
            menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
            menuBar.Append(optionsMenu,"&Options") # Adding the "filemenu" to the MenuBar
            menuBar.Append(actionMenu,"&Action") # Adding the "filemenu" to the MenuBar
            self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
            # Menu Events.
            self.Bind(wx.EVT_MENU, self.MENUOnSave, menuSave)
            self.Bind(wx.EVT_MENU, self.MENUExport, menuExport)
            self.Bind(wx.EVT_MENU, self.MENUOnExit, menuExit)
            self.Bind(wx.EVT_MENU, self.MENUOnAbout, menuAbout)

            self.StatusBar = self.CreateStatusBar() # A StatusBar in the bottom of the window
            self.SetStatusText("Ready!")

            # create the sizers
            self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)

            # Checkbox
            ##  self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?")
            ##  grid.Add(self.insure, pos=(4,0), span=(1,2), flag=wx.BOTTOM, border=5)
            ##  self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)

            # Radio Boxes
            ##  radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
            ##  rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
                        ##  style=wx.RA_SPECIFY_COLS)
            ##  grid.Add(rb, pos=(5,0), span=(1,2))
            ##  self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)

            # create the Lpanel
            leftSizer = wx.BoxSizer(wx.VERTICAL)
            self.LeftPanel = wx.Panel(self.MainPanel, -1)
            self.RGridGap = 80
            gridSize = (self.PanelSize[0]*self.LPwidth, self.PanelSize[1]-self.RGridGap)
            self.RGridInitSize = gridSize
            self.LeftPanel.SetMinSize(gridSize)

            # Define a dynamically updatable grid
            self.RGrid_scrollwindow = wx.ScrolledWindow(id=-1, size=gridSize,
                                                        name='RGrid_scrollwindow', parent=self.LeftPanel,
                                                        style=wx.HSCROLL | wx.VSCROLL)

            self.RGrid = wx.grid.Grid(id=-1, name='RGrid', size=gridSize,
                                      parent=self.RGrid_scrollwindow,
                                      style=0)
            self.RGridCol = len(self.rlabels)

            self.RGrid.CreateGrid(len(self._cmod_.reactions), self.RGridCol)
            #self.RGrid.SetSize(gridSize)
            #self.RGrid.SetMinSize(gridSize)
            ##  self.RGrid.SetMargins(5,5)
            self.RGrid.EnableScrolling(True, True)
            self.RGrid.SetScrollbars(10, 10, 10, 10)

            self.RGrid.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.EvtRGridCellSelect)
            self.RGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.EvtRGridCellChange)

            # Some buttons
            buttonSizer = wx.BoxSizer(wx.HORIZONTAL)

            ButtonPanel = wx.Panel(self.LeftPanel, -1)
            #ButtonPanel.SetSize((20,30))
            #ButtonPanel.SetMinSize((20,30))
            self.BUT_optimise = wx.Button(ButtonPanel, label="Optimize")
            self.BUT_minsum = wx.Button(ButtonPanel, label="Min. SumAbsFlux")
            self.Bind(wx.EVT_BUTTON, self.EVT_BUT_optimise, self.BUT_optimise)
            self.Bind(wx.EVT_BUTTON, self.EVT_BUT_minsum, self.BUT_minsum)
            buttonSizer.Add(self.BUT_optimise, 1, wx.CENTER)
            buttonSizer.Add(self.BUT_minsum, 1, wx.CENTER)
            ButtonPanel.SetSizer(buttonSizer)

            # add status text controls
            statusSizer = wx.GridSizer(2,4,1,1)
            StatusPanel = wx.Panel(self.LeftPanel, -1, size=wx.Size(self.PanelSize[0]*self.LPwidth,-1))
            STS_OBJ_FUNC_LBL = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            STS_OBJ_FUNC_LBL.write('ObjValue')
            STS_OBJ_FUNC_LBL.SetBackgroundColour(wx.Colour(255,255,153))
            statusSizer.Add(STS_OBJ_FUNC_LBL)
            STS_OBJ_SENSE_LBL = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            STS_OBJ_SENSE_LBL.write('ObjSense')
            STS_OBJ_SENSE_LBL.SetBackgroundColour(wx.Colour(255,255,153))
            statusSizer.Add(STS_OBJ_SENSE_LBL)
            STS_OBJ_COND_LBL = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            STS_OBJ_COND_LBL.write('ObjStatus')
            STS_OBJ_COND_LBL.SetBackgroundColour(wx.Colour(255,255,153))
            statusSizer.Add(STS_OBJ_COND_LBL)

            SEARCH_OBJ_LBL = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            SEARCH_OBJ_LBL.write('SearchReactions')
            SEARCH_OBJ_LBL.SetMinSize(wx.Size(130,-1))
            SEARCH_OBJ_LBL.SetBackgroundColour(wx.Colour(102,255,255))
            statusSizer.Add(SEARCH_OBJ_LBL)

            ## the combobox Control
            #self.SEARCH_SELECT_COMB = wx.ComboBox(StatusPanel, size=wx.Size(130, -1), choices=['AutoComplete', 'SubString'], style=wx.CB_READONLY)
            #self.SEARCH_SELECT_COMB.SetStringSelection('AutoComplete')
            #statusSizer.Add(self.SEARCH_SELECT_COMB)
            ## self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)

            self.STS_OBJ_FUNC = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            statusSizer.Add(self.STS_OBJ_FUNC)
            self.STS_OBJ_SENSE = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            statusSizer.Add(self.STS_OBJ_SENSE)
            self.STS_OBJ_COND = wx.TextCtrl(StatusPanel, -1, style=wx.TE_READONLY | wx.TE_CENTER)
            statusSizer.Add(self.STS_OBJ_COND)

            def SearchSelectCallback(sList):
                print('Selection list', sList)
                self.UpdateReactionGraph(sList[0])
                self.updateInfoFromReactionName(sList[0])
                self.SelectGridRow(sList[0])


            try:
                self.SEARCH_OBJ = TextCtrlAutoComplete(StatusPanel, choices=self._cmod_.getReactionIds(),\
                                                       selectCallback=SearchSelectCallback, style=wx.TE_LEFT)
                self.SEARCH_OBJ.SetMinSize(wx.Size(130, -1))
                self.SEARCH_OBJ.Enable(True)
            except:
                self.SEARCH_OBJ = wx.TextCtrl(StatusPanel, -1, choices=self._cmod_.getReactionIds(), style=wx.TE_READONLY | wx.TE_CENTER)
                self.SEARCH_OBJ.SetMinSize(wx.Size(130,-1))
                self.SEARCH_OBJ.Enable(True)
            statusSizer.Add(self.SEARCH_OBJ)
            StatusPanel.SetSizer(statusSizer)
            self.StatusPanel = StatusPanel

            leftSizer.Add(StatusPanel)
            leftSizer.AddSpacer(2)
            leftSizer.Add(ButtonPanel)
            leftSizer.AddSpacer(2)
            leftSizer.Add(self.RGrid_scrollwindow)
            self.LeftPanel.SetSizer(leftSizer)

            # create the Rpanel notebook
            rightSizer = wx.BoxSizer(wx.VERTICAL)
            self.RightPanel = wx.Panel(self.MainPanel, -1)
            NoteB1size = (self.PanelSize[0]*self.RPwidth, self.PanelSize[1]*self.RPheight)
            self.NoteB1 = wx.Notebook(self.RightPanel, size=NoteB1size)
            self.NoteB1.SetSize(NoteB1size)
            self.NoteB1.SetMinSize(NoteB1size)
            ##  self.NoteB1.SetPadding((10,-1))
            # create panels
            RinfSize = wx.Size(NoteB1size[0]-8, NoteB1size[1]-25)
            self.NoteB1_Psession =  wx.Panel(self.NoteB1, -1)
            self.NoteB1_Preaction = wx.Panel(self.NoteB1, -1)
            self.NoteB1_Pspecies = wx.Panel(self.NoteB1, -1)
            self.NoteB1_Prelate = wx.Panel(self.NoteB1, -1)
            self.NoteB1_Pgene = wx.Panel(self.NoteB1, -1)
            #self.NoteB1_PRedit = wx.Panel(self.NoteB1, -1)
            self.NoteB1_PRedit = wx.lib.scrolledpanel.ScrolledPanel(self.NoteB1, -1, style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
            self.NoteB1_PRedit.SetAutoLayout(1)
            self.NoteB1_PRedit.SetupScrolling()

            #create pybox: python script window
            self.PyBox = wx.TextCtrl(self.NoteB1_Psession, -1, size=self.NoteB1.GetVirtualSize(), style=wx.TE_MULTILINE | wx.HSCROLL)
            PyBox_sizer = wx.BoxSizer(wx.VERTICAL)
            PyBox_sizer.Add(self.PyBox, -1, wx.EXPAND)
            #self.NoteB1_Psession.Fit(PyBox_sizer)
            self.PyBox.SetEditable(False)
            self.writeCmd("#############################\n# ")
            self.writeCmd("# PySCeS-CBM GUI generated command file ")
            self.writeCmd("# Please note that the model instance is: cmod ")
            self.writeCmd("#\n#############################\n")
            self.writeCmd("import cbmpy\n")
            self.writeCmd("# cmod = cbmpy.CBRead.readSBML2FBA(ModelFile, ModelDir)\n")
            self.PyBox.SetMinSize(self.NoteB1.GetVirtualSize())

            # create html panel for reaction information
            self.Rinfbox = wx.html.HtmlWindow(self.NoteB1_Preaction, -1, size=self.NoteB1.GetVirtualSize(), style=wx.html.HW_SCROLLBAR_AUTO)
            self.Rinfbox.SetBorders(0)
            Rinfbox_sizer = wx.BoxSizer(wx.VERTICAL)
            Rinfbox_sizer.Add(self.Rinfbox, 0, wx.EXPAND)
            self.Rinfbox.SetMinSize(self.NoteB1.GetVirtualSize())

            # create reaction editor
            self.FontCache.update({'SanSer12CS' : wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, u'Comic Sans MS')})
            self.FontCache.update({'SanSer12' : wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False)})
            self.FontCache.update({'SanSer10' : wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)})

            # create the box sizer
            Reditbox_sizer = wx.BoxSizer(wx.VERTICAL)

            # create the edit button
            #self.BUT_EnableEdit = wx.Button(self.NoteB1_PRedit, label="Edit Reaction")
            #self.Bind(wx.EVT_BUTTON, self.EVT_BUT_EnableEdit, self.BUT_EnableEdit)
            #Reditbox_sizer.Add(self.BUT_EnableEdit)

            # create the component grid
            #Reditgrid_sizer = wx.GridSizer(3,2,1,1)
            #grow = 10
            #gcol = 2
            #cntr = True
            #self.TEXTBOXES_Redit = []
            #for tp in range(grow*gcol):
                #cellHeight = -1
                #if cntr:
                    #tx = wx.TextCtrl(self.NoteB1_PRedit, style=wx.DEFAULT | wx.TE_READONLY | wx.TE_CENTER)
                    #tx.SetSize(wx.Size(RinfSize[1]/4, cellHeight))
                    #tx.SetMinSize(wx.Size(RinfSize[1]/4, cellHeight))
                    #tx.SetFont(self.FontCache['SanSer10'])
                    #cntr = False
                #else:
                    #tx = wx.TextCtrl(self.NoteB1_PRedit, style=wx.DEFAULT | wx.TE_LEFT | wx.EXPAND )
                    #tx.SetSize(wx.Size(RinfSize[1]/2, cellHeight))
                    #tx.SetMinSize(wx.Size(RinfSize[1]/2, cellHeight))
                    #tx.SetFont(self.FontCache['SanSer10'])
                    #cntr = True
                #tx.Disable()
                #tx.WriteText(str(tp+1))
                #tx.PID = str(tp+1)
                #Reditgrid_sizer.Add(tx, wx.EXPAND)
                #self.TEXTBOXES_Redit.append(tx)
            #Reditbox_sizer.Add(Reditgrid_sizer, wx.EXPAND) # add to boxsizer

            # create the annotate button
            #self.BUT_Annotate = wx.Button(self.NoteB1_PRedit, label="SemanticSBML")
            #self.Bind(wx.EVT_BUTTON, self.EVT_BUT_Annotate, self.BUT_Annotate)
            #Reditbox_sizer.Add(self.BUT_Annotate)

            # the big text box
            #self.TEXTBOX_Annotate = wx.TextCtrl(self.NoteB1_PRedit, style=wx.TE_MULTILINE | wx.EXPAND )
            #self.TEXTBOX_Annotate.SetMinSize(wx.Size(self.NoteB1.GetVirtualSize()[1], -1))
            #self.TEXTBOX_Annotate.SetFont(self.FontCache['SanSer10'])
            #self.TEXTBOX_Annotate.Disable()
            #Reditbox_sizer.Add(self.TEXTBOX_Annotate, wx.EXPAND) # add to boxsizer

            # add boxSizer to Panel
            self.NoteB1_PRedit.SetSizer(Reditbox_sizer, wx.DEFAULT)
            self.NoteB1_PRedit.SetMinSize(self.NoteB1.GetVirtualSize())

            # create html panel for reagent information
            self.Sinfbox = wx.lib.ClickableHtmlWindow.PyClickableHtmlWindow(self.NoteB1_Pspecies, -1, size=self.NoteB1.GetVirtualSize(), style=wx.html.HW_SCROLLBAR_AUTO)
            self.Sinfbox.SetBorders(0)
            Sinfbox_sizer = wx.BoxSizer(wx.VERTICAL)
            Sinfbox_sizer.Add(self.Sinfbox, -1, wx.EXPAND)
            self.Sinfbox.SetMinSize(self.NoteB1.GetVirtualSize())

            # Create canvas panels for graphical representations
            wx.lib.colourdb.updateColourDB()
            self.WX_COLOURS = wx.lib.colourdb.getColourList()
            self.FC_LineStyles = list(FloatCanvas.DrawObject.LineStyleList)

            # create a met/gen canvas and do voodoo to get it to fill the notebook panel
            self.MetCanvas = NavCanvas.NavCanvas(self.NoteB1_Prelate, -1, size=self.NoteB1.GetVirtualSize(), Debug=0, ProjectionFun=None, BackgroundColor="DARK SLATE BLUE")
            self.FCanvas_met = self.MetCanvas.Canvas
            self.FCanvas_met.SetProjectionFun(None)
            self.FCanvas_met.InitAll()
            self.GenCanvas = NavCanvas.NavCanvas(self.NoteB1_Pgene, -1, size=self.NoteB1.GetVirtualSize(), Debug=0, ProjectionFun=None, BackgroundColor="DARK SLATE BLUE")
            self.FCanvas_gen = self.GenCanvas.Canvas
            self.FCanvas_gen.SetProjectionFun(None)
            self.FCanvas_gen.InitAll()

            NC1_sizer = wx.BoxSizer(wx.VERTICAL)
            NC1_sizer.Add(self.MetCanvas, 1, wx.EXPAND)
            NC2_sizer = wx.BoxSizer(wx.VERTICAL)
            NC2_sizer.Add(self.GenCanvas, 1, wx.EXPAND)
            self.FCanvas_met.SetMinSize(self.NoteB1.GetVirtualSize())
            self.FCanvas_gen.SetMinSize(self.NoteB1.GetVirtualSize())

            ##  self.FCanvas_met.Bind(FloatCanvas.EVT_LEFT_DOWN, self.EVT_FC1_onClick)
            ##  self.FCanvas_gen.Bind(FloatCanvas.EVT_LEFT_DOWN, self.EVT_FC2_onClick)

            # add the pages to the notebook with the label to show on the tab
            self.NoteB1.AddPage(self.NoteB1_Psession, "Session")
            self.NoteB1.AddPage(self.NoteB1_Preaction, "Reaction")
            self.NoteB1.AddPage(self.NoteB1_Prelate, "Metabolism")
            # TODO
            self.NoteB1.AddPage(self.NoteB1_Pgene, "Genes")
            self.NoteB1.AddPage(self.NoteB1_PRedit, "ReacEdt")
            self.NoteB1.AddPage(self.NoteB1_Pspecies, "MIRIAM")

            rightSizer.Add(self.NoteB1)
            #self.RightPanel.Fit(rightSizer)

            # Add components to the main panel

            self.mainSizer.Add(self.LeftPanel, 1, wx.EXPAND)
            self.mainSizer.Add(self.RightPanel, 1, wx.EXPAND)
            self.MainPanel.SetSizerAndFit(self.mainSizer)

            ## Resize control
            self.Bind(wx.EVT_SIZE, self.EVT_FRAME_resize, self)

            ##GENERIC click event
            #self.Bind(wx.EVT_SIZE, self.EVT_onClick, self.MainPanel)

        def UpdateModelStatus(self):
            self.STS_OBJ_SENSE.Clear()
            self.STS_OBJ_COND.Clear()
            self.STS_OBJ_FUNC.Clear()
            self.STS_OBJ_COND.write(str(self._cmod_.SOLUTION_STATUS))
            if self._cmod_.SOLUTION_STATUS == 'LPS_OPT':
                self.STS_OBJ_COND.SetBackgroundColour(wx.Colour(255,255,255))
            else:
                self.STS_OBJ_COND.SetBackgroundColour(wx.Colour(255,0,51))
            if self._cmod_.getActiveObjective() != None:
                self.STS_OBJ_FUNC.write(str(self._cmod_.getActiveObjective().getValue()))
                self.STS_OBJ_SENSE.write(self._cmod_.getActiveObjective().operation)
            else:
                self.STS_OBJ_FUNC.write('None')
                self.STS_OBJ_SENSE.write('None')

        def UpdateReactionInfo(self,rid):
            print('Updating reaction info')
            r = self._cmod_.getReaction(rid)

            rs = '<h2>%s</h2><p>%s</p>' % (rid, r.getName())
            rs += '<h3>Equation</h3><p>%s</p>' % self.GetEquation(r)
            rs += '<p><br/></p>'
            ##  rs += '<h3>Details</h3>'
            props = '<tr><th colspan="2"><strong>Properties</strong></th></tr>'
            props += '<tr><td>%s</td><td>%s</td></tr><tr><td>%s</td><td>%s</td></tr>' % ('Reversible',r.reversible,'Exchange',r.is_exchange)
            props += '<tr><th colspan="2"><strong>Annotations</strong></th></tr>'
            RA = r.getAnnotations()
            for a in RA:
                props += '<tr><td>%s</td><td>%s</td></tr>' % (a, RA[a])
            ##  rs += '<table border="1" cellpadding="5" width="70%s">%s</table>' % ('\\%', props)
            rs += '<table border="1" cellpadding="5">%s</table>' % (props)

            if self.ReactionBalanceInfo != None:
                if rid in self.ReactionBalanceInfo:
                    rs += '<h3>Balancing information</h3>'
                    bi = self.ReactionBalanceInfo[rid]
                    #print bi
                    bal = '<tr><td colspan="2">Charge balanced: %s {\'charge\', %s}</td></tr>' % (bi['charge_balanced'], bi['charge'])
                    bal += '<tr><td colspan="2">Element balanced: %s %s</td></tr>' % (bi['element_balanced'], bi['elements'])
                    for rre in bi['stuff']:
                        sbal = ''
                        coeff = None
                        for det in range(len(rre)):
                            out = ''
                            if det == 0:
                                out = rre[det]
                            if det == 1:
                                coeff = rre[det]
                                #out = re[det]
                            elif det == 3:
                                out = ''
                                if rre[2] == '' or rre[3] == None:
                                    out = 'Unknown'
                                else:
                                    for e in rre[det]:
                                        out += '%.1f %s, ' % (coeff*e[1], e[0])
                                    out = out[:-2]
                            if out != '':
                                sbal += '<td>%s</td>' % out
                        bal += '<tr>%s</tr>' % (sbal)
                    rs += '<table border="1" cellpadding="5">%s</table>' % (bal)
            props = ''
            rs += '<h3>Reagents</h3>'
            for sid in r.getSpeciesIds():
                s = self._cmod_.getSpecies(sid)
                props += '<h4>%s</h4><table border="1" cellpadding="5">' % sid
                ##  props += '<tr><th colspan="2"><strong>%s</strong></th></tr>' % sid
                props += '<tr><td>%s</td><td>%s</td></tr><tr><td>%s</td><td>%s</td></tr>' % ('Name',s.getName(),'Compartment',s.compartment)
                props += '<tr><td>%s</td><td>%s</td></tr>' % ('Fixed',s.is_boundary)
                props += '<tr><td>%s</td><td>%s</td></tr>' % ('Coefficient', r.getReagentWithSpeciesRef(sid).coefficient)
                ##  props += '<tr><th colspan="2">Annotations</th></tr>'
                props += '<tr><td>%s</td><td>%s</td></tr><tr><td>%s</td><td>%s</td></tr>' % ('ChemFormula',s.chemFormula,'Charge',s.charge)
                ##  SA = s.getAnnotations()
                ##  for a in RA:
                    ##  props += '<tr><td>%s</td><td>%s</td></tr>' % (a, SA[a])
                ##  rs += '<table border="1" cellpadding="5" width="70%s">%s</table>' % ('\\%', props)
                props += '</table>'
            rs += props
                ##  rs += '<table border="1" cellpadding="3" width="80%s"><tr><th>Property</th><th>Value</th></tr>%s</table>' % ('\\%', props)


            self.Rinfbox.SetPage("<html><body>%s</body></html>" % rs)

        def SelectGridRow(self, rid):
            self.RGrid.SelectRow(self.RGridRow.index(rid))
            self.RGrid.MakeCellVisible(self.RGridRow.index(rid), 0)

        def UpdateReactionGraph(self, rid):
            R = self._cmod_.getReaction(rid)
            subs = []
            prods = []
            self.FCanvas_met.ClearAll()
            STB_LW = 2
            STB_PAD = 0.15
            STB_SZ = 0.25
            STB_LC = 'Black'
            GC = (0,0)

            FC_OBJ = []
            if rid not in self.RGdict:
                STB = FloatCanvas.ScaledTextBox(rid, GC, Size=STB_SZ, PadSize=STB_PAD,\
                                                LineWidth=STB_LW, LineColor=STB_LC,\
                                                Family=wx.FONTFAMILY_DEFAULT, Weight=wx.BOLD,\
                                                BackgroundColor='White')
                self.__STB_center__(STB)
                STB.Name = rid
                STB.HitFill = True
                STB.HitLineWidth = 5
                FC_OBJ.append(STB)

                for rr in R.reagents:
                    if rr.coefficient < 0:
                        subs.append(rr)
                    elif rr.coefficient > 0:
                        prods.append(rr)
                radius = None
                if len(R.reagents) == 1:
                    radius = 2
                    cxy = [(0,radius)]
                elif len(R.reagents) == 2:
                    radius = 2
                    cxy = circlePoints(totalPoints=len(R.reagents), startAngle=0, arc=360, circleradius=radius, centerxy=GC, direction='forward', evenDistribution=True)
                else:
                    if len(R.reagents) < 12:
                        radius = 3
                    else:
                        radius = 10
                    cxy = circlePoints(totalPoints=len(R.reagents), startAngle=270, arc=360, circleradius=radius, centerxy=GC, direction='forward', evenDistribution=True)

                scntr = 0
                rcntr = 0
                for s in range(len(subs)):
                    if self._cmod_.getSpecies(subs[s].species_ref).is_boundary:
                        STB_LC = 'Red'
                    else:
                        STB_LC = 'Black'
                    if abs(subs[s].coefficient) != 1:
                        cf = '{%.1f} ' % abs(subs[s].coefficient)
                    else:
                        cf = ''
                    STB = FloatCanvas.ScaledTextBox('%s%s' % (cf,subs[s].species_ref), cxy[rcntr], Size=STB_SZ, PadSize=STB_PAD, LineWidth=STB_LW, LineColor=STB_LC, Family=wx.SWISS, BackgroundColor='Green')
                    STB.Name = subs[s].species_ref
                    self.__STB_center__(STB)
                    STB.HitFill = True
                    STB.HitLineWidth = 5
                    FC_OBJ.append(STB)
                    scntr += 1
                    rcntr += 1
                pcntr = 0
                for p in range(len(prods)):
                    if self._cmod_.getSpecies(prods[p].species_ref).is_boundary:
                        STB_LC = 'Red'
                    else:
                        STB_LC = 'Black'
                    if abs(prods[p].coefficient) != 1:
                        cf = '{%.1f} ' % abs(prods[p].coefficient)
                    else:
                        cf = ''
                    STB = FloatCanvas.ScaledTextBox('%s%s' % (cf, prods[p].species_ref), cxy[rcntr], Size=STB_SZ, PadSize=STB_PAD, LineWidth=STB_LW, LineColor=STB_LC, Family=wx.SWISS, BackgroundColor='Yellow')
                    STB.Name = prods[p].species_ref
                    self.__STB_center__(STB)
                    STB.HitFill = True
                    STB.HitLineWidth = 5
                    FC_OBJ.append(STB)
                    pcntr += 1
                    rcntr += 1
                self.RGdict.update({rid : {'obj' : FC_OBJ}})
                ##  print 'Drawing from scratch'
            else:
                ##  print 'Using cached graph'
                FC_OBJ = self.RGdict[rid]['obj']
            self.FCanvas_met.AddObjects(FC_OBJ)
            for o in FC_OBJ:
                o.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.HIT_STB_onClick)
            self.FCanvas_met.Draw(True)
            self.FCanvas_met.ZoomToBB()

        def __STB_center__(self, stb):
            box = stb.GetBoxRect()
            stb.SetPoint( ( box[0][0] - abs(box[1][0])/2.0, box[0][1] - abs(box[1][1])/2.0) )
            return stb.GetBoxRect()

        def UpdateSpeciesGraph(self, sid):
            #print sid, self.Smap[sid]
            STB_LW = 2
            STB_PAD = 0.15
            STB_SZ = 0.25
            STB_LC = 'Black'
            GC = (0,0)
            radius = None

            rList = self.Smap[sid]
            if self.OnlyActiveReactions:
                rList = [j for j in self.Smap[sid] if round(abs(self._cmod_.getReaction(j).getValue()),6) != 0.0]

            self.FCanvas_met.ClearAll()
            FC_OBJ = []
            if sid not in self.SGdict:
                orphan = False
                print('ReactionList', rList)
                if len(rList) == 0:
                    radius = 2
                    cxz = GC
                elif len(rList) == 1:
                    radius = 2
                    cxy = [(0,radius)]
                elif len(rList) == 2:
                    radius = 2
                    cxy = circlePoints(totalPoints=len(rList), startAngle=0, arc=360, circleradius=radius, centerxy=GC, direction='forward', evenDistribution=True)
                else:
                    if len(rList) < 12:
                        radius = 3
                    elif len(rList) < 24:
                        radius = 6
                    elif len(rList) < 36:
                        radius = 9
                    else:
                        radius = 15
                    cxy = circlePoints(totalPoints=len(rList), startAngle=270, arc=360, circleradius=radius, centerxy=GC, direction='forward', evenDistribution=True)
                    print(cxy)

                STB = FloatCanvas.ScaledTextBox(sid, GC, Size=STB_SZ, PadSize=STB_PAD, LineWidth=STB_LW, LineColor=STB_LC, Family=wx.NORMAL, Weight=wx.BOLD, BackgroundColor='White')
                self.__STB_center__(STB)
                STB.Name = sid
                STB.HitFill = True
                STB.HitLineWidth = 5
                FC_OBJ.append(STB)

                rcntr = 0
                for R in range(len(rList)):
                    cf = ''
                    STB = FloatCanvas.ScaledTextBox('%s%s' % (cf, rList[R]), cxy[R], Size=STB_SZ, PadSize=STB_PAD, LineWidth=STB_LW, LineColor=STB_LC, Family=wx.SWISS, BackgroundColor='Yellow')
                    STB.Name = self.Smap[sid][R]
                    self.__STB_center__(STB)
                    STB.HitFill = True
                    STB.HitLineWidth = 5
                    FC_OBJ.append(STB)
                    rcntr += 1
                self.SGdict.update({sid : {'obj' : FC_OBJ}})
                ##  print 'Drawing from scratch'
            else:
                ##  print 'Using cached graph'
                FC_OBJ = self.SGdict[sid]['obj']
            self.FCanvas_met.AddObjects(FC_OBJ)
            for o in FC_OBJ:
                o.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.HIT_STB_onClick)
            self.FCanvas_met.Draw(True)
            self.FCanvas_met.ZoomToBB()

        def CreateMaps(self):
            for S in self._cmod_.species:
                self.Smap.update({S.getId() : S.isReagentOf()})
            for R in self._cmod_.reactions:
                self.Rmap.update({R.getId() : R.getSpeciesIds()})
            self.GPRmap = self._cmod_.getAllGeneProteinAssociations()
            self.PRGmap = self._cmod_.getAllProteinGeneAssociations()

        def GetEquation(self, R):
            sub = ''
            prod = ''
            for r in R.reagents:
                coeff = abs(r.coefficient)
                if r.role == 'substrate':
                    if coeff == 1.0:
                        sub += '%s + ' % (r.species_ref)
                    else:
                        sub += '{%s} %s + ' % (coeff, r.species_ref)
                else:
                    if coeff == 1.0:
                        prod += '%s + ' % (r.species_ref)
                    else:
                        prod += '{%s} %s + ' % (coeff, r.species_ref)
            if R.reversible:
                eq = '%s = %s' % (sub[:-3], prod[:-2])
            else:
                eq = '%s > %s' % (sub[:-3], prod[:-2])
            return eq

        def CreateRGrid(self):
            assert len(self.rlabels) == self.RGridCol, '\nlabels != #col'
            grid = self.RGrid
            reactions = self._cmod_.reactions
            fluxObjs = []
            if self._cmod_.getActiveObjective() != None:
                fluxObjs = self._cmod_.getActiveObjective().getFluxObjectiveReactions()
            for c in range(self.RGridCol):
                grid.SetColLabelValue(c, self.rlabels[c])
            for r in range(len(reactions)):
                for c in range(self.RGridCol):
                    ##  print r,c
                    ##  print reactions[r].getId()
                    if self.rlabels[c] == 'LB':
                        ##  grid.SetColFormatFloat(c, 10, 3)
                        grid.SetCellValue(r, c, '%s' % self._cmod_.getReactionLowerBound(reactions[r].getId()))
                    elif self.rlabels[c] == 'UB':
                        ##  grid.SetColFormatFloat(c, 10, 3)
                        grid.SetCellValue(r, c, '%s' % self._cmod_.getReactionUpperBound(reactions[r].getId()))
                    elif self.rlabels[c] == 'd':
                        grid.SetCellValue(r, c, '%s' % '   ')
                    elif self.rlabels[c] == 'Balanced':
                        grid.SetReadOnly(r,c,True)
                        txt = reactions[r].is_balanced
                        grid.SetCellValue(r, c, '  %s' % str(txt) )
                        if txt:
                            grid.SetCellBackgroundColour(r, c, wx.Colour(255,193,96))
                    elif self.rlabels[c] == 'Flux':
                        Rval = reactions[r].getValue()
                        if Rval != None:
                            Rval = round(Rval, 10)
                        else:
                            Rval = 'None'
                        grid.SetCellValue(r, c, '  %s' % Rval)
                        grid.SetReadOnly(r,c,True)
                        # colour by sign
                        if Rval == None or Rval == '' or Rval == 'None' or Rval == 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(255,255,255))
                        elif Rval < 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(255,204,204))
                        elif Rval > 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(153,255,153))
                        # boundary detection
                    elif self.rlabels[c] == 'Exch':
                        grid.SetReadOnly(r,c,True)
                        grid.SetCellValue(r, c, str(reactions[r].is_exchange))
                    elif self.rlabels[c] == 'RCost':
                        grid.SetReadOnly(r,c,True)
                        if not self.__ScaledReducedCost:
                            if reactions[r].reduced_cost != None:
                                rcval = '%2.3e' % reactions[r].reduced_cost
                            else:
                                rcval = 'None'
                            grid.SetCellValue(r, c, rcval)
                    elif self.rlabels[c] == 'Reaction':
                        grid.SetCellBackgroundColour(r,c,wx.Colour(198,226,255))
                        grid.SetCellValue(r, c, str(reactions[r].getId()))
                        grid.SetReadOnly(r,c,True)
                    elif self.rlabels[c] == 'Name':
                        ##  grid.SetCellBackgroundColour(r,c,wx.Colour(255,255,153))
                        grid.SetCellValue(r, c, str(reactions[r].getName()))
                    else:
                        grid.SetCellValue(r, c, '')
                    if reactions[r].getId() in fluxObjs:
                        grid.SetCellBackgroundColour(r,c,wx.Colour(255,255,153))
            for c in range(self.RGridCol):
                if self.rlabels[c] not in ('Name'):
                    grid.AutoSizeColumn(c, True)
                else:
                    grid.SetColMinimalWidth(c, 40)

        def UpdateRGridData(self):
            """
            Updates Rgrid numeric data
            """
            grid = self.RGrid
            reactions = self._cmod_.reactions
            if self._cmod_.getActiveObjective() != None:
                fluxObjs = self._cmod_.getActiveObjective().getFluxObjectiveReactions()
            else:
                fluxObjs = []

            for r in range(len(reactions)):
                for c in range(self.RGridCol):
    ##                 if self.rlabels[c] == 'LB':
    ##                     grid.SetCellValue(r, c, '%s' % self._cmod_.getFluxBoundByReactionID(reactions[r].getId(), 'lower').getValue())
    ##                 elif self.rlabels[c] == 'UB':
    ##                     grid.SetCellValue(r, c, '%s' % self._cmod_.getFluxBoundByReactionID(reactions[r].getId(), 'upper').getValue())
                    if self.rlabels[c] == 'Flux':
                        Rval = reactions[r].getValue()
                        try:
                            Rval_curr = float(grid.GetCellValue(r, c))
                        except:
                            Rval_curr = None
                        if Rval != None:
                            Rval = round(Rval, 10)
                        else:
                            Rval = 'None'
                        grid.SetCellValue(r, c, '  %s' % Rval)
                        # colour delta
                        D_col_idx = self.rlabels.index('d')
                        if Rval in self.NOVAL or Rval_curr in self.NOVAL or abs(Rval - Rval_curr) <= self.ZERO_TOL:
                            grid.SetCellValue(r, D_col_idx, '  ')
                            grid.SetCellTextColour(r, D_col_idx, wx.Colour(255,255,255))
                        elif abs(round(Rval,10)) < abs(round(Rval_curr,10)):
                            grid.SetCellValue(r, D_col_idx, ' \/ ')
                            grid.SetCellTextColour(r, D_col_idx, wx.Colour(255,0,0))
                        elif abs(round(Rval,10)) > abs(round(Rval_curr,10)):
                            grid.SetCellValue(r, D_col_idx, ' /\ ')
                            grid.SetCellTextColour(r, D_col_idx, wx.Colour(0,204,0))
                        # colour by sign J
                        if Rval in self.NOVAL or Rval == 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(255,255,255))
                        elif Rval < 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(255,204,204))
                        elif Rval > 0.0:
                            grid.SetCellBackgroundColour(r,c,wx.Colour(153,255,153))

                        # boundary detection
                        if self.__BoundaryDetection:
                            LB = self._cmod_.getReactionLowerBound(reactions[r].getId())
                            UB = self._cmod_.getReactionUpperBound(reactions[r].getId())
                            try:
                                if abs(Rval - round(LB, 10)) <= self.ZERO_TOL:
                                    grid.SetCellBackgroundColour(r,self.rlabels.index('LB'), wx.Colour(255,204,204))
                                else:
                                    grid.SetCellBackgroundColour(r,self.rlabels.index('LB'), wx.Colour(255,255,255))
                            except:
                                print('INFO: LowerBound detector failed')
                            try:
                                if abs(Rval - round(UB, 10)) <= self.ZERO_TOL:
                                    grid.SetCellBackgroundColour(r,self.rlabels.index('UB'), wx.Colour(153,255,153))
                                else:
                                    grid.SetCellBackgroundColour(r,self.rlabels.index('UB'), wx.Colour(255,255,255))
                            except:
                                print('INFO: UpperBound detector failed')
                    elif self.rlabels[c] == 'RCost':
                        if not self.__ScaledReducedCost:
                            if reactions[r].reduced_cost != None:
                                rcval = '%2.3e' % reactions[r].reduced_cost
                            else:
                                rcval = 'None'
                            grid.SetCellValue(r, c, rcval)
                    elif self.rlabels[c] == 'Balanced':
                        bval = reactions[r].is_balanced
                        grid.SetCellValue(r, c, str(bval))
                        if bval != None and not bval:
                            grid.SetCellBackgroundColour(r, c, wx.Colour(255,193,96))

                    if reactions[r].getId() in fluxObjs:
                        grid.SetCellBackgroundColour(r,c,wx.Colour(255,255,153))
            grid.ForceRefresh()

        ##  def EvtGridCellSelect(self, event):
            ##  row = event.GetRow()
            ##  col = event.GetCol()
            ##  self.cell_selected_value = self.RGrid.GetCellValue(row, col)
            ##  print 'selected value', self.cell_selected_value
            ##  event.Skip()

        def writeCmd(self, txt):
            print('cmd: '+txt)
            self.PyBox.write(txt+'\n')


        def MENUOnAbout(self,e):
            # Create a message dialog box
            dlg = wx.MessageDialog(self, " PySCes-CBM model editor\n(C) Brett G. Olivier, Amsterdam 2012", "About PySCes-CBM model editor", wx.OK)
            dlg.ShowModal() # Shows it
            dlg.Destroy() # finally destroy it when finished.

        def MENUOnExit(self,e):
            self.Close(True)  # Close the frame.

        def MENUExport(self,e):
            dlg = wx.FileDialog(self, "Enter filename", self.OUT_dir, "", "Python files (*.py)|*.py|All files (*.*)|*.*", wx.FD_SAVE)
            if dlg.ShowModal() == wx.ID_OK:
                OUT_file = dlg.GetFilename()
                self.OUT_dir = dlg.GetDirectory()
                if OUT_file[-3:] != '.py':
                    OUT_file += '.py'
                F = file(os.path.join(self.OUT_dir, OUT_file), 'w')
                F.write(self.PyBox.GetValue())
                F.flush()
                F.close()
            dlg.Destroy()

        def MENUOnSave(self,e):
            """ Open a file"""
            dlg = wx.FileDialog(self, "Enter filename", self.OUT_dir, "", "SBML files (*.xml)|*.xml|All files (*.*)|*.*", wx.FD_SAVE)
            if dlg.ShowModal() == wx.ID_OK:
                self.OUT_file = dlg.GetFilename()
                self.OUT_dir = dlg.GetDirectory()
                if self.OUT_file[-4:] != '.xml':
                    self.OUT_file += '.xml'
                self._cmod_.inputfile_id = self.OUT_file
                self._cbm_.CBWrite.writeSBML2FBA(self._cmod_, self.OUT_file, directory=self.OUT_dir, sbml_level_version=None)
            dlg.Destroy()

        def MENUAnalyseBalances(self, e):
            ''' Check the reaction balances'''
            wait = wx.BusyCursor()
            rids = self._cmod_.getReactionIds()
            self.ReactionBalanceInfo = self._cbm_.CBTools.checkReactionBalanceElemental(self._cmod_, Rid=rids)
            for r in range(len(self._cmod_.reactions)):
                for c in range(self.RGridCol):
                    if self.rlabels[c] == 'Balanced':
                        bval = self._cmod_.reactions[r].is_balanced
                        self.RGrid.SetCellValue(r, c, str(bval))
                        if bval != None and not bval:
                            self.RGrid.SetCellBackgroundColour(r, c, wx.Colour(255,193,96))
            del wait
            wx.MessageBox('Balance Check Complete', 'Info', wx.OK | wx.ICON_INFORMATION)

        def chkFloat(self, val):
            try:
                x = float(val)
                self.StatusBar.SetStatusText('')
                return True
            except:
                self.StatusBar.SetStatusText('Invalid input %s was not a float!' % val)
                return False

        def EvtRGridCellSelect(self, event):
            print("Cell select event")
            row = event.GetRow()
            if row != self.__ActiveReaction:
                cval = self.RGrid.GetCellValue(row, 0)
                self.updateInfoFromReactionName(cval)
            event.Skip()

        def updateInfoFromReactionName(self, cval):
            self.UpdateReactionInfo(cval)
            #self.UpdateSpeciesInfoForReaction(cval)
            self.UpdateReactionGraph(cval)
            self.__ActiveReaction = cval

        def EvtRGridCellChange(self, event):
            row = event.GetRow()
            col = event.GetCol()
            clbl = self.RGrid.GetColLabelValue(col)
            #All cells have a value, regardless of the editor.
            print('Changed cell: ({}, {})'.format(row, col))
            rid = self.RGrid.GetCellValue(row, 0)
            cell_val = self.RGrid.GetCellValue(row, col)
            print('Row/Col: {} | {} | {}'.format(rid, clbl, cell_val))
            # update reaction info
            self.UpdateReactionInfo(rid)
            print(cell_val, type(cell_val))
            cell_val = str(cell_val)
            if clbl == 'LB':
                if self.chkFloat(cell_val):
                    print(cell_val, type(cell_val))
                    print('\nLB old', self._cmod_.getReactionLowerBound(rid))
                    self._cmod_.setReactionLowerBound(rid, cell_val)
                    print('LB new', self._cmod_.getReactionLowerBound(rid))
                    if cell_val == 'inf' or cell_val == '-inf':
                        self.writeCmd("cmod.setReactionLowerBound('%s', '%s')" % (rid, cell_val))
                    else:
                        self.writeCmd("cmod.setReactionLowerBound('%s', %s)" % (rid, cell_val))
                else:
                    self.RGrid.SetCellValue(row, col, str(self._cmod_.getReactionLowerBound(rid)))
            elif clbl == 'UB':
                if self.chkFloat(cell_val):
                    print('\nUB old', self._cmod_.getReactionUpperBound(rid))
                    self._cmod_.setReactionUpperBound(rid, cell_val)
                    print('UB new', self._cmod_.getReactionUpperBound(rid))
                    if cell_val == 'inf' or cell_val == '-inf':
                        self.writeCmd("cmod.setReactionUpperBound('%s', '%s')" % (rid, cell_val))
                    else:
                        self.writeCmd("cmod.setReactionUpperBound('%s', %s)" % (rid, cell_val))
                else:
                    self.RGrid.SetCellValue(row, col, str(self._cmod_.getReactionUpperBound(rid)))
            elif clbl == 'Reaction':
                pass
            elif clbl == 'Name':
                print('\nName old', self._cmod_.getReaction(rid).getName())
                self._cmod_.getReaction(rid).setName(cell_val)
                print('\nName new', self._cmod_.getReaction(rid).getName())
                self.writeCmd("cmod.getReaction('%s').setName('%s')" % (rid, cell_val))
            event.Skip()

        #def EVT_BUT_EnableEdit(self,event):
            #print 'EVT_BUT_EnableEdit'
            #for tb in self.TEXTBOXES_Redit:
                #tb.Enable()
            #event.Skip()

        def SemSBML_id(self,event):
            if self.__ActiveReaction != None:
                R = self._cmod_.getReaction(self.__ActiveReaction)
                self.SelectGridRow(R.getId())
                searchString = R.getId()
                self.CallSemanticSBML(searchString)
            else:
                self.Sinfbox.SetPage("<html><body>'<h1>Please select a reaction!</h1>'</body></html>")
            self.NoteB1.ChangeSelection(5)
            event.Skip()

        def SemSBML_name(self,event):
            if self.__ActiveReaction != None:
                R = self._cmod_.getReaction(self.__ActiveReaction)
                self.SelectGridRow(R.getId())
                searchString = R.getName()
                self.CallSemanticSBML(searchString)
            else:
                self.Sinfbox.SetPage("<html><body>'<h1>Please select a reaction!</h1>'</body></html>")
            self.NoteB1.ChangeSelection(5)
            event.Skip()

        def CallSemanticSBML(self, searchString):
            print('MENUAnalyseSemanticSBML')
            #self.TEXTBOX_Annotate.Enable()
            rs = ''
            if HAVE_URLLIB2:
                wait = wx.BusyCursor()
                #self.TEXTBOX_Annotate.Clear()

                rs = '<h2>SemanticSBML query</h2><br/><h3>%s</h3>' % searchString
                # semanticSBML REST web services
                site_root = "www.semanticsbml.org"
                reply_mode = '.xml'
                try:
                    self.SemanticSBMLClient.Connect(site_root)
                    searchString = self.SemanticSBMLClient.URLEncode(searchString)
                    RESTquery = "/semanticSBML/annotate/search%s?q=%s" % (reply_mode, searchString.strip().replace(' ','+'))
                    print(RESTquery)
                    data1 = self.SemanticSBMLClient.Get(RESTquery)
                    data1 = self.SemanticSBMLClient.URLDecode(data1)
                    self.SemanticSBMLClient.Close()
                    self.writeCmd('SemanticSBMLClient = cbm.CBNetDB.SemanticSBML()')
                    self.writeCmd('SemanticSBMLClient.Connect(\"%s\")' % (site_root))
                    self.writeCmd('data1 = SemanticSBMLClient.Get(\"%s\")' % (RESTquery))
                    self.writeCmd('SemanticSBMLClient.Close()')
                    print(self.SemanticSBMLClient.GetLog())

                    item_re = re.compile('<item>.+?</item>')
                    items = re.findall(item_re, data1)
                    items = [i.replace('<item>','').replace('</item>','').strip() for i in items]
                    #self.TEXTBOX_Annotate.write('SemanticSBML results for: %s\n\n' % (urllib2.unquote(searchString)))
                    rs += '<table border="1" cellpadding="5">'
                    for i in items:
                        #url = self.SemanticSBMLClient.MiriamURN2IdentifiersURL(i)
                        url = i
                        rs += '<tr><td>%s</td><td><a href=%s>%s</a></td></tr>' % (i, url, url)
                        print(i)
                        #self.TEXTBOX_Annotate.write('%s\n' % i)<a href="/Education/">NCBI Education</a>
                    rs += '</table border="1" cellpadding="5">'
                    del wait


                except Exception as ex:
                    rs += '<h1>Error connecting to: %s</h1>' % (site_root)
                    #self.TEXTBOX_Annotate.WriteText('\n******************************************************\n')
                    #self.TEXTBOX_Annotate.WriteText('\n* Error connecting to: %s *\n\n' % (site_root))
                    #self.TEXTBOX_Annotate.WriteText('******************************************************\n\n')
                    print(ex)
            elif HAVE_URLLIB2:
                #self.TEXTBOX_Annotate.WriteText('Please select a reaction!')
                rs += '<h1>Please select a reaction!</h1>'
            else:
                #self.TEXTBOX_Annotate.WriteText('HTTPLIB not available')
                rs += '<h1>HTTPLIB not available!</h1>'
            self.Sinfbox.SetPage("<html><body>%s</body></html>" % rs)



        def EVT_BUT_optimise(self, event):
            wait = wx.BusyCursor()
            self._cbm_.CBSolver.analyzeModel(self._cmod_)
            self.UpdateRGridData()
            self.UpdateModelStatus()
            self.writeCmd("cbmpy.CBSolver.analyzeModel(cmod)")
            del wait
            event.Skip()

        def EVT_BUT_minsum(self,event):
            wait = wx.BusyCursor()
            self._cbm_.CBSolver.cplx_MinimizeSumOfAbsFluxes(self._cmod_, selected_reactions=None, pre_opt=True, tol=None, objF2constr=True, rhs_sense='lower', optPercentage=100.0, work_dir=None, quiet=False, debug=False, objective_coefficients={}, return_lp_obj=False)
            self.UpdateRGridData()
            self.UpdateModelStatus()
            self.writeCmd("cbmpy.CBSolver.cplx_MinimizeSumOfAbsFluxes(cmod, selected_reactions=None, pre_opt=True, tol=None, objF2constr=True, rhs_sense='lower', optPercentage=100.0, objective_coefficients={}, return_lp_obj=False)")
            del wait
            event.Skip()
            ##  cbmpy.CBSolver.cplx_MinimizeSumOfAbsFluxes

        def PrintCoords(self,event):
            print("coords are: {}".format(event.Coords), end=" ")
            print("pixel coords are: {}\n".format(event.GetPosition()), end=" ")

        def EVT_FRAME_resize(self,event):
            #print "Pixel coords are: %s"%(event.GetPosition(),)
            print('Panel size', self.MainPanel.GetSize())
            #print 'Frame size', self.GetSize()
            #print 'Panel minsize', self.MainPanel.GetMinSize()
            #print 'Frame minsize', self.GetMinSize()
            #print 'Panel virtualsize', self.MainPanel.GetVirtualSize()
            #print 'Frame virtualsize', self.GetVirtualSize()
            #print 'mainSizer minsize', self.mainSizer.GetMinSize()
            self.FRAME_resize()
            #print '\n'

        def FRAME_resize(self):
            # get the frame size and adjust the main panel to it

            self.PanelSize = self.GetVirtualSize()
            self.MainPanel.SetSize(self.GetVirtualSize())

            #LPsize = wx.Size(self.PanelSize[0]*self.LPwidth, self.PanelSize[1]*self.LPheight)
            #RPsize = wx.Size(self.PanelSize[0]*self.RPwidth, self.PanelSize[1]*self.RPheight)
            #LPsize = wx.Size(self.PanelSize[0]*self.LPwidth, self.RGridInitSize[1])
            LPsize = wx.Size(self.PanelSize[0]*self.LPwidth, self.PanelSize[1]-self.RGridGap)
            RPsize = wx.Size(self.PanelSize[0]*self.RPwidth, self.PanelSize[1])
            # using the new panels to resize the components
            # self.StatusPanel.SetMinSize(wx.Size(LPsize[0],self.StatusPanel.GetVirtualSize()[1]))
            self.RGrid_scrollwindow.SetSize(LPsize)
            self.RGrid.SetSize(LPsize)
            self.NoteB1.SetSize(RPsize)
            RinfSize = wx.Size(self.PanelSize[0]*self.RPwidth-8, self.PanelSize[1]*self.RPheight-25)

            self.PyBox.SetSize(RinfSize)
            self.Rinfbox.SetSize(RinfSize)
            self.Sinfbox.SetSize(RinfSize)
            self.MetCanvas.SetSize(RinfSize)
            self.GenCanvas.SetSize(RinfSize)
            self.RightPanel.SetSize(RPsize)
            #self.LeftPanel.Layout()
            self.MainPanel.CenterOnParent()


        def EVT_onClick(self,event):
            print("Pixel coords are: {}".format(event.GetPosition()), end=" ")
            #print 'Panel size', self.MainPanel.GetSize()
            #print 'Frame size', self.GetSize()
            #print 'Panel minsize', self.MainPanel.GetMinSize()
            #print 'Frame minsize', self.GetMinSize()
            #print 'Panel virtualsize', self.MainPanel.GetVirtualSize()
            #print 'Frame virtualsize', self.GetVirtualSize()
            #print '\nmainSizer minsize', self.mainSizer.GetMinSize()
            #print '\n'


        def __C_BUFF_ADD__(self, obj):
            self.__C_BUFF__.append(obj)
            if len(self.__C_BUFF__) > 10:
                return self.__C_BUFF__.pop(0)
            else:
                return None


        def HIT_STB_onClick(self, Object):
            print('')
            print(repr(Object))
            print(Object.Name + " got Hit with Left")
            if Object.Name in self.Smap:
                self.UpdateSpeciesGraph(Object.Name)
            if Object.Name in self.Rmap:
                self.UpdateReactionGraph(Object.Name)
                self.updateInfoFromReactionName(Object.Name)
                self.RGrid.SelectRow(self.RGridRow.index(Object.Name))
                self.RGrid.MakeCellVisible(self.RGridRow.index(Object.Name), 0)



            ##  Object.SetLineColor('Green')
            ##  self.FCanvas_met.Draw(True)
            ##  self.FCanvas_met.ZoomToBB()
            ##  wx.CallAfter(self.FCanvas_met.ZoomToBB)
            ##  wx.CallAfter(self.FCanvas_met.Draw)


[docs] class HtmlWindowMod(wx.html.HtmlWindow): """ Overrides 'OnLinkClicked' to open links in external browser """ def __init__(self, *args, **kwargs): wx.html.HtmlWindow.__init__(*args, **kwargs) if "gtk2" in wx.PlatformInfo: self.SetStandardFonts() def OnLinkClicked(self, link): wx.LaunchDefaultBrowser(link.GetHref())
[docs] def circlePoints(totalPoints=4, startAngle=0, arc=360, circleradius=1, centerxy=(0,0), direction='forward', evenDistribution=True): """ Returns a list of points evenly spread around a circle: - *totalPoints* how many points - *startAngle* where to start - *arc* how far to go - *circleradius* radius - *centerxy* origin - *direction* 'forward' or 'backward' - *evenDistribution* True/False This code has been adapted from the Flash example that can be found here: http://www.lextalkington.com/blog/2009/12/generate-points-around-a-circles-circumference/ """ ## totalPoints = 4 ## startAngle = 270 ## arc = 180 ## direction = 'forward' ## evenDistribution = True ## circleradius = 1 ## centerx = 0 ## centery = 0 roundfact = 10 mpi = math.pi/180.0 startRadians = startAngle * mpi incrementAngle = float(arc)/float(totalPoints) incrementRadians = incrementAngle * mpi if arc < 360: # this spreads the points out evenly across the arc if evenDistribution: incrementAngle = float(arc)/(float(totalPoints-1)) incrementRadians = incrementAngle * mpi else: incrementAngle = float(arc)/float(totalPoints) incrementRadians = incrementAngle * mpi; cxy = [] for p in range(totalPoints): xp = centerxy[0] + math.sin(startRadians) * circleradius yp = centerxy[1] + math.cos(startRadians) * circleradius if direction == 'forward': startRadians += incrementRadians else: startRadians -= incrementRadians cxy.append((round(xp,roundfact), round(yp, roundfact))) ## print (round(xp,roundfact), round(yp, roundfact)) return cxy
''' wxPython Custom Widget Collection 20060207 Written By: Edward Flick (eddy -=at=- cdf-imaging -=dot=- com) Michele Petrazzo (michele -=dot=- petrazzo -=at=- unipex -=dot=- it) Will Sadkin (wsadkin-=at=- nameconnector -=dot=- com) Copyright 2006 (c) CDF Inc. ( http://www.cdf-imaging.com ) Contributed to the wxPython project under the wxPython project's license. ''' #---------------------------------------------------------------------- def getSmallUpArrowData(): return \ '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ \x00\x00<IDAT8\x8dcddbf\xa0\x040Q\xa4{h\x18\xf0\xff\xdf\xdf\xffd\x1b\x00\xd3\ \x8c\xcf\x10\x9c\x06\xa0k\xc2e\x08m\xc2\x00\x97m\xd8\xc41\x0c \x14h\xe8\xf2\ \x8c\xa3)q\x10\x18\x00\x00R\xd8#\xec\xb2\xcd\xc1Y\x00\x00\x00\x00IEND\xaeB`\ \x82' def getSmallUpArrowBitmap(): return BitmapFromImage(getSmallUpArrowImage()) def getSmallUpArrowImage(): stream = csio.StringIO(getSmallUpArrowData()) return ImageFromStream(stream) def getSmallDnArrowData(): return \ "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ \x00\x00HIDAT8\x8dcddbf\xa0\x040Q\xa4{\xd4\x00\x06\x06\x06\x06\x06\x16t\x81\ \xff\xff\xfe\xfe'\xa4\x89\x91\x89\x99\x11\xa7\x0b\x90%\ti\xc6j\x00>C\xb0\x89\ \xd3.\x10\xd1m\xc3\xe5*\xbc.\x80i\xc2\x17.\x8c\xa3y\x81\x01\x00\xa1\x0e\x04e\ ?\x84B\xef\x00\x00\x00\x00IEND\xaeB`\x82" def getSmallDnArrowBitmap(): return BitmapFromImage(getSmallDnArrowImage()) def getSmallDnArrowImage(): stream = csio.StringIO(getSmallDnArrowData()) return ImageFromStream(stream) #---------------------------------------------------------------------- class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): def __init__(self, parent, ID=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) listmix.ListCtrlAutoWidthMixin.__init__(self) class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin ): def __init__ ( self, parent, colNames=None, choices = None, multiChoices=None, showHead=True, dropDownClick=True, colFetch=-1, colSearch=0, hideOnNoMatch=True, selectCallback=None, entryCallback=None, matchFunction=None, **therest) : ''' Constructor works just like wx.TextCtrl except you can pass in a list of choices. You can also change the choice list at any time by calling setChoices. ''' if 'style' in therest: therest['style']=wx.TE_PROCESS_ENTER | therest['style'] else: therest['style']=wx.TE_PROCESS_ENTER wx.TextCtrl.__init__(self, parent, **therest ) #Some variables self._dropDownClick = dropDownClick self._colNames = colNames self._multiChoices = multiChoices self._showHead = showHead self._choices = choices self._lastinsertionpoint = 0 self._hideOnNoMatch = hideOnNoMatch self._selectCallback = selectCallback self._entryCallback = entryCallback self._matchFunction = matchFunction self._screenheight = wx.SystemSettings.GetMetric( wx.SYS_SCREEN_Y ) #sort variable needed by listmix self.itemDataMap = dict() #Load and sort data if not (self._multiChoices or self._choices): raise ValueError("Pass me at least one of multiChoices OR choices") #widgets self.dropdown = wx.PopupWindow( self ) #Control the style flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING if not (showHead and multiChoices) : flags = flags | wx.LC_NO_HEADER #Create the list and bind the events self.dropdownlistbox = myListCtrl( self.dropdown, style=flags, pos=wx.Point( 0, 0) ) #initialize the parent if multiChoices: ln = len(multiChoices) else: ln = 1 #else: ln = len(choices) listmix.ColumnSorterMixin.__init__(self, ln) #load the data if multiChoices: self.SetMultipleChoices (multiChoices, colSearch=colSearch, colFetch=colFetch) else: self.SetChoices ( choices ) gp = self while ( gp != None ) : gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) gp = gp.GetParent() self.Bind( wx.EVT_KILL_FOCUS, self.onControlChanged, self ) self.Bind( wx.EVT_TEXT , self.onEnteredText, self ) self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self ) #If need drop down on left click if dropDownClick: self.Bind ( wx.EVT_LEFT_DOWN , self.onClickToggleDown, self ) self.Bind ( wx.EVT_LEFT_UP , self.onClickToggleUp, self ) self.dropdown.Bind( wx.EVT_LISTBOX , self.onListItemSelected, self.dropdownlistbox ) self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.onListClick) self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.onListDClick) self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.onListColClick) #self.il = wx.ImageList(16, 16) #self.sm_dn = self.il.Add(getSmallDnArrowBitmap()) #self.sm_up = self.il.Add(getSmallUpArrowBitmap()) #self.dropdownlistbox.SetImageList(self.il, wx.IMAGE_LIST_SMALL) #self._ascending = True #-- methods called from mixin class def GetSortImages(self): return (self.sm_dn, self.sm_up) def GetListCtrl(self): return self.dropdownlistbox # -- event methods def onListClick(self, evt): toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() ) #no values on poition, return if toSel == -1: return self.dropdownlistbox.Select(toSel) def onListDClick(self, evt): self._setValueFromSelected() def onListColClick(self, evt): col = evt.GetColumn() #reverse the sort if col == self._colSearch: self._ascending = not self._ascending self.SortListItems( evt.GetColumn(), ascending=self._ascending ) self._colSearch = evt.GetColumn() evt.Skip() def onEnteredText(self, event): text = event.GetString() if self._entryCallback: self._entryCallback() if not text: # control is empty; hide dropdown if shown: if self.dropdown.IsShown(): self._showDropDown(False) event.Skip() return found = False if self._multiChoices: #load the sorted data into the listbox dd = self.dropdownlistbox choices = [dd.GetItem(x, self._colSearch).GetText() for x in xrange(dd.GetItemCount())] else: choices = self._choices for numCh, choice in enumerate(choices): if self._matchFunction and self._matchFunction(text, choice): found = True elif choice.lower().startswith(text.lower()) : found = True if found: self._showDropDown(True) item = self.dropdownlistbox.GetItem(numCh) toSel = item.GetId() self.dropdownlistbox.Select(toSel) break if not found: self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False) if self._hideOnNoMatch: self._showDropDown(False) self._listItemVisible() event.Skip () def onKeyDown ( self, event ) : """ Do some work when the user press on the keys: up and down: move the cursor left and right: move the search """ skip = True sel = self.dropdownlistbox.GetFirstSelected() visible = self.dropdown.IsShown() KC = event.GetKeyCode() if KC == wx.WXK_DOWN : if sel < (self.dropdownlistbox.GetItemCount () - 1) : self.dropdownlistbox.Select ( sel+1 ) self._listItemVisible() self._showDropDown () skip = False elif KC == wx.WXK_UP : if sel > 0 : self.dropdownlistbox.Select ( sel - 1 ) self._listItemVisible() self._showDropDown () skip = False elif KC == wx.WXK_LEFT : if not self._multiChoices: return if self._colSearch > 0: self._colSearch -=1 self._showDropDown () elif KC == wx.WXK_RIGHT: if not self._multiChoices: return if self._colSearch < self.dropdownlistbox.GetColumnCount() -1: self._colSearch += 1 self._showDropDown() if visible : if event.GetKeyCode() == wx.WXK_RETURN : self._setValueFromSelected() skip = False if event.GetKeyCode() == wx.WXK_ESCAPE : self._showDropDown( False ) skip = False if skip : event.Skip() def onListItemSelected (self, event): self._setValueFromSelected() event.Skip() def onClickToggleDown(self, event): self._lastinsertionpoint = self.GetInsertionPoint() event.Skip () def onClickToggleUp ( self, event ) : if ( self.GetInsertionPoint() == self._lastinsertionpoint ) : self._showDropDown ( not self.dropdown.IsShown() ) event.Skip () def onControlChanged(self, event): self._showDropDown( False ) event.Skip() # -- Interfaces methods def SetMultipleChoices(self, choices, colSearch=0, colFetch=-1): ''' Set multi-column choice ''' self._multiChoices = choices self._choices = None if not isinstance(self._multiChoices, list): self._multiChoices = [ x for x in self._multiChoices] flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING if not self._showHead: flags |= wx.LC_NO_HEADER self.dropdownlistbox.SetWindowStyleFlag(flags) #prevent errors on "old" systems if sys.version.startswith("2.3"): self._multiChoices.sort(lambda x, y: cmp(x[0].lower(), y[0].lower())) else: self._multiChoices.sort(key=lambda x: locale.strxfrm(x[0]).lower() ) self._updateDataList(self._multiChoices) lChoices = len(choices) if lChoices < 2: raise ValuError( "You have to pass me a multi-dimension list") for numCol, rowValues in enumerate(choices[0]): if self._colNames: colName = self._colNames[numCol] else: colName = "Select %i" % numCol self.dropdownlistbox.InsertColumn(numCol, colName) for numRow, valRow in enumerate(choices): for numCol, colVal in enumerate(valRow): if numCol == 0: index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) self.dropdownlistbox.SetStringItem(index, numCol, colVal) self.dropdownlistbox.SetItemData(index, numRow) self._setListSize() self._colSearch = colSearch self._colFetch = colFetch def SetChoices(self, choices): ''' Sets the choices available in the popup wx.ListBox. The items will be sorted case insensitively. ''' self._choices = choices self._multiChoices = None flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER self.dropdownlistbox.SetWindowStyleFlag(flags) if not isinstance(choices, list): self._choices = [ x for x in choices] #prevent errors on "old" systems if sys.version.startswith("2.3"): self._choices.sort(lambda x, y: cmp(x.lower(), y.lower())) else: self._choices.sort(key=lambda x: locale.strxfrm(x).lower()) self._updateDataList(self._choices) self.dropdownlistbox.InsertColumn(0, "") for num, colVal in enumerate(self._choices): index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1) self.dropdownlistbox.SetStringItem(index, 0, colVal) self.dropdownlistbox.SetItemData(index, num) self._setListSize() # there is only one choice for both search and fetch if setting a single column: self._colSearch = 0 self._colFetch = -1 def GetChoices(self): if self._choices: return self._choices else: return self._multiChoices def SetSelectCallback(self, cb=None): self._selectCallback = cb def SetEntryCallback(self, cb=None): self._entryCallback = cb def SetMatchFunction(self, mf=None): self._matchFunction = mf #-- Internal methods def _setValueFromSelected( self ) : ''' Sets the wx.TextCtrl value from the selected wx.ListCtrl item. Will do nothing if no item is selected in the wx.ListCtrl. ''' sel = self.dropdownlistbox.GetFirstSelected() if sel > -1: if self._colFetch != -1: col = self._colFetch else: col = self._colSearch itemtext = self.dropdownlistbox.GetItem(sel, col).GetText() if self._selectCallback: dd = self.dropdownlistbox values = [dd.GetItem(sel, x).GetText() for x in xrange(dd.GetColumnCount())] self._selectCallback( values ) self.SetValue (itemtext) self.SetInsertionPointEnd () self.SetSelection ( -1, -1 ) self._showDropDown ( False ) def _showDropDown ( self, show = True ) : ''' Either display the drop down list (show = True) or hide it (show = False). ''' if show : size = self.dropdown.GetSize() width, height = self . GetSizeTuple() x, y = self . ClientToScreenXY ( 0, height ) if size.GetWidth() != width : size.SetWidth(width) self.dropdown.SetSize(size) self.dropdownlistbox.SetSize(self.dropdown.GetClientSize()) if (y + size.GetHeight()) < self._screenheight : self.dropdown . SetPosition ( wx.Point(x, y) ) else: self.dropdown . SetPosition ( wx.Point(x, y - height - size.GetHeight()) ) self.dropdown.Show ( show ) def _listItemVisible( self ) : ''' Moves the selected item to the top of the list ensuring it is always visible. ''' toSel = self.dropdownlistbox.GetFirstSelected () if toSel == -1: return self.dropdownlistbox.EnsureVisible( toSel ) def _updateDataList(self, choices): #delete, if need, all the previous data if self.dropdownlistbox.GetColumnCount() != 0: self.dropdownlistbox.DeleteAllColumns() self.dropdownlistbox.DeleteAllItems() #and update the dict if choices: for numVal, data in enumerate(choices): self.itemDataMap[numVal] = data else: numVal = 0 self.SetColumnCount(numVal) def _setListSize(self): if self._multiChoices: choices = self._multiChoices else: choices = self._choices longest = 0 for choice in choices : longest = max(len(choice), longest) longest += 6 itemcount = min( len( choices ) , 20 ) + 2 charheight = self.dropdownlistbox.GetCharHeight() charwidth = self.dropdownlistbox.GetCharWidth() self.popupsize = wx.Size( charwidth*longest, charheight*itemcount ) self.dropdownlistbox.SetSize ( self.popupsize ) self.dropdown.SetClientSize( self.popupsize ) class MyAUIFrame(wx.Frame): def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) self.mgr = wx.aui.AuiManager(self) leftpanel = wx.Panel(self, -1, size = (200, 150)) rightpanel = wx.Panel(self, -1, size = (200, 150)) bottompanel = wx.Panel(self, -1, size = (200, 150)) toppanel = wx.Panel(self, -1, size = (200, 150)) centerpanel = wx.Panel(self, -1, size = (200, 150)) wx.TextCtrl(rightpanel, -1, 'rightpanel') wx.TextCtrl(leftpanel, -1, 'leftpanel') wx.TextCtrl(toppanel, -1, 'toppanel') wx.TextCtrl(bottompanel, -1, 'bottompanel') wx.TextCtrl(centerpanel, -1, 'centerpanel') self.mgr.AddPane(leftpanel, wx.aui.AuiPaneInfo().Left().Layer(1)) self.mgr.AddPane(rightpanel, wx.aui.AuiPaneInfo().Right().Layer(1)) self.mgr.AddPane(bottompanel, wx.aui.AuiPaneInfo().Bottom().Layer(2)) self.mgr.AddPane(toppanel, wx.aui.AuiPaneInfo().Top().Layer(2)) self.mgr.AddPane(centerpanel, wx.aui.AuiPaneInfo().Center().Layer(1)) self.Maximize() self.mgr.Update() class MyAUIApp(wx.App): def OnInit(self): frame = MyAUIFrame(None, -1, 'CBMPy Gen2 GUI') frame.Show() self.SetTopWindow(frame) return 1 def runModelEditor(mod): app = wx.App(False) frame = ModelEditor(mod) frame.Show(True) app.SetTopWindow(frame) app.MainLoop() def runMyAUIApp(): app = MyAUIApp(0) app.MainLoop()