def down_click(self, part, event=None): """ Process down click over highlighted part All highlighted parts elicit a call :part: highlighted part :event: event if available :Returns: True if processing is complete """ if self.down_click_call is not None: return self.down_click_call(part, event=event) """ Self processing """ if part.is_edge() and not part.is_turned_on(): SlTrace.lg("turning on %s" % part, "turning_on") self.drawn_lines.append(part) part.turn_on(player=self.get_player()) regions = part.get_adjacents() # Look if we completed any squares for square in regions: if square.is_complete(): self.complete_square(part, square) return True # Indicate processing is done return False
def set_play_level(self, play_level=None): """ Set players' level via comma separated string :play_level: comma separated string of playing player's Labels """ players = self.get_players(all=True) play_levels = [x.strip() for x in play_level.split(',')] def_level = "2" if len(play_levels) == 0: SlTrace.lg("Setting to default level: {}".format(def_level)) play_levels = def_level # Default player_idx = -1 for player in players: player_idx += 1 if player_idx < len(play_levels): player_level = play_levels[player_idx] else: player_level = play_levels[-1] # Use last level plevel = int(player_level) playing_var = player.ctls_vars["level"] player.level = plevel playing_var.set(plevel) SlTrace.lg("Setting {} play level to {:d}".format(player, plevel))
def __init__(self, *args, title=None, control_prefix=None, image_hash=None, select_list_hash=None, **kwargs): """ Control players """ self.max_color_len = len("#80ffff") # For fields which have to contain hex color representation if image_hash is None: image_hash = ImageHash() # Create our own self.image_hash = image_hash if select_list_hash is None: select_list_hash = ImageHash() self.select_list_hash = select_list_hash # Protect against early checking self.players = {} # Dictionary of SelectPlayer self.playing = [] # Playing, in playing order self.cur_pindex = None # Current player index in self.playing list self.controls_frame = None # Set as no controls self.is_players_displayed = False # Set when displayed self.players_frame = None # Set when initialized self.player_field_by_widget = {} if title is None: title = "Player Control" if control_prefix is None: control_prefix = PlayerControl.CONTROL_NAME_PREFIX super().__init__(*args, title=title, control_prefix=control_prefix, **kwargs) SlTrace.set_mw(self.mw) # Setup for reporting super().control_display() # Do base work self.player_props = PlayerProps(self) self.control_display_base() self.mw.protocol("WM_DELETE_WINDOW", self.delete_window) self.selected_widget = None # Last applicable field clicked self.mw.bind ("<Button-1>", self.button_click) self.mw.bind ("<Double-Button-1>", self.double_click) self.player_changed = True # Tested regularly by game play self.score_changed = True # Tested regularly by game play
def get_component_val(self, name, comp_name=None, default=None): """ Get component value of named control If widget is present, get from widget else if control is present, get from control else if properties is present, get from properties :name: - control name :comp_name: if present, append with "_" comp_name :default: - use if value not found - MANDATORY data type is used if control entry data type is unknown """ if default is None: raise SelectError( "get_component_val: %s_%s mandatory default parameter missing" % (name, comp_name)) name_comp = name if comp_name is not None: name_comp += "_" + comp_name comp_entry = self.get_ctl_entry(name_comp) if comp_entry is None: raise SelectError( "get_component_value(%s, %s - component NOT FOUD" % (name, comp_name)) if comp_entry is not None: if comp_entry.value_type is None: comp_entry.value_type = type(default) ctl_widget = comp_entry.ctl_widget if ctl_widget is not None: widget_val = comp_entry.get_input() if widget_val is not None: vt = comp_entry.value_type if vt == int: if widget_val == "": widget_val = "0" # Treat undefined as 0 value = int(widget_val) elif vt == float: if widget_val == "": widget_val = "0" # Treat undefined as 0 value = float(widget_val) elif vt == str: value = widget_val else: value = widget_val # No change - should we check? SlTrace.lg( "get_component_val: %s ??? value_type(%s) widget_val=%s type(%s)" % (name_comp, vt, widget_val, type(widget_val))) SlTrace.lg(" type(int)=%s comp_entry.value_type=%s" % (type(int), comp_entry.value_type)) return value else: SlTrace.lg( "get_component_val: %s None return from comp_entry.get_input" % comp_name) return comp_entry.value return default # No component entry - return default
def procCmdString(cls, string=None, prefix=None): """ Process python code string, with prefix text :string: input string :prefix: optional string to prefix code string for compile :Returns: resulting cls """ cls.result = False # Set True if OK if string is None: string = cls.puzzle_string if string is None: raise SelectError("string is missing") gbls = { 'version': cls.version, 'puzzle': cls.puzzle, 'row': cls.row, 'end_puzzle': cls.end_puzzle } compile_str = "" if prefix is not None: compile_str = prefix if not prefix.endswith("\n"): compile_str += "\n" # Insure ending newline if SlTrace.trace("puzzle_load"): compile_str += test_prefix compile_str += string compile_str += "\nend_puzzle()\n" # End puzzle try: exec(compile_str, gbls) cls.result = True return cls.sudoku_puzzle except Exception as e: _, _, tb = sys.exc_info() tbs = traceback.extract_tb(tb) SlTrace.lg( f"Error while executing text from {cls.file_name}\n {str(e)}" ) inner_cmds = False for tbfr in tbs: # skip bottom (in dots_commands.py) tbfmt = 'File "%s", line %d, in %s' % (tbfr.filename, tbfr.lineno, tbfr.name) if not inner_cmds and tbfr.filename.endswith( "dots_commands.py"): inner_cmds = True SlTrace.lg(" --------------------" ) # show bottom (in dots_commands.py) SlTrace.lg(" %s\n %s" % (tbfmt, tbfr.line)) cls.result = False raise SelectError("compile error")
def test_is_covering_but_once(): SlTrace.lg("\ntest_covering_but_once") good1 = ['g7', 'g8', 'h8', 'h7'] dgood1 = DisplayedPath(path=good1, disp_board=dboard) ctv = ChessTourValidation(locs=good1) if ctv.is_covering_but_once(dgood1): SlTrace.lg(f"expected: pass of {good1}") else: SlTrace.lg(f"UNEXPECTED FAIL of {good1}")
def remove_props(self): """ Remove all properties for :player_infos: """ base_key = self.get_base_key(include_num=False) SlTrace.lg(f"remove_props: base_key={base_key}", "player_prop") if self.sect_name is None: base_pat = base_key + r'\d+' base_match = re.compile(base_pat) SlTrace.lg(f"base_pat={base_pat}", "player_prop") prop_keys = SlTrace.getPropKeys() for prop_key in prop_keys: if prop_key.startswith(base_key): if self.sect_name is None: m = base_match.match(prop_key) if m is None: continue # Not base: ...nnn SlTrace.lg(f"remove_props: key={prop_key}", "player_prop") SlTrace.deleteProperty(prop_key)
def is_square_complete(self, part, squares=None, ifadd=False): """ Determine if this edge completes one or more square(s) region(s) :part: - possibly completing edge :squares: - any squares completed are appended to this list default: no regions are appended :ifadd: If part is added default: part must already be added """ if part.is_edge(): SlTrace.lg("complete square testing %s" % part, "square_completion") regions = part.get_adjacents() # Look if we completed any square ncomp = 0 for square in regions: if (ifadd and square.is_complete(added=part) or square.is_complete() ): ncomp += 1 if squares is not None: squares.append(square) if ncomp > 0: return True return False
def __init__(self, road_width=.05, road_length=.05 * 2, surface=SurfaceType.DEFAULT, **kwargs): """ Setup object :road_width: road width as a fraction of track's width dimensions' e.g. 1,1 with container==None ==> block is whole canvas :road_length: road width as a fraction of track's width dimensions' e.g. 1,1 with container==None ==> block is whole canvas :surface: road surface type - determines look and operation/handeling collisions) """ SlTrace.lg("RoadTrack: %s" % (self)) super().__init__(**kwargs) if self.container is None: canvas = self.get_canvas() if canvas is None: self.canvas = Canvas(width=self.cv_width, height=self.cv_height) self.roads = [] # List of components # Parts of track self.road_width = road_width self.road_length = road_length self.surface = surface self.background = "lightgreen"
def filelist_proc(filename): """ Process file containing list of puzzle files :filename: filename of file containing list of puzzle files Default directory for files in list is dir(filename) """ with open(filename) as f: file_list_files[filename] = 1 # Record as being used lines = f.readlines() filedir = os.path.dirname(filename) for i in range(len(lines)): line = lines[i] ml = re.match(r'^(\.*)#.*$', line) if ml: line = ml[1] # Before comment line = line.strip() if re.match(r'^\s*$', line): continue # Skip blank lines name = line if not os.path.isabs(name): name = os.path.join(filedir, name) if name in file_list_files: SlTrace.lg(f"file: {file} already used - avoiding recursive use ") continue file_proc(filename=name, run_after_load=True)
def get_component_next_val(self, name, nrange=50, inc_dir=1, default_value=None): """ Next value for this component :name: control name :nrange: - number of samples for incremental :default_value: default value """ cur_value = self.get_current_val(name, 1) next_direction = self.get_component_val(name, "next", "same") if SlTrace.trace("get_next_val"): SlTrace.lg("get_next_val %s cur_val=%s next=%s" % (name, cur_value, next_direction)) if isinstance(cur_value, str): next_value = self.ctl_list_value(name) SlTrace.lg("get_value %s str next=%s val=%s" % (name, next_direction, next_value), "get_next_val") return next_value else: min_value = float(self.get_component_val(name, "min", cur_value)) max_value = float(self.get_component_val(name, "max", cur_value)) inc_value = (max_value-min_value)/nrange if next_direction == "ascend": new_value = cur_value + inc_dir * inc_value elif next == "descend": new_value = cur_value - inc_dir * inc_value elif next_direction == "random": new_value = random.randint(min_value, max_value) else: new_value = cur_value + inc_value if new_value < min_value or new_value > max_value: new_value = self.step_end(name, new_value) self.set_current_val(name, new_value)
def __init__(self, car_bin): """ Setup car bin :car_bin: car bin instance """ self.car_bin = car_bin SlTrace.lg("CarBinSetup: car_bin pts: %s" % self.car_bin.get_absolute_points()) car_rot = 0 # HACK nentries = 4 # Number of entries (space) entry_space = .08 # between entries entry_width = 5 * (1. - nentries * entry_space) / nentries entry_height = .25 * (1. - 2 * entry_space) # height of entry pos_inc = Pt(0., 1.2 * entry_height) # to next entry pos = Pt(entry_space, entry_space) # HACK - Add extra to move to right entry = CarSimple(self.car_bin, rotation=car_rot, position=pos, width=entry_width, height=entry_height, base_color="red") SlTrace.lg("entry pts: %s" % entry.get_absolute_points()) self.add_entry(entry) pos += pos_inc entry = CarSimple(self.car_bin, rotation=car_rot, position=pos, width=entry_width, height=entry_height, base_color="blue") SlTrace.lg("entry pts: %s" % entry.get_absolute_points()) self.add_entry(entry) pos += pos_inc entry = CarSimple(self.car_bin, rotation=car_rot, position=pos, width=entry_width, height=entry_height, base_color="green") SlTrace.lg("entry pts: %s" % entry.get_absolute_points()) self.add_entry(entry)
def get_selected(cls, block=None): """ Get selected info, block if selected, else None :block: block to check. """ if block is None: raise SelectError("get_selected: with block is None") selected = None for sid in cls.selects: selected = cls.selects[sid] if selected.block.id == sid: break if selected.block is None: raise SelectError("selected with None for block") if selected.x_coord is None: SlTrace.lg("selected with None for x_coord") cv_width = cls.get_cv_width() cv_height = cls.get_cv_height() selected = SelectInfo(block=None, x_coord=cv_width / 2, y_coord=cv_height / 2) # HACK return selected
def player_info(self, base_prefix, snapshot=None): """ Return current player info, based on properties :startswith: each key starts with this :returns: snapshot with current state but as if n the stack """ if snapshot is None: snapshot = SlTrace.snapshot_properties() if not base_prefix.endswith("."): base_prefix += "." sn = snapshot.snapshot_properties(sn=snapshot, req_match=re.escape(base_prefix) + r'\d+\.\w+') pp = PlayerProp(self.player_control) player_info = pp.get_player_infos(snapshot=sn) return player_info[0]
def next_path(self): """ Get next path 1. if necessary, build path to nrow*ncol depth 2. get list of moves from stack :returns: next path, None if none """ try: if not self.build_path_stack(): return None path = self.path_stack_path() return path except SelectTimeout: t = self.time_limit if t is None: t = -1 SlTrace.lg(f"path find timeout({t:.3f} sec)") return None except BaseException: SlTrace.lg("path find exception: {traceback.format_exc()}") raise Exception
def test_attr(name): """ Test/exercise attribute :name: attribute name """ global nrun nrun += 1 SlTrace.lg(f"\n{nrun:3} Testing Attribute: {name}") attr = atc.get_attr(name) SlTrace.lg(f" {attr.get_name()}:" f" changes: {attr.get_changes()}" f" index: {attr.get_index()}") atc.add_value(name=name, value=f"{name}_test_value_{nrun}") values = attr.get_values() SlTrace.lg(f"values: {values}") for n in range(len(values) * 3): SlTrace.lg(f" {n}: {atc.get_next(name)}" f" index: {attr.get_index()}")
def next_color(self, colr=None, changing=None): """ Get next color based on color_current, color_changing :colr: color default: use self.color_current :changing: is color changing default: use self.color_changing """ if colr is None: colr = self.color_current self.color_current = colr if changing is None: changing = self.color_changing self.color_changing = changing if self.color_current == 'w': if self.color_changing: self.color_index += 1 if self.color_index >= len(self.colors): self.color_index = 0 # wrap around new_color = self.colors[self.color_index] elif self.color_current == '2': if changing: self.color_index += 1 if self.color_index >= len(self.custom_colors): self.color_index = 0 new_color = self.custom_colors[self.color_index] else: new_color = self.color_current if type(new_color) != str: SlTrace.report(f"new_color:{new_color} not str") return SlTrace.lg( f"new_color:{new_color}" f" color_current:{self.color_current}" f" color_index:{self.color_index}" f" color_changing:{self.color_changing}", "color") return new_color
def cmd_images(self, keysym): """ Create image markers replacing previous marker, if one j - choose from current group k - rotate through animals l - rotate through family [ (bracketleft) - rotate through princesses ] (bracketright)- rotate other :keysym: image type """ char2group = { 'k': "animals", 'l': "family", 'bracketleft': "princesses", 'bracketright': "other_stuff", } if keysym in char2group: group = char2group[keysym] else: SlTrace.report(f"image command {keysym} is not available yet") return False return self.cmd_do_image(group=group)
def get_part(self, id=None, type=None, sub_type=None, row=None, col=None): """ Get basic part :id: unique part id :type: part type e.g., edge, region, corner default: "edge" :sub_type: must match if present e.g. v for vertical, h for horizontal :row: part row :col: part column :returns: part, None if not found """ if id is not None: if id not in self.parts_by_id: SlTrace.lg("part not in parts_by_id") return None part = self.parts_by_id[id] return part if type is None: type = "edge" for part in self.parts: if part.part_type == type and (sub_type is None or part.sub_type() == sub_type): if part.row == row and part.col == col: return part
def select_set(self, parts, keep=False): """ Select part(s) :parts: part/id or list :keep: keep previously selected default: False """ parts = copy.copy(parts) if not keep: self.select_clear() if not isinstance(parts, list): parts = [parts] for part in parts: part_id = part.part_id self.selects[part_id] = part if SlTrace.trace("selected"): self.list_selected("select_set AFTER")
def display(self, msg=None): display_str = "" if msg is None: msg = "Data Display" display_str += f"{msg}\n" if self.vals is None: raise SelectError("data gone") horiz_grp_divider = " " + "-" * (2 * self.nCol + self.nSubCol - 1) + "\n" for nr in range(1, self.nRow + 1): if nr % self.nSubRow == 1: display_str += horiz_grp_divider for nc in range(1, self.nCol + 1): if nc == 1: display_str += "|" val = self.getCellVal(row=nr, col=nc) disp = " " if self.isEmpty(val) else f"{val} " display_str += disp if nc % self.nSubCol == 0: display_str += "|" display_str += "\n" display_str += horiz_grp_divider SlTrace.lg(display_str)
def get_image_files(self, file_dir=None, name=None): """ Get list of image files given name :file_dir: file directory default: self.image_path :name: selection name default: all images :returns: list of absolute paths to image files """ if file_dir is None: file_dir = self.image_path if name is not None: file_dir = os.path.join(file_dir, name) names = os.listdir(file_dir) image_files = [] for name in sorted(names): path = os.path.join(file_dir, name) if (os.path.exists(path) and not os.path.isdir(path)): try: im = Image.open(path) image_files.append(path) im.close() except IOError: SlTrace.lg(f"Not an image file: {path}") return image_files
def set_check_box(self, frame=None, field=None, label=None, value=False, command=None): """ Set up check box for field :field: local field name :label: button label - default final section of field name :value: value to set "string" :command: function to call with new value when box changes default: no call """ if frame is None: frame = self.base_frame if label is None: label = field if label is not None: wlabel = Label(frame, text=label) wlabel.pack(side="left") content = BooleanVar() full_field = self.field_name(field) value = self.get_prop_val(full_field, value) content.set(value) cmd = None if command is not None: # HACK - only works for ONE checkbox self.check_box_change_content = content self.check_box_change_callback = command cmd = self.check_box_change widget = Checkbutton(frame, variable=content, command=cmd) widget.pack(side="left", fill="none", expand=True) self.ctls[full_field] = widget self.ctls_vars[full_field] = content SlTrace.lg(f"set_check_box adding field:{full_field}") self.set_prop_val(full_field, value)
def open(self, src_file=None): """ Open command file Opens output files, if specified :src_file: Source file default, if no extension ext="csrc" If no extension: look for .py, then .csrc in source_directories else look for file in source_directories :returns: True iff successful open (note that py type flles are are opened when first get_tok is called """ self.lineno = 0 # Src line number self.eof = True # Cleared on open, set on EOF if src_file is None: if self.src_file_name is None: raise SelectError( "open: no src_file and no self.src_file_name") src_file = self.src_file_name self.src_file_name = src_file if not self.check_file(self.src_file_name): return False self.new_file = False if self.file_type == "csrc": try: if self.src_lst or SlTrace.trace("csrc"): SlTrace.lg("Open %s" % self.src_file_path) self.in_file = open(self.src_file_path, "r") except IOError as e: errno, strerror = e.args SlTrace.lg("Can't open command source file %s: %d %s" % (self.src_file_path, errno, strerror)) self.eof = True return False self.eof = False return True
def trace_item(self, rec_id=None, track_no=None, prefix="", begin=-3, end=-1): """ Trace(list) entry :rec_id: record id :trac_no: tracing number :begin: start stack list (-99 99 most recent entries) :end: end stack list (-1 most recent entry) """ if rec_id is not None: if rec_id in self.by_rec_id: track = self.by_rec_id[rec_id] else: return # Ignore if not tracked elif track_no is not None: if track_no in self.by_track_no: track = self.by_track_no[track] else: return # Ignore if not tracked else: raise("trace_item has neither rec_id nor track_no") rec_id = track[0] tags = self.gettags(rec_id) track_no = track[1] stack = track[2] entries = traceback.format_list(stack) # Remove the last two entries for the call to extract_stack() and to # the one before that, this function. Each entry consists of single # string with consisting of two lines, the script file path then the # line of source code making the call to this function. ###del entries[-2:] part = self.get_part_from_tags(tags) SlTrace.lg("%s%s %s %s" % (prefix, rec_id, tags, part)) for entry in entries[begin:end]: SlTrace.lg("%s" % (entry))
def get_drawn_line_points(self, p1, p2, width=None, point_resolution=None): """ Get drawn line fill points Find perimeter of surrounding points of a rectangle For simplicity we consider vertical width :p1: beginning point :p2: end point :width: width of line default: self.line_width :point_resolution: fill point spacing default: self.point_resolution :returns: set of fill points """ ###pts = self.get_line_points(p1,p2) ###return set(pts) if width is None: width = self.line_width if point_resolution is None: point_resolution = self.point_resolution pr = point_resolution SlTrace.lg( f"get_drawn_Line_points {p1} {p2}" f" width: {width} res: {pr}", "xpoint") p1x, p1y = p1 p2x, p2y = p2 self.set_line_funs(p1, p2) dx = self.lfun_unorm_x * width / 2 # draw width offsets dy = self.lfun_unorm_y * width / 2 pp1 = (p1x + dx, p1y + dy) # upper left corner pp2 = (p2x + dx, p2y + dy) # upper right corner pp3 = (p2x - dx, p2y - dy) # lower right corner pp4 = (p1x - dx, p1y - dy) # lower left corner perim_list = [pp1, pp2, pp3, pp4] filled_points = self.fill_points(perim_list) return filled_points
def fill_points(self, point_list, point_resolution=None): """ Fill surrounding points assuming points are connected and enclose an area. - we will, eventualy, do "what turtle would do". Our initial technique assumes a convex region: given every sequential group of points (pn, pn+1,pn+2), pn+1 is within the fill region. We divide up the fill region into triangles: ntriangle = len(point_list)-2 for i in rage(1,ntriangle): fill_triangle(pl[0],pl[i],pl[i+1]) :point_list: list (iterable) of surrounding points :point_resolution: distance under which cells containing each point will cover region with no gaps default: self.point_resolution :returns: set of points (ix,iy) whose cells cover fill region """ if point_resolution is None: point_resolution = self.point_resolution SlTrace.lg(f"fill_points: {pl(point_list)} res:{point_resolution}", "xpoint") fill_point_set = set() if len(point_list) < 3: return set() plist = iter(point_list) p1 = point_list[0] for i in range(2, len(point_list)): p2 = point_list[i] p3 = point_list[i - 1] points = self.get_points_triangle( p1, p2, p3, point_resolution=point_resolution) fill_point_set.update(points) return fill_point_set
def list_selected(self, prefix=None): if prefix is None: prefix = "" else: prefix = prefix + " " part_ids = list(self.parts_by_id) n_on = 0 for part_id in part_ids: part = self.get_part(id=part_id) if part.is_selected(): n_on += 1 SlTrace.lg("%sparts selected on(%d of %d):" % (prefix, n_on, len(part_ids))) for part_id in part_ids: part = self.get_part(id=part_id) if part.is_selected(): SlTrace.lg(" %s" % (part)) if SlTrace.trace("selected_selects"): selecteds = self.get_selects() SlTrace.lg("parts in selecteds:") for part_id in selecteds: SlTrace.lg(" %s" % self.get_part(part_id))
def displaySetCellList(self, ): setCells = self.getSetList() SlTrace.lg(f"setCells:", "display") for r_set in setCells: col = r_set.col row = r_set.row val = r_set.val SlTrace.lg(f"C{col}R{row}:{val}", "display") SlTrace.lg(f"")
def get_transform_inverse(cls, block): xtran = BlockBlock.get_transform(block) if xtran is None: return xtran # No translation SlTrace.lg("xtran = %s" % tran2matrix(xtran)) ixtran = xtran.inverse() SlTrace.lg("ixtran = %s" % tran2matrix(ixtran)) xtran_ti = xtran * ixtran SlTrace.lg("xtran * ixtran=%s" % tran2matrix(xtran_ti)) return ixtran