class Node: def __init__(self, x: int, y: int, width: int): self.x = x self.y = y self.width = width self.__neighbors = OrderedSet() self.__conn_ins = [] self.__edge_cost = {} def add_edge(self, node: "Node", delay: int = 0, force_connect: bool = False): if not force_connect: assert self.width == node.width if node not in self.__neighbors: self.__neighbors.add(node) node.__conn_ins.append(self) self.__edge_cost[node] = delay def remove_edge(self, node: "Node"): if node in self.__neighbors: self.__edge_cost.pop(node) self.__neighbors.remove(node) # remove the incoming connections as well node.__conn_ins.remove(self) def get_edge_cost(self, node: "Node") -> int: if node not in self.__edge_cost: return MAX_DEFAULT_DELAY else: return self.__edge_cost[node] def get_conn_in(self) -> List["Node"]: return self.__conn_ins def __iter__(self) -> Iterator["Node"]: return iter(self.__neighbors) def __len__(self): return len(self.__neighbors) @abstractmethod def __repr__(self): pass @abstractmethod def node_str(self): pass def clear(self): self.__neighbors.clear() self.__edge_cost.clear() self.__conn_ins.clear() def __contains__(self, item): return item in self.__neighbors def __hash__(self): return hash(self.width) ^ hash(self.x) ^ hash(self.y)
def test_clear(): set1 = OrderedSet('abracadabra') set1.clear() assert len(set1) == 0 assert set1 == OrderedSet()
class CCompiler(object): """Clara compiler. Compiles the application logical description, i.e. simple/conditional routing schema in a sets of instructions for a specified service. Below is an example of the application code, written in the specific Clara language: S1 + S2; if ( S1 == "abc" && S2 != "xyz") { S2 + S3; } elseif ( S1 == "fred" ) { S2 + S4; } else { S2 + S5,S6,S7; } S4,S5 + &S8; """ _SYNTAX_ERROR = "Syntax error in the Clara routing program. Malformed " +\ "routing statement" def __init__(self, service_name): self._instructions = OrderedSet() self._service_name = service_name @staticmethod def _no_blanks(string): no_blank_str = string.strip() return ''.join(no_blank_str.split()) def compile(self, composition): self._instructions.clear() ppi = list(self._pre_process(CCompiler._no_blanks(composition))) i = 0 while i < len(ppi): scs1 = ppi[i] if (scs1.startswith("if(") or scs1.startswith("}if(") or scs1.startswith("}else") or scs1.startswith("}elseif(")): instruction = self._parse_condition(scs1) j = i + 1 while j < len(ppi): scs2 = ppi[j] if (not scs2.startswith("}") and not scs2.startswith("if(") and not scs2.startswith("}if(") and not scs2.startswith("}elseif(") and not scs2.startswith("}else")): if instruction: self._parse_conditional_statement( scs2, instruction) else: i = j - 1 break j += 1 if instruction: self._instructions.add(instruction) else: self._parse_statement(scs1) i += 1 if not bool(self._instructions): raise ClaraException("Composition is irrelevant for a service.") @staticmethod def _pre_process(code_string): if code_string.find(";") == -1 and not code_string.endswith(";"): raise ClaraException("Syntax error in the Clara routing program." + "Missing end of statement operator = \";\"") instruction_set = [] for text in code_string.split(";"): if text != "" and text != "}": instruction_set.append(text) return instruction_set def _parse_statement(self, statement_string): ti = Instruction(self._service_name) statement_string = remove_first(statement_string, "}") pattern = re.compile(Regex.ROUTING_STATEMENT) match = pattern.match(statement_string) if match: if not (self._service_name in statement_string): return False ts = Statement(statement_string, self._service_name) ti.unconditional_statements.add(ts) self._instructions.add(ti) return True else: raise ClaraException(self._SYNTAX_ERROR) def _parse_conditional_statement(self, statement_string, instruction): pattern = re.compile(Regex.ROUTING_STATEMENT) match = pattern.match(statement_string) if match: if not (self._service_name in statement_string): return False ts = Statement(statement_string, self._service_name) if instruction.if_condition: instruction.if_statements.add(ts) elif instruction.elseif_statements: instruction.elseif_statements.add(ts) else: instruction.else_statements.add(ts) return True else: raise ClaraException(self._SYNTAX_ERROR) def _parse_condition(self, condition): pattern = re.compile(Regex.CONDITION) match = pattern.match(condition) if match: try: index = condition.find("{") + 1 statement_string = condition[index:] if not (self._service_name in statement_string): return None ts = Statement(statement_string, self._service_name) ti = Instruction(self._service_name) if condition.startswith("if(") or condition.startswith("}if("): par1 = condition.find("(") + 1 par2 = condition.rfind(")") tc = Condition(condition[par1:par2], self._service_name) ti.if_condition = tc ti.if_statements.add(ts) elif condition.startswith("}elseif("): par1 = condition.find("(") + 1 par2 = condition.rfind(")") tc = Condition(condition[par1:par2], self._service_name) ti.elseif_condition = tc ti.elseif_statements.add(ts) elif condition.startswith("}else"): ti.else_statements.add(ts) return ti except Exception as e: print e raise ClaraException(self._SYNTAX_ERROR) else: raise ClaraException(self._SYNTAX_ERROR) def get_unconditional_links(self): uncond = set() for instruction in self._instructions: for statement in instruction.unconditional_statements: uncond.update(statement.get_output_links()) return uncond def get_links(self, owner_ss, input_ss): outputs = set() in_condition = False condition_chosen = False for instruction in self._instructions: if bool(instruction.unconditional_statements): in_condition = False for statement in instruction.unconditional_statements: output = statement.get_output_links() outputs.update(output) continue if instruction.if_condition: in_condition = True condition_chosen = False if instruction.if_condition.is_true(owner_ss, input_ss): condition_chosen = True for statement in instruction.if_statements: output = statement.get_output_links() outputs.update(output) continue if in_condition and not condition_chosen: if instruction.elseif_condition: if instruction.elseif_condition.is_true( owner_ss, input_ss): condition_chosen = True for statement in instruction.elseif_statements: output = statement.get_output_links() outputs.update(output) continue if instruction.else_statements: condition_chosen = True for statement in instruction.else_statements: output = statement.get_output_links() outputs.update(output) return outputs
class CategorizedListbox(Frame): #AKA CLB def __init__(self, parent, listview): Frame.__init__(self, parent) self.root = parent #creates the list that will house all controlled ModlistListboxes self.modlists = [] self.selected_modlists = OrderedSet([]) self.listview = listview self.current_index = 0 self.grid_columnconfigure(0, weight=1) #checks for whether a pre-made CLB was input if len(self.modlists) <= 0: #Creates the default category 'Mods' when a new CLB is created self.insert(0, 'Mods') else: #Populates CLB with modlists given self.modlists = modlists def split(self, name, modlist_index, first, Last=None): '''Creates a new mod listbox with the mods of its original mod listbox based on the given indices, or an empty mod listbox''' modlist = self.modlists[modlist_index] if Last is None: pass def load(self, named_modlists): for i in range(len(self.modlists)): self.delete(0, force_delete=True) for named_modlist in named_modlists: self.insert(END, named_modlist[0], named_modlist[1]) def get_mod_count(self, colors): n = 0 for modlist in self.modlists: for mod in modlist.modlabel_list: if mod.color not in colors: n += 1 ## n += len(modlist.modlabel_list) return 'Total Modcount: ' + str(n) def insert(self, index, name, modlist_info=None, is_collapsed=False): '''Create and insert a new modlist at the given modlist index''' modlist = ModlistListbox(self, self.listview, name) #if modlist info given, populate modlistbox with mods if modlist_info is not None and modlist_info != []: for i in range(len(modlist_info)): modlist.insert(i, modlist_info[i]) #collapse mods if necessary if is_collapsed: modlist.force_collapse() #check last index values and set index accordingly if index == END or index >= len(self.modlists): index = len(self.modlists) #insert modlist into modlists list self.modlists.insert(index, modlist) #move modlists down if after inserted modlist if len(self.modlists) > index: for x in range(index, len(self.modlists)): self.modlists[x].grid(row=x, column=0, sticky='nsew') else: modlist.grid(row=len(self.mod_list), column=0, sticky='nsew') #Set modlist name label size self.update() def insert_input(self, index): if index == END: index = len(self.modlists) name = askstring('New Category at Index ' + str(index + 1), 'Name of new category:') if name is not None and name != '': self.insert(index, name) def merge_up(self, index): '''Merge the modlist at the given index with the modlist above it''' msgBox = messagebox.askquestion('Merge Categories Confirmation', 'Merge "' + self.modlists[index].name + '" into "' + self.modlists[index - 1].name + '"?', icon='warning') if msgBox == 'yes': #populate list of mod info lists to add to and from, then get name l1 = self.modlists[index - 1].get_all_info() l2 = self.modlists[index].get_all_info() l_name = self.modlists[index - 1].get_name() #insert new merged modlist self.insert(index - 1, l_name, l1 + l2) #delete both previous modlists for x in range(2): self.delete(index) def merge_down(self, index): '''Merge the modlist at the given index with the modlist below it''' msgBox = messagebox.askquestion('Merge Categories Confirmation', 'Merge "' + self.modlists[index].name + '" into "' + self.modlists[index + 1].name + '"?', icon='warning') if msgBox == 'yes': #populate list of mod info lists to add to and from, then get name l1 = self.modlists[index + 1].get_all_info() l2 = self.modlists[index].get_all_info() l_name = self.modlists[index + 1].get_name() #insert new merged modlist self.insert(index, l_name, l1 + l2) #delete both previous modlists for x in range(2): self.delete(index + 1) def delete_selected(self): '''delete all selected modlists''' if len(self.selected_modlists) == len(self.modlists): messagebox.showinfo('Selection Size Too Large', 'You cannot delete all the categories in the ' 'list. There must always be at least one.', icon='warning') elif len(self.selected_modlists) > 0: msgBox = messagebox.askquestion('Removing Selected Categories', 'Remove all selected categories ' 'and their contents?', icon='warning') if msgBox == 'yes': for x in range(len(self.selected_modlists)): self.delete(self.modlists.index(self.selected_modlists[0])) def delete_confirm(self, index): '''Add a confirmation to delete commands''' msgBox = messagebox.askquestion('Removing Category', 'Remove the "' + self.modlists[index].name + '" Category and its contents?', icon='warning') if msgBox == 'yes': self.delete(index) def delete(self, index, force_delete=False): '''Delete a modlist at the given index''' if not force_delete and len(self.modlists) == 1: messagebox.showinfo( 'Prohibited Action', 'You must always have at least one category in the list.') else: if index == END: index = len(self.mod_list) if index < len(self.modlists) - 1: for x in range(index, len(self.modlists) - 1): self.modlists[x + 1].grid(row=x, column=0, sticky='nsew') if self.modlists[index] in self.selected_modlists: self.selected_modlists.remove(self.modlists[index]) self.modlists[index].grid_forget() self.modlists[index].destroy() del self.modlists[index] def delete_mod(self, modlist_index, mod_index): '''Delete a mod at the given indices''' mod = self.modlists[modlist_index].modlabel_list[mod_index] msgBox = messagebox.askquestion('Removing Mod', 'Remove "{}"?'.format( mod.get_info()[1]), icon='warning') if msgBox == 'yes': self.modlists[modlist_index].delete(mod_index) def delete_selected_mod(self, event=None): selection_exists = False for modlist in self.modlists: if len(modlist.selected_modlabel_list) > 0: selection_exists = True if selection_exists: msgBox = messagebox.askquestion( 'Removing Selected', 'Remove selected mods from the list?', icon='warning') if msgBox == 'yes': for modlist in self.modlists: modlist.delete_selected() def delete_all_mods(self): msgBox = messagebox.askquestion('Removing All', 'Remove all mods from the list?', icon='warning') if msgBox == 'yes': for modlist in self.modlists: modlist.delete_all() def delete_all_cat(self, modlist_index): '''Delete all mods in a category at the given index''' modlist = self.modlists[modlist_index] msgBox = messagebox.askquestion('Removing All', 'Remove all mods from the "' + modlist.name + '" Category?', icon='warning') if msgBox == 'yes': modlist.delete_all() def collapse_all(self): '''Collapses all mod listboxes''' for mod in self.modlists: if not mod.is_collapsed: mod.force_collapse() def expand_all(self): for mod in self.modlists: if mod.is_collapsed: mod.force_expand() def get_info(self): '''Gets a list of lists of mods throughout ALL modlists''' list = [] for modlist in self.modlists: list.append(modlist.get_all_info()) return list def get_all_info(self): '''Gets a list of lists of all modlists''' return self.modlists def rename(self, index): '''Rename a category at the given index by remaking the category''' name = askstring('Rename Category at Index ' + str(index + 1), 'New name of category:') if name is not None and name != '': data = self.modlists[index].get_all_info() is_collapsed = self.modlists[index].is_collapsed self.delete(index, force_delete=True) self.insert(index, name, data) self.modlists[index].forceSelectTop() if is_collapsed: self.modlists[index].force_collapse() #====passed or modified modlist functions==== def onShiftClickEvent(self, event): if len(self.selected_modlists) > 0: #set original index to start multi-selection from origin = self.modlists.index(self.selected_modlists[-1]) for x in range(len(self.modlists)): #checks every modlist for a valid multi-selection activation if self.modlists[x].is_top_entered: #checks whether the index of the target modlists is above #or below origin, then multi-selects accordingly if (x - origin) > 0: for y in range(origin, x + 1): self.selected_modlists.append(self.modlists[y]) self.modlists[y].forceSelectTop() elif (x - origin) < 0: for y in range(x, origin): self.selected_modlists.append(self.modlists[y]) self.modlists[y].forceSelectTop() else: for modlist in self.modlists: modlist.onShiftClickEvent(event) def dragSelection(self, event): '''Moves selected mods depending on mouse movement, and moves mods into and out of categories they are moved into and out of''' for modlist in self.modlists: modlist.dragSelection(event) def moveInto(self, direction, modlist): '''Depending on the direction, move the selected mods from the modlist into the modlist below or above it''' modlist_index = self.modlists.index(modlist) if direction == -1 and modlist_index != 0: #Move up for mod in sorted(modlist.selected_modlabel_list, key=lambda x: x.get_index()): self.modlists[modlist_index - 1].insert(END, mod.get_info()) #messy code to make the mod in the new category selected new_upper_mod = self.modlists[modlist_index - 1].modlabel_list[-1] new_upper_mod.force_select() self.modlists[modlist_index - 1].selected_modlabel_list.append(new_upper_mod) selected_list_len = len(modlist.selected_modlabel_list) for i in range(selected_list_len): modlist.delete(0) modlist.selected_modlabel_list.clear() self.modlists[modlist_index - 1].force_expand() elif direction == 1 and modlist_index != len(self.modlists) - 1: #Move down for mod in sorted(modlist.selected_modlabel_list, key=lambda x: x.get_index(), reverse=True): self.modlists[modlist_index + 1].insert(0, mod.get_info()) #messy code to make the mod in the new category selected new_lower_mod = self.modlists[modlist_index + 1].modlabel_list[0] new_lower_mod.force_select() self.modlists[modlist_index + 1].selected_modlabel_list.append(new_lower_mod) selected_list_len = len(modlist.selected_modlabel_list) for i in range(selected_list_len): modlist.delete(END) modlist.selected_modlabel_list.clear() self.modlists[modlist_index + 1].force_expand() def moveSelectionUp(self, event=None): focused_widget = self.master.master.focus_get() if event is not None and type(focused_widget) in [ Entry, Text ] and focused_widget.cget('state') == 'normal': return else: top_selected = False if len(self.selected_modlists) > 0: for modlist in self.modlists: if modlist.is_top_selected: top_selected = True if top_selected: sorted_selected_modlists = sorted( self.selected_modlists, key=lambda x: self.modlists.index(x)) if sorted_selected_modlists[-1] == self.modlists[0]: return for modlist in sorted_selected_modlists: modlist_index = self.modlists.index(modlist) list_to_move = self.modlists[modlist_index - 1].get_all_info() list_to_move_name = self.modlists[modlist_index - 1].get_name() list_to_move_is_collapsed = self.modlists[modlist_index - 1].is_collapsed self.delete(modlist_index - 1) self.insert(modlist_index, list_to_move_name, list_to_move, list_to_move_is_collapsed) #Collapse the category moved if it was collapsed if list_to_move_is_collapsed: self.modlists[modlist_index].force_collapse() else: for modlist in self.modlists: n = 0 n = modlist.moveSelectionUp() if n == -1: self.moveInto(n, modlist) def moveSelectionDown(self, event=None): focused_widget = self.master.master.focus_get() if event is not None and type(focused_widget) in [ Entry, Text ] and focused_widget.cget('state') == 'normal': return else: top_selected = False if len(self.selected_modlists) > 0: for modlist in self.modlists: if modlist.is_top_selected: top_selected = True if top_selected: sorted_selected_modlists = sorted( self.selected_modlists, key=lambda x: self.modlists.index(x)) if sorted_selected_modlists[-1] == self.modlists[-1]: return for modlist in sorted_selected_modlists: modlist_index = self.modlists.index(modlist) list_to_move = self.modlists[modlist_index + 1].get_all_info() list_to_move_name = self.modlists[modlist_index + 1].get_name() list_to_move_is_collapsed = self.modlists[modlist_index + 1].is_collapsed self.delete(self.modlists.index(modlist) + 1) self.insert(modlist_index, list_to_move_name, list_to_move, list_to_move_is_collapsed) #Collapse the category moved if it was collapsed if list_to_move_is_collapsed: self.modlists[modlist_index].force_collapse() else: for modlist in self.modlists: n = 0 n = modlist.moveSelectionDown() if n == 1: self.moveInto(n, modlist) return def onClickEvent(self, event): '''When the player clicks, control whether categories should be selected''' deselect_others = True #if clicked mod is already part of selection, prevents the deselection of other mods for x in range(len(self.modlists)): if self.modlists[x].is_top_entered and self.modlists[ x].is_top_selected: deselect_others = False if deselect_others: for x in range(len(self.modlists)): #Controls the selection of category names modlist = self.modlists[x] modlist.selectTop() if modlist.is_top_selected and modlist not in self.selected_modlists: self.current_index = x self.selected_modlists.append(modlist) elif not modlist.is_top_selected and modlist in self.selected_modlists: self.selected_modlists.remove(modlist) for modlist in self.modlists: modlist.onClickEvent(event) def selectAll(self): for modlist in self.modlists: modlist.forceDeselectTop() modlist.selectAll() self.selected_modlists.clear() def insert_mod(self, modlist_index, mod_index): self.modlists[modlist_index].insertInput(mod_index) def insert_custom_mod(self, modlist_index, mod_index): self.modlists[modlist_index].insertCustomInput(mod_index) def batch_insert_mod(self, modlist_index, mod_index): l = [] LinkGrabber(self, l, nexus=True) if len(l) == 1 and l[0] == False: messagebox.showinfo( 'No Valid Data Found', 'Either none of the ' 'links provided were valid Nexus mod links, ' 'or the Nexus web server is currently unava' 'ilable.') else: for info in reversed(l): self.modlists[modlist_index].insert(mod_index, info) def move_mod_to(self, modlist_index, target_modlist): modlist = self.modlists[modlist_index] for mod in sorted(modlist.selected_modlabel_list, key=lambda x: x.get_index()): target_modlist.insert(END, mod.get_info()) modlist.delete_selected() def rightClickMenu(self, event, rc_menu): ## #Select proper categories ## for i in self.modlists: ## i.selectTop() self.onClickEvent(event) #Initialize submenus colors_menu = Menu(self.master.master, tearoff=0) remove_menu = Menu(self.master.master, tearoff=0) merge_menu = Menu(self.master.master, tearoff=0) select_menu = Menu(self.master.master, tearoff=0) links_menu = Menu(self.master.master, tearoff=0) move_menu = Menu(self.master.master, tearoff=0) #Get clicked indices and modlist modlist_index = self.dynamic_nearest() mod_index = self._get_clicked_mod_index(modlist_index) modlist = self.modlists[modlist_index] #General modlist commands rc_menu.add_command( label='Insert Nexus Mod Here...', command=lambda: self.insert_mod(modlist_index, mod_index)) rc_menu.add_command( label='Insert Multiple Nexus Mods Here...', command=lambda: self.batch_insert_mod(modlist_index, mod_index)) rc_menu.add_command( label='Insert Non-Nexus Mod Here...', command=lambda: self.insert_custom_mod(modlist_index, mod_index)) y = self._get_clicked_cat_index(modlist_index) rc_menu.add_command(label='Insert Category Here...', command=lambda: self.insert_input(y)) rc_menu.add_command(label='Insert Category At End...', command=lambda: self.insert_input(END)) #Move options rc_menu.add_separator() rc_menu.add_cascade(label='Move Selected Mods To...', menu=move_menu) if len(modlist.modlabel_list) > 0 and \ len(modlist.selected_modlabel_list) > 0: for ml in self.modlists: move_menu.add_command(label=ml.name, command=lambda ml=ml: self.move_mod_to( \ modlist_index,ml)) if ml == modlist: move_menu.entryconfig(ml.name, state='disabled') #Color options if len(modlist.modlabel_list) > 0: rc_menu.add_separator() rc_menu.add_cascade(label="Change Selected Mods' Color To...", menu=colors_menu) colors_menu.add_command(label='Default', command=lambda: \ self.update_selected_colors('#383838')) colors_menu.add_command(label='Red', command=lambda: \ self.update_selected_colors('red')) colors_menu.add_command(label='Blue', command=lambda: \ self.update_selected_colors('blue')) colors_menu.add_command(label='Green', command=lambda: \ self.update_selected_colors('green')) colors_menu.add_command(label='Yellow', command=lambda: \ self.update_selected_colors('yellow')) rc_menu.add_separator() #incompatibilities commands rc_menu.add_command(label='Manage Incompatibilities...', command=lambda: \ self.manage_incomp(modlist_index, mod_index)) if len(self.modlists[modlist_index].modlabel_list[mod_index]. conflicts) > 0: rc_menu.add_command(label='View Conflicts', command=lambda: self.view_conflicts( \ modlist_index, mod_index)) rc_menu.add_separator() rc_menu.add_command(label='Rename Category', command=lambda: self.rename(y)) #Link options rc_menu.add_separator() rc_menu.add_command( label='Copy Mod Link', command=lambda: self.copyURL(modlist_index, mod_index)) rc_menu.add_cascade(label='Open Links...', menu=links_menu) links_menu.add_command(label='Open Selected Mod Links', command=self.open_selected) links_menu.add_command(label='Open All Mod Links in Category Here', command=lambda x=modlist_index: self.openAll(x)) #Selection options rc_menu.add_separator() rc_menu.add_cascade(label='Select...', menu=select_menu) select_menu.add_command( label='Select Here', command=lambda: modlist.rightClickSelect(mod_index)) select_menu.add_command(label='Select All Mods in Category Here', command=modlist.selectAll) select_menu.add_command(label='Select All Mods', command=self.selectAll) #Merge options rc_menu.add_separator() rc_menu.add_cascade(label='Merge Category...', menu=merge_menu) merge_menu.add_command(label='Merge Category Here Into Upper', command=lambda: self.merge_up(modlist_index)) merge_menu.add_command(label='Merge Category Here Into Lower', command=lambda: self.merge_down(modlist_index)) if modlist_index == 0: merge_menu.entryconfig('Merge Category Here Into Upper', state='disabled') if modlist_index == len(self.modlists) - 1: merge_menu.entryconfig('Merge Category Here Into Lower', state='disabled') #Removal options rc_menu.add_separator() rc_menu.add_cascade(label='Remove...', menu=remove_menu) remove_menu.add_command( label='Remove Mod Here', command=lambda: self.delete_mod(modlist_index, mod_index)) remove_menu.add_command(label='Remove Selected Mods', command=self.delete_selected_mod) remove_menu.add_command( label='Remove All In Category', command=lambda: self.delete_all_cat(modlist_index)) remove_menu.add_command( label='Remove Category Here', command=lambda: self.delete_confirm(modlist_index)) remove_menu.add_command(label='Remove Selected Categories', command=lambda: self.delete_selected()) remove_menu.add_command(label='Remove All Mods', command=self.delete_all_mods) #Disables the appropriate menu options if len(modlist.modlabel_list) == 0: remove_menu.entryconfig('Remove Mod Here', state='disabled') remove_menu.entryconfig('Remove All In Category', state='disabled') select_menu.entryconfig('Select Here', state='disabled') select_menu.entryconfig('Select All Mods in Category Here', state='disabled') links_menu.entryconfig('Open All Mod Links in Category Here', state='disabled') if len(self.selected_modlists) == 0: remove_menu.entryconfig('Remove Selected Categories', state='disabled') #Selects and deselects appropriate mods and categories i = 0 for modlist in self.modlists: i += len(modlist.selected_modlabel_list) ## modlist.onClickEvent(event) modlist.rightClickMenu(event, rc_menu) if i == 0: links_menu.entryconfig('Open Selected Mod Links', state='disabled') def view_conflicts(self, modlist_index, mod_index): conflicts = self.modlists[modlist_index].modlabel_list[ mod_index].conflicts ConflictListbox(self, conflicts) def copyURL(self, modlist_index, mod_index): self.master.master.clipboard_clear() self.master.master.clipboard_append( self.modlists[modlist_index].modlabel_list[mod_index].get_info() [0]) def openAll(self, modlist_index): msgBox = messagebox.askquestion('Opening All Mod Links', 'Open all mod links in the "' + self.modlists[modlist_index].name + '" category in your default browser?', icon='warning') if msgBox == 'yes': self.modlists[modlist_index].open_all_links() def open_selected(self): for modlist in self.modlists: modlist.open_selected_links() def update_color(self, modlist_index, mod_index, color, state='normal'): '''Update a single mod's label color''' self.modlists[modlist_index].modlabel_list[ \ mod_index].update_color(color,state) def update_selected_colors(self, color, state='normal'): for modlist in self.modlists: for mod in modlist.selected_modlabel_list: mod.update_color(color, state) def manage_incomp(self, modlist_index, mod_index): l = self.modlists[modlist_index].modlabel_list[ mod_index].incompatibilities IncompatibilityManager(self, l) def _get_clicked_mod_index(self, modlist_index): '''return updated index if mouse is below the last mod in a given list''' modlist = self.modlists[modlist_index] mod_index = modlist.nearest() if modlist.listview: height = modlist.listview_height else: height = modlist.defaultview_height mouse_y = modlist.mlb_frame.winfo_pointery( ) - modlist.mlb_frame.winfo_rooty() if len(modlist.modlabel_list) > 1 and (height * mod_index + height) < mouse_y: return mod_index + 1 else: return mod_index def _get_clicked_cat_index(self, modlist_index): '''return updated index if mouse is below the last category''' modlist = self.modlists[modlist_index] height = modlist.winfo_height() mouse_y = self.winfo_pointery() - self.winfo_rooty() if (modlist.winfo_y() + modlist.winfo_height()) < mouse_y: return modlist_index + 1 else: return modlist_index def onDoubleClickEvent(self, event): for modlist in self.modlists: modlist.onDoubleClickEvent(event) def toggle_view(self): for modlist in self.modlists: modlist.toggle_view() def dynamic_nearest(self): '''get index of mod listbox nearest to the mouse y position. designed to work with widgets of variable sizes''' index = 0 current_nearest_index = 0 #get the absolute position of the mouse in relation to the ModlistListbox position mouse_y = self.winfo_pointery() - self.winfo_rooty() if len(self.modlists) > 1: #initialize y_maps, a list of 2-lengthed lists that store the #start and end y values of each modlist y_maps = [] for i in range(len(self.modlists)): #populate heights modlist = self.modlists[i] #Set y-extending values if i == 0: base = 0 else: base = y_maps[i - 1][1] if modlist.listview: mod_height = modlist.listview_height * len( modlist.modlabel_list) else: mod_height = modlist.defaultview_height * len( modlist.modlabel_list) #set start and end values if modlist.is_collapsed: y_maps.append([base, base + modlist.name_height]) else: y_maps.append( [base, base + modlist.name_height + mod_height]) for i in range(len(y_maps)): #find the index within the y mappings if y_maps[i][0] <= mouse_y < y_maps[i][1]: index = i return index
class ModlistListbox(Frame): def __init__(self, parent, listview=False, name='Mods'): Frame.__init__(self, parent, bg='#2d2d2d') self.mod_list = [] self.modlabel_list = [] self.selected_modlabel_list = OrderedSet([]) self.listview = listview self.listview_height = 29 self.defaultview_height = 68 self.name_height = 35 self.current_index = None #category selection variables self.is_top_entered = False self.is_top_selected = False #collapse variable self.is_collapsed = False #enter variable self.is_entered_all = False #create mod listbox frame self.mlb_frame = Frame(self) #naming initalization self.name = name self.name_label = TagLabel(self, bordercolor='#666666', size=12, font=('roboto', 16, 'bold'), text=self.name, bg='#444444', fg='#f0f0f0', borderwidth=4, relief='flat') #limit the minimum size of name label if self.name_label.label.winfo_reqwidth() < 205: self.name_label.label.configure(width=15) #enter and leave events self.name_label.bind('<Enter>', self.on_enter_top) self.name_label.bind('<Leave>', self.on_leave_top) self.bind('<Enter>', self.on_enter_all) self.bind('<Leave>', self.on_leave_all) #lay out the name frame and mod listbox frame self.name_label.pack(side='top', fill='y', anchor='sw', padx=10) self.mlb_frame.pack(side='bottom', fill='both', expand=True) self.mlb_frame.grid_columnconfigure(0, weight=1) self.update_idletasks() def update_name(self, name): self.name = name ## self.name_label.update_text(name) #resizing a pre-existing TagLabel isn't working when changing text #so just make a new one 4Head new_name = TagLabel(self, bordercolor='#666666', size=12, font=('roboto', 16, 'bold'), text=name, bg='#444444', fg='#f0f0f0', borderwidth=4, relief='flat') self.name_label.pack_forget() self.name_label.destroy() self.name_label = new_name self.name_label.pack(side='top', fill='y', anchor='sw', padx=10) if self.name_label.label.winfo_reqwidth() < 205: self.name_label.label.configure(width=15) def get_name(self): return self.name def toggle_collapse(self): '''Collapses or expands the mod listbox''' if len(self.modlabel_list) > 0: if self.is_collapsed: self.force_expand() else: self.force_collapse() self.update_idletasks() def force_collapse(self): if len(self.modlabel_list) > 0: for mod in self.modlabel_list: mod.grid_remove() self.mlb_frame.configure(height=1) self.is_collapsed = True self.name_label.configure(bg='white') def force_expand(self): if len(self.modlabel_list) > 0: for mod in self.modlabel_list: mod.grid() self.is_collapsed = False self.name_label.configure(bg='#666666') def insert(self, index, info): '''inserts a mod at the given index, and with the given info''' if index == END or index > len(self.mod_list): index = len(self.mod_list) mod_label = ModLabel(self.mlb_frame, info=info, index=index, listview=self.listview) #Try to update the new indices. Fails if from an older mod try: mod_label.update_color(info[6]) mod_label.incompatibilities = info[7] except IndexError: pass self.mod_list.insert(index, info) self.modlabel_list.insert(index, mod_label) if len(self.mod_list) > index: for x in range(index, len(self.mod_list)): self.modlabel_list[x].update_index(x) self.modlabel_list[x].grid(row=x, column=0, sticky='nsew') else: mod_label.grid(row=len(self.mod_list), column=0, sticky='nsew') if self.is_collapsed: self.force_expand() def delete(self, index): '''Delete a mod at the given index''' if index == END: index = len(self.mod_list) - 1 if index < len(self.mod_list) - 1: for x in range(index, len(self.mod_list) - 1): self.modlabel_list[x + 1].grid(row=x, column=0, sticky='nsew') self.modlabel_list[x + 1].update_index(x) if self.modlabel_list[index] in self.selected_modlabel_list: self.selected_modlabel_list.remove(self.modlabel_list[index]) self.modlabel_list[index].grid_forget() self.modlabel_list[index].destroy() del self.mod_list[index] del self.modlabel_list[index] if len(self.modlabel_list) == 0: self.mlb_frame.configure(height=1) if self.is_collapsed: self.force_expand() #Quick fix for a category not properly expanding if last mod deleted self.is_collapsed = False self.name_label.configure(bg='#666666') def delete_selected(self): for x in range(len(self.selected_modlabel_list)): self.delete(self.selected_modlabel_list[0].get_index()) def delete_all_confirm(self): msgBox = messagebox.askquestion('Removing All', 'Remove all mods from the ' + self.name + ' Category?', icon='warning') if msgBox == 'yes': self.delete_all() def delete_all(self): '''deletes all mods from the list''' for x in range(len(self.modlabel_list)): self.delete(0) #Quick fix for a category not properly expanding if last mod deleted self.is_collapsed = False self.name_label.configure(bg='#666666') def open_link(self, modlabel): webbrowser.open_new(modlabel.get_info()[0]) def open_selected_links(self): for mod in sorted(self.selected_modlabel_list, key=lambda x: x.get_index()): self.open_link(mod) def open_all_links(self): for mod in self.modlabel_list: self.open_link(mod) def get_size(self): return len(self.modlabel_list) def get_all_info(self): '''return a list of lists of all mod info''' l = [] for mod in self.modlabel_list: l.append(mod.get_info()) return l def get_info(self, index): '''return a list of the mod info at the given index''' return self.modlabel_list[index].get_info() def get_height(self): return len(self.modlabel_list) def toggle_view(self): for mod in self.modlabel_list: mod.toggle_view() self.listview = not self.listview def force_listview(self): for mod in self.modlabel_list: mod.display_listview() self.listview = True def force_defaultview(self): for mod in self.modlabel_list: mod.display_default() self.listview = False def on_enter_top(self, event): '''event to determine if the list's category title is focused''' self.is_top_entered = True def on_leave_top(self, event): self.is_top_entered = False def on_enter_all(self, event): self.is_entered_all = True def on_leave_all(self, event): self.is_entered_all = False #====Right Click Menu Functionality def rightClickMenu(self, event, rc_menu): for mod in self.modlabel_list: if mod.is_focused: #If the mod clicked is not selected, select it if mod not in self.selected_modlabel_list: self.onClickEvent(event) ## rc_menu.add_command(label='Select', ## command=lambda mod=mod: self.rightClickSelect(mod)) ## rc_menu.add_command(label='Select All Mods in "'+self.name+'" Category', ## command=self.selectAll) ## rc_menu.add_separator() ## rc_menu.add_separator() ## rc_menu.add_command(label='Remove', ## command=lambda mod=mod: self.delete(mod.get_index())) ## if len(self.selected_modlabel_list) > 0: ## rc_menu.add_command(label='Remove Selected', ## command=self.delete_selected) ## if len(self.modlabel_list) > 0: ## rc_menu.add_command(label='Remove All Mods In "'+self.name+'" Category', ## command=self.delete_all_confirm) def insertInput(self, index): url = askstring('New Mod at Index ' + str(index + 1), 'Input Nexus URL') info = None if (url is not None): info = ParseURL.parse_nexus_url(url) if info is not None: self.insert(index, info) def insertCustomInput(self, index): info = [] CM = CustomModMessageBox(self, 'New Mod at Index ' + str(index + 1), info) if info != []: self.insert(index, info) #====Selection Functionality==== def onClickEvent(self, event): deselect_others = True self._check_descs() if len(self.modlabel_list) > 0: #if clicked mod is already part of selection, prevents the deselection of other mods for x in range(len(self.modlabel_list)): if self.modlabel_list[ x].is_index_focused and self.modlabel_list[ x].is_selected: deselect_others = False self.current_index = x if deselect_others: #checks every modlabel to see if any are selected for x in range(len(self.modlabel_list)): self.modlabel_list[x].select() #code for multi-selection capabilities if self.modlabel_list[x].is_selected and self.modlabel_list[ x] not in self.selected_modlabel_list: #adds selected modlabels to the selected modlabels list self.current_index = x self.selected_modlabel_list.append( self.modlabel_list[x]) elif not self.modlabel_list[ x].is_selected and self.modlabel_list[ x] in self.selected_modlabel_list: #removes deselected modlabels from the selected modlabels list self.selected_modlabel_list.remove( self.modlabel_list[x]) def _check_descs(self): '''checks whether mouse is over a mod description or not''' #Get widget type under mouse x, y = self.winfo_pointerxy() widget = self.winfo_containing(x, y) for mod in self.modlabel_list: if widget is not mod.description: mod.disable_desc_edit() else: mod.enable_desc_edit() def onDoubleClickEvent(self, event): if self.is_top_entered and len(self.modlabel_list) > 0: self.toggle_collapse() def onShiftClickEvent(self, event): #code for multi-selection if len(self.selected_modlabel_list) > 0: #set original index to start multi-selection from origin = self.selected_modlabel_list[-1].get_index() for x in range(len(self.modlabel_list)): # checks every modlabel for a valid multi-selection activation if self.modlabel_list[x].is_index_focused: #checks whether the index of the target modlabel is above #or below origin, then multi-selects accordingly if (x - origin) > 0: for y in range(origin, x + 1): self.selected_modlabel_list.append( self.modlabel_list[y]) self.modlabel_list[y].force_select() elif (x - origin) < 0: for y in range(x, origin): self.selected_modlabel_list.append( self.modlabel_list[y]) self.modlabel_list[y].force_select() def rightClickSelect(self, mod_index): mod = self.modlabel_list[mod_index] self.deselectAll() mod.force_select() self.selected_modlabel_list.append(mod) def selectAll(self): '''Selects all the mods''' if len(self.modlabel_list) > 0: for mod in self.modlabel_list: self.selected_modlabel_list.append(mod) mod.force_select() def deselectAll(self): if len(self.selected_modlabel_list) > 0: for mod in self.selected_modlabel_list: mod.force_deselect() self.selected_modlabel_list.clear() def selectTop(self): if self.is_top_entered: self.forceSelectTop() else: self.forceDeselectTop() def forceSelectTop(self): self.is_top_selected = True self.name_label.label.configure(bg='#f0f0f0', fg='#444444') def forceDeselectTop(self): self.is_top_selected = False self.name_label.label.configure(bg='#444444', fg='#f0f0f0') def is_top_selected(self): return self.is_top_selected #====Drag and Drop Functionality==== def moveSelectionUp(self): '''Goes through the selected mods and moves them up by 1''' if len(self.selected_modlabel_list) > 0: for mod in self.selected_modlabel_list: #if mod at upper limit, don't move anything if mod.get_index() == 0: return -1 #sorts selected mods using the index as the key, then iterates for mod in sorted(self.selected_modlabel_list, key=lambda x: x.get_index()): modtomove_data = self.modlabel_list[mod.get_index() - 1].get_info() modtomove_index = mod.get_index() - 1 self.delete(modtomove_index) self.insert(modtomove_index + 1, modtomove_data) def moveSelectionDown(self): '''Goes through the selected mods and moves them down by 1''' if len(self.selected_modlabel_list) > 0: for mod in self.selected_modlabel_list: #if mod at lower limit, don't move anything if mod.get_index() == len(self.modlabel_list) - 1: return 1 #sorts selected mods using the index as the key, then iterates for mod in sorted(self.selected_modlabel_list, key=lambda x: x.get_index(), reverse=True): modtomove_data = self.modlabel_list[mod.get_index() + 1].get_info() modtomove_index = mod.get_index() + 1 self.delete(modtomove_index) self.insert(modtomove_index - 1, modtomove_data) def dragSelection(self, event): '''Moves all selected ModLabels up or down depending on mouse movement while the mouse is held''' i = self.nearest() if self.current_index is not None: if i < self.current_index and i != -1: self.moveSelectionUp() self.current_index = i elif i > self.current_index and i != len(self.modlabel_list): self.moveSelectionDown() self.current_index = i return i def nearest(self): '''get index of ModLabel item nearest to the mouse y position using hardcoded ModLabel heights''' index = 0 current_nearest_index = 0 #get the correct height of the ModLabels if self.listview: height = self.listview_height else: height = self.defaultview_height #get the absolute position of the mouse in relation to the ModlistListbox position mouse_y = self.mlb_frame.winfo_pointery() - self.mlb_frame.winfo_rooty( ) if len(self.modlabel_list) > 0: current_index = 0 distance_from_current = abs((height / 2) - mouse_y) for i in range(len(self.modlabel_list)): distance_from_next = abs((i * height + (height / 2)) - mouse_y) if distance_from_current > distance_from_next: distance_from_current = distance_from_next current_index = i #if going beyond the list, return an index beyond the list to signify it moving into a different category ## if(current_index = index = current_index return index #====Code that doesn't f****n' work==== '''NOTES: This nearest() is based off of each widget's actual position on the board. It bugs out when a mod is
class Tile: def __init__(self, x: int, y: int, track_width: int, switchbox: SwitchBox, height: int = 1): self.x = x self.y = y self.track_width = track_width self.height = height # create a copy of switch box because the switchbox nodes have to be # created self.switchbox: SwitchBox = SwitchBox(x, y, switchbox.num_track, switchbox.width, switchbox.internal_wires) self.ports: Dict[str, PortNode] = {} self.inputs = OrderedSet() self.outputs = OrderedSet() # hold for the core self.core: InterconnectCore = None def __eq__(self, other): if not isinstance(other, Tile): return False return self.x == other.x and self.y == other.y and \ self.height == other.height def __repr__(self): return f"TILE ({self.x}, {self.y}, {self.height}, " +\ f"{self.switchbox.id})" def set_core(self, core: InterconnectCore): self.inputs.clear() self.outputs.clear() self.ports.clear() self.core = core # this is to clear to core if core is None: return inputs = core.inputs()[:] inputs.sort(key=lambda x: x[1]) for width, port_name in inputs: if width == self.track_width: self.inputs.add(port_name) # create node self.ports[port_name] = PortNode(port_name, self.x, self.y, width) outputs = core.outputs()[:] outputs.sort(key=lambda x: x[1]) for width, port_name in outputs: if width == self.track_width: self.outputs.add(port_name) # create node self.ports[port_name] = PortNode(port_name, self.x, self.y, width) def core_has_input(self, port: str): return port in self.inputs def core_has_output(self, port: str): return port in self.outputs def name(self): return str(self) def get_sb(self, side: SwitchBoxSide, track: int, io: SwitchBoxIO) -> Union[SwitchBoxNode, None]: return self.switchbox.get_sb(side, track, io) def set_core_connection(self, port_name: str, connection_type: List[SBConnectionType]): # make sure that it's an input port is_input = self.core_has_input(port_name) is_output = self.core_has_output(port_name) if not is_input and not is_output: # the core doesn't have that port_name return elif not (is_input ^ is_output): raise ValueError("core design error. " + port_name + " cannot be " " both input and output port") port_node = self.ports[port_name] # add to graph node first, we will handle magma in a different pass # based on the graph, since we need to compute the mux height for side, track, io in connection_type: sb = self.get_sb(side, track, io) if is_input: sb.add_edge(port_node) else: port_node.add_edge(sb) @staticmethod def create_tile(x: int, y: int, bit_width: int, num_tracks: int, internal_wires: List[Tuple[int, SwitchBoxSide, int, SwitchBoxSide]], height: int = 1) -> "Tile": switch = SwitchBox(x, y, num_tracks, bit_width, internal_wires) tile = Tile(x, y, bit_width, switch, height) return tile def clone(self): # clone the switchbox switchbox = self.switchbox.clone() tile = Tile(self.x, self.y, self.track_width, switchbox, self.height) # tile creates an empty copy of it, so we have to replace it tile.switchbox = switchbox # we don't clone the cores tile.set_core(self.core) return tile