def test_autocompletecombobox(self): box = AutocompleteCombobox(self.window, completevalues=["Apple", "Pear", "Banana"]) box.pack() self.window.update() box.insert(0, "A") self.window.update() for item in ["A", "Up", "Down", "Left", "Right", "Return"]: box.handle_keyrelease(TestEvent(item)) box.autocomplete(0) box.set_completion_list(["Apply"])
def test_autocompletecombobox(self): box = AutocompleteCombobox(self.window, completevalues=["Apple", "Pear", "Banana"]) box.pack() self.window.update() self.assertIn('completevalues', box.keys()) self.assertEqual(box['completevalues'], sorted(["Apple", "Pear", "Banana"])) box.insert(0, "A") self.window.update() for item in ["A", "Up", "Down", "Left", "Right", "Return"]: box.handle_keyrelease(TestEvent(item)) box.autocomplete(0) box.set_completion_list(["Apply"]) self.assertEqual(box['completevalues'], ["Apply"]) box['completevalues'] = ["Test"] self.assertEqual(box['completevalues'], ["Test"])
class FileInput(BaseInput): def __init__(self, master, **kwargs): self._onlydir = False self._ext = ('Any File', '.*') self._rootdir = None self._excludefiles = [] self._suggestions = [] self._file_suggestions = None self._textv = StringVar() self._box = ttk.Frame(master=master) self._box.grid_columnconfigure(0, weight=1) self._box.grid_columnconfigure(1, weight=0) self._box.grid_rowconfigure(0, weight=1) self._input = AutocompleteCombobox( master=self._box, completevalues=[], textvariable=self._textv ) # Redirect configure to input setattr(self._box, "config", self._input.config) setattr(self._box, "configure", self._input.configure) setattr(self._box, "keys", self._input.keys) setattr(self._box, "cget", self._input.cget) setattr(self._box, "winfo_class", self._input.winfo_class) setattr(self._box, "bind", self._input.bind) setattr(self._box, "set_completion_list", self._input.set_completion_list) super().__init__(tk=self._box, **kwargs) self.connect_to_prop("onlydir", self._on_onlydir_changed) self.connect_to_prop("ext", self._on_ext_changed) self.connect_to_prop("rootdir", self._on_rootdir_changed) self.connect_to_prop("excludefiles", self._on_excludefiles_changed) #self.connect_to_prop("suggestion", self.on_changed_suggestion) self._input.drop_target_register(DND_FILES) self._input.dnd_bind('<<Drop>>', self._drop) self._input.grid(row=0, column=0, sticky="news") self._btn = ttk.Button(master=self._box, command=self._load_file, text="Browse...") self._btn.grid(row=0, column=1) def _on_excludefiles_changed(self, value): self._excludefiles = value if value else [] self._set_suggestions() def _on_onlydir_changed(self, value): self._onlydir = value def on_changed_suggestion(self, value): if value: self._suggestions = value self._set_suggestions() def _set_suggestions(self): if self._rootdir and Path(self._rootdir).exists() and self._file_suggestions == None: get_suggestion_from_ext = lambda ext: list(filter(lambda fn: Path(fn).stem not in self._excludefiles, [str(fn) for fn in Path(self._rootdir).rglob(f"*{ext[1]}")])) if isinstance(self._ext, list): from functools import reduce self._file_suggestions = reduce(lambda a, curr: a + get_suggestion_from_ext(curr), []) else: self._file_suggestions = get_suggestion_from_ext(self._ext) if isinstance(self._file_suggestions, list): self._input.set_completion_list(self._suggestions + self._file_suggestions) else: self._input.set_completion_list(self._suggestions) def _on_ext_changed(self, value): self._ext = value if value else ('Any File', '.*') self._set_suggestions() def _on_rootdir_changed(self, value): self._rootdir = value self._set_suggestions() @property def container(self): return self._box def on_disposed(self): self._box.destroy() self._btn.destroy() super().on_disposed() def _load_file(self): if self._onlydir: f = tk.filedialog.askdirectory() else: f = tk.filedialog.askopenfilename(filetypes=self._ext if isinstance(self._ext, list) else [self._ext]) if f is None or f == '': return self._textv.set(str(Path(f))) def _drop(self, event): if event.data: files = self._tk.tk.splitlist(event.data) for f in files: self._textv.set(f) break return event.action
class IsogeoSearchForm(ttk.Frame): def __init__(self, master=None, async_loop=None): tk.Frame.__init__(self, master) self.async_loop = async_loop # basics # master.resizable(width=True, height=True) master.title( "Isogeo Python SDK v{} - Sample desktop search form".format(pysdk_version) ) master.focus_force() self.grid(sticky="NSWE") self.grid_propagate(1) # styling self.style = ttk.Style(self) self.s1 = tk.PhotoImage( master=self, name="search1", data=data, format="gif -index 0" ) self.s2 = tk.PhotoImage( master=self, name="search2", data=data, format="gif -index 1" ) self.style.element_create( "Search.field", "image", "search1", ("focus", "search2"), border=[22, 7, 14], sticky="ew", ) self.style.layout( "Search.entry", [ ( "Search.field", { "sticky": "nswe", "border": 1, "children": [ ( "Entry.padding", { "sticky": "nswe", "children": [ ("Entry.textarea", {"sticky": "nswe"}) ], }, ) ], }, ) ], ) self.style.configure("Search.entry") # frames fr_global = ttk.Frame(self, name="global") fr_search = ttk.Frame(self, name="search_form") # UI vars self.app_name = StringVar(fr_global, "Sample desktop form") self.app_total = StringVar(fr_global, "0") self.app_url = StringVar(fr_global, "http://isogeo-api-pysdk.readthedocs.io") self.app_results = StringVar(fr_search, "0") # -- WIDGETS CREATION ------------------------------------------------- # add widgets lbl_app_name = tk.Label(fr_global, textvariable=self.app_name) lbl_app_total = ttk.Label(fr_global, textvariable=self.app_total) btn_app_url = ttk.Button( fr_global, text="APP Website", command=lambda: self.worker_allocator( async_loop=self.async_loop, to_do="open_web", **{"url": self.app_url} ), ) lbl_actions = ttk.Label(fr_search, text="Linked action") lbl_contacts = ttk.Label(fr_search, text="Contact") lbl_formats = ttk.Label(fr_search, text="Source format") lbl_inspires = ttk.Label(fr_search, text="INSPIRE theme") lbl_keywords = ttk.Label(fr_search, text="Keyword") lbl_licenses = ttk.Label(fr_search, text="License") lbl_owners = ttk.Label(fr_search, text="Owner") lbl_shares = ttk.Label(fr_search, text="Share") lbl_srs = ttk.Label(fr_search, text="Source spatial reference system") lbl_types = ttk.Label(fr_search, text="Type") # add form widgets self.ent_search = AutocompleteEntry( fr_search, style="Search.entry", width=20, completevalues=list() ) self.cb_actions = AutocompleteCombobox(fr_search) self.cb_contacts = AutocompleteCombobox(fr_search) self.cb_formats = AutocompleteCombobox(fr_search) self.cb_inspires = AutocompleteCombobox(fr_search) self.cb_keywords = AutocompleteCombobox(fr_search) self.cb_licenses = AutocompleteCombobox(fr_search) self.cb_owners = AutocompleteCombobox(fr_search) self.cb_shares = AutocompleteCombobox(fr_search) self.cb_srs = AutocompleteCombobox(fr_search) self.cb_types = AutocompleteCombobox(fr_search) lbl_results = ttk.Label(fr_search, textvariable=self.app_results) btn_reset = ttk.Button( master, text="Reset", command=lambda: self.worker_allocator( async_loop=self.async_loop, to_do="form_clear", **{"clear": 1} ), ) btn_close = ttk.Button(master, text="Close", command=master.destroy) # after UI build self.worker_allocator( async_loop=self.async_loop, to_do="form_clear", **{"clear": 1} ) # -- WIDGETS PLACEMENT ------------------------------------------------ d_pad = {"padx": 5, "pady": 5, "sticky": "NSEW"} lbl_app_name.grid(row=0, column=0, **d_pad) btn_app_url.grid(row=1, column=0, **d_pad) lbl_app_total.grid(row=2, column=0, **d_pad) self.ent_search.grid(row=1, columnspan=3, **d_pad) self.cb_actions.grid(row=3, column=0, **d_pad) self.cb_contacts.grid(row=3, column=1, **d_pad) self.cb_formats.grid(row=3, column=2, **d_pad) self.cb_inspires.grid(row=5, column=0, **d_pad) self.cb_keywords.grid(row=5, column=1, **d_pad) self.cb_licenses.grid(row=5, column=2, **d_pad) self.cb_owners.grid(row=7, column=0, **d_pad) self.cb_shares.grid(row=7, column=1, **d_pad) self.cb_srs.grid(row=7, column=2, **d_pad) self.cb_types.grid(row=9, column=1, **d_pad) lbl_actions.grid(row=2, column=0, **d_pad) lbl_contacts.grid(row=2, column=1, **d_pad) lbl_formats.grid(row=2, column=2, **d_pad) lbl_inspires.grid(row=4, column=0, **d_pad) lbl_keywords.grid(row=4, column=1, **d_pad) lbl_licenses.grid(row=4, column=2, **d_pad) lbl_owners.grid(row=6, column=0, **d_pad) lbl_shares.grid(row=6, column=1, **d_pad) lbl_srs.grid(row=6, column=2, **d_pad) lbl_types.grid(row=8, column=1, **d_pad) lbl_results.grid(row=22, column=0, columnspan=2, **d_pad) fr_global.grid(row=0, columnspan=1, **d_pad) fr_search.grid(row=1, columnspan=1, **d_pad) btn_reset.grid(row=2, column=0, sticky="NSW", padx=5, pady=5) btn_close.grid(row=2, column=0, sticky="NSE", padx=5, pady=5) # connecting comboboxes event self.cb_actions.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_contacts.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_formats.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_inspires.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_keywords.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_licenses.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_owners.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_shares.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_srs.bind("<<ComboboxSelected>>", self.cbs_manager) self.cb_types.bind("<<ComboboxSelected>>", self.cbs_manager) # -- TASKS HUB ------------------------------------------------------------ def cbs_manager(self, event): self.worker_allocator( async_loop=self.async_loop, to_do="form_update", **{"clear": 0} ) def worker_allocator(self, async_loop, to_do, **kwargs): """ Handler starting the asyncio part. """ d = kwargs threading.Thread( target=self._asyncio_thread, args=(async_loop, to_do, d) ).start() def _asyncio_thread(self, async_loop, to_do, kwargus): if to_do == "form_clear": async_loop.run_until_complete(self.fill_form(clear=1)) elif to_do == "form_update": async_loop.run_until_complete(self.fill_form(clear=0)) elif to_do == "open_web": async_loop.run_until_complete(self.open_url(kwargus.get("url").get())) else: pass # -- ASYNC METHODS -------------------------------------------------------- async def open_url(self, url): open_new_tab(url) async def fill_form(self, clear=0): if not hasattr(self, "isogeo"): self._init_isogeo() else: logging.info("App is already connected to Isogeo API") pass # search if clear: # clear self.ent_search.delete(0, "end") self.cb_actions.set("") self.cb_contacts.set("") self.cb_formats.set("") self.cb_inspires.set("") self.cb_keywords.set("") self.cb_licenses.set("") self.cb_owners.set("") self.cb_shares.set("") self.cb_srs.set("") self.cb_types.set("") # new search search = self.isogeo.search( self.token, page_size=0, whole_share=0, augment=1, tags_as_dicts=1 ) app_total = results_total = search.get("total") self.app_total.set("Total: {} metadata".format(app_total)) else: query = self.ent_search.get() + " " query += self.tags.get("actions").get(self.cb_actions.get(), "") + " " query += self.tags.get("contacts").get(self.cb_contacts.get(), "") + " " query += self.tags.get("formats").get(self.cb_formats.get(), "") + " " query += self.tags.get("inspires").get(self.cb_inspires.get(), "") + " " query += self.tags.get("keywords").get(self.cb_keywords.get(), "") + " " query += self.tags.get("licenses").get(self.cb_licenses.get(), "") + " " query += self.tags.get("owners").get(self.cb_owners.get(), "") + " " query += self.tags.get("shares").get(self.cb_shares.get(), "") + " " query += self.tags.get("srs").get(self.cb_srs.get(), "") + " " query += self.tags.get("types").get(self.cb_types.get(), "") + " " search = self.isogeo.search( self.token, page_size=0, whole_share=0, augment=1, tags_as_dicts=1, query=query, ) results_total = search.get("total") logging.debug(search.get("query")) self.tags = search.get("tags") # set values self.app_results.set("Results count: {} metadata".format(results_total)) self.ent_search.set_completion_list(list(self.tags.get("keywords").values())) self.cb_actions.set_completion_list(list(self.tags.get("actions"))) self.cb_contacts.set_completion_list(list(self.tags.get("contacts"))) self.cb_formats.set_completion_list(list(self.tags.get("formats"))) self.cb_inspires.set_completion_list(list(self.tags.get("inspires"))) self.cb_keywords.set_completion_list(list(self.tags.get("keywords"))) self.cb_licenses.set_completion_list(list(self.tags.get("licenses"))) self.cb_owners.set_completion_list(list(self.tags.get("owners"))) self.cb_shares.set_completion_list(list(self.tags.get("shares"))) self.cb_srs.set_completion_list(list(self.tags.get("srs"))) self.cb_types.set_completion_list(list(self.tags.get("types"))) def _init_isogeo(self): api_credentials = utils.credentials_loader("client_secrets.json") self.isogeo = Isogeo( client_id=api_credentials.get("client_id"), client_secret=api_credentials.get("client_secret"), ) self.token = self.isogeo.connect() # app properties self.isogeo.get_app_properties(self.token) self.app_props = self.isogeo.app_properties self.app_name.set( "Authenticated application: {}".format(self.app_props.get("name")) ) self.app_url.set(self.app_props.get("url", "https://www.isogeo.com"))