class ArrowDesigner(WindowedPlugin): """ The ArrowDesigner plugin that subclasses WindowedPlugin. """ metadata = PluginMetadata( name='ArrowDesigner', author='Gary Geng', version='0.0.1', short_desc='Arrow tip designer for reactions.', long_desc='Arrow tip designer for reactions.', category=PluginCategory.APPEARANCE, ) def __init__(self): super().__init__() self.arrow_tip = api.get_arrow_tip() def create_window(self, dialog): """ Called when creating a window. Create the designer window as well as control buttons. """ window = wx.Window(dialog, size=(500, 500)) sizer = wx.BoxSizer(wx.VERTICAL) self.designer = DesignerWindow(window, self.arrow_tip) save_btn = wx.Button(window, label='Save') save_btn.Bind(wx.EVT_BUTTON, self.OnSave) restore_btn = wx.Button(window, label='Restore default') restore_btn.Bind(wx.EVT_BUTTON, self.OnRestore) sizerflags = wx.SizerFlags().Align(wx.ALIGN_CENTER_HORIZONTAL).Border( wx.TOP, 20) sizer.Add(self.designer, sizerflags) sizer.Add(save_btn, sizerflags) sizer.Add(restore_btn, sizerflags) dialog.SetSizer(sizer) return window def OnSave(self, evt): """ Handler for the "save" button. Save the new arrow tip. """ api.set_arrow_tip(self.arrow_tip) api.refresh_canvas() def OnRestore(self, evt): """ Update the arrow point to be set to default values. """ default_tip = api.get_default_arrow_tip() api.set_arrow_tip(default_tip) self.designer.update_arrow_tip(default_tip)
class LayoutNetworkX(WindowedPlugin): metadata = PluginMetadata( name='Auto Layout', author='Carmen and Herbert M Sauro', version='0.5.2', short_desc='Auto Layout using networkX.', long_desc='Rearrange a random network into a neat auto layout', category=PluginCategory.UTILITIES, ) def create_window(self, dialog): ''' Create a window with several inputs and buttons. Args: self dialog ''' # TODO: k, gravity, useMagnetism, useBoundary, useGrid self.window = wx.Panel(dialog, pos=(5,100), size=(350, 220)) self.sizer = wx.FlexGridSizer(cols=2, vgap=10, hgap=0) self.sizer.AddGrowableCol(0, 0.6) self.sizer.AddGrowableCol(1, 0.4) self.sizer.Add((0, 10)) self.sizer.Add((0, 10)) self.MaxIterText = wx.TextCtrl(self.window, -1, "100", size=(100, -1)) self.MaxIterText.SetInsertionPoint(0) self.MaxIterText.Bind(wx.EVT_TEXT, self.OnText_MaxIter) self.MaxIterValue = int(self.MaxIterText.GetValue()) self.AddField('Max Number of Iterations', self.MaxIterText) self.kText = wx.TextCtrl(self.window, -1, "70", size=(100, -1)) self.kText.SetInsertionPoint(0) self.kText.Bind(wx.EVT_TEXT, self.OnText_k) self.kValue = float(self.kText.GetValue()) self.AddField('k (float > 0)', self.kText) self.scaleText = wx.TextCtrl(self.window, -1, "550", size=(100, -1)) self.scaleText.SetInsertionPoint(0) self.scaleText.Bind(wx.EVT_TEXT, self.OnText_scale) self.scaleValue = float(self.scaleText.GetValue()) self.AddField('Scale of Layout', self.scaleText) self.useCentroid = False self.centroidCheckBox = wx.CheckBox(self.window) self.centroidCheckBox.Bind(wx.EVT_CHECKBOX, self.OnCheckUseCentroid) self.AddField('Also Arrange Centroids', self.centroidCheckBox) # add spacer left of button self.sizer.Add((0, 0)) apply_btn = wx.Button(self.window, -1, 'Run', (220, 130)) apply_btn.Bind(wx.EVT_BUTTON, self.Apply) self.sizer.Add(apply_btn) self.window.SetPosition (wx.Point(10,10)) self.window.SetSizer(self.sizer) return self.window def AddField(self, text, field): self.sizer.Add(wx.StaticText(self.window, label=text), wx.SizerFlags().Border(wx.LEFT, 20)) self.sizer.Add(field) def OnText_MaxIter(self, evt): try: update = evt.GetString() if update != '': self.MaxIterValue = int(self.MaxIterText.GetValue()) except ValueError: wx.MessageBox('Value must be a number', 'Error', wx.OK | wx.ICON_INFORMATION) def OnText_k(self, evt): try: update = evt.GetString() if update != '': self.kValue = float(self.kText.GetValue()) except ValueError: wx.MessageBox('Value must be a number', 'Error', wx.OK | wx.ICON_INFORMATION) def OnText_scale(self, evt): try: update = evt.GetString() if update != '': self.scaleValue = float(self.scaleText.GetValue()) except ValueError: wx.MessageBox('Value must be a number', 'Error', wx.OK | wx.ICON_INFORMATION) def OnCheckUseCentroid(self, evt): cb = evt.GetEventObject() self.useCentroid = cb.GetValue() def Apply(self, evt): if api.node_count(0) == 0: return G = nx.Graph() nodesInd = np.array(list(api.get_node_indices(0))) reactionsInd = np.array(list(api.get_reaction_indices(0))) originalPos = {} def generateGraph(): # add nodes and centroids as "nodes" for networkX nodes = np.array(list(api.get_node_indices(0))) #reactionsInd = np.array(list(api.get_reaction_indices(0))) cStr = np.empty_like(reactionsInd, dtype=str) cStr[:,] = "c" centroidId = np.char.add(cStr, reactionsInd.astype(str)) G.add_nodes_from(centroidId) nStr = np.empty_like(nodes, dtype=str) nStr[:,] = "n" nodesId = np.array(list(np.char.add(nStr, nodesInd.astype(str)))) G.add_nodes_from(nodesId) ''' for n in nodes: centroidsTo = np.array(list(api.get_reactions_as_product(0, n))) # Gets the reactions in which it is a product -> TO cStr = np.empty_like(centroidsTo, dtype=str) cStr[:,] = "c" centroidsToIn = np.char.add(cStr, centroidsTo.astype(str)) # centroids from which the node is product centroidsFrom = np.array(list(api.get_reactions_as_reactant(0, n))) # reactions in which it is a reactanr -> FROM cStr = np.empty_like(centroidsFrom, dtype=str) cStr[:,] = "c" centroidsFromIn = np.char.add(cStr, centroidsFrom.astype(str)) # centroids to which the node is reactant nS = np.empty_like(centroidsToIn, dtype = str) nS[:,] = "n" numS = np.empty_like(centroidsToIn, dtype = int) numS[:,] = n nodeIndArrayTo = np.char.add(nS, numS.astype(str)) nS = np.empty_like(centroidsFromIn, dtype = str) nS[:,] = "n" numS = np.empty_like(centroidsFromIn, dtype = int) numS[:,] = n nodeIndArrayFrom = np.char.add(nS, numS.astype(str)) edgesTo = np.array(list(zip(centroidsToIn, nodeIndArrayTo))) edgesFrom = np.array(list(zip(nodeIndArrayFrom, centroidsFromIn))) G.add_edges_from(edgesTo) G.add_edges_from(edgesFrom) ''' # Add edges from reactant to centroid and centroid to product (undirected) edges = list() for reaction in api.get_reactions(0): for s in reaction.sources: edges.append(('n' + str(s), 'c' + str(reaction.index))) for t in reaction.targets: edges.append(('c' + str(reaction.index), 'n' + str(t))) G.add_edges_from(edges) cn = 0 for rea in api.get_reactions(0): cent = api.compute_centroid(0, rea.sources, rea.targets) #originalPos[centroidId[cn]] = list([cent.x, cent.y]) originalPos['c' + str(rea)] = list([random.randint(0,600), random.randint(0,600)]) cn = cn + 1 for nod in api.get_nodes(0): #originalPos[nodesId[cn]] = list([nod.position.x, nod.position.y]) # random.randint(0,500), nod.position.y+random.randint (0,500)]) originalPos['n' + str(nod)] = list([random.randint(0,600), random.randint (0,600)]) cn = cn + 1 generateGraph() #print(nx.to_dict_of_lists(G)) #nodeIds = list (api.get_node_indices(0)) with api.group_action(): for t in range(1): pos = (nx.fruchterman_reingold_layout(G, k = self.kValue, iterations = self.MaxIterValue, scale = self.scaleValue, pos = originalPos, weight=1)) positions = np.array(list(pos.values())) minX = 0 minY = 0 for p in positions: if p[0] < minX: minX = p[0] if p[1] < minY: minY = p[1] positions = positions - np.array([minX, minY]) centroids = positions[0: len(reactionsInd)] nodes = positions[len(reactionsInd): len(positions)] count = 0 for n in nodes: newX = float(n[0]) newY = float(n[1]) api.move_node(0, nodesInd[count], position = Vec2(newX, newY), allowNegativeCoordinates=True) count = count + 1 if self.useCentroid: count = 0 for c in centroids: newX = float(c[0]) newY = float(c[1]) r = api.get_reaction_by_index(0, reactionsInd[count]) handles = api.default_handle_positions(0, r.index) api.update_reaction(0, r.index, center_pos=Vec2(newX, newY), handle_positions=handles) count = count + 1 else: for index in api.get_reaction_indices(0): api.update_reaction(0, index, center_pos=None) handles = api.default_handle_positions(0, index) api.update_reaction(0, index, handle_positions=handles) ''' for r in api.get_reactions(0): currCentroid = centroids[r.index] newX = float(currCentroid[0]) newY = float(currCentroid[1]) api.update_reaction(0, r.index, center_pos=(Vec2(newX, newY))) #api.update_reaction(0, r.index, center_pos=None) handles = api.default_handle_positions(0, r.index) api.set_reaction_center_handle(0, r.index, handles[0]) count = 1 for s in r.sources: api.set_reaction_node_handle(0, r.index, s, True, handles[count]) count += 1 for t in r.targets: api.set_reaction_node_handle(0, r.index, t, False, handles[count]) count += 1 ''' ws = api.window_size() offset = Vec2(ws.x/4, ws.y/4) api.translate_network(0, offset, check_bounds = True)
class AddReaction(WindowedPlugin): metadata = PluginMetadata( name='AddReaction', author='Jin Xu and Herbert Sauro', version='0.0.1', short_desc='Add Reactions.', long_desc= 'Add different reactions including UniUni, BiUni, UniBi and BiBi.', category=PluginCategory.UTILITIES, ) def __init__(self): """ Initialize the StructuralAnalysis Plugin. Args: self """ super().__init__() self.uniuniState = False self.biuniState = False self.unibiState = False self.bibiState = False self.src = [] self.dest = [] def create_window(self, dialog): """ Create a window to do the structural analysis. Args: self dialog """ window = wx.Panel(dialog, pos=(5, 100), size=(150, 190)) wx.StaticText(window, -1, 'Select reaction type', (15, 10)) wx.StaticText(window, -1, 'then select nodes:', (15, 25)) self.UniUni_btn = wx.ToggleButton(window, -1, 'UniUni', (36, 22 + 25), size=(62, 22)) self.UniUni_btn.Bind(wx.EVT_TOGGLEBUTTON, self.UniUni) self.BiUni_btn = wx.ToggleButton(window, -1, 'BiUni', (36, 47 + 25), size=(62, 22)) self.BiUni_btn.Bind(wx.EVT_TOGGLEBUTTON, self.BiUni) self.UniBi_btn = wx.ToggleButton(window, -1, 'UniBi', (36, 72 + 25), size=(62, 22)) self.UniBi_btn.Bind(wx.EVT_TOGGLEBUTTON, self.UniBi) self.BiBi_btn = wx.ToggleButton(window, -1, 'BiBi', (36, 97 + 25), size=(62, 22)) self.BiBi_btn.Bind(wx.EVT_TOGGLEBUTTON, self.BiBi) window.SetPosition(wx.Point(10, 10)) return window def on_did_create_dialog(self): # Set position of popup window to top-left corner of screen self.dialog.SetPosition((240, 250)) def on_will_close_window(self, evt): # print ("***** Window Closed *****") evt.Skip() def getUniqueName(self, base: str, names: list) -> str: increment = 0 # keep incrementing until you find a unique Id while True: suffix = '_{}'.format(increment) cur_id = base + suffix if cur_id in names: increment += 1 continue else: # loop finished normally; done return cur_id def addReaction(self, src, dest): # THis method is callde for all reactions names = [] # Get a unique reaction name for r in api.get_reactions(0): names.append(r.id) reactionId = self.getUniqueName('reaction', names) r_idx = api.add_reaction(0, reactionId, src, dest, fill_color=api.Color(129, 123, 255)) def on_selection_did_change(self, evt): """ Overrides base class event handler to update number of items selected. Args: self node_indices(List[int]): List of node indices changed. reaction_indices (List[int]): List of reaction indices changed. compartment_indices (List[int]): List of compartment indices changed. """ if len(list(evt.node_indices)) == 0: self.src = [] self.dest = [] return if (not self.uniuniState) and (not self.biuniState) and \ (not self.unibiState) and (not self.bibiState): return self.node_clicked = list(evt.node_indices) try: if self.uniuniState: if len(self.src) == 0: self.src.append(self.node_clicked[0]) return if len(self.dest) == 0: self.dest.append(self.node_clicked[0]) self.addReaction(self.src, self.dest) return if self.biuniState: if len(self.src) == 0: self.src.append(self.node_clicked[0]) return if len(self.src) == 1: self.src.append(self.node_clicked[0]) return if (len(self.src) <= 2) and len(self.dest) == 0: self.dest.append(self.node_clicked[0]) self.addReaction(self.src, self.dest) return if self.unibiState: if len(self.src) == 0: self.src.append(self.node_clicked[0]) return if len(self.dest) == 0: self.dest.append(self.node_clicked[0]) return if (len(self.src) <= 1) and len(self.dest) == 1: self.dest.append(self.node_clicked[0]) self.addReaction(self.src, self.dest) return if self.bibiState: if len(self.src) == 0: self.src.append(self.node_clicked[0]) return if len(self.src) == 1: self.src.append(self.node_clicked[0]) return if len(self.dest) == 0: self.dest.append(self.node_clicked[0]) return if (len(self.src) <= 2) and len(self.dest) == 1: self.dest.append(self.node_clicked[0]) self.addReaction(self.src, self.dest) return except: pass #wx.MessageBox("Error: Try again", "Message", wx.OK | wx.ICON_INFORMATION) def UniUni(self, evt): """ Handler for the "UniUni" button. add a UniUni reaction. """ state = evt.GetEventObject().GetValue() if state == True: self.src = [] self.dest = [] # This code is to make the buttons work as a radionbutton self.uniuniState = True self.biuniState = False self.unibiState = False self.bibiState = False self.BiUni_btn.SetValue(False) self.UniBi_btn.SetValue(False) self.BiBi_btn.SetValue(False) else: self.uniuniState = False def BiUni(self, evt): """ Handler for the "BiUni" button. add a BiUni reaction. """ state = evt.GetEventObject().GetValue() if state == True: self.src = [] self.dest = [] # This code is to make the buttons work as a radionbutton self.uniuniState = False self.biuniState = True self.unibiState = False self.bibiState = False self.UniUni_btn.SetValue(False) self.UniBi_btn.SetValue(False) self.BiBi_btn.SetValue(False) else: self.biuniState = False def UniBi(self, evt): """ Handler for the "UniBi" button. add a UniBi reaction. """ state = evt.GetEventObject().GetValue() if state == True: self.src = [] self.dest = [] # This code is to make the buttons work as a radionbutton self.uniuniState = False self.biuniState = False self.unibiState = True self.bibiState = False self.UniUni_btn.SetValue(False) self.BiUni_btn.SetValue(False) self.BiBi_btn.SetValue(False) else: self.unibiState = False def BiBi(self, evt): """ Handler for the "UniBi" button. add a UniBi reaction. """ state = evt.GetEventObject().GetValue() if state == True: self.src = [] self.dest = [] # This code is to make the buttons work as a radionbutton self.uniuniState = False self.biuniState = False self.unibiState = False self.bibiState = True self.UniUni_btn.SetValue(False) self.BiUni_btn.SetValue(False) self.UniBi_btn.SetValue(False) else: self.bibiState = False
class DuplicateReaction(CommandPlugin): metadata = PluginMetadata( name='DuplicateReaction', author='Claire Samuels', version='0.0.1', short_desc='Duplicate a reaction.', long_desc= 'Creates an exact copy of the selected reactions and all connected nodes.', category=PluginCategory.UTILITIES, ) def __init__(self): """ Initialize the DuplicateReaction. Args: self """ super().__init__() def run(self): net_index = 0 selected_reacts = api.selected_reaction_indices() if len(selected_reacts) < 1: selected_reacts = api.get_reaction_indices(net_index) def copy_node(node_index, net_index): ''' Creates a new node, translated 300 px down and to the right. Parameters: node index Returns: index of new node ''' node = api.get_node_by_index(net_index, node_index) try: inx = api.add_node(net_index, id='copy_{}'.format(node.id), shape_index=node.shape_index, size=Vec2(node.size[0] + 60, node.size[1]), position=Vec2(node.position[0] + 300, node.position[1] + 300)) except IDRepeatError: # find a unique id all_ids = [] ns = api.get_nodes(net_index) for n in ns: all_ids.append(n.id) c = 1 new_id = 'copy_{}_{}'.format(node.id, c) while new_id in all_ids: c += 1 new_id = 'copy_{}_{}'.format(node.id, c) inx = api.add_node(net_index, id=new_id, shape_index=node.shape_index, size=Vec2(node.size[0] + 60, node.size[1]), position=Vec2(node.position[0] + 300, node.position[1] + 300)) return inx orig_node_set = set() reaction_info = {} for r_index in selected_reacts: r = api.get_reaction_by_index(net_index, r_index) r_info = {"sources": r.sources, "targets": r.targets, "id": r.id} reaction_info[r_index] = r_info orig_node_set = orig_node_set.union(set(r.sources)) orig_node_set = orig_node_set.union(set(r.targets)) node_indices = {} for n_index in orig_node_set: new_index = copy_node(n_index, net_index) node_indices[n_index] = new_index for reaction in reaction_info: new_sources = [] for src in reaction_info[reaction]["sources"]: new_sources.append(node_indices[src]) new_targets = [] for trg in reaction_info[reaction]["targets"]: new_targets.append(node_indices[trg]) reac_id = reaction_info[reaction]["id"] n_index = api.add_reaction(net_index, id="copy" + str(reaction), reactants=new_sources, products=new_targets) api.update_reaction(net_index, n_index, id='{}_copy_{}'.format(reac_id, n_index))
class StructuralAnalysis(WindowedPlugin): metadata = PluginMetadata( name='StructuralAnalysis', author='Jin Xu', version='0.0.1', short_desc='Structural Analysis.', long_desc= 'StructuralAnalysis Plugin is to calculate and visualize the stoichiometry matrix and conserved moieties for the network.', category=PluginCategory.ANALYSIS) def __init__(self): """ Initialize the StructuralAnalysis Plugin. Args: self """ super().__init__() self.index_list = [] def create_window(self, dialog): """ Create a window to do the structural analysis. Args: self dialog """ topPanel = wx.Panel(dialog, pos=(0, 0), size=(700, 500)) # Create two panels size by side panel1 = wx.Panel(topPanel, -1, pos=(0, 100), size=(200, 100)) panel2 = wx.Panel(topPanel, -1, pos=(100, 100), size=(450, 100)) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(panel1, 0, wx.EXPAND | wx.ALL, border=10) sizer.Add(panel2, 0, wx.EXPAND | wx.ALL, border=10) topPanel.SetSizer(sizer) # Add a notebook to the second panel nb = wx.Notebook(panel2) # Create the tabs self.tab1 = TabOne(nb) self.tab2 = TabTwo(nb) nb.AddPage(self.tab1, "Stoichiometry Matrix") nb.AddPage(self.tab2, "Conservation Matrix") # Make sure the second panel fills the right side. sizer = wx.BoxSizer() sizer.Add(nb, 1, wx.EXPAND) panel2.SetSizer(sizer) Compute_btn = wx.Button(panel1, -1, 'Compute Matrix', (20, 20)) Compute_btn.Bind(wx.EVT_BUTTON, self.Compute) wx.StaticText(panel1, -1, 'Select a row from the table of', (20, 100)) wx.StaticText(panel1, -1, 'Moiety Conservation Laws', (20, 120)) wx.StaticText(panel1, -1, 'and pick a color:', (20, 140)) Picker = wx.ColourPickerCtrl(panel1, pos=(20, 160)) Picker.Bind(wx.EVT_COLOURPICKER_CHANGED, self.color_callback) wx.StaticText(panel1, -1, 'Unhighlight the nodes:', (20, 240)) Clear_Btn = wx.Button(panel1, -1, 'Clear', (20, 260)) Clear_Btn.Bind(wx.EVT_BUTTON, self.unhighlight) return topPanel def on_did_create_dialog(self): # Set position of popup window to top-left corner of screen self.dialog.SetPosition((240, 250)) def Compute(self, evt): """ Handler for the "Compute" button. Get the network on canvas. Calculate the Stoichiometry Matrix and Conservation Matrix for the randon network. """ def nullspace(A, atol=1e-13, rtol=0): A = _np.atleast_2d(A) u, s, vh = _np.linalg.svd(A) tol = max(atol, rtol * s[0]) nnz = (s >= tol).sum() ns = vh[nnz:].conj().T return ns #https://gist.github.com/sgsfak/77a1c08ac8a9b0af77393b24e44c9547 def rref(B, tol=1e-8, debug=False): A = B.copy() rows, cols = A.shape r = 0 pivots_pos = [] row_exchanges = _np.arange(rows) for c in range(cols): if debug: print("Now at row", r, "and col", c, "with matrix:") print(A) ## Find the pivot row: pivot = _np.argmax(_np.abs(A[r:rows, c])) + r m = _np.abs(A[pivot, c]) if debug: print("Found pivot", m, "in row", pivot) if m <= tol: ## Skip column c, making sure the approximately zero terms are ## actually zero. A[r:rows, c] = _np.zeros(rows - r) if debug: print("All elements at and below (", r, ",", c, ") are zero.. moving on..") else: ## keep track of bound variables pivots_pos.append((r, c)) if pivot != r: ## Swap current row and pivot row A[[pivot, r], c:cols] = A[[r, pivot], c:cols] row_exchanges[[pivot, r]] = row_exchanges[[r, pivot]] if debug: print("Swap row", r, "with row", pivot, "Now:") print(A) ## Normalize pivot row A[r, c:cols] = A[r, c:cols] / A[r, c] ## Eliminate the current column v = A[r, c:cols] ## Above (before row r): if r > 0: ridx_above = _np.arange(r) A[ridx_above, c:cols] = A[ridx_above, c:cols] - _np.outer( v, A[ridx_above, c]).T if debug: print("Elimination above performed:") print(A) ## Below (after row r): if r < rows - 1: ridx_below = _np.arange(r + 1, rows) A[ridx_below, c:cols] = A[ridx_below, c:cols] - _np.outer( v, A[ridx_below, c]).T if debug: print("Elimination below performed:") print(A) r += 1 ## Check if done if r == rows: break return (A, pivots_pos, row_exchanges) netIn = 0 numNodes = api.node_count(netIn) if numNodes == 0: wx.MessageBox("Please import a network on canvas", "Message", wx.OK | wx.ICON_INFORMATION) else: allNodes = api.get_nodes(netIn) #id = allNodes[0].id[0:-2] node = allNodes[0] try: primitive, transform = node.shape.items[0] self.default_color = primitive.fill_color except: self.default_color = api.Color(255, 204, 153) #random network node color largest_node_index = 0 for i in range(numNodes): if allNodes[i].index > largest_node_index: largest_node_index = allNodes[i].index row = largest_node_index + 1 numReactions = api.reaction_count(netIn) #print("numReactions:", numReactions) col = numReactions self.st = _np.zeros((row, col)) allReactions = api.get_reactions(netIn) for i in range(numReactions): for j in range(len(allReactions[i].sources)): #print(allReactions[i].sources[j]) for m in range(row): if allReactions[i].sources[j] == m: self.st.itemset((m, i), -1) for j in range(len(allReactions[i].targets)): #print(allReactions[i].targets[j]) for m in range(row): if allReactions[i].targets[j] == m: self.st.itemset((m, i), 1) stt = _np.transpose(self.st) m = _np.transpose(nullspace(stt)) moi_mat = rref(m)[0] # set all the values of non-existing nodes to zero for i in range(moi_mat.shape[0]): for j in range(moi_mat.shape[1]): if _np.array_equal(self.st[j, :], _np.zeros(self.st.shape[1])): moi_mat.itemset((i, j), 0.) for i in range(self.st.shape[1]): self.tab1.grid_st.SetColLabelValue(i, "J" + str(i)) for i in range(self.st.shape[0]): #self.tab1.grid_st.SetRowLabelValue(i, id + "_" + str(i)) id = allNodes[i].id self.tab1.grid_st.SetRowLabelValue(i, id) for row in range(self.st.shape[0]): for col in range(self.st.shape[1]): self.tab1.grid_st.SetCellValue( row, col, "%d" % self.st.item(row, col)) for i in range(moi_mat.shape[1]): #self.tab2.grid_moi.SetColLabelValue(i, id + "_" + str(i)) id = allNodes[i].id self.tab2.grid_moi.SetColLabelValue(i, id) CSUM_id = 0 for i in range(moi_mat.shape[0]): a = moi_mat[i, :] a = [0. if abs(a_) < 0.005 else a_ for a_ in a] # some elements are very small if _np.array_equal( a, _np.zeros(moi_mat.shape[1]) ): # delete the row if all the elements are zero CSUM_id = CSUM_id else: self.tab2.grid_moi.SetRowLabelValue( CSUM_id, "CSUM" + str(CSUM_id)) for j in range(moi_mat.shape[1]): #self.tab2.grid_moi.SetCellValue(CSUM_id, j, format (moi_mat[i][j], ".2f")) self.tab2.grid_moi.SetCellValue( CSUM_id, j, format(a[j], ".2f")) CSUM_id += 1 def printSelectedCells(self, top_left, bottom_right): """ Based on code from http://ginstrom.com/scribbles/2008/09/07/getting-the-selected-cells-from-a-wxpython-grid/ """ cells = [] rows_start = top_left[0] rows_end = bottom_right[0] cols_start = top_left[1] cols_end = bottom_right[1] rows = range(rows_start, rows_end + 1) cols = range(cols_start, cols_end + 1) cells.extend([(row, col) for row in rows for col in cols]) self.index_list = [] for cell in cells: row, col = cell value = self.tab2.grid_moi.GetCellValue(row, col) if value != "0.00" and value != "+0.00" and value != "-0.00" and value != "": self.index_list.append(int(col)) #print("selected nodes:", self.index_list) def color_callback(self, evt): """ Get whatever cells are currently selected """ cells = self.tab2.grid_moi.GetSelectedCells() if not cells: if self.tab2.grid_moi.GetSelectionBlockTopLeft(): top_left = self.tab2.grid_moi.GetSelectionBlockTopLeft()[0] bottom_right = self.tab2.grid_moi.GetSelectionBlockBottomRight( )[0] self.printSelectedCells(top_left, bottom_right) #else: # print (self.currentlySelectedCell) else: print("no cells are selected") """ Callback for the color picker control; sets the color of every node/reaction selected. """ wxcolor = evt.GetColour() color = Color.from_rgb(wxcolor.GetRGB()) # start group action context for undo purposes with api.group_action(): # color selected nodes #for index in api.selected_node_indices(): if len(self.index_list) == 0: wx.MessageBox("Please select a row and pick a color again", "Message", wx.OK | wx.ICON_INFORMATION) try: for index in self.index_list: #api.update_node(api.cur_net_index(), index, fill_color=color, forder_color=color) api.update_node(api.cur_net_index(), index, fill_color=color) except: wx.MessageBox("Please select a row and pick a color again", "Message", wx.OK | wx.ICON_INFORMATION) def unhighlight(self, evt): """ Callback for the color picker control; sets the color of every node/reaction selected. """ # start group action context for undo purposes with api.group_action(): # color selected nodes #for index in api.selected_node_indices(): try: for index in self.index_list: api.update_node(api.cur_net_index(), index, fill_color=self.default_color) except: wx.MessageBox("There is no highlighted nodes", "Message", wx.OK | wx.ICON_INFORMATION)
class RandomNetwork(WindowedPlugin): metadata = PluginMetadata( name='RandomNetwork', author='Jin Xu, Herbert M Sauro', version='0.0.1', short_desc='Random network.', long_desc= 'Display a random network with certain number of species and reactions as input.', category=PluginCategory.UTILITIES) def __init__(self): """ Initialize the RandomNetwork. Args: self """ super().__init__() def create_window(self, dialog): """ Create a window with several inputs and buttons. Args: self dialog """ window = wx.Panel(dialog, pos=(5, 100), size=(300, 320)) numSpecs = wx.StaticText(window, -1, 'Number of Species:', (20, 20)) self.numSpecsText = wx.TextCtrl(window, -1, str(DefaultValues.maxSpecies), (160, 20), size=(100, -1)) self.numSpecsText.SetInsertionPoint(0) self.numSpecsText.Bind(wx.EVT_TEXT, self.OnText_numSpecs) self.numSpecsValue = int(self.numSpecsText.GetValue()) numRxns = wx.StaticText(window, -1, 'Number of Reactions:', (20, 50)) self.numRxnsText = wx.TextCtrl(window, -1, str(DefaultValues.maxReactions), (160, 50), size=(100, -1)) self.numRxnsText.SetInsertionPoint(0) self.numRxnsText.Bind(wx.EVT_TEXT, self.OnText_numRxns) self.numRxnsValue = int(self.numRxnsText.GetValue()) probUniUni = wx.StaticText(window, -1, 'Probability of UniUni:', (20, 90)) self.probUniUniText = wx.TextCtrl(window, -1, str(DefaultValues.probUniUniValue), (160, 90), size=(100, -1)) self.probUniUniText.SetInsertionPoint(0) self.probUniUniText.Bind(wx.EVT_TEXT, self.OnText_UniUni) self.probUniUniValue = float(self.probUniUniText.GetValue()) probBiUni = wx.StaticText(window, -1, 'Probability of BiUni:', (20, 120)) self.probBiUniText = wx.TextCtrl(window, -1, str(DefaultValues.probBiUniValue), (160, 120), size=(100, -1)) self.probBiUniText.SetInsertionPoint(0) self.probBiUniText.Bind(wx.EVT_TEXT, self.OnText_BiUni) self.probBiUniValue = float(self.probBiUniText.GetValue()) probUniBi = wx.StaticText(window, -1, 'Probability of UniBi:', (20, 150)) self.probUniBiText = wx.TextCtrl(window, -1, str(DefaultValues.probUniBiValue), (160, 150), size=(100, -1)) self.probUniBiText.SetInsertionPoint(0) self.probUniBiText.Bind(wx.EVT_TEXT, self.OnText_UniBi) self.probUniBiValue = float(self.probUniBiText.GetValue()) probBiBi = wx.StaticText(window, -1, 'Probability of BiBi:', (20, 180)) self.probBiBiText = wx.TextCtrl(window, -1, str(DefaultValues.probBiBiValue), (160, 180), size=(100, -1)) self.probBiBiText.SetInsertionPoint(0) self.probBiBiText.Bind(wx.EVT_TEXT, self.OnText_BiBi) self.probBiBiValue = float(self.probBiBiText.GetValue()) randomSeed = wx.StaticText(window, -1, 'Random seed:', (20, 210)) randomSeed = wx.StaticText(window, -1, '0 means no seed setup', (20, 230)) self.randomSeedText = wx.TextCtrl(window, -1, str(DefaultValues.randomSeed), (160, 210), size=(100, -1)) self.randomSeedText.SetInsertionPoint(0) self.randomSeedText.Bind(wx.EVT_TEXT, self.OnText_randomSeed) self.randomSeedValue = float(self.randomSeedText.GetValue()) apply_btn = wx.Button(window, -1, 'Apply', (160, 250)) apply_btn.Bind(wx.EVT_BUTTON, self.Apply) return window def OnText_numSpecs(self, evt): update = evt.GetString() if update != '': try: self.numSpecsValue = int(self.numSpecsText.GetValue()) except: wx.MessageBox( "Please enter an integer for the number of species.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_numRxns(self, evt): update = evt.GetString() if update != '': try: self.numRxnsValue = int(self.numRxnsText.GetValue()) except: wx.MessageBox( "Please enter an integer for the number of reactions.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_UniUni(self, evt): update = evt.GetString() if update != '': try: self.probUniUniValue = float(self.probUniUniText.GetValue()) except: wx.MessageBox( "Please enter a floating point number for the probability of UniUni.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_BiUni(self, evt): update = evt.GetString() if update != '': #DefaultValues.probBiUniValue = float(self.probBiUniText.GetValue()) try: self.probBiUniValue = float(self.probBiUniText.GetValue()) except: wx.MessageBox( "Please enter a floating point number for the probability of BiUni.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_UniBi(self, evt): update = evt.GetString() if update != '': try: self.probUniBiValue = float(self.probUniBiText.GetValue()) except: wx.MessageBox( "Please enter a floating point number for the probability of UniBi.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_BiBi(self, evt): update = evt.GetString() if update != '': try: self.probBiBiValue = float(self.probBiBiText.GetValue()) except: wx.MessageBox( "Please enter a floating point number for the probability of BiBi.", "Message", wx.OK | wx.ICON_INFORMATION) def OnText_randomSeed(self, evt): update = evt.GetString() if update != '': try: self.randomSeedValue = float(self.randomSeedText.GetValue()) except: wx.MessageBox( "Please enter a valid random seed other than zero.", "Message", wx.OK | wx.ICON_INFORMATION) def Apply(self, evt): """ Handler for the "apply" button. apply the random network. """ if self.randomSeedValue != 0: _random.seed(self.randomSeedValue) class _TReactionType: UNIUNI = 0 BIUNI = 1 UNIBI = 2 BIBI = 3 def _pickReactionType(): rt = _random.random() if rt < self.probUniUniValue: return _TReactionType.UNIUNI elif rt < self.probUniUniValue + self.probBiUniValue: return _TReactionType.BIUNI elif rt < self.probUniUniValue + self.probBiUniValue + self.probUniBiValue: return _TReactionType.UNIBI else: return _TReactionType.BIBI # Generates a reaction network in the form of a reaction list # reactionList = [nSpecies, reaction, reaction, ....] # reaction = [reactionType, [list of reactants], [list of products], rateConsta> # Disallowed reactions: # S1 -> S1 # S1 + S2 -> S2 # Can't have the same reactant and product # S1 + S1 -> S1 def _generateReactionList(nSpecies, nReactions): reactionList = [] for r in range(nReactions): rateConstant = _random.random() rt = _pickReactionType() if rt == _TReactionType.UNIUNI: # UniUni reactant = _random.randint(0, nSpecies - 1) product = _random.randint(0, nSpecies - 1) # Disallow S1 -> S1 type of reaction while product == reactant: product = _random.randint(0, nSpecies - 1) reactionList.append( [rt, [reactant], [product], rateConstant]) if rt == _TReactionType.BIUNI: # BiUni # Pick two reactants reactant1 = _random.randint(0, nSpecies - 1) reactant2 = _random.randint(0, nSpecies - 1) # pick a product but only products that don't include the reactants species = range(nSpecies) # Remove reactant1 and 2 from the species list species = _np.delete(species, [reactant1, reactant2], axis=0) # Then pick a product from the reactants that are left product = species[_random.randint(0, len(species) - 1)] reactionList.append( [rt, [reactant1, reactant2], [product], rateConstant]) if rt == _TReactionType.UNIBI: # UniBi reactant1 = _random.randint(0, nSpecies - 1) # pick a product but only products that don't include the reactant species = range(nSpecies) # Remove reactant1 from the species list species = _np.delete(species, [reactant1], axis=0) # Then pick a product from the reactants that are left product1 = species[_random.randint(0, len(species) - 1)] product2 = species[_random.randint(0, len(species) - 1)] reactionList.append( [rt, [reactant1], [product1, product2], rateConstant]) if rt == _TReactionType.BIBI: # BiBi reactant1 = _random.randint(0, nSpecies - 1) reactant2 = _random.randint(0, nSpecies - 1) # pick a product but only products that don't include the reactant species = range(nSpecies) # Remove reactant1 and 2 from the species list species = _np.delete(species, [reactant1, reactant2], axis=0) # Then pick a product from the reactants that are left product1 = species[_random.randint(0, len(species) - 1)] product2 = species[_random.randint(0, len(species) - 1)] element = [ rt, [reactant1, reactant2], [product1, product2], rateConstant ] reactionList.append(element) reactionList.insert(0, nSpecies) return reactionList # Includes boundary and floating species # Returns a list: # [New Stoichiometry matrix, list of floatingIds, list of boundaryIds] def _getFullStoichiometryMatrix(reactionList): nSpecies = reactionList[0] reactionListCopy = _copy.deepcopy(reactionList) reactionListCopy.pop(0) st = _np.zeros((nSpecies, len(reactionListCopy))) for index, r in enumerate(reactionListCopy): if r[0] == _TReactionType.UNIUNI: # UniUni reactant = reactionListCopy[index][1][0] st[reactant, index] = -1 product = reactionListCopy[index][2][0] st[product, index] = 1 if r[0] == _TReactionType.BIUNI: # BiUni reactant1 = reactionListCopy[index][1][0] st[reactant1, index] = -1 reactant2 = reactionListCopy[index][1][1] st[reactant2, index] = -1 product = reactionListCopy[index][2][0] st[product, index] = 1 if r[0] == _TReactionType.UNIBI: # UniBi reactant1 = reactionListCopy[index][1][0] st[reactant1, index] = -1 product1 = reactionListCopy[index][2][0] st[product1, index] = 1 product2 = reactionListCopy[index][2][1] st[product2, index] = 1 if r[0] == _TReactionType.BIBI: # BiBi reactant1 = reactionListCopy[index][1][0] st[reactant1, index] = -1 reactant2 = reactionListCopy[index][1][1] st[reactant2, index] = -1 product1 = reactionListCopy[index][2][0] st[product1, index] = 1 product2 = reactionListCopy[index][2][1] st[product2, index] = 1 return st def _getRateLaw(floatingIds, boundaryIds, reactionList, isReversible): nSpecies = reactionList[0] # Remove the first element which is the nSpecies reactionListCopy = _copy.deepcopy(reactionList) reactionListCopy.pop(0) antStr_tot = [] for index, r in enumerate(reactionListCopy): antStr = '' antStr = antStr + 'J' + str(index) + ': ' if r[0] == _TReactionType.UNIUNI: # UniUni antStr = antStr + '(k' + str(index) + '*S' + str( reactionListCopy[index][1][0]) if isReversible: antStr = antStr + ' - k' + str( index) + 'r' + '*S' + str( reactionListCopy[index][2][0]) antStr = antStr + ')' if r[0] == _TReactionType.BIUNI: # BiUni antStr = antStr + '(k' + str(index) + '*S' + str( reactionListCopy[index][1][0]) + '*S' + str( reactionListCopy[index][1][1]) if isReversible: antStr = antStr + ' - k' + str( index) + 'r' + '*S' + str( reactionListCopy[index][2][0]) antStr = antStr + ')' if r[0] == _TReactionType.UNIBI: # UniBi antStr = antStr + '(k' + str(index) + '*S' + str( reactionListCopy[index][1][0]) if isReversible: antStr = antStr + ' - k' + str( index) + 'r' + '*S' + str( reactionListCopy[index][2][0]) + '*S' + str( reactionListCopy[index][2][1]) antStr = antStr + ')' if r[0] == _TReactionType.BIBI: # BiBi antStr = antStr + '(k' + str(index) + '*S' + str( reactionListCopy[index][1][0]) + '*S' + str( reactionListCopy[index][1][1]) if isReversible: antStr = antStr + ' - k' + str( index) + 'r' + '*S' + str( reactionListCopy[index][2][0]) + '*S' + str( reactionListCopy[index][2][1]) antStr = antStr + ')' antStr_tot.append(antStr) return antStr_tot test_prob = self.probUniUniValue + self.probBiUniValue + self.probUniBiValue + self.probBiBiValue if test_prob != 1: wx.MessageBox("The sum of probabilities should be one!", "Message", wx.OK | wx.ICON_INFORMATION) else: net_index = 0 api.clear_network(net_index) rl = _generateReactionList(self.numSpecsValue, self.numRxnsValue) st = _getFullStoichiometryMatrix(rl) antStr = _getRateLaw(st[1], st[2], rl, isReversible=True) numNodes = st.shape[0] numRxns = st.shape[1] nodeIdx = [] for i in range(numNodes): nodeIdx.append( api.add_node(net_index, 'node_{}'.format(i), size=Vec2(60, 40), fill_color=api.Color(255, 204, 153), border_color=api.Color(255, 108, 9), position=Vec2( 40 + math.trunc(_random.random() * 800), 40 + math.trunc(_random.random() * 800)))) for i in range(numRxns): src = [] dest = [] for j in range(numNodes): if (st.item(j, i) == -1): src.append(nodeIdx[j]) if (st.item(j, i) == 1): dest.append(nodeIdx[j]) r_idx = api.add_reaction(net_index, 'reaction_{}'.format(i), src, dest, fill_color=api.Color(91, 176, 253)) # Need to remove orphan nodes for i in range(numNodes): if _np.array_equal(st[i, :], _np.zeros(numRxns)): api.delete_node(net_index, nodeIdx[i])
class IMPORTSBML(WindowedPlugin): metadata = PluginMetadata( name='ImportSBML', author='Jin Xu', version='0.0.3', short_desc='Import SBML.', long_desc= 'Import an SBML String from a file and visualize it as a network on canvas.', category=PluginCategory.ANALYSIS) def create_window(self, dialog): """ Create a window to import SBML. Args: self dialog """ self.window = wx.Panel(dialog, pos=(5, 100), size=(300, 320)) self.sbmlStr = '' show_btn = wx.Button(self.window, -1, 'Show', (5, 5)) show_btn.Bind(wx.EVT_BUTTON, self.Show) copy_btn = wx.Button(self.window, -1, 'Copy', (100, 5)) copy_btn.Bind(wx.EVT_BUTTON, self.Copy) visualize_btn = wx.Button(self.window, -1, 'Visualize', (195, 5)) visualize_btn.Bind(wx.EVT_BUTTON, self.Visualize) wx.StaticText(self.window, -1, 'SBML string:', (5, 30)) self.SBMLText = wx.TextCtrl(self.window, -1, "", (10, 50), size=(260, 220), style=wx.TE_MULTILINE | wx.HSCROLL) self.SBMLText.SetInsertionPoint(0) return self.window def Show(self, evt): """ Handler for the "Import" button. Open the SBML file and show it in the TextCtrl box. """ self.dirname = "" #set directory name to blank dlg = wx.FileDialog( self.window, "Choose a file to open", self.dirname, wildcard="SBML files (*.xml)|*.xml", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) #open the dialog boxto open file if dlg.ShowModal() == wx.ID_OK: #if positive button selected.... self.filename = dlg.GetFilename() #get the filename of the file self.dirname = dlg.GetDirectory( ) #get the directory of where file is located f = open( os.path.join(self.dirname, self.filename), 'r') #traverse the file directory and find filename in the OS self.sbmlStr = f.read() self.SBMLText.SetValue( f.read()) #open the file from location as read self.SBMLText.WriteText(self.sbmlStr) f.close dlg.Destroy() def Copy(self, evt): """ Handler for the "Copy" button. Copy the SBML string to a clipboard. """ self.dataObj = wx.TextDataObject() self.dataObj.SetText(self.SBMLText.GetValue()) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(self.dataObj) wx.TheClipboard.Close() else: wx.MessageBox("Unable to open the clipboard", "Error") def Visualize(self, evt): """ Handler for the "Visualize" button. Visualize the SBML string to a network shown on the canvas. """ self.DisplayModel(self.sbmlStr, True, False) def DisplayModel(self, sbmlStr, showDialogues, useSeed): """ Visualize an SBML string as a network shown on the canvas. Args: self document: SBMLDocument object created from the sbml string sbmlStr: sbml string to display showDialogues: if false, hides pop-up windows useSeed: if true, constant seed for random number generation used, ensuring that different visualizations created from the same file will always have the same layout """ if useSeed: _random.seed(13) def hex_to_rgb(value): value = value.lstrip('#') return tuple(int(value[i:i + 2], 16) for i in (0, 2, 4)) if len(sbmlStr) == 0: if showDialogues: wx.MessageBox("Please import an SBML file.", "Message", wx.OK | wx.ICON_INFORMATION) else: net_index = 0 api.clear_network(net_index) comp_id_list = [] comp_dimension_list = [] comp_position_list = [] spec_id_list = [] specGlyph_id_list = [] spec_specGlyph_id_list = [] spec_dimension_list = [] spec_position_list = [] shapeIdx = 0 #set the default values without render info: comp_fill_color = (158, 169, 255) comp_border_color = (0, 29, 255) comp_border_width = 2.0 spec_fill_color = (255, 204, 153) spec_border_color = (255, 108, 9) spec_border_width = 2.0 reaction_line_color = (129, 123, 255) reaction_line_width = 3.0 ### from here for layout ### document = readSBMLFromString(sbmlStr) model_layout = document.getModel() mplugin = (model_layout.getPlugin("layout")) if mplugin is None: if showDialogues: wx.MessageBox( "There is no layout information, so positions are randomly assigned.", "Message", wx.OK | wx.ICON_INFORMATION) # # Get the first Layout object via LayoutModelPlugin object. # else: layout = mplugin.getLayout(0) if layout is None: if showDialogues: wx.MessageBox( "There is no layout information, so positions are randomly assigned.", "Message", wx.OK | wx.ICON_INFORMATION) else: numCompGlyphs = layout.getNumCompartmentGlyphs() numSpecGlyphs = layout.getNumSpeciesGlyphs() numReactionGlyphs = layout.getNumReactionGlyphs() flag_text_out = 0 for i in range(numCompGlyphs): compGlyph = layout.getCompartmentGlyph(i) temp_id = compGlyph.getCompartmentId() comp_id_list.append(temp_id) boundingbox = compGlyph.getBoundingBox() height = boundingbox.getHeight() width = boundingbox.getWidth() pos_x = boundingbox.getX() pos_y = boundingbox.getY() comp_dimension_list.append([width, height]) comp_position_list.append([pos_x, pos_y]) # for i in range(numSpecGlyphs): # specGlyph = layout.getSpeciesGlyph(i) # spec_id = specGlyph.getSpeciesId() # spec_id_list.append(spec_id) # boundingbox = specGlyph.getBoundingBox() # height = boundingbox.getHeight() # width = boundingbox.getWidth() # pos_x = boundingbox.getX() # pos_y = boundingbox.getY() # spec_dimension_list.append([width,height]) # spec_position_list.append([pos_x,pos_y]) reaction_id_list = [] kinetics_list = [] rct_specGlyph_list = [] prd_specGlyph_list = [] for i in range(numReactionGlyphs): reactionGlyph = layout.getReactionGlyph(i) reaction_id = reactionGlyph.getReactionId() reaction_id_list.append(reaction_id) reaction = model_layout.getReaction(reaction_id) kinetics = reaction.getKineticLaw().getFormula() kinetics_list.append(kinetics) numSpecRefGlyphs = reactionGlyph.getNumSpeciesReferenceGlyphs( ) rct_specGlyph_temp_list = [] prd_specGlyph_temp_list = [] for j in range(numSpecRefGlyphs): specRefGlyph = reactionGlyph.getSpeciesReferenceGlyph( j) #specRefGlyph_id = specRefGlyph.getSpeciesReferenceGlyphId() role = specRefGlyph.getRoleString() specGlyph_id = specRefGlyph.getSpeciesGlyphId() specGlyph = layout.getSpeciesGlyph(specGlyph_id) #textGlyph = layout.getTextGlyph(textGlyph_id) spec_id = specGlyph.getSpeciesId() spec_boundingbox = specGlyph.getBoundingBox() #text_boundingbox = textGlyph.getBoundingBox() height = spec_boundingbox.getHeight() width = spec_boundingbox.getWidth() pos_x = spec_boundingbox.getX() pos_y = spec_boundingbox.getY() #text_pos_x = text_boundingbox.getX() #text_pos_y = text_boundingbox.getY() #if (pos_x,pos_y) !=(text_pos_x,text_pos_y): # flag_text_out = 1 if specGlyph_id not in specGlyph_id_list: spec_id_list.append(spec_id) specGlyph_id_list.append(specGlyph_id) spec_specGlyph_id_list.append( [spec_id, specGlyph_id]) spec_dimension_list.append([width, height]) spec_position_list.append([pos_x, pos_y]) if role == "substrate": #it is a rct rct_specGlyph_temp_list.append(specGlyph_id) elif role == "product": #it is a prd prd_specGlyph_temp_list.append(specGlyph_id) rct_specGlyph_list.append(rct_specGlyph_temp_list) prd_specGlyph_list.append(prd_specGlyph_temp_list) rPlugin = layout.getPlugin("render") if (rPlugin != None and rPlugin.getNumLocalRenderInformationObjects() > 0): #wx.MessageBox("The diversity of each graphical object is not shown.", "Message", wx.OK | wx.ICON_INFORMATION) info = rPlugin.getRenderInformation(0) color_list = [] for j in range(0, info.getNumColorDefinitions()): color = info.getColorDefinition(j) color_list.append( [color.getId(), color.createValueString()]) for j in range(0, info.getNumStyles()): style = info.getStyle(j) group = style.getGroup() typeList = style.createTypeString() if 'COMPARTMENTGLYPH' in typeList: for k in range(len(color_list)): if color_list[k][0] == group.getFill(): comp_fill_color = hex_to_rgb( color_list[k][1]) if color_list[k][0] == group.getStroke(): comp_border_color = hex_to_rgb( color_list[k][1]) comp_border_width = group.getStrokeWidth() elif 'SPECIESGLYPH' in typeList: for k in range(len(color_list)): if color_list[k][0] == group.getFill(): spec_fill_color = hex_to_rgb( color_list[k][1]) if color_list[k][0] == group.getStroke(): spec_border_color = hex_to_rgb( color_list[k][1]) spec_border_width = group.getStrokeWidth() name_list = [] for element in group.getListOfElements(): name = element.getElementName() name_list.append(name) try: NumRenderpoints = element.getListOfElements( ).getNumRenderPoints() except: NumRenderpoints = 0 if name == "ellipse": #circel and text-outside shapeIdx = 1 elif name == "polygon" and NumRenderpoints == 6: shapeIdx = 2 elif name == "polygon" and NumRenderpoints == 2: shapeIdx = 3 elif name == "polygon" and NumRenderpoints == 3: shapeIdx = 4 elif name == "rectangle" and spec_fill_color == '#ffffff' and spec_border_color == '#ffffff': shapeIdx = 5 #elif name == "ellipse" and flag_text_out == 1: # shapeIdx = 6 else: # name == "rectangle"/demo combo/others as default (rectangle) shapeIdx = 0 elif 'REACTIONGLYPH' in typeList: for k in range(len(color_list)): if color_list[k][0] == group.getStroke(): reaction_line_color = hex_to_rgb( color_list[k][1]) reaction_line_width = group.getStrokeWidth() model = simplesbml.loadSBMLStr(sbmlStr) numFloatingNodes = model.getNumFloatingSpecies() FloatingNodes_ids = model.getListOfFloatingSpecies() numBoundaryNodes = model.getNumBoundarySpecies() BoundaryNodes_ids = model.getListOfBoundarySpecies() numRxns = model.getNumReactions() Rxns_ids = model.getListOfReactionIds() numComps = model.getNumCompartments() Comps_ids = model.getListOfCompartmentIds() numNodes = numFloatingNodes + numBoundaryNodes for i in range(numComps): temp_id = Comps_ids[i] vol = model.getCompartmentVolume(i) if temp_id == "_compartment_default_": api.add_compartment(net_index, id=temp_id, volume=vol, size=Vec2(3900, 2400), position=Vec2(10, 10), fill_color=api.Color(255, 255, 255), border_color=api.Color(255, 255, 255), border_width=comp_border_width) else: if len(comp_id_list) != 0: #if mplugin is not None: #for j in range(numComps): for j in range(numCompGlyphs): if comp_id_list[j] == temp_id: dimension = comp_dimension_list[j] position = comp_position_list[j] else: # no layout info about compartment, # then the whole size of the canvas is the compartment size # modify the compartment size using the max_rec function above # random assigned network: # dimension = [800,800] # position = [40,40] # the whole size of the compartment: 4000*2500 dimension = [3900, 2400] position = [10, 10] comp_fill_color = (255, 255, 255) comp_border_color = (255, 255, 255) api.add_compartment( net_index, id=temp_id, volume=vol, size=Vec2(dimension[0], dimension[1]), position=Vec2(position[0], position[1]), fill_color=api.Color(comp_fill_color[0], comp_fill_color[1], comp_fill_color[2]), border_color=api.Color(comp_border_color[0], comp_border_color[1], comp_border_color[2]), border_width=comp_border_width) comp_node_list = [0] * numComps #comp_node_list = [0]*numCompGlyphs for i in range(numComps): #for i in range(numCompGlyphs): comp_node_list[i] = [] #if there is layout info: if len(spec_id_list) != 0: id_list = [] nodeIdx_list = [ ] #get_nodes idx do not follow the same order of add_node nodeIdx_specGlyph_list = [] nodeIdx_specGlyph_alias_list = [] numSpec_in_reaction = len(spec_specGlyph_id_list) #numSpecGlyphs is larger than numSpec_in_reaction if there orphan nodes if numSpecGlyphs > numSpec_in_reaction: if showDialogues: wx.MessageBox("Orphan nodes are removed.", "Message", wx.OK | wx.ICON_INFORMATION) for i in range(numSpec_in_reaction): temp_id = spec_specGlyph_id_list[i][0] tempGlyph_id = spec_specGlyph_id_list[i][1] dimension = spec_dimension_list[i] position = spec_position_list[i] comp_id = model.getCompartmentIdSpeciesIsIn(temp_id) for j in range(numFloatingNodes): if temp_id == FloatingNodes_ids[j]: if temp_id not in id_list: nodeIdx_temp = api.add_node( net_index, id=temp_id, floating_node=True, size=Vec2(dimension[0], dimension[1]), position=Vec2(position[0], position[1]), fill_color=api.Color( spec_fill_color[0], spec_fill_color[1], spec_fill_color[2]), border_color=api.Color( spec_border_color[0], spec_border_color[1], spec_border_color[2]), border_width=spec_border_width, shape_index=shapeIdx) id_list.append(temp_id) nodeIdx_list.append(nodeIdx_temp) nodeIdx_specGlyph_list.append( [nodeIdx_temp, tempGlyph_id]) else: index = id_list.index(temp_id) nodeIdx_temp = api.add_alias( net_index, original_index=index, size=Vec2(dimension[0], dimension[1]), position=Vec2(position[0], position[1])) id_list.append(temp_id) nodeIdx_list.append(nodeIdx_temp) nodeIdx_specGlyph_alias_list.append( [nodeIdx_temp, tempGlyph_id]) #for k in range(numComps): for k in range(numCompGlyphs): if len(comp_id_list ) != 0 and comp_id == comp_id_list[k]: comp_node_list[k].append(nodeIdx_temp) for j in range(numBoundaryNodes): if temp_id == BoundaryNodes_ids[j]: if temp_id not in id_list: nodeIdx_temp = api.add_node( net_index, id=temp_id, floating_node=False, size=Vec2(dimension[0], dimension[1]), position=Vec2(position[0], position[1]), fill_color=api.Color( spec_fill_color[0], spec_fill_color[1], spec_fill_color[2]), border_color=api.Color( spec_border_color[0], spec_border_color[1], spec_border_color[2]), border_width=spec_border_width, shape_index=shapeIdx) id_list.append(temp_id) nodeIdx_list.append(nodeIdx_temp) nodeIdx_specGlyph_list.append( [nodeIdx_temp, tempGlyph_id]) else: index = id_list.index(temp_id) nodeIdx_temp = api.add_alias( net_index, original_index=index, size=Vec2(dimension[0], dimension[1]), position=Vec2(position[0], position[1])) id_list.append(temp_id) nodeIdx_list.append(nodeIdx_temp) nodeIdx_specGlyph_alias_list.append( [nodeIdx_temp, tempGlyph_id]) #for k in range(numComps): for k in range(numCompGlyphs): if len(comp_id ) != 0 and comp_id == comp_id_list[k]: comp_node_list[k].append(nodeIdx_temp) if len(comp_id_list) != 0: for i in range(numComps): temp_id = Comps_ids[i] if temp_id == '_compartment_default_': node_list_default = [ item for item in range(numNodes) ] for j in range(len(node_list_default)): api.set_compartment_of_node( net_index=net_index, node_index=node_list_default[j], comp_index=i) #for j in range(numComps): for j in range(numCompGlyphs): if comp_id_list[j] == temp_id: node_list_temp = comp_node_list[j] else: node_list_temp = [] for k in range(len(node_list_temp)): #print(node_list_temp) api.set_compartment_of_node( net_index=net_index, node_index=node_list_temp[k], comp_index=i) else: for i in range(len(nodeIdx_list)): api.set_compartment_of_node(net_index=net_index, node_index=nodeIdx_list[i], comp_index=0) #handle_positions, center_pos was set as the default: #can not find a way from libsml to do this so far nodeIdx_specGlyph_whole_list = nodeIdx_specGlyph_list + nodeIdx_specGlyph_alias_list for i in range(numReactionGlyphs): src = [] dst = [] temp_id = reaction_id_list[i] kinetics = kinetics_list[i] rct_num = len(rct_specGlyph_list[i]) prd_num = len(prd_specGlyph_list[i]) for j in range(rct_num): temp_specGlyph_id = rct_specGlyph_list[i][j] for k in range(numSpec_in_reaction): if temp_specGlyph_id == nodeIdx_specGlyph_whole_list[ k][1]: rct_idx = nodeIdx_specGlyph_whole_list[k][0] src.append(rct_idx) for j in range(prd_num): temp_specGlyph_id = prd_specGlyph_list[i][j] for k in range(numSpec_in_reaction): if temp_specGlyph_id == nodeIdx_specGlyph_whole_list[ k][1]: prd_idx = nodeIdx_specGlyph_whole_list[k][0] dst.append(prd_idx) api.add_reaction(net_index, id=temp_id, reactants=src, products=dst, rate_law=kinetics, fill_color=api.Color( reaction_line_color[0], reaction_line_color[1], reaction_line_color[2]), line_thickness=reaction_line_width) else: # there is no layout information, assign position randomly and size as default comp_id_list = Comps_ids for i in range(numFloatingNodes): temp_id = FloatingNodes_ids[i] comp_id = model.getCompartmentIdSpeciesIsIn(temp_id) nodeIdx_temp = api.add_node( net_index, id=temp_id, size=Vec2(60, 40), floating_node=True, position=Vec2(40 + math.trunc(_random.random() * 800), 40 + math.trunc(_random.random() * 800)), fill_color=api.Color(spec_fill_color[0], spec_fill_color[1], spec_fill_color[2]), border_color=api.Color(spec_border_color[0], spec_border_color[1], spec_border_color[2]), border_width=spec_border_width, shape_index=shapeIdx) for j in range(numComps): if comp_id == comp_id_list[j]: comp_node_list[j].append(nodeIdx_temp) for i in range(numBoundaryNodes): temp_id = BoundaryNodes_ids[i] comp_id = model.getCompartmentIdSpeciesIsIn(temp_id) nodeIdx_temp = api.add_node( net_index, id=temp_id, size=Vec2(60, 40), floating_node=False, position=Vec2(40 + math.trunc(_random.random() * 800), 40 + math.trunc(_random.random() * 800)), fill_color=api.Color(spec_fill_color[0], spec_fill_color[1], spec_fill_color[2]), border_color=api.Color(spec_border_color[0], spec_border_color[1], spec_border_color[2]), border_width=spec_border_width, shape_index=shapeIdx) for j in range(numComps): if comp_id == comp_id_list[j]: comp_node_list[j].append(nodeIdx_temp) for i in range(numComps): temp_id = Comps_ids[i] for j in range(numComps): if comp_id_list[j] == temp_id: node_list_temp = comp_node_list[j] for k in range(len(node_list_temp)): api.set_compartment_of_node( net_index=net_index, node_index=node_list_temp[k], comp_index=i) #handle_positions, center_pos was set as the default numNodes = api.node_count(net_index) allNodes = api.get_nodes(net_index) for i in range(numRxns): src = [] dst = [] temp_id = Rxns_ids[i] kinetics = model.getRateLaw(i) rct_num = model.getNumReactants(i) prd_num = model.getNumProducts(i) for j in range(rct_num): rct_id = model.getReactant(temp_id, j) for k in range(numNodes): if allNodes[k].id == rct_id: src.append(allNodes[k].index) for j in range(prd_num): prd_id = model.getProduct(temp_id, j) for k in range(numNodes): if allNodes[k].id == prd_id: dst.append(allNodes[k].index) api.add_reaction(net_index, id=temp_id, reactants=src, products=dst, rate_law=kinetics, fill_color=api.Color( reaction_line_color[0], reaction_line_color[1], reaction_line_color[2]), line_thickness=reaction_line_width)
class CaptureSBML(WindowedPlugin): metadata = PluginMetadata( name='CaptureSBML', author='Claire Samuels', version='0.0.2', short_desc='Visualize and capture SBML or Antimony.', long_desc='Import a directory of SBML and Antimony files, visualize reactions, capture and save images.', category=PluginCategory.ANALYSIS ) def create_window(self, dialog): """ Create a window to export the SBML. Args: self dialog """ # requires importSBML version 0.0.3 v = IMPORTSBML.metadata.version.split(".") importSBMLvers = 0 for i in range(3): importSBMLvers += pow(10,i)*int(v[len(v)-1-i]) if importSBMLvers < 3: self.window = wx.Panel(dialog, pos=(5,100), size=(300, 320)) txt = wx.StaticText(self.window, -1, "CaptureSBML requires ImportSBML version 0.0.3 or later!", (10,10)) txt.Wrap(250) return self.window # import button self.window = wx.Panel(dialog, pos=(5,100), size=(300, 320)) import_btn = wx.Button(self.window, -1, 'Import', (5, 5)) import_btn.Bind(wx.EVT_BUTTON, self.Import) # Directory text: self.display_dir = wx.StaticText(self.window, -1, '', (85,10), (180,25), style= wx.ST_NO_AUTORESIZE | wx.ST_ELLIPSIZE_START) # files to import wx.StaticText(self.window, -1, 'Files to Display:', (5,30)) self.selectedFiles = wx.TextCtrl(self.window, -1, "", (10,50), (240, 150), style=wx.TE_MULTILINE | wx.TE_READONLY) # directory to save images to output_dir_label = wx.StaticText(self.window, -1, "Output Directory:", (5,215)) self.output_dir = wx.TextCtrl(self.window, -1, "", (100,210), (180,25)) # go button go_btn = wx.Button(self.window, -1, 'Go', (200,250)) go_btn.Bind(wx.EVT_BUTTON, self.Go) return self.window def Import(self, evt): ''' Handler for "Import" button Choose a directory to iterate through''' self.dirname = "" dlg = wx.DirDialog(self.window, "Choose a directory", self.dirname, wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) self.file_list = [] if dlg.ShowModal() == wx.ID_OK: self.dirname = dlg.GetPath() for file in os.listdir(self.dirname): # only want .xml files and .ant filename = os.fsdecode(file) if filename.endswith(".xml") | filename.endswith(".ant"): self.file_list.append(filename) self.display_dir.SetLabel(str(self.dirname)) self.selectedFiles.SetLabel(str(self.file_list)) self.output_dir.Clear() self.output_dir.write('{}\\reaction_visualizations'.format(self.dirname)) dlg.Destroy() def Go(self, evt): ''' Handler for "Go" button. Reads each file in selected directory. If readable SBML, displays on canvas''' reader = SBMLReader() self.output_dir_path = self.output_dir.Value if not os.path.isdir(self.output_dir_path): os.mkdir(self.output_dir_path) else: dir_cnt = 1 while os.path.isdir('{} ({})'.format(self.output_dir_path, dir_cnt)): dir_cnt += 1 self.output_dir_path = '{} ({})'.format(self.output_dir_path, dir_cnt) os.mkdir(self.output_dir_path) self.blank_canvas = [] for filename in self.file_list: f = open(os.path.join(self.dirname, filename), 'r') sbmlStr = f.read() # convert .ant files to .xml if filename.endswith(".ant"): try: sbmlStr = tellurium.tellurium.antimonyToSBML(sbmlStr) except: pass doc = reader.readSBMLFromString(sbmlStr) if doc.getNumErrors() > 0: self.blank_canvas.append(filename) else: try: IMPORTSBML.DisplayModel(self, sbmlStr, False, True) except ValueError: pass self.Capture(filename) if len(self.blank_canvas) > 0: flist = "" for blkf in self.blank_canvas: flist += blkf + ", " flist = flist[:-2] wx.MessageBox("Done. Images saved to\n{}\nWarning: No node or reaction information found for {}.\nNo visualizations saved for these files.".format(self.output_dir_path, flist), "Message", wx.OK | wx.ICON_INFORMATION) else: wx.MessageBox("Done. Images saved to\n{}".format(self.output_dir_path), "Message", wx.OK | wx.ICON_INFORMATION) def Capture(self, filename): ''' Saves a png of the current canvas to the chosen output directory Args: self filename: name of sbml file currently displayed''' # ensure that the canvas isn't blank if api.node_count(0) == 0 and api.reaction_count(0) == 0 and api.compartments_count(0) <= 1: self.blank_canvas.append(filename) else: pathname = os.path.join(self.output_dir_path, 'img_{}.png'.format(filename)) canv = api.get_canvas() img = canv.DrawActiveRectToImage() img.SaveFile(pathname)
class AlignCircle(WindowedPlugin): metadata = PluginMetadata(name='AlignCircle', author='Evan Yip (2021)', version='0.0.1', short_desc='Align Circle', long_desc='Aligns the nodes into a circle', category=PluginCategory.UTILITIES) def __init__(self): """ Initialize the AlignCircle Args: self """ # allows the align circle class to inherit # the methods of the Windowed Plugin Class super().__init__() def create_window(self, dialog): """ Create a window to do the structural analysis. Args: self dialog """ # Making the window window = wx.Panel(dialog, pos=(5, 100), size=(300, 155)) wx.StaticText(window, -1, 'Select nodes to arrange in circle', (15, 10)) # Use default radius wx.StaticText(window, -1, 'Use default radius:', (15, 30)) self.defaultCheck = wx.CheckBox(window, -1, pos=(150, 30)) self.defaultCheck.SetValue(True) # Making radius setable wx.StaticText(window, -1, 'Input desired radius:', (15, 55)) self.radiusText = wx.TextCtrl(window, -1, '0', (150, 55), size=(120, 22)) self.radiusText.SetInsertionPoint(0) self.radiusText.Bind(wx.EVT_TEXT, self.OnText_radius) # binding test self.radiusValue = float(self.radiusText.GetValue()) # Making the toggle button apply_btn = wx.ToggleButton(window, -1, 'Apply', (100, 85), size=(80, 22)) apply_btn.SetValue(False) # Binding the method to the button apply_btn.Bind(wx.EVT_TOGGLEBUTTON, self.Apply) return window def find_center(self, num_nodes, nodes): """ Takes in the number of nodes and list of node indices and computes the optimal centerpoint for the circle Parameters: num_nodes(int) - number of nodes, node_indices(list) - list of nodes indices Returns: center(tuple) """ # Max dimension of the node size max_dim = 0 # will consider this the diameter of a circle node for i in nodes: # Get node node = api.get_node_by_index(0, i) for dim in node.size: if dim > max_dim: max_dim = dim # Approximate circumference estimate spacing = max_dim / 4 circum_estimate = num_nodes * (max_dim + spacing) # Computing radius r = circum_estimate / (2 * np.pi) + max_dim center = (r, r) return center def cart(self, r, theta): """ Converts from polar coordinates to cartesian coordinates Parameters: r(double), theta(double in radians) Returns: (x,y) cartesian coordinate tuple """ x = r * math.cos(theta) y = r * math.sin(theta) return x, y def get_new_position(self, node_index, r, theta): """ Takes in the node index and outputs the new position for that node Parameters: r(double), theta(double in radians) Returns: (x,y) cartesian coordinate tuple """ node = api.get_node_by_index(0, node_index) size = node.size # (width, height) # accounting for slight offset, converting to cartesian nodeCenter = self.cart(r - size[0], theta) # accounting for node position being specified by top left corner x = r + nodeCenter[0] - size[0] / 2 y = r + nodeCenter[1] + size[1] / 2 return Vec2(x, y) def OnText_radius(self, event): """ Catches exception if self.radiusText can not be converted to a floating point number. Opens a window. """ update = event.GetString() if update != '': try: self.radiusValue = float(self.radiusText.GetValue()) except: wx.MessageBox( "Please enter a floating point number" "for the desired radius", "Message", wx.OK | wx.ICON_INFORMATION) def CheckSelection(self, nodes): """ Verifies that there are selected nodes. Raises window if no nodes are selected, but apply button is pressed """ if nodes == 0: wx.MessageBox("Please select desired nodes to arrange" "in circle", "Message", wx.OK | wx.ICON_INFORMATION) return True def Apply(self, event): """ If apply button is clicked, the nodes will be arranged in a circle using either a default radius or a user input radius. """ def translate_nodes(node_indices, r, phi): """ Takes in list of node indices, desired radius, and phi and moves the current node to its new position """ # Iterate through the nodes and change their position. node_num = 0 for i in node_ind: theta = node_num * phi # angle position for node newPos = self.get_new_position(i, r, theta) if newPos[0] < 0 or newPos[1] < 0: wx.MessageBox("Please increase radius size", "Message", wx.OK | wx.ICON_INFORMATION) return api.move_node(0, i, newPos, False) node_num += 1 # updating node number # Get button and checkbox state btn_state = event.GetEventObject().GetValue() # chk_state = self.OnDefaultCheck() chk_state = self.defaultCheck.GetValue() # If the button is pressed, arrange the nodes into a circle if btn_state is True: # Get number of nodes selected node_len = len(api.selected_node_indices()) # get list of node indices node_ind = api.selected_node_indices() # If no nodes selected raise error select = self.CheckSelection(node_len) if select is True: return # If default radius is checked if chk_state is True: # Compute the expected center of the circle center = self.find_center(node_len, node_ind) # Compute the angle step between each node phi = 2 * math.pi / node_len r = center[0] # radius translate_nodes(node_ind, r, phi) else: r = self.radiusValue center = Vec2(r, r) phi = 2 * math.pi / node_len # angle step between each node translate_nodes(node_ind, r, phi) # translating nodes # Making the reactions straight lines rxns = api.get_reaction_indices(0) for rxn in rxns: api.update_reaction(net_index=0, reaction_index=rxn, use_bezier=False) # Setting button state back to False (unclicked) event.GetEventObject().SetValue(False)
class ExportSBML(WindowedPlugin): metadata = PluginMetadata( name='ExportSBML', author='Jin Xu', version='0.0.2', short_desc='Export SBML.', long_desc= 'Export the SBML String from the network on canvas and save it to a file.', category=PluginCategory.ANALYSIS) def create_window(self, dialog): """ Create a window to export the SBML. Args: self dialog """ self.window = wx.Panel(dialog, pos=(5, 100), size=(300, 320)) show_btn = wx.Button(self.window, -1, 'Show', (5, 5)) show_btn.Bind(wx.EVT_BUTTON, self.Show) copy_btn = wx.Button(self.window, -1, 'Copy', (100, 5)) copy_btn.Bind(wx.EVT_BUTTON, self.Copy) save_btn = wx.Button(self.window, -1, 'Save', (195, 5)) save_btn.Bind(wx.EVT_BUTTON, self.Save) wx.StaticText(self.window, -1, 'SBML string:', (5, 30)) self.SBMLText = wx.TextCtrl(self.window, -1, "", (10, 50), size=(260, 220), style=wx.TE_MULTILINE | wx.HSCROLL) self.SBMLText.SetInsertionPoint(0) return self.window def Copy(self, evt): """ Handler for the "Copy" button. Copy the SBML string to a clipboard. """ self.dataObj = wx.TextDataObject() self.dataObj.SetText(self.SBMLText.GetValue()) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(self.dataObj) wx.TheClipboard.Close() else: wx.MessageBox("Unable to open the clipboard", "Error") def Show(self, evt): """ Handler for the "Export" button. Get the network on canvas and change it to an SBML string. """ isReversible = True netIn = 0 numNodes = api.node_count(netIn) numReactions = api.reaction_count(netIn) if numNodes == 0 or numReactions == 0: wx.MessageBox( "Please import a network with at least one reaction on canvas", "Message", wx.OK | wx.ICON_INFORMATION) else: allNodes = api.get_nodes(netIn) allReactions = api.get_reactions(netIn) allcompartments = api.get_compartments(netIn) numCompartments = len(allcompartments) ####################################### # Creates an SBMLNamespaces object with the given SBML level, version # package name, package version. # # (NOTE) By default, the name of package (i.e. "layout") will be used # if the argument for the prefix is missing or empty. Thus the argument # for the prefix can be added as follows: # # SBMLNamespaces sbmlns(3,1,"layout",1,"LAYOUT") # sbmlns = SBMLNamespaces(3, 1, "layout", 1) # create the document document = SBMLDocument(sbmlns) # set the "required" attribute of layout package to "true" document.setPkgRequired("layout", False) # create the Model model = document.createModel() model.setId("Model_layout") document.setModel(model) # create the Compartment and species comp_id_list = [] for i in range(numCompartments): comp_id_list.append(allcompartments[i].id) if numCompartments != 0: if "_compartment_default_" not in comp_id_list: compartment = model.createCompartment() comp_id = "_compartment_default_" compartment.setId(comp_id) compartment.setConstant(True) for i in range(numCompartments): compartment = model.createCompartment() comp_id = allcompartments[i].id compartment.setId(comp_id) compartment.setConstant(True) for i in range(numNodes): original_index = allNodes[i].original_index if original_index == -1: spec_id = allNodes[i].id species = model.createSpecies() species.setId(spec_id) comp_idx = allNodes[i].comp_idx if comp_idx != -1: comp_id = allcompartments[comp_idx].id species.setCompartment(comp_id) else: species.setCompartment("_compartment_default_") species.setInitialConcentration(1.0) species.setHasOnlySubstanceUnits(False) species.setBoundaryCondition(False) species.setConstant(False) if allNodes[i].floating_node == False: species.setBoundaryCondition(True) species.setConstant(True) else: #set default compartment compartment = model.createCompartment() comp_id = "_compartment_default_" compartment.setId(comp_id) compartment.setConstant(True) for i in range(numNodes): original_index = allNodes[i].original_index if original_index == -1: spec_id = allNodes[i].id species = model.createSpecies() species.setId(spec_id) species.setCompartment(comp_id) species.setInitialConcentration(1.0) species.setHasOnlySubstanceUnits(False) species.setBoundaryCondition(False) species.setConstant(False) if allNodes[i].floating_node == False: species.setBoundaryCondition(True) species.setConstant(True) # create reactions: for i in range(numReactions): reaction_id = allReactions[i].id rct = [] # id list of the rcts prd = [] rct_num = len(allReactions[i].sources) prd_num = len(allReactions[i].targets) for j in range(rct_num): rct.append( get_node_by_index(netIn, allReactions[i].sources[j]).id) for j in range(prd_num): prd.append( get_node_by_index(netIn, allReactions[i].targets[j]).id) kinetic_law = '' parameter_list = [] kinetic_law = kinetic_law + 'E' + str(i) + '*(k' + str(i) parameter_list.append('E' + str(i)) parameter_list.append('k' + str(i)) for j in range(rct_num): kinetic_law = kinetic_law + '*' + rct[j] reaction = model.createReaction() reaction.setId(allReactions[i].id) reaction.setReversible(False) reaction.setFast(False) if isReversible: reaction.setReversible(True) kinetic_law = kinetic_law + ' - k' + str(i) + 'r' parameter_list.append('k' + str(i) + 'r') for j in range(prd_num): kinetic_law = kinetic_law + '*' + prd[j] kinetic_law = kinetic_law + ')' for j in range(len(parameter_list)): parameters = model.createParameter() parameters.setId(parameter_list[j]) parameters.setValue(0.1) parameters.setConstant(True) kinetics = reaction.createKineticLaw() kinetics.setFormula(kinetic_law) for j in range(rct_num): reference = reaction.createReactant() reference.setSpecies(rct[j]) ref_id = "SpecRef_" + reaction_id + "_rct" + str(j) reference.setId(ref_id) reference.setStoichiometry(1.) reference.setConstant(False) for j in range(prd_num): reference = reaction.createProduct() reference.setSpecies(prd[j]) ref_id = "SpecRef_" + reaction_id + "_prd" + str(j) reference.setId(ref_id) reference.setStoichiometry(1.) reference.setConstant(False) # create the Layout # # set the LayoutPkgNamespaces for Level 3 Version1 Layout Version 1 # layoutns = LayoutPkgNamespaces(3, 1, 1) renderns = RenderPkgNamespaces(3, 1, 1) # # Get a LayoutModelPlugin object plugged in the model object. # # The type of the returned value of SBase::getPlugin() function is SBasePlugin, and # thus the value needs to be casted for the corresponding derived class. # mplugin = model.getPlugin("layout") # rPlugin = model.getPlugin("render") # if rPlugin is None: # print("there is no render outside layout.") # lolPlugin = mplugin.getListOfLayouts().getPlugin("render") # if lolPlugin is None: # print("there is no render info inside layout.") if mplugin is None: # print( # "[Fatal Error] Layout Extension Level " + layoutns.getLevel() + " Version " + layoutns.getVersion() + " package version " + layoutns.getPackageVersion() + " is not registered.") # sys.exit(1) wx.MessageBox("There is no layout information.", "Message", wx.OK | wx.ICON_INFORMATION) # # Creates a Layout object via LayoutModelPlugin object. # layout = mplugin.createLayout() layout.setId("Layout_1") layout.setDimensions(Dimensions(layoutns, 800.0, 800.0)) # random network (40+800x, 40+800y) #create the CompartmentGlyph and SpeciesGlyphs if numCompartments != 0: # if "_compartment_default_" not in comp_id_list: # comp_id= "_compartment_default_" # compartmentGlyph = layout.createCompartmentGlyph() # compG_id = "CompG_" + comp_id # compartmentGlyph.setId(compG_id) # compartmentGlyph.setCompartmentId(comp_id) # bb_id = "bb_" + comp_id # pos_x = 10 # pos_y = 10 # width = 3900 # height = 2400 # compartmentGlyph.setBoundingBox(BoundingBox(layoutns, bb_id, pos_x, pos_y, width, height)) for i in range(numCompartments): comp_id = allcompartments[i].id if comp_id != "_compartment_default_": compartmentGlyph = layout.createCompartmentGlyph() compG_id = "CompG_" + comp_id compartmentGlyph.setId(compG_id) compartmentGlyph.setCompartmentId(comp_id) bb_id = "bb_" + comp_id pos_x = allcompartments[i].position.x pos_y = allcompartments[i].position.y width = allcompartments[i].size.x height = allcompartments[i].size.y compartmentGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x, pos_y, width, height)) for i in range(numNodes): spec_id = allNodes[i].id spec_index = allNodes[i].index spec_shapeIdx = allNodes[i].shape_index speciesGlyph = layout.createSpeciesGlyph() specG_id = "SpecG_" + spec_id + '_idx_' + str(spec_index) speciesGlyph.setId(specG_id) speciesGlyph.setSpeciesId(spec_id) bb_id = "bb_" + spec_id + '_idx_' + str(spec_index) pos_x = allNodes[i].position.x pos_y = allNodes[i].position.y width = allNodes[i].size.x height = allNodes[i].size.y speciesGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x, pos_y, width, height)) textGlyph = layout.createTextGlyph() textG_id = "TextG_" + spec_id + '_idx_' + str(spec_index) textGlyph.setId(textG_id) bb_id = "bb_spec_text_" + spec_id + '_idx_' + str( spec_index) if spec_shapeIdx == 6: #rough by eyes pos_x_text = pos_x + 50 pos_y_text = pos_y + 30 else: pos_x_text = pos_x pos_y_text = pos_y textGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x_text, pos_y_text, width, height)) textGlyph.setOriginOfTextId(specG_id) textGlyph.setGraphicalObjectId(specG_id) else: #there is no compartment comp_id = "_compartment_default_" compartmentGlyph = layout.createCompartmentGlyph() compG_id = "CompG_" + comp_id compartmentGlyph.setId(compG_id) compartmentGlyph.setCompartmentId(comp_id) bb_id = "bb_" + comp_id pos_x = 10 pos_y = 10 width = 3900 height = 2400 compartmentGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x, pos_y, width, height)) for i in range(numNodes): spec_id = allNodes[i].id spec_index = allNodes[i].index spec_shapeIdx = allNodes[i].shape_index speciesGlyph = layout.createSpeciesGlyph() specG_id = "SpecG_" + spec_id + '_idx_' + str(spec_index) speciesGlyph.setId(specG_id) speciesGlyph.setSpeciesId(spec_id) bb_id = "bb_" + spec_id + '_idx_' + str(spec_index) pos_x = allNodes[i].position.x pos_y = allNodes[i].position.y width = allNodes[i].size.x height = allNodes[i].size.y speciesGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x, pos_y, width, height)) textGlyph = layout.createTextGlyph() textG_id = "TextG_" + spec_id + '_idx_' + str(spec_index) textGlyph.setId(textG_id) if spec_shapeIdx == 6: #rough by eyes pos_x_text = pos_x + 50 pos_y_text = pos_y + 30 else: pos_x_text = pos_x pos_y_text = pos_y bb_id = "bb_spec_text_" + spec_id + '_idx_' + str( spec_index) textGlyph.setBoundingBox( BoundingBox(layoutns, bb_id, pos_x_text, pos_y_text, width, height)) textGlyph.setOriginOfTextId(specG_id) textGlyph.setGraphicalObjectId(specG_id) # create the ReactionGlyphs and SpeciesReferenceGlyphs for i in range(numReactions): reaction_id = allReactions[i].id reactionGlyph = layout.createReactionGlyph() reactionG_id = "RectionG_" + reaction_id reactionGlyph.setId(reactionG_id) reactionGlyph.setReactionId(reaction_id) reactionCurve = reactionGlyph.getCurve() ls = reactionCurve.createLineSegment() centroid = api.compute_centroid(0, allReactions[i].sources, allReactions[i].targets) ls.setStart(Point(layoutns, centroid.x, centroid.y)) ls.setEnd(Point(layoutns, centroid.x, centroid.y)) rct = [] # id list of the rcts prd = [] rct_index = [] prd_index = [] rct_num = len(allReactions[i].sources) prd_num = len(allReactions[i].targets) for j in range(rct_num): rct.append( get_node_by_index(netIn, allReactions[i].sources[j]).id) rct_index.append( get_node_by_index(netIn, allReactions[i].sources[j]).index) for j in range(prd_num): prd.append( get_node_by_index(netIn, allReactions[i].targets[j]).id) prd_index.append( get_node_by_index(netIn, allReactions[i].targets[j]).index) for j in range(rct_num): ref_id = "SpecRef_" + reaction_id + "_rct" + str(j) speciesReferenceGlyph = reactionGlyph.createSpeciesReferenceGlyph( ) specsRefG_id = "SpecRefG_" + reaction_id + "_rct" + str(j) specG_id = "SpecG_" + rct[j] + '_idx_' + str(rct_index[j]) speciesReferenceGlyph.setId(specsRefG_id) speciesReferenceGlyph.setSpeciesGlyphId(specG_id) speciesReferenceGlyph.setSpeciesReferenceId(ref_id) speciesReferenceGlyph.setRole(SPECIES_ROLE_SUBSTRATE) speciesReferenceCurve = speciesReferenceGlyph.getCurve() cb = speciesReferenceCurve.createCubicBezier() #cb = speciesReferenceCurve.createLineSegment() cb.setStart(Point(layoutns, centroid.x, centroid.y)) handles = api.default_handle_positions( netIn, allReactions[i].index) pos_x = handles[1 + j].x pos_y = handles[1 + j].y cb.setBasePoint1(Point(layoutns, pos_x, pos_y)) cb.setBasePoint2(Point(layoutns, pos_x, pos_y)) pos_x = get_node_by_index( netIn, allReactions[i].sources[j]).position.x pos_y = get_node_by_index( netIn, allReactions[i].sources[j]).position.y width = get_node_by_index( netIn, allReactions[i].sources[j]).size.x height = get_node_by_index( netIn, allReactions[i].sources[j]).size.y cb.setEnd( Point(layoutns, pos_x + 0.5 * width, pos_y - 0.5 * height)) for j in range(prd_num): ref_id = "SpecRef_" + reaction_id + "_prd" + str(j) speciesReferenceGlyph = reactionGlyph.createSpeciesReferenceGlyph( ) specsRefG_id = "SpecRefG_" + reaction_id + "_prd" + str(j) specG_id = "SpecG_" + prd[j] + '_idx_' + str(prd_index[j]) speciesReferenceGlyph.setId(specsRefG_id) speciesReferenceGlyph.setSpeciesGlyphId(specG_id) speciesReferenceGlyph.setSpeciesReferenceId(ref_id) speciesReferenceGlyph.setRole(SPECIES_ROLE_PRODUCT) speciesReferenceCurve = speciesReferenceGlyph.getCurve() cb = speciesReferenceCurve.createCubicBezier() #cb = speciesReferenceCurve.createLineSegment() cb.setStart(Point(layoutns, centroid.x, centroid.y)) handles = api.default_handle_positions( netIn, allReactions[i].index) pos_x = handles[1 + j].x pos_y = handles[1 + j].y cb.setBasePoint1(Point(layoutns, pos_x, pos_y)) cb.setBasePoint2(Point(layoutns, pos_x, pos_y)) pos_x = get_node_by_index( netIn, allReactions[i].targets[j]).position.x pos_y = get_node_by_index( netIn, allReactions[i].targets[j]).position.y width = get_node_by_index( netIn, allReactions[i].targets[j]).size.x height = get_node_by_index( netIn, allReactions[i].targets[j]).size.y cb.setEnd( Point(layoutns, pos_x + 0.5 * width, pos_y - 0.5 * height)) sbmlStr_layout = writeSBMLToString( document) #sbmlStr is w/o layout info #self.SBMLText.SetValue(sbmlStr_layout) doc = readSBMLFromString(sbmlStr_layout) model_layout = doc.getModel() mplugin = model_layout.getPlugin("layout") # add render information to the first layout layout = mplugin.getLayout(0) rPlugin = layout.getPlugin("render") uri = RenderExtension.getXmlnsL2() if doc.getLevel( ) == 2 else RenderExtension.getXmlnsL3V1V1() # enable render package doc.enablePackage(uri, "render", True) doc.setPackageRequired("render", False) rPlugin = layout.getPlugin("render") rInfo = rPlugin.createLocalRenderInformation() rInfo.setId("info") rInfo.setName("Render Information") rInfo.setProgramName("RenderInformation") rInfo.setProgramVersion("1.0") # add some colors color = rInfo.createColorDefinition() color.setId("black") color.setColorValue("#000000") if numCompartments != 0: for i in range(len(allcompartments)): temp_id = allcompartments[i].id if temp_id != '_compartment_default': fill_color = allcompartments[i].fill_color border_color = allcompartments[i].border_color comp_border_width = allcompartments[i].border_width fill_color_str = '#%02x%02x%02x' % ( fill_color.r, fill_color.g, fill_color.b) border_color_str = '#%02x%02x%02x' % ( border_color.r, border_color.g, border_color.b) # color = rInfo.createColorDefinition() # color.setId("comp_fill_color" + str(i)) # color.setColorValue(fill_color_str) # color = rInfo.createColorDefinition() # color.setId("comp_border_color" + str(i)) # color.setColorValue(border_color_str) # # add a list of styles # style = rInfo.createStyle("compStyle" + str(i)) # style.getGroup().setFillColor("comp_fill_color" + str(i)) # style.getGroup().setStroke("comp_border_color" + str (i)) # style.getGroup().setStrokeWidth(comp_border_width) # style.addType("COMPARTMENTGLYPH") # rectangle = style.getGroup().createRectangle() # rectangle.setCoordinatesAndSize(RelAbsVector(0,0),RelAbsVector(0,0),RelAbsVector(0,0),RelAbsVector(0,100),RelAbsVector(0,100)) color = rInfo.createColorDefinition() color.setId("comp_fill_color") color.setColorValue(fill_color_str) color = rInfo.createColorDefinition() color.setId("comp_border_color") color.setColorValue(border_color_str) # add a list of styles style = rInfo.createStyle("compStyle") style.getGroup().setFillColor("comp_fill_color") style.getGroup().setStroke("comp_border_color") style.getGroup().setStrokeWidth(comp_border_width) style.addType("COMPARTMENTGLYPH") rectangle = style.getGroup().createRectangle() rectangle.setCoordinatesAndSize(RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 100), RelAbsVector(0, 100)) else: comp_border_width = 2. #fill_color_str = '#9ea9ff' #border_color_str = '#001dff' #set default compartment with white color fill_color_str = '#ffffff' border_color_str = '#ffffff' color = rInfo.createColorDefinition() color.setId("comp_fill_color") color.setColorValue(fill_color_str) color = rInfo.createColorDefinition() color.setId("comp_border_color") color.setColorValue(border_color_str) # add a list of styles style = rInfo.createStyle("compStyle") style.getGroup().setFillColor("comp_fill_color") style.getGroup().setStroke("comp_border_color") style.getGroup().setStrokeWidth(comp_border_width) style.addType("COMPARTMENTGLYPH") rectangle = style.getGroup().createRectangle() rectangle.setCoordinatesAndSize(RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 100), RelAbsVector(0, 100)) for i in range(len(allNodes)): node = allNodes[i] #print(node.shape) try: primitive, transform = node.shape.items[0] spec_fill_color = primitive.fill_color spec_border_color = primitive.border_color spec_fill_color_str = '#%02x%02x%02x' % (spec_fill_color.r, spec_fill_color.g, spec_fill_color.b) spec_border_color_str = '#%02x%02x%02x' % ( spec_border_color.r, spec_border_color.g, spec_border_color.b) spec_border_width = primitive.border_width except: #text-only #spec_fill_color_str = '#ffcc99' #spec_border_color_str = '#ff6c09' #set default species/node with white color spec_fill_color_str = '#ffffff' spec_border_color_str = '#ffffff' #transparent color does not work #spec_fill_color_str = '#000000' #spec_border_color_str = '#000000' spec_border_width = 2. color = rInfo.createColorDefinition() color.setId("spec_fill_color" + str(i)) color.setColorValue(spec_fill_color_str) color = rInfo.createColorDefinition() color.setId("spec_border_color" + str(i)) color.setColorValue(spec_border_color_str) style = rInfo.createStyle("specStyle" + str(i)) style.getGroup().setFillColor("spec_fill_color" + str(i)) style.getGroup().setStroke("spec_border_color" + str(i)) style.getGroup().setStrokeWidth(spec_border_width) style.addType("SPECIESGLYPH") if node.shape_index == 1 or node.shape_index == 6: #ellipse/text-outside ellipse = style.getGroup().createEllipse() ellipse.setCenter2D(RelAbsVector(0, 50), RelAbsVector(0, 50)) ellipse.setRadii(RelAbsVector(0, 50), RelAbsVector(0, 50)) elif node.shape_index == 2: #hexagon(6) polygon = style.getGroup().createPolygon() renderPoint1 = polygon.createPoint() renderPoint1.setCoordinates(RelAbsVector(0, 100), RelAbsVector(0, 50)) renderPoint2 = polygon.createPoint() renderPoint2.setCoordinates(RelAbsVector(0, 75), RelAbsVector(0, 7)) renderPoint3 = polygon.createPoint() renderPoint3.setCoordinates(RelAbsVector(0, 25), RelAbsVector(0, 7)) renderPoint4 = polygon.createPoint() renderPoint4.setCoordinates(RelAbsVector(0, 0), RelAbsVector(0, 50)) renderPoint5 = polygon.createPoint() renderPoint5.setCoordinates(RelAbsVector(0, 25), RelAbsVector(0, 86)) renderPoint6 = polygon.createPoint() renderPoint6.setCoordinates(RelAbsVector(0, 75), RelAbsVector(0, 86)) elif node.shape_index == 3: #line(2) polygon = style.getGroup().createPolygon() renderPoint1 = polygon.createPoint() renderPoint1.setCoordinates(RelAbsVector(0, 0), RelAbsVector(0, 50)) renderPoint2 = polygon.createPoint() renderPoint2.setCoordinates(RelAbsVector(0, 100), RelAbsVector(0, 50)) elif node.shape_index == 4: #triangle(3) polygon = style.getGroup().createPolygon() renderPoint1 = polygon.createPoint() renderPoint1.setCoordinates(RelAbsVector(0, 100), RelAbsVector(0, 50)) renderPoint2 = polygon.createPoint() renderPoint2.setCoordinates(RelAbsVector(0, 25), RelAbsVector(0, 7)) renderPoint3 = polygon.createPoint() renderPoint3.setCoordinates(RelAbsVector(0, 25), RelAbsVector(0, 86)) else: #rectangle shape_index = 0/text-only 5/demo-combo 7/others as default (rectangle) rectangle = style.getGroup().createRectangle() rectangle.setCoordinatesAndSize(RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 0), RelAbsVector(0, 100), RelAbsVector(0, 100)) style = rInfo.createStyle("textStyle") style.getGroup().setStroke("black") style.getGroup().setStrokeWidth(1.) style.addType("TEXTGLYPH") if numReactions != 0: for i in range(len(allReactions)): reaction_fill_color = allReactions[i].fill_color reaction_fill_color_str = '#%02x%02x%02x' % ( reaction_fill_color.r, reaction_fill_color.g, reaction_fill_color.b) reaction_line_thickness = allReactions[i].line_thickness color = rInfo.createColorDefinition() color.setId("reaction_fill_color" + str(i)) color.setColorValue(reaction_fill_color_str) style = rInfo.createStyle("reactionStyle" + str(i)) style.getGroup().setStroke("reaction_fill_color" + str(i)) style.getGroup().setStrokeWidth(reaction_line_thickness) style.addType("REACTIONGLYPH SPECIESREFERENCEGLYPH") sbmlStr_layout_render = writeSBMLToString(doc) self.SBMLText.SetValue(sbmlStr_layout_render) def Save(self, evt): """ Handler for the "Save" button. Save the SBML string to a file. """ self.dirname = "" #set directory name to blank dlg = wx.FileDialog(self.window, "Save As", self.dirname, wildcard="SBML files (*.xml)|*.xml", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: # Grab the content to be saved itcontains = self.SBMLText.GetValue() # Open the file for write, write, close self.filename = dlg.GetFilename() self.dirname = dlg.GetDirectory() filehandle = open(os.path.join(self.dirname, self.filename), 'w') filehandle.write(itcontains) filehandle.close() # Get rid of the dialog to keep things tidy dlg.Destroy()
class ExportAntimony(WindowedPlugin): metadata = PluginMetadata( name='ExportAntimony', author='Jin Xu', version='0.0.2', short_desc='Export Antimony.', long_desc='Export the Antimony String from the network on canvas.', category=PluginCategory.ANALYSIS) def create_window(self, dialog): """ Create a window to do the antimony export. Args: self dialog """ self.window = wx.Panel(dialog, pos=(5, 100), size=(300, 320)) show_btn = wx.Button(self.window, -1, 'Show', (5, 5)) show_btn.Bind(wx.EVT_BUTTON, self.Show) copy_btn = wx.Button(self.window, -1, 'Copy', (100, 5)) copy_btn.Bind(wx.EVT_BUTTON, self.Copy) save_btn = wx.Button(self.window, -1, 'Save', (195, 5)) save_btn.Bind(wx.EVT_BUTTON, self.Save) wx.StaticText(self.window, -1, 'Antimony string:', (5, 30)) self.antimonyText = wx.TextCtrl(self.window, -1, "", (10, 50), size=(260, 220), style=wx.TE_MULTILINE | wx.HSCROLL) self.antimonyText.SetInsertionPoint(0) return self.window def Show(self, evt): """ Handler for the "Export" button. Get the network on canvas and change it to an Antimony string. """ isReversible = True netIn = 0 numNodes = api.node_count(netIn) if numNodes == 0: wx.MessageBox("Please import a network on canvas", "Message", wx.OK | wx.ICON_INFORMATION) else: allNodes = api.get_nodes(netIn) numReactions = api.reaction_count(netIn) antStr = '' allReactions = api.get_reactions(netIn) for i in range(numReactions): antStr = antStr + 'J' + str(i) + ': ' rct_num = len(allReactions[i].sources) prd_num = len(allReactions[i].targets) for j in range(rct_num - 1): antStr = antStr + allNodes[allReactions[i].sources[j]].id antStr = antStr + ' + ' antStr = antStr + allNodes[allReactions[i].sources[rct_num - 1]].id antStr = antStr + ' -> ' for j in range(prd_num - 1): antStr = antStr + allNodes[allReactions[i].targets[j]].id antStr = antStr + ' + ' antStr = antStr + allNodes[allReactions[i].targets[prd_num - 1]].id antStr = antStr + '; E' + str(i) + '*(k' + str(i) for j in range(rct_num): antStr = antStr + '*' + allNodes[ allReactions[i].sources[j]].id if isReversible: antStr = antStr + ' - k' + str(i) + 'r' for j in range(prd_num): antStr = antStr + '*' + allNodes[ allReactions[i].targets[j]].id antStr = antStr + ')' antStr = antStr + ';\n' self.antimonyText.SetValue(antStr) def Copy(self, evt): """ Handler for the "Copy" button. Copy the Antimony string to a clipboard. """ self.dataObj = wx.TextDataObject() self.dataObj.SetText(self.antimonyText.GetValue()) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(self.dataObj) wx.TheClipboard.Close() else: wx.MessageBox("Unable to open the clipboard", "Error") def Save(self, evt): """ Handler for the "Save" button. Save the Antimony string to a file. """ self.dirname = "" #set directory name to blank dlg = wx.FileDialog(self.window, "Save As", self.dirname, wildcard="Antimony files (*.ant)|*.ant", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: # Grab the content to be saved itcontains = self.antimonyText.GetValue() # Open the file for write, write, close self.filename = dlg.GetFilename() self.dirname = dlg.GetDirectory() filehandle = open(os.path.join(self.dirname, self.filename), 'w') filehandle.write(itcontains) filehandle.close() # Get rid of the dialog to keep things tidy dlg.Destroy()