def OnBeginLabelEdit(self, event): start_match_col = 0 end_match_col = 0 for col in self.GetColumnsOrder() if self.HasColumnOrderSupport() else range(0, len(config.default_columns)): end_match_col += self.GetColumnWidth(col) if col == 2: break start_match_col = end_match_col mouse_pos = wx.GetMousePosition() position = self.ScreenToClient(mouse_pos) if position.x >= start_match_col and position.x <= end_match_col: event.Veto() row_id = event.GetIndex() dia = PickCandidate(self, row_id, wx.Point(mouse_pos.x - 10, mouse_pos.y - 20), selectNextOnClose=True) dia.Show() dia.text.SetFocus() else: event.Allow() if get_config()["show_fullpath"]: d = Path(event.GetLabel()).name else: d = event.GetLabel() (self.GetEditControl()).SetValue(d) if not get_config()["hide_extension"]: index_of_dot = d.rfind('.') if index_of_dot > 0: (self.GetEditControl()).SetSelection(0, index_of_dot)
def get_match_standalone(source, candidates, match_firstletter): ret = None if not candidates: return ret f_masked = masks.FileMasked(source[0]) if not f_masked.masked[1]: return ret if match_firstletter: first_letter = f_masked.masked[1][0] if first_letter in candidates.keys(): ret = fuzzywuzzy.process.extract( f_masked, candidates[first_letter].keys(), scorer=similarityScorers[get_config()["similarityscorer"]], processor=fuzz_processor, limit=10, ) else: ret = fuzzywuzzy.process.extract( f_masked, candidates["all"].keys(), scorer=similarityScorers[get_config()["similarityscorer"]], processor=fuzz_processor, limit=10, ) # [{"key": candidate_key_1, "candidates": [...], "score":score1}, {"key": candidate_key_2: "candidates": [...], "score":score2}, ...] if ret: ret = [ {"key": canditate_key, "candidates": [candidate.file for candidate in candidates["all"][canditate_key]], "score": score} for canditate_key, score in ret ] return ret
def test_args_preview_rename(self): get_config()["workers"] = 1 get_config()["masks"] = "+Ending Disk#\n" + r'"(\s?_disk\d)$"' + "\n" masks.FileMasked.masks = masks.CompileMasks(get_config()["masks"]) filters.FileFiltered.filters = filters.CompileFilters( get_config()["filters"]) if os.path.exists(self.outdir): shutil.rmtree(self.outdir) shutil.copytree( os.path.abspath(os.path.join(os.path.dirname(__file__), "./data")), self.outdir) sourcesDir = os.path.join(self.outdir, "sources_multimatch") choicesDir = os.path.join(self.outdir, "choices_multimatch") args.theArgs = args.theArgsParser.parse_args([ "--sources", sourcesDir, "--choices", choicesDir, "preview_rename" ]) with io.StringIO() as buf, redirect_stdout(buf): frame = main_dlg.MainFrame() shutil.rmtree(self.outdir) output = buf.getvalue() self.assertEqual( "Renaming : " + os.path.join( sourcesDir, "Acanthe à feuilles molles_disk2.txt") + " --> " + os.path.join(sourcesDir, "Acanthus mollis_disk2.txt\n") + "Renaming : " + os.path.join(sourcesDir, "Acanthe épineuse.txt") + " --> " + os.path.join(sourcesDir, "Acanthus spinosus_disk1.txt\n") + "Copying : " + os.path.join(sourcesDir, "Acanthus spinosus_disk1.txt") + " --> " + os.path.join(sourcesDir, "Acanthus spinosus_disk2.txt\n") + "Renaming : " + os.path.join(sourcesDir, "Aconit vénéneux.txt") + " --> " + os.path.join(sourcesDir, "Aconitum anthora.txt\n") + "Copying : " + os.path.join(sourcesDir, "Aconitum anthora.txt") + " --> " + os.path.join(sourcesDir, "Aconitum anthora_disk2.txt\n") + "Renaming : " + os.path.join(sourcesDir, "Aconit vénéneux_disk1.txt") + " --> " + os.path.join(sourcesDir, "Aconitum anthora_disk1.txt\n") + "Renaming : " + os.path.join( sourcesDir, "Aconit vénéneux_disk3.txt") + " --> " + os.path.join(sourcesDir, "Aconitum anthora_disk3.txt\n") + "Renaming : " + os.path.join( sourcesDir, "Violette cornue_disk1.txt") + " --> " + os.path.join(sourcesDir, "Viola cornuta_disk1.txt\n") + "Renaming : " + os.path.join( sourcesDir, "Volutaire à fleurs tubulées_disk1.txt") + " --> " + os.path.join(sourcesDir, "Volutaria tubuliflora_disk1.txt\n"), output, )
def get_matches(sources): numtasks = len(sources) # Create the task list Tasks = [((), ()) for i in range(numtasks)] Results = [None for i in range(numtasks)] if not main_dlg.candidates: return Results Qmatch_firstletter = get_config()["match_firstletter"] similarityscorer = get_config()["similarityscorer"] for i in range(numtasks): f_masked = masks.FileMasked(sources[i][0]) if f_masked.masked[1]: if Qmatch_firstletter: first_letter = f_masked.masked[1][0] if first_letter in main_dlg.candidates.keys(): Tasks[i] = ( (), (f_masked, sources[i], list(main_dlg.candidates[first_letter].keys()),similarityscorer,), ) else: Tasks[i] = ( (), (f_masked, sources[i], list(main_dlg.candidates["all"].keys()),similarityscorer,), ) numproc = get_config()["workers"] if not get_args().mode: ts = taskserver.TaskServerMP( processCls=TaskMatch, numprocesses=numproc, tasks=Tasks, results=Results, msgfunc=progress_msg, title="Match Progress", ) else: updatefunc_ = None if get_args().mode == "report_match": updatefunc_ = update_console ts = taskserver.TaskServerMP( processCls=TaskMatch, numprocesses=numproc, tasks=Tasks, results=Results, updatefunc=updatefunc_, progress=False ) ts.run() # [ [{"key": candidate_key_1, "candidates": [...], "score":score1]}, {"key": candidate_key_2, "candidates": [...], "score":score2}, ...], ...] for i in range(numtasks): if Results[i]: Results[i] = [ {"key": canditate_key, "candidates": [candidate.file for candidate in main_dlg.candidates["all"][canditate_key]], "score": score} for canditate_key, score in Results[i] ] return Results
def SetUnmatched(self, unmatched): Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] self.log.Clear() self.log.Freeze() for u in unmatched: parent, stem, suffix = utils.GetFileParentStemAndSuffix(u) d = parent + stem + suffix if Qview_fullpath else ( stem if Qhide_extension else stem + suffix) self.log.AppendText(d + "\n") self.log.Thaw()
def update_console(output, msgfunc=None): if not output["args"] or not output["result"]: return Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] f_masked = output["args"][0].masked[1] print_source = f_masked canditate_key, score = output["result"][0] print_match = canditate_key print("%s --> %s (%.2f)" % (print_source, print_match, score))
def OnEndLabelEdit(self, event): if event.IsEditCancelled() or not event.GetLabel(): event.Veto() return row_id = event.GetIndex() pos = self.GetItemData(row_id) # 0-based unsorted index new_name = event.GetLabel() old_path = self.listdata[pos][config.D_FILENAME] old_name = old_path.name new_name_clean = utils.strip_extra_whitespace(utils.strip_illegal_chars(new_name)) event.Veto() # do not allow further process as we will edit ourself the item label if new_name_clean == old_name: return old_file = str(old_path) new_file = os.path.join(str(old_path.parent), new_name_clean) new_path = Path(new_file) try: if not old_path.is_file(): return os.rename(old_file, new_file) wx.LogMessage("Renaming : %s --> %s" % (old_file, new_file)) Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] new_match = match.get_match(new_path) if new_match: matching_results = new_match[0]["candidates"] nb_match = len(matching_results) self.RefreshItem( row_id, score=new_match[0]["score"], matchnames=matching_results, nbmatch=nb_match, status=config.MatchStatus.MATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) else: self.RefreshItem( row_id, score=0, matchnames=[], nbmatch=0, status=config.MatchStatus.NOMATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) except (OSError, IOError): wx.LogMessage("Error when renaming : %s --> %s" % (old_file, new_file))
def RefreshList(self): Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] self.Freeze() row_id = -1 while True: row_id = self.GetNextItem(row_id) if row_id == -1: break self.RefreshItem(row_id, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension) self.Thaw()
def test_rename_keep_ext(self): get_config()["keep_original"] = True get_config()["keep_match_ext"] = True if os.path.exists(self.outdir): shutil.rmtree(self.outdir) os.makedirs(self.outdir) self.frame.panel.SetOutputDirectory(self.outdir) sourcesDir = os.path.abspath( os.path.join(os.path.dirname(__file__), "./data/sources")) self.frame.panel.AddSourcesFromDir(sourcesDir) choicesDir = os.path.abspath( os.path.join(os.path.dirname(__file__), "./data/choices")) self.frame.panel.AddChoicesFromDir(choicesDir) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Best match": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Rename": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) renamed = [] for f in sorted(Path(self.outdir).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass shutil.rmtree(self.outdir) self.assertEqual( [ "Abutilon hybridum.txt.txt", "Acanthus mollis.txt.txt", "Acanthus spinosus.txt.txt", "Aconitum anthora.txt.txt", "Viola cornuta.txt.txt", "Volutaria tubuliflora.txt.txt", ], renamed, )
def ToggleLog(self): logTabIdx = -1 for idx in range(0, self.GetPageCount()): if self.GetPageText(idx) == "Log": logTabIdx = idx break # Show log tab if hidden if get_config()["show_log"] and logTabIdx != -1 and self.GetHidden( logTabIdx): self.HidePage(logTabIdx, hidden=False) # Hide log tab if shown elif not get_config( )["show_log"] and logTabIdx != -1 and not self.GetHidden(logTabIdx): self.HidePage(logTabIdx, hidden=True)
def ReMatchSelectionCb(self, event): selected = utils.get_selected_items(self) sources = [] for row_id in selected: pos = self.GetItemData(row_id) # 0-based unsorted index sources.append(self.listdata[pos][config.D_FILENAME]) matches = match.get_matches(sources) Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] count = 0 self.Freeze() for row_id in selected: if len(matches) < count + 1: break f = self.GetItemFont(row_id) if not f.IsOk(): f = self.list_ctrl.GetFont() font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.FONTWEIGHT_NORMAL) font.SetStyle(f.GetStyle()) self.SetItemFont(row_id, font) if matches[count]: matching_results = matches[count][0]["candidates"] nb_match = len(matching_results) self.RefreshItem( row_id, score=matches[count][0]["score"], matchnames=matching_results, nbmatch=nb_match, status=config.MatchStatus.MATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) else: self.RefreshItem( row_id, score=0, matchnames=[], nbmatch=0, status=config.MatchStatus.NOMATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) count += 1 self.Thaw()
def MenuForceMatchCb(self, row_id, forced_match, idx_file, event): pos = self.GetItemData(row_id) # 0-based unsorted index similarityscorer = get_config()["similarityscorer"] similarity = match.similarityScorers[similarityscorer]( masks.FileMasked(self.listdata[pos][config.D_FILENAME][0]).masked[1], forced_match, ) Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] Qsource_w_multiple_choice = get_config()["source_w_multiple_choice"] if forced_match in main_dlg.candidates["all"]: matching_results = main_dlg.candidates["all"][forced_match] if Qsource_w_multiple_choice or idx_file == -1: nb_match = len(matching_results) self.RefreshItem( row_id, score=similarity, matchnames=[result.file for result in matching_results], nbmatch=nb_match, status=config.MatchStatus.USRMATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) else: self.RefreshItem( row_id, score=similarity, matchnames=[matching_results[idx_file].file], nbmatch=1, status=config.MatchStatus.USRMATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) else: self.RefreshItem( row_id, score=0, matchnames=[], nbmatch=0, status=config.MatchStatus.NOMATCH, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) f = self.GetItemFont(row_id) if not f.IsOk(): f = self.GetFont() font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.FONTWEIGHT_BOLD) font.SetStyle(f.GetStyle()) self.SetItemFont(row_id, font)
def __init__(self, parent, panel, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, pos=pos, size=size, style=style) listmix.TextEditMixin.__init__(self) self.EnableCheckBoxes() self.panel = panel self.unplug_preview = True self.Bind(wx.EVT_CHAR, self.onKeyPress) self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit) self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndLabelEdit) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self._startDrag) self.Bind(wx.EVT_CONTEXT_MENU, self.RightClickCb) self.Bind(wx.EVT_LIST_ITEM_CHECKED, self.CheckCb) self.Bind(wx.EVT_LIST_ITEM_UNCHECKED, self.CheckCb) self.InsertColumn(0, "#", width=35) self.InsertColumn(1, "Filter Name", width=150) self.InsertColumn(2, "Pattern", width=150) self.InsertColumn(3, "Replace", width=150) dt = FilterListCtrlDropTarget(self) dt.SetDefaultAction(wx.DragMove) self.SetDropTarget(dt) self.PopulateFilters(get_config()["filters"]) self.unplug_preview = False
def OnAuiNotebookPageClose(self, event): # prevent Log to be closed , hide it instead page_idx = event.GetSelection() if self.GetPageText(page_idx) == "Log": event.Veto() self.HidePage(page_idx, hidden=True) get_config()["show_log"] = False self.GetParent().GetParent().mnu_show_log.Check(False)
def test_args_rename(self): get_config()["workers"] = 1 get_config()["keep_original"] = False get_config()["masks"] = "+Ending Disk#\n" + r'"(\s?_disk\d)$"' + "\n" masks.FileMasked.masks = masks.CompileMasks(get_config()["masks"]) filters.FileFiltered.filters = filters.CompileFilters( get_config()["filters"]) if os.path.exists(self.outdir): shutil.rmtree(self.outdir) shutil.copytree( os.path.abspath(os.path.join(os.path.dirname(__file__), "./data")), self.outdir) sourcesDir = os.path.join(self.outdir, "sources_multimatch") choicesDir = os.path.join(self.outdir, "choices_multimatch") args.theArgs = args.theArgsParser.parse_args( ["--sources", sourcesDir, "--choices", choicesDir, "rename"]) with io.StringIO() as buf, redirect_stdout(buf): frame = main_dlg.MainFrame() renamed = [] for f in sorted(Path( os.path.join(self.outdir, "sources_multimatch")).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass shutil.rmtree(self.outdir) self.assertEqual( [ "Acanthus mollis_disk2.txt", "Acanthus spinosus_disk1.txt", "Acanthus spinosus_disk2.txt", "Aconitum anthora.txt", "Aconitum anthora_disk1.txt", "Aconitum anthora_disk2.txt", "Aconitum anthora_disk3.txt", "Viola cornuta_disk1.txt", "Volutaria tubuliflora_disk1.txt", ], renamed, )
def test_args_report_match(self): get_config()["workers"] = 1 get_config()["show_fullpath"] = False get_config()["hide_extension"] = True get_config()["masks"] = "+Ending Disk#\n" + r'"(\s?_disk\d)$"' + "\n" masks.FileMasked.masks = masks.CompileMasks(get_config()["masks"]) filters.FileFiltered.filters = filters.CompileFilters( get_config()["filters"]) if os.path.exists(self.outdir): shutil.rmtree(self.outdir) shutil.copytree( os.path.abspath(os.path.join(os.path.dirname(__file__), "./data")), self.outdir) sourcesDir = os.path.join(self.outdir, "sources_multimatch") choicesDir = os.path.join(self.outdir, "choices_multimatch") args.theArgs = args.theArgsParser.parse_args( ["--sources", sourcesDir, "--choices", choicesDir, "report_match"]) with io.StringIO() as buf, redirect_stdout(buf): frame = main_dlg.MainFrame() shutil.rmtree(self.outdir) output = buf.getvalue() self.assertEqual( "acanthe à feuilles molles --> acanthus mollis (70.00)\n" "acanthe épineuse --> acanthus spinosus (73.00)\n" "aconit vénéneux --> aconitum anthora (52.00)\n" "violette cornue --> viola cornuta (71.00)\n" "volutaire à fleurs tubulées --> volutaria tubuliflora (54.00)\n", output, )
def __init__(self, parent, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, pos=pos, size=size, style=style) listmix.ColumnSorterMixin.__init__(self, len(config.default_columns)) self.EnableCheckBoxes() self.Bind(wx.EVT_CHAR, self.onKeyPress) self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit) self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnEndLabelEdit) self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.ItemRightClickCb) self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.ColRightClickCb) self.Bind(wx.EVT_LIST_ITEM_CHECKED, self.CheckedCb) self.Bind(wx.EVT_LIST_ITEM_UNCHECKED, self.UncheckedCb) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.SelectCb) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.UnselectCb) self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick) for col in range(0, len(config.default_columns)): self.InsertColumn( col, config.default_columns[col]["label"], width=get_config()["col%d_size" % (col + 1)], ) if self.HasColumnOrderSupport(): order = [get_config()["col%d_order" % (col + 1)] for col in range(0, len(config.default_columns))] self.SetColumnsOrder(order) imagelist = wx.ImageList(16, 16) self.img_red = imagelist.Add(icons.RedSquare_16_PNG.GetBitmap()) self.img_yellow = imagelist.Add(icons.YellowSquare_16_PNG.GetBitmap()) self.img_orange = imagelist.Add(icons.OrangeSquare_16_PNG.GetBitmap()) self.img_green = imagelist.Add(icons.GreenSquare_16_PNG.GetBitmap()) self.img_downarrow = imagelist.Add(icons.DownArrow_16_PNG.GetBitmap()) self.img_uparrow = imagelist.Add(icons.UpArrow_16_PNG.GetBitmap()) self.AssignImageList(imagelist, wx.IMAGE_LIST_SMALL) self.listdata = {} self.listdataname = {} self.listdatanameinv = {} self.itemDataMap = self.listdata
def get_renames(old_pathes, preview_pathes, simulate=False): numtasks = len(old_pathes) # Create the task list Tasks = [((), ()) for i in range(numtasks)] Results = [None for i in range(numtasks)] Qkeep_original = get_config()["keep_original"] for i in range(numtasks): Tasks[i] = ( (), (old_pathes[i], preview_pathes[i], Qkeep_original, simulate), ) numproc = get_config()["workers"] if not get_args().mode: ts = taskserver.TaskServerMP( processCls=TaskRename, numprocesses=numproc, tasks=Tasks, results=Results, msgfunc=progress_msg, title="Rename Progress", ) else: ts = taskserver.TaskServerMP( processCls=TaskRename, numprocesses=numproc, tasks=Tasks, results=Results, progress=False, ) ts.run() return Results
def NoMatchSelectionCb(self, event): Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] selected = utils.get_selected_items(self) self.Freeze() for row_id in selected: self.RefreshItem( row_id, score=0, matchnames=[], nbmatch=0, status=config.MatchStatus.NONE, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension, ) f = self.GetItemFont(row_id) if not f.IsOk(): f = self.list_ctrl.GetFont() font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) font.SetWeight(wx.FONTWEIGHT_NORMAL) font.SetStyle(f.GetStyle()) self.SetItemFont(row_id, font) self.Thaw()
def AddToList(self, newdata): Qview_fullpath = get_config()["show_fullpath"] Qhide_extension = get_config()["hide_extension"] index = 0 if not self.listdata else sorted(self.listdata.keys())[-1] + 1 # start indexing after max index row_id = self.GetItemCount() self.Freeze() for f in newdata: key = masks.FileMasked(f, useFilter=False).masked[1] if key in self.listdataname: pos = self.listdataname[key] row_id0 = self.FindItem(-1, pos) if row_id0 != -1: if not f in self.listdata[pos][config.D_FILENAME]: self.listdata[pos][config.D_FILENAME].append(f) self.RefreshItem(row_id0, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension) else: # Treat duplicate file stem, suffix = utils.GetFileStemAndSuffix(f) item_name = str(f) if Qview_fullpath else (stem if Qhide_extension else f.name) found = self.FindItem(-1, item_name) if found != -1: continue self.listdataname[key] = index self.listdatanameinv[index] = key self.listdata[index] = [[f], 0, [], [], 0, config.MatchStatus.NONE, True] self.InsertItem(row_id, item_name, -1) self.SetItemData(row_id, index) self.RefreshItem(row_id, Qview_fullpath=Qview_fullpath, Qhide_extension=Qhide_extension) self.CheckItem(row_id, True) row_id += 1 index += 1 self.Thaw()
def get_match(source): return get_match_standalone(source, main_dlg.candidates, get_config()["match_firstletter"])
def __init__(self, parent): wx.Panel.__init__(self, parent, style=wx.WANTS_CHARS) self.notebook = wx.Notebook(self) page_filters = wx.Panel(self.notebook) page_masks = wx.Panel(self.notebook) self.notebook.AddPage(page_masks, "Masks on Sources") self.notebook.AddPage(page_filters, "Matching Filters") self.filters_list = filters_listctrl.FilterListCtrl( page_filters, self, size=(-1, -1), style=wx.LC_REPORT | wx.BORDER_SUNKEN ) label1 = wx.StaticText(page_filters, label="Test String", size=(60, -1)) self.preview_filters = wx.TextCtrl(page_filters, value=get_config()["filters_test"], size=(300, -1),) label2 = wx.StaticText(page_filters, label="Result", size=(60, -1)) self.result_preview_filters = wx.TextCtrl(page_filters, value="", size=(300, -1), style=wx.TE_READONLY) wx.FileSystem.AddHandler(wx.MemoryFSHandler()) image_Info = wx.MemoryFSHandler() image_Info.AddFile("info.png", icons.Info_16_PNG.GetBitmap(), wx.BITMAP_TYPE_PNG) html_desc_filters = wx.html.HtmlWindow(page_filters, size=(600, 135)) html_desc_filters.SetPage( '<img src="memory:info.png">' " These filters, using Python regular expression patterns, are applied to <b>sources</b> and <b>choices</b> strings before matching occurs." "It is used to help matching by cleaning strings (removing tags, ...) beforehand.<br><br>" "For example, replacing the pattern <font face=\"verdana\">'(\\(\\d{4}\\))'</font> by <font face=\"verdana\">''</font>:<br>" '<ul><li><i><font face="verdana">The Wire <b>(2002)</b></font></i> → <i><font face="verdana">The Wire</font></i></li></ul>' ) sizer2 = wx.BoxSizer(wx.HORIZONTAL) sizer2.Add(label1, 0, wx.ALL, 5) sizer2.Add(self.preview_filters, 1, wx.EXPAND | wx.ALL, 0) sizer3 = wx.BoxSizer(wx.HORIZONTAL) sizer3.Add(label2, 0, wx.ALL, 5) sizer3.Add(self.result_preview_filters, 1, wx.EXPAND | wx.ALL, 0) sizer_filters = wx.BoxSizer(wx.VERTICAL) sizer_filters.Add(self.filters_list, 2, wx.ALL | wx.EXPAND, 1) sizer_filters.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 0) sizer_filters.Add(sizer2, 0, wx.ALL | wx.EXPAND, 1) sizer_filters.Add(sizer3, 0, wx.ALL | wx.EXPAND, 1) sizer_filters.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 0) sizer_filters.Add(html_desc_filters, 0, wx.EXPAND | wx.ALL) page_filters.SetSizer(sizer_filters) self.masks_list = masks_listctrl.MaskListCtrl(page_masks, self, size=(-1, -1), style=wx.LC_REPORT | wx.BORDER_SUNKEN) label21 = wx.StaticText(page_masks, label="Test String", size=(80, -1)) self.preview_masks = wx.TextCtrl(page_masks, value=get_config()["masks_test"], size=(300, -1),) self.result_preview_masks_lead = wx.TextCtrl(page_masks, value="", size=(40, -1), style=wx.TE_READONLY) self.result_preview_masks_mid = wx.TextCtrl(page_masks, value="", size=(220, -1), style=wx.TE_READONLY) self.result_preview_masks_trail = wx.TextCtrl(page_masks, value="", size=(40, -1), style=wx.TE_READONLY) label22 = wx.StaticText(page_masks, label="Lead-Mid-Trail", size=(80, -1)) html_desc_masks = wx.html.HtmlWindow(page_masks, size=(600, 200)) html_desc_masks.SetPage( '<img src="memory:info.png">' " These masks, using Python regular expression patterns, are removed from <b>sources</b> and <b>choices</b> strings before filtering and matching occur." "It is used to remove leading and trailing expressions (year, disk#...) before matching and restore them at renaming.<br><br>" "For example, masking the pattern <font face=\"verdana\">'(\\s?disk\\d)$'</font>:<br>" '<ol><li>Source→masked source: <i><font face="verdana" color="blue">The Wiiire <b>Disk1</b></font></i> → <i><font face="verdana" color="blue">The Wiiire <font face="verdana" color="green"><b>[Disk1]</b></font></font></i></li>' '<li>Choice→masked choice: <i><font face="verdana" color="red">The Wire</font></i> → <i><font face="verdana" color="red">The Wire</font></i></li>' '<li>Masked source→best choice: <i><font face="verdana" color="blue">The Wiiire <font face="verdana" color="green"><b>[Disk1]</b></font></font></i> → <i><font face="verdana" color="red">The Wire</font></i></li>' '<li>Best choice→renamed unmasked source: <i><font face="verdana" color="red">The Wire</font></i> → <i><font face="verdana" color="blue">The Wire <b>Disk1</b></font></i></li>' '<li>Source→renamed sources: <i><font face="verdana" color="blue">The Wiiire <b>Disk1</b></font></i> → <i><font face="verdana" color="blue">The Wire <b>Disk1</b></i></li></ol>' "<br><br>It is also used to match a single <b>source</b> with multiple <b>choices</b> and generate multiple renamed files.<br><br>" "For example, masking the pattern <font face=\"verdana\">'(\\s?disk\\d)$'</font>:<br>" '<ol><li>Source→masked source: <i><font face="verdana" color="blue">The Wiiire</font></i> → <i><font face="verdana" color="blue">The Wiiire</font></i></li>' '<li>Choices→masked choices: <i><font face="verdana" color="red">The Wire <b>Disk1</b>, The Wire <b>Disk2</b></font></i> → <i><font face="verdana" color="red">The Wire <font face="verdana" color="green"><b>[Disk1, Disk2]</b></font></font></i></li>' '<li>Masked source→best choice: <i><font face="verdana" color="blue">The Wiiire</font></i> → <i><font face="verdana" color="red">The Wire <font face="verdana" color="green"><b>[Disk1, Disk2]</b></font></font></i></li>' '<li>Best choice→renamed unmasked source: <i><font face="verdana" color="red">The Wire <font face="verdana" color="green"><b>[Disk1, Disk2]</b></font></font></i> → <i><font face="verdana" color="blue">The Wire <b>Disk1</b>, The Wire <b>Disk2</b></font></i></li>' '<li>Source→renamed sources: <i><font face="verdana" color="blue">The Wiiire</font></i> → <i><font face="verdana" color="blue">The Wire <b>Disk1</b>, The Wire <b>Disk2</b></font></i></li></ol>' ) sizer22 = wx.BoxSizer(wx.HORIZONTAL) sizer22.Add(label21, 0, wx.ALL, 5) sizer22.Add(self.preview_masks, 1, wx.EXPAND | wx.ALL, 0) sizer32 = wx.BoxSizer(wx.HORIZONTAL) sizer32.Add(label22, 0, wx.ALL, 5) sizer32.Add(self.result_preview_masks_lead, 1, wx.EXPAND | wx.ALL, 0) sizer32.Add(self.result_preview_masks_mid, 5, wx.EXPAND | wx.ALL, 0) sizer32.Add(self.result_preview_masks_trail, 1, wx.EXPAND | wx.ALL, 0) sizer_masks = wx.BoxSizer(wx.VERTICAL) sizer_masks.Add(self.masks_list, 1, wx.ALL | wx.EXPAND, 1) sizer_masks.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 0) sizer_masks.Add(sizer22, 0, wx.ALL | wx.EXPAND, 1) sizer_masks.Add(sizer32, 0, wx.ALL | wx.EXPAND, 1) sizer_masks.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 0) sizer_masks.Add(html_desc_masks, 0, wx.EXPAND | wx.ALL) page_masks.SetSizer(sizer_masks) sizer = wx.BoxSizer() sizer.Add(self.notebook, 1, wx.EXPAND) self.SetSizer(sizer) self.UpdateMaskPreview() self.UpdateFilterPreview() self.Bind(wx.EVT_TEXT, self.onChangePreviewFilters, self.preview_filters) self.Bind(wx.EVT_TEXT, self.onChangePreviewMasks, self.preview_masks) page_filters.Fit() page_masks.Fit() self.Fit()
def test_rename_undo(self): get_config()["keep_original"] = False if os.path.exists(self.outdir): shutil.rmtree(self.outdir) shutil.copytree( os.path.abspath(os.path.join(os.path.dirname(__file__), "./data")), self.outdir) sourcesDir = os.path.join(self.outdir, "sources") self.frame.panel.AddSourcesFromDir(sourcesDir) choicesDir = os.path.join(self.outdir, "choices") self.frame.panel.AddChoicesFromDir(choicesDir) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Best match": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) lst = self.frame.panel.list_ctrl item = -1 item = lst.GetNextItem(item) def Pick(): for dlg in lst.GetChildren(): if isinstance(dlg, main_listctrl.PickCandidate): dlg.text.SetValue("viola cornuta") event = wx.CommandEvent(wx.wxEVT_TEXT_ENTER, dlg.text.GetId()) dlg.text.GetEventHandler().ProcessEvent(event) lst.Select(item) event = wx.KeyEvent(wx.wxEVT_CHAR) event.SetKeyCode(wx.WXK_CONTROL_P) wx.CallAfter(Pick) lst.GetEventHandler().ProcessEvent(event) wx.Yield() item = lst.GetNextItem(item) lst.CheckItem(item, False) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Rename": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) renamed = [] for f in sorted(Path(os.path.join(self.outdir, "sources")).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass self.assertEqual( [ "Acanthe à feuilles molles.txt", "Acanthus spinosus.txt", "Aconitum anthora.txt", "Viola cornuta.txt", "Violette cornue.txt", "Volutaria tubuliflora.txt", ], renamed, ) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Undo Rename": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) renamed = [] for f in sorted(Path(os.path.join(self.outdir, "sources")).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass shutil.rmtree(self.outdir) self.assertEqual( [ "Abutilon à feuilles marbrées.txt", "Acanthe à feuilles molles.txt", "Acanthe épineuse.txt", "Aconit vénéneux.txt", "Violette cornue.txt", "Volutaire à fleurs tubulées.txt", ], renamed, )
def test_rename_undo_multimatch(self): get_config()["keep_original"] = False if os.path.exists(self.outdir): shutil.rmtree(self.outdir) shutil.copytree( os.path.abspath(os.path.join(os.path.dirname(__file__), "./data")), self.outdir) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Masks && Filters...": btn = each break def setMasks(): for tlw in wx.GetTopLevelWindows(): if "masksandfiltersDialog" in type(tlw).__name__: dlg = tlw break dlg.panel.notebook.SetSelection(0) event = wx.KeyEvent(wx.wxEVT_CHAR) event.SetKeyCode(wx.WXK_CONTROL_A) dlg.panel.masks_list.GetEventHandler().ProcessEvent(event) event = wx.KeyEvent(wx.wxEVT_CHAR) event.SetKeyCode(wx.WXK_DELETE) dlg.panel.masks_list.GetEventHandler().ProcessEvent(event) self.addMask(dlg.panel.masks_list, "my mask", "(_disk\\w)$", True) event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, wx.ID_OK) dlg.panel.masks_list.GetEventHandler().ProcessEvent(event) wx.CallAfter(setMasks) event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) sourcesDir = os.path.join(self.outdir, "sources_multimatch") self.frame.panel.AddSourcesFromDir(sourcesDir) choicesDir = os.path.join(self.outdir, "choices_multimatch") self.frame.panel.AddChoicesFromDir(choicesDir) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Best match": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) lst = self.frame.panel.list_ctrl item = -1 item = lst.GetNextItem(item) self.assertEqual( [ "Acanthe à feuilles molles_disk2.txt", "70", "Acanthus mollis[_disk1,_disk2].txt", "Acanthus mollis_disk2.txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Acanthe épineuse.txt", "73", "Acanthus spinosus[_disk1,_disk2].txt", "Acanthus spinosus[_disk1,_disk2].txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Aconit vénéneux[ ,_disk1,_disk3].txt", "52", "Aconitum anthora[ ,_disk2,_disk3].txt", "Aconitum anthora[ ,_disk1,_disk2,_disk3].txt", "3", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Violette cornue_disk1.txt", "71", "Viola cornuta.txt", "Viola cornuta_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Volutaire à fleurs tubulées_disk1.txt", "54", "Volutaria tubuliflora_disk2.txt", "Volutaria tubuliflora_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Rename": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) renamed = [] for f in sorted(Path(os.path.join( self.outdir, "sources_multimatch")).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass self.assertEqual( [ "Acanthus mollis_disk2.txt", "Acanthus spinosus_disk1.txt", "Acanthus spinosus_disk2.txt", "Aconitum anthora.txt", "Aconitum anthora_disk1.txt", "Aconitum anthora_disk2.txt", "Aconitum anthora_disk3.txt", "Viola cornuta_disk1.txt", "Volutaria tubuliflora_disk1.txt", ], renamed, ) item = -1 item = lst.GetNextItem(item) self.assertEqual( [ "Acanthus mollis_disk2.txt", "100", "Acanthus mollis[_disk1,_disk2].txt", "Acanthus mollis_disk2.txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Acanthus spinosus[_disk1,_disk2].txt", "100", "Acanthus spinosus[_disk1,_disk2].txt", "Acanthus spinosus[_disk1,_disk2].txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Aconitum anthora[ ,_disk1,_disk2,_disk3].txt", "100", "Aconitum anthora[ ,_disk2,_disk3].txt", "Aconitum anthora[ ,_disk1,_disk2,_disk3].txt", "3", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Viola cornuta_disk1.txt", "100", "Viola cornuta.txt", "Viola cornuta_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Volutaria tubuliflora_disk1.txt", "100", "Volutaria tubuliflora_disk2.txt", "Volutaria tubuliflora_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) for each in self.button_panel.GetChildren(): if each.GetLabel() == "Undo Rename": btn = each break event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId()) btn.GetEventHandler().ProcessEvent(event) renamed = [] for f in sorted(Path(os.path.join( self.outdir, "sources_multimatch")).resolve().glob("*"), key=os.path.basename): try: if f.is_file(): renamed.append(f.name) except (OSError, IOError): pass shutil.rmtree(self.outdir) self.assertEqual( [ "Acanthe à feuilles molles_disk2.txt", "Acanthe épineuse.txt", "Aconit vénéneux.txt", "Aconit vénéneux_disk1.txt", "Aconit vénéneux_disk3.txt", "Violette cornue_disk1.txt", "Volutaire à fleurs tubulées_disk1.txt", ], renamed, ) item = -1 item = lst.GetNextItem(item) self.assertEqual( [ "Acanthe à feuilles molles_disk2.txt", "70", "Acanthus mollis[_disk1,_disk2].txt", "Acanthus mollis_disk2.txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Acanthe épineuse.txt", "73", "Acanthus spinosus[_disk1,_disk2].txt", "Acanthus spinosus[_disk1,_disk2].txt", "2", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Aconit vénéneux[ ,_disk1,_disk3].txt", "52", "Aconitum anthora[ ,_disk2,_disk3].txt", "Aconitum anthora[ ,_disk1,_disk2,_disk3].txt", "3", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Violette cornue_disk1.txt", "71", "Viola cornuta.txt", "Viola cornuta_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], ) item = lst.GetNextItem(item) self.assertEqual( [ "Volutaire à fleurs tubulées_disk1.txt", "54", "Volutaria tubuliflora_disk2.txt", "Volutaria tubuliflora_disk1.txt", "1", "Matched", "True", ], [ lst.GetItemText(item, col) for col in range(0, len(config.default_columns)) ], )