Exemple #1
0
 def __init__(self, mainwindow):
     #: :class:`.MainWindow`
     self.mainwindow = mainwindow
     #: :class:`.Document`
     self.document = mainwindow.document
     self._original_names = []
     self._filtered_names = []
     self._search_query = ''
     #: *int*. Currently selected index in the filtered/ordered list.
     self.selected_index = 0
     self.view = NoopGUI()
Exemple #2
0
 def __init__(self, mainwindow):
     self.mainwindow = mainwindow
     self.document = mainwindow.document
     self._original_names = []
     self._filtered_names = []
     self._search_query = ''
     self.selected_index = 0
     self.view = NoopGUI()
Exemple #3
0
 def __init__(self, mainwindow):
     #: :class:`.MainWindow`
     self.mainwindow = mainwindow
     #: :class:`.Document`
     self.document = mainwindow.document
     self._original_names = []
     self._filtered_names = []
     self._search_query = ''
     #: *int*. Currently selected index in the filtered/ordered list.
     self.selected_index = 0
     self.view = NoopGUI()
Exemple #4
0
class Lookup:
    """Fuzzily-filtered list of items that match the few letters that the user typed.

    A Lookup allows the user to quickly find a object he looks for by name by typing only a few
    letters from that name. When first invoked, we start with a list of all possible items, and as
    letters are typed, those items and filtered *and* ordered in a way that fuzzily matches what
    our user typed.

    The list's filter is very inclusive and will include object that very remotely can match user's
    input, but the order is based on what matches the most.

    For example, if the user types "ab", the lookup list would contain both "barber", "absolute", 
    and "albinos" (all those names contain both "a" and "b"), but would be ordered so that
    "absolute" comes first because it's the closest match.

    Pressing Return will "activate" (see :meth:`go`) the first item in the list, but the user can
    also navigate the list with the arrow keys (or click on the wanted item), which is why we keep
    track of :attr:`selected_index`.

    This class is designed to be subclassed with concrete named objects to lookup in. It does
    nothing by itself.
    """
    def __init__(self, mainwindow):
        #: :class:`.MainWindow`
        self.mainwindow = mainwindow
        #: :class:`.Document`
        self.document = mainwindow.document
        self._original_names = []
        self._filtered_names = []
        self._search_query = ''
        #: *int*. Currently selected index in the filtered/ordered list.
        self.selected_index = 0
        self.view = NoopGUI()

    def _apply_query(self):
        # On top, we want exact matches (the name starts with the query). Then, we want matches
        # that contain all the letters, sorted in order of names that have query letters as close
        # to each other as possible.
        q = sort_string(self._search_query)
        matches1, rest = extract(lambda n: n.startswith(q),
                                 self._original_names)
        matches2, rest = extract(lambda n: q in n, rest)
        matches3, rest = extract(lambda n: has_letters(n, q), rest)
        matches3.sort(key=lambda n: letters_distance(n, q))
        self._filtered_names = matches1 + matches2 + matches3
        self.selected_index = max(self.selected_index, 0)
        self.selected_index = min(self.selected_index,
                                  len(self._filtered_names) - 1)

    def _generate_lookup_names(self):
        """*Virtual*. Return a list of names in which we'll search."""
        return []

    def _go(self, name):
        """*Virtual*. Select an item named ``name``.

        The nature of this action depends on what kind of object we perform the lookup on. Were
        those accounts? Then open up the account named ``name``.
        """

    def _refresh(self):
        self._search_query = ''
        self.selected_index = 0
        names = self._generate_lookup_names()
        normalized_names = [sort_string(n) for n in names]
        self._normalized2original = {}
        for normalized, original in zip(normalized_names, names):
            self._normalized2original[normalized] = original
        self._original_names = normalized_names
        self._filtered_names = normalized_names

    def go(self):
        """Activate the :attr:`selected item <selected_index>` and close the lookup.

        To "activate", we call :meth:`_go` with the name of our selected item.
        """
        try:
            name = self.names[self.selected_index]
            self._go(name)
        except IndexError:
            pass  # No result, do nothing
        self.view.hide()

    def show(self):
        """Refreshes the name list and show the lookup."""
        self._refresh()
        self.view.refresh()
        self.view.show()

    # --- Properties
    @property
    def names(self):
        """List of filtered/ordered names based on :attr:`search_query`."""
        return [self._normalized2original[n] for n in self._filtered_names]

    @property
    def search_query(self):
        """*get/set*. *str*. Search query typed by the user."""
        return self._search_query

    @search_query.setter
    def search_query(self, value):
        if value == self._search_query:
            return
        self._search_query = value
        self._apply_query()
        self.view.refresh()
Exemple #5
0
class Lookup:
    def __init__(self, mainwindow):
        self.mainwindow = mainwindow
        self.document = mainwindow.document
        self._original_names = []
        self._filtered_names = []
        self._search_query = ''
        self.selected_index = 0
        self.view = NoopGUI()
    
    def _apply_query(self):
        # On top, we want exact matches (the name starts with the query). Then, we want matches
        # that contain all the letters, sorted in order of names that have query letters as close
        # to each other as possible.
        q = sort_string(self._search_query)
        matches1, rest = extract(lambda n: n.startswith(q), self._original_names)
        matches2, rest = extract(lambda n: q in n, rest)
        matches3, rest = extract(lambda n: has_letters(n, q), rest)
        matches3.sort(key=lambda n: letters_distance(n, q))
        self._filtered_names = matches1 + matches2 + matches3
        self.selected_index = max(self.selected_index, 0)
        self.selected_index = min(self.selected_index, len(self._filtered_names)-1)
    
    def _generate_lookup_names(self): # Virtual
        return []
    
    def _go(self, name): #Virtual
        pass
    
    def _refresh(self):
        self._search_query = ''
        self.selected_index = 0
        names = self._generate_lookup_names()
        normalized_names = [sort_string(n) for n in names]
        self._normalized2original = {}
        for normalized, original in zip(normalized_names, names):
            self._normalized2original[normalized] = original
        self._original_names = normalized_names
        self._filtered_names = normalized_names
    
    def go(self):
        try:
            name = self.names[self.selected_index]
            self._go(name)
        except IndexError:
            pass # No result, do nothing
        self.view.hide()
    
    def show(self):
        self._refresh()
        self.view.refresh()
        self.view.show()
    
    #--- Properties
    @property
    def names(self):
        return [self._normalized2original[n] for n in self._filtered_names]
    
    @property
    def search_query(self):
        return self._search_query
    
    @search_query.setter
    def search_query(self, value):
        if value == self._search_query:
            return
        self._search_query = value
        self._apply_query()
        self.view.refresh()
Exemple #6
0
class Lookup:
    """Fuzzily-filtered list of items that match the few letters that the user typed.

    A Lookup allows the user to quickly find a object he looks for by name by typing only a few
    letters from that name. When first invoked, we start with a list of all possible items, and as
    letters are typed, those items and filtered *and* ordered in a way that fuzzily matches what
    our user typed.

    The list's filter is very inclusive and will include object that very remotely can match user's
    input, but the order is based on what matches the most.

    For example, if the user types "ab", the lookup list would contain both "barber", "absolute", 
    and "albinos" (all those names contain both "a" and "b"), but would be ordered so that
    "absolute" comes first because it's the closest match.

    Pressing Return will "activate" (see :meth:`go`) the first item in the list, but the user can
    also navigate the list with the arrow keys (or click on the wanted item), which is why we keep
    track of :attr:`selected_index`.

    This class is designed to be subclassed with concrete named objects to lookup in. It does
    nothing by itself.
    """
    def __init__(self, mainwindow):
        #: :class:`.MainWindow`
        self.mainwindow = mainwindow
        #: :class:`.Document`
        self.document = mainwindow.document
        self._original_names = []
        self._filtered_names = []
        self._search_query = ''
        #: *int*. Currently selected index in the filtered/ordered list.
        self.selected_index = 0
        self.view = NoopGUI()
    
    def _apply_query(self):
        # On top, we want exact matches (the name starts with the query). Then, we want matches
        # that contain all the letters, sorted in order of names that have query letters as close
        # to each other as possible.
        q = sort_string(self._search_query)
        matches1, rest = extract(lambda n: n.startswith(q), self._original_names)
        matches2, rest = extract(lambda n: q in n, rest)
        matches3, rest = extract(lambda n: has_letters(n, q), rest)
        matches3.sort(key=lambda n: letters_distance(n, q))
        self._filtered_names = matches1 + matches2 + matches3
        self.selected_index = max(self.selected_index, 0)
        self.selected_index = min(self.selected_index, len(self._filtered_names)-1)
    
    def _generate_lookup_names(self):
        """*Virtual*. Return a list of names in which we'll search."""
        return []
    
    def _go(self, name):
        """*Virtual*. Select an item named ``name``.

        The nature of this action depends on what kind of object we perform the lookup on. Were
        those accounts? Then open up the account named ``name``.
        """
    
    def _refresh(self):
        self._search_query = ''
        self.selected_index = 0
        names = self._generate_lookup_names()
        normalized_names = [sort_string(n) for n in names]
        self._normalized2original = {}
        for normalized, original in zip(normalized_names, names):
            self._normalized2original[normalized] = original
        self._original_names = normalized_names
        self._filtered_names = normalized_names
    
    def go(self):
        """Activate the :attr:`selected item <selected_index>` and close the lookup.

        To "activate", we call :meth:`_go` with the name of our selected item.
        """
        try:
            name = self.names[self.selected_index]
            self._go(name)
        except IndexError:
            pass # No result, do nothing
        self.view.hide()
    
    def show(self):
        """Refreshes the name list and show the lookup."""
        self._refresh()
        self.view.refresh()
        self.view.show()
    
    # --- Properties
    @property
    def names(self):
        """List of filtered/ordered names based on :attr:`search_query`."""
        return [self._normalized2original[n] for n in self._filtered_names]
    
    @property
    def search_query(self):
        """*get/set*. *str*. Search query typed by the user."""
        return self._search_query
    
    @search_query.setter
    def search_query(self, value):
        if value == self._search_query:
            return
        self._search_query = value
        self._apply_query()
        self.view.refresh()
 def __init__(self, mainwindow):
     self.mainwindow = mainwindow
     self.document = mainwindow.document
     self.app = mainwindow.document.app
     self.view = NoopGUI()
     self._old_date_range = None
class DateRangeSelector:
    def __init__(self, mainwindow):
        self.mainwindow = mainwindow
        self.document = mainwindow.document
        self.app = mainwindow.document.app
        self.view = NoopGUI()
        self._old_date_range = None
    
    # --- Private
    def _date_range_starting_point(self):
        if self.mainwindow.selected_transactions:
            return self.mainwindow.selected_transactions[0].date
        elif datetime.date.today() in self.document.date_range:
            return datetime.date.today()
        else:
            return self.document.date_range
    
    # --- Public
    def select_month_range(self):
        self.document.select_month_range(starting_point=self._date_range_starting_point())
    
    def select_quarter_range(self):
        self.document.select_quarter_range(starting_point=self._date_range_starting_point())
    
    def select_year_range(self):
        self.document.select_year_range(starting_point=self._date_range_starting_point())
    
    def select_year_to_date_range(self):
        self.document.select_year_to_date_range()
    
    def select_running_year_range(self):
        self.document.select_running_year_range()
    
    def select_all_transactions_range(self):
        self.document.select_all_transactions_range()
    
    def select_custom_date_range(self):
        self.document.select_custom_date_range()
    
    def select_prev_date_range(self):
        self.document.select_prev_date_range()
    
    def select_next_date_range(self):
        self.document.select_next_date_range()
    
    def select_today_date_range(self):
        self.document.select_today_date_range()
    
    def select_saved_range(self, slot):
        saved_range = self.app.saved_custom_ranges[slot]
        if saved_range:
            self.document.select_custom_date_range(saved_range.start, saved_range.end)
    
    def remember_current_range(self):
        self._old_date_range = self.document.date_range
    
    def refresh(self):
        self.view.refresh()
        old = self._old_date_range
        if old is not None:
            new = self.document.date_range
            if type(new) == type(old):
                if new.start > old.start:
                    self.view.animate_forward()
                else:
                    self.view.animate_backward()
    
    def refresh_custom_ranges(self):
        self.view.refresh_custom_ranges()
    
    # --- Properties
    @property
    def can_navigate(self):
        return self.document.date_range.can_navigate
    
    @property
    def custom_range_names(self):
        return [(r.name if r else None) for r in self.app.saved_custom_ranges]
    
    @property
    def display(self):
        return self.document.date_range.display
 def __init__(self, mainwindow):
     self.mainwindow = mainwindow
     self.document = mainwindow.document
     self.app = mainwindow.document.app
     self.view = NoopGUI()
     self._old_date_range = None
class DateRangeSelector:
    def __init__(self, mainwindow):
        self.mainwindow = mainwindow
        self.document = mainwindow.document
        self.app = mainwindow.document.app
        self.view = NoopGUI()
        self._old_date_range = None

    # --- Private
    def _date_range_starting_point(self):
        if self.mainwindow.selected_transactions:
            return self.mainwindow.selected_transactions[0].date
        elif datetime.date.today() in self.document.date_range:
            return datetime.date.today()
        else:
            return self.document.date_range

    # --- Public
    def select_month_range(self):
        self.document.select_month_range(
            starting_point=self._date_range_starting_point())

    def select_quarter_range(self):
        self.document.select_quarter_range(
            starting_point=self._date_range_starting_point())

    def select_year_range(self):
        self.document.select_year_range(
            starting_point=self._date_range_starting_point())

    def select_year_to_date_range(self):
        self.document.select_year_to_date_range()

    def select_running_year_range(self):
        self.document.select_running_year_range()

    def select_all_transactions_range(self):
        self.document.select_all_transactions_range()

    def select_custom_date_range(self):
        self.document.select_custom_date_range()

    def select_prev_date_range(self):
        self.document.select_prev_date_range()

    def select_next_date_range(self):
        self.document.select_next_date_range()

    def select_today_date_range(self):
        self.document.select_today_date_range()

    def select_saved_range(self, slot):
        saved_range = self.app.saved_custom_ranges[slot]
        if saved_range:
            self.document.select_custom_date_range(saved_range.start,
                                                   saved_range.end)

    def remember_current_range(self):
        self._old_date_range = self.document.date_range

    def refresh(self):
        self.view.refresh()
        old = self._old_date_range
        if old is not None:
            new = self.document.date_range
            if type(new) == type(old):
                if new.start > old.start:
                    self.view.animate_forward()
                else:
                    self.view.animate_backward()

    def refresh_custom_ranges(self):
        self.view.refresh_custom_ranges()

    # --- Properties
    @property
    def can_navigate(self):
        return self.document.date_range.can_navigate

    @property
    def custom_range_names(self):
        return [(r.name if r else None) for r in self.app.saved_custom_ranges]

    @property
    def display(self):
        return self.document.date_range.display