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"])
Example #3
0
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
Example #4
0
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"))