コード例 #1
0
class Select_Py_Version(_Dialog):
    """
    Select_Py_Version is a tkinter pop-up dialog used to select a python
    interpreter for use with Tk_Nosy.
    """

    def body(self, master):
        dialogframe = Frame(master, width=300, height=300)
        dialogframe.pack()


        self.Label_1 = Label(dialogframe, text="Select Python Version")
        self.Label_1.pack()

        if self.dialogOptions:
            rbL = self.dialogOptions.get('rbL', ['No Options','No Options'])
        else:
            rbL = ['No Options', 'No Options']

        self.RadioGroup1_StringVar = StringVar()
        self.RadioGroup1_StringVar.set(rbL[0])
        self.RadioGroup1_StringVar_traceName = \
            self.RadioGroup1_StringVar.trace_variable("w", self.RadioGroup1_StringVar_Callback)

        for rb in rbL:
            self.Radiobutton_1 = Radiobutton(dialogframe, text=rb, value=rb)
            self.Radiobutton_1.pack(anchor=W)
            self.Radiobutton_1.configure( variable=self.RadioGroup1_StringVar )
        self.resizable(0, 0) # Linux may not respect this


    def RadioGroup1_StringVar_Callback(self, varName, index, mode):
        """When radio group selection changes, print message to CLI."""
        print( "RadioGroup1_StringVar_Callback varName, index, mode",
                varName, index, mode )
        print( "    new StringVar value =", self.RadioGroup1_StringVar.get() )


    def validate(self):
        """Validates and packages dialog selections prior to return to calling
           routine.
           set values in "self.result" dictionary for return
        """
        self.result = {} # return a dictionary of results

        self.result["selection"] = self.RadioGroup1_StringVar.get()
        return 1

    def apply(self):
        print( 'apply called')
コード例 #2
0
class Select_Py_Version(_Dialog):
    """
    Select_Py_Version is a tkinter pop-up dialog used to select a python
    interpreter for use with Tk_Nosy.
    """
    def body(self, master):
        dialogframe = Frame(master, width=300, height=300)
        dialogframe.pack()

        self.Label_1 = Label(dialogframe, text="Select Python Version")
        self.Label_1.pack()

        if self.dialogOptions:
            rbL = self.dialogOptions.get('rbL', ['No Options', 'No Options'])
        else:
            rbL = ['No Options', 'No Options']

        self.RadioGroup1_StringVar = StringVar()
        self.RadioGroup1_StringVar.set(rbL[0])
        self.RadioGroup1_StringVar_traceName = \
            self.RadioGroup1_StringVar.trace_variable("w", self.RadioGroup1_StringVar_Callback)

        for rb in rbL:
            self.Radiobutton_1 = Radiobutton(dialogframe, text=rb, value=rb)
            self.Radiobutton_1.pack(anchor=W)
            self.Radiobutton_1.configure(variable=self.RadioGroup1_StringVar)
        self.resizable(0, 0)  # Linux may not respect this

    def RadioGroup1_StringVar_Callback(self, varName, index, mode):
        """When radio group selection changes, print message to CLI."""
        print("RadioGroup1_StringVar_Callback varName, index, mode", varName,
              index, mode)
        print("    new StringVar value =", self.RadioGroup1_StringVar.get())

    def validate(self):
        """Validates and packages dialog selections prior to return to calling
           routine.
           set values in "self.result" dictionary for return
        """
        self.result = {}  # return a dictionary of results

        self.result["selection"] = self.RadioGroup1_StringVar.get()
        return 1

    def apply(self):
        print('apply called')
コード例 #3
0
class ResolveIdleTime(Dialog):
    def body(self, master):
        """
        !!! copy completions filter/listbox setup

            Assign time period [   time period entry     ]
            to [    keyword combo box                    ]
            need:
                options['idle_delta']
                options['keywords']

                file to append idle time assigned actions
                options['idle_file']
                file to append new keywords
                options['keywords_file']
        """
        self.idle_delta = self.options['idle_delta']
        self.completions = self.options['keywords']
        self.currfile = self.options['currfile']
        self.tz = self.options['tz']
        period_frame = Frame(master, background=BGCOLOR)
        period_frame.pack(side="top", fill="x", padx=4, pady=2)

        period_label = Label(period_frame, text=_("Assign"), bg=BGCOLOR)
        period_label.pack(side="left")
        self.time_period = StringVar(self)
        self.period_entry = Entry(period_frame,
                                  textvariable=self.time_period,
                                  highlightbackground=BGCOLOR)

        self.idletime = StringVar(self)
        self.idletime.set(fmt_period(self.idle_delta))
        self.idle_label = Label(period_frame,
                                textvariable=self.idletime,
                                bg=BGCOLOR,
                                takefocus=0)
        self.idle_label.pack(side="right", padx=2)

        of_label = Label(period_frame, text=_("of"), bg=BGCOLOR, takefocus=0)
        of_label.pack(side="right")
        self.period_entry.pack(side="left", fill="x", expand=1, padx=4)
        self.keyword_frame = keyword_frame = Frame(master, background=BGCOLOR)
        keyword_frame.pack(side="top", fill="both", padx=4, expand=1)
        self.outcome = StringVar(self)
        self.outcome.set("")
        self.outcome_label = Label(keyword_frame,
                                   textvariable=self.outcome,
                                   bg=BGCOLOR,
                                   takefocus=0)
        self.outcome_label.pack(side="bottom")
        self.filterValue = StringVar(self)
        self.filterValue.set("")
        self.filterValue.trace_variable("w", self.setCompletions)
        self.fltr = Entry(self.keyword_frame,
                          textvariable=self.filterValue,
                          highlightbackground=BGCOLOR)
        self.fltr.pack(fill="x")
        self.fltr.icursor(END)
        self.listbox = listbox = Listbox(
            self.keyword_frame,
            exportselection=False,
            width=self.parent.options['completions_width'])
        listbox.pack(fill="both", expand=True, padx=2, pady=2)
        self.keyword_frame.bind("<Double-1>", self.apply)
        self.keyword_frame.bind("<Return>", self.apply)
        self.listbox.bind("<Up>", self.cursorUp)
        self.listbox.bind("<Down>", self.cursorDown)
        self.fltr.bind("<Up>", self.cursorUp)
        self.fltr.bind("<Down>", self.cursorDown)
        self.setCompletions()
        return self.period_entry

    def setCompletions(self, *args):
        match = self.filterValue.get()
        self.matches = matches = [
            x for x in self.completions
            if x and x.lower().startswith(match.lower())
        ]
        self.listbox.delete(0, END)
        for item in matches:
            self.listbox.insert(END, item)
        self.listbox.select_set(0)
        self.listbox.see(0)

    def cursorUp(self, event=None):
        cursel = int(self.listbox.curselection()[0])
        newsel = max(0, cursel - 1)
        self.listbox.select_clear(cursel)
        self.listbox.select_set(newsel)
        self.listbox.see(newsel)
        return "break"

    def cursorDown(self, event=None):
        cursel = int(self.listbox.curselection()[0])
        newsel = min(len(self.matches) - 1, cursel + 1)
        self.listbox.select_clear(cursel)
        self.listbox.select_set(newsel)
        self.listbox.see(newsel)
        return "break"

    def apply(self):
        """
        Make sure values are ok, write action and update idle time
        """
        period_str = self.period_entry.get()
        keyword_str = self.matches[int(self.listbox.curselection()[0])]
        if not (period_str and keyword_str):
            return
        try:
            period = parse_period(period_str)
        except:
            self.outcome.set(
                _("Could not parse period: {0}").format(period_str))
            return
        hsh = {
            'itemtype': '~',
            '_summary': 'idle time',
            's': get_current_time(),
            'e': period,
            'k': keyword_str,
            'z': self.tz
        }
        self.parent.loop.append_item(hsh, self.currfile)
        self.outcome.set(
            _("assigned {0} to {1}").format(fmt_period(period), keyword_str))
        self.time_period.set("")
        self.idle_delta -= period
        self.idletime.set(fmt_period(self.idle_delta))
        if self.idle_delta < ONEMINUTE:
            self.cancel()

    def ok(self, event=None):
        self.apply()
コード例 #4
0
class FileChoice(object):
    def __init__(self,
                 master=None,
                 title=None,
                 prefix=None,
                 list=[],
                 start='',
                 ext="txt",
                 new=False):
        self.master = master
        self.value = None
        self.prefix = prefix
        self.list = list
        if prefix and start:
            self.start = relpath(start, prefix)
        else:
            self.start = start
        self.ext = ext
        self.new = new

        self.modalPane = Toplevel(self.master,
                                  highlightbackground=BGCOLOR,
                                  background=BGCOLOR)
        if master:
            logger.debug('winfo: {0}, {1}; {2}, {3}'.format(
                master.winfo_rootx(), type(master.winfo_rootx()),
                master.winfo_rooty(), type(master.winfo_rooty())))
            self.modalPane.geometry(
                "+%d+%d" %
                (master.winfo_rootx() + 50, master.winfo_rooty() + 50))

        self.modalPane.transient(self.master)
        self.modalPane.grab_set()

        self.modalPane.bind("<Return>", self._choose)
        self.modalPane.bind("<Escape>", self._cancel)

        if title:
            self.modalPane.title(title)

        if new:
            nameFrame = Frame(self.modalPane,
                              highlightbackground=BGCOLOR,
                              background=BGCOLOR)
            nameFrame.pack(side="top", padx=18, pady=2, fill="x")

            nameLabel = Label(nameFrame,
                              text=_("file:"),
                              bd=1,
                              relief="flat",
                              anchor="w",
                              padx=0,
                              pady=0,
                              highlightbackground=BGCOLOR,
                              background=BGCOLOR)
            nameLabel.pack(side="left")

            self.fileName = StringVar(self.modalPane)
            self.fileName.set("untitled.{0}".format(ext))
            self.fileName.trace_variable("w", self.onSelect)
            self.fname = Entry(nameFrame,
                               textvariable=self.fileName,
                               bd=1,
                               highlightbackground=BGCOLOR)
            self.fname.pack(side="left", fill="x", expand=1, padx=0, pady=0)
            self.fname.icursor(END)
            self.fname.bind("<Up>", self.cursorUp)
            self.fname.bind("<Down>", self.cursorDown)

        filterFrame = Frame(self.modalPane,
                            highlightbackground=BGCOLOR,
                            background=BGCOLOR)
        filterFrame.pack(side="top", padx=18, pady=4, fill="x")

        filterLabel = Label(filterFrame,
                            text=_("filter:"),
                            bd=1,
                            relief="flat",
                            anchor="w",
                            padx=0,
                            pady=0,
                            highlightbackground=BGCOLOR,
                            background=BGCOLOR)
        filterLabel.pack(side="left")

        self.filterValue = StringVar(self.modalPane)
        self.filterValue.set("")
        self.filterValue.trace_variable("w", self.setMatching)
        self.fltr = Entry(filterFrame,
                          textvariable=self.filterValue,
                          bd=1,
                          highlightbackground=BGCOLOR)
        self.fltr.pack(side="left", fill="x", expand=1, padx=0, pady=0)
        self.fltr.icursor(END)

        prefixFrame = Frame(self.modalPane,
                            highlightbackground=BGCOLOR,
                            background=BGCOLOR)
        prefixFrame.pack(side="top", padx=8, pady=2, fill="x")

        self.prefixLabel = Label(prefixFrame,
                                 text=_("{0}:").format(prefix),
                                 bd=1,
                                 highlightbackground=BGCOLOR,
                                 background=BGCOLOR)
        self.prefixLabel.pack(side="left", expand=0, padx=0, pady=0)

        buttonFrame = Frame(self.modalPane,
                            highlightbackground=BGCOLOR,
                            background=BGCOLOR)
        buttonFrame.pack(side="bottom", padx=10, pady=2)

        chooseButton = Button(buttonFrame,
                              text="Choose",
                              command=self._choose,
                              highlightbackground=BGCOLOR,
                              background=BGCOLOR,
                              pady=2)
        chooseButton.pack(side="right", padx=10)

        cancelButton = Button(buttonFrame,
                              text="Cancel",
                              command=self._cancel,
                              highlightbackground=BGCOLOR,
                              background=BGCOLOR,
                              pady=2)
        cancelButton.pack(side="left")

        selectionFrame = Frame(self.modalPane,
                               highlightbackground=BGCOLOR,
                               background=BGCOLOR)
        selectionFrame.pack(side="bottom", padx=8, pady=2, fill="x")

        self.selectionValue = StringVar(self.modalPane)
        self.selectionValue.set("")
        self.selection = Label(selectionFrame,
                               textvariable=self.selectionValue,
                               bd=1,
                               highlightbackground=BGCOLOR,
                               background=BGCOLOR)
        self.selection.pack(side="left", fill="x", expand=1, padx=0, pady=0)

        listFrame = Frame(self.modalPane,
                          highlightbackground=BGCOLOR,
                          background=BGCOLOR,
                          width=40)
        listFrame.pack(side="top", fill="both", expand=1, padx=5, pady=2)

        scrollBar = Scrollbar(listFrame, width=8)
        scrollBar.pack(side="right", fill="y")
        self.listBox = Listbox(listFrame, selectmode=BROWSE, width=36)
        self.listBox.pack(side="left",
                          fill="both",
                          expand=1,
                          ipadx=4,
                          padx=2,
                          pady=0)
        self.listBox.bind('<<ListboxSelect>>', self.onSelect)
        self.listBox.bind("<Double-1>", self._choose)
        self.modalPane.bind("<Return>", self._choose)
        self.modalPane.bind("<Escape>", self._cancel)
        # self.modalPane.bind("<Up>", self.cursorUp)
        # self.modalPane.bind("<Down>", self.cursorDown)
        self.fltr.bind("<Up>", self.cursorUp)
        self.fltr.bind("<Down>", self.cursorDown)
        scrollBar.config(command=self.listBox.yview)
        self.listBox.config(yscrollcommand=scrollBar.set)
        self.setMatching()

    def ignore(self, e=None):
        return "break"

    def onSelect(self, *args):
        # Note here that Tkinter passes an event object to onselect()

        if self.listBox.curselection():
            firstIndex = self.listBox.curselection()[0]
            value = self.matches[int(firstIndex)]
            r = value[1]
            p = os.path.join(self.prefix, r)
            if self.new:
                if os.path.isfile(p):
                    p = os.path.split(p)[0]
                    r = os.path.split(r)[0]
                f = self.fileName.get()
                r = os.path.join(r, f)
                p = os.path.join(p, f)

            self.selectionValue.set(r)
            self.value = p
        return "break"

    def cursorUp(self, event=None):
        cursel = int(self.listBox.curselection()[0])
        newsel = max(0, cursel - 1)
        self.listBox.select_clear(cursel)
        self.listBox.select_set(newsel)
        self.listBox.see(newsel)
        self.onSelect()
        return "break"

    def cursorDown(self, event=None):
        cursel = int(self.listBox.curselection()[0])
        newsel = min(len(self.list) - 1, cursel + 1)
        self.listBox.select_clear(cursel)
        self.listBox.select_set(newsel)
        self.listBox.see(newsel)
        self.onSelect()
        return "break"

    def setMatching(self, *args):
        # disabled = "#BADEC3"
        # disabled = "#91CC9E"
        disabled = "#62B374"
        match = self.filterValue.get()
        if match:
            self.matches = matches = [
                x for x in self.list if x and match.lower() in x[1].lower()
            ]
        else:
            self.matches = matches = self.list
        self.listBox.delete(0, END)
        index = 0
        init_index = 0
        for item in matches:
            if type(item) is tuple:
                # only show the label
                # (label, value, disabled)FF
                self.listBox.insert(END, item[0])
                if self.new:
                    if not item[-1]:
                        self.listBox.itemconfig(index, fg=disabled)
                    else:
                        self.listBox.itemconfig(index, fg="blue")
                        if self.start and item[1] == self.start:
                            init_index = index
                else:
                    if item[-1]:
                        self.listBox.itemconfig(index, fg=disabled)
                    else:
                        self.listBox.itemconfig(index, fg="blue")
                        if self.start and item[1] == self.start:
                            init_index = index
            # elif files:
            else:
                self.listBox.insert(END, item)
            index += 1
        self.listBox.select_set(init_index)
        self.listBox.see(init_index)
        self.fltr.focus_set()
        self.onSelect()

    def _choose(self, event=None):
        try:
            if self.listBox.curselection():
                firstIndex = self.listBox.curselection()[0]
                if self.new:
                    if not self.value or os.path.isfile(self.value):
                        return
                else:
                    tup = self.matches[int(firstIndex)]
                    if tup[-1]:
                        return
                    self.value = os.path.join(self.prefix, tup[1])
            else:
                return
        except IndexError:
            self.value = None
        self.modalPane.destroy()

    def _cancel(self, event=None):
        self.value = None
        self.modalPane.destroy()

    def returnValue(self):
        self.master.wait_window(self.modalPane)
        return self.value
コード例 #5
0
from guis import settings
import utils

# Affectation de la langue par défaut si non présente
if utils.isWindows():
    if locale.getlocale(locale.LC_ALL) == (None, None):
        locale.setlocale(locale.LC_ALL, locale.getdefaultlocale()[0][:2])
else:
    if locale.getlocale(locale.LC_CTYPE) == (None, None):
        locale.setlocale(locale.LC_CTYPE, locale.getdefaultlocale()[0][:2])

# Affectation de la langue d'utilisation en fonction des paramètres ou du système d'exploitation (français ou anglais)
LOCALE = StringVar(value=locale.getlocale()[0][:2])
if LOCALE.get() != "fr" and LOCALE.get() != "en":
    LOCALE.set("en")
LOCALE.trace_variable("w", lambda *args: settings.saveSettings())


class MSG(StringVar):
    def __init__(self, english_message: str, french_message: str):
        """
		Constructeur d'un message localisé
		Le premier argument est le message en anglais, le second est en français
		"""
        super().__init__()
        self.en = english_message
        self.fr = french_message

        # Si un des messages est vide, l'autre est choisi
        if len(self.fr) == 0:
            self.fr = self.en
コード例 #6
0
class App(Tk):
    STEP_MODE: IntVar  # Пошаговое проигрывание анимации.
    SHOW_INFLECTION_POINTS: IntVar

    started = False  # Мы начали искать корни, в это время нельзя менять менять уравнение.
    paused = False  # Пауза для анимации.
    done: bool = False
    st: float
    en: float
    div: int
    eps: float

    lin_x: any  # Множество точек для построения графика.
    lin_space_size: int = 400  # Кол-во точек для построения графика

    solver: HordEquationSolver

    expr: StringVar  # Введенное пользователем выражение

    # График
    ax: plt.Axes
    plots: List[Line2D] = []
    h_lines: List[Line2D] = []  # Горизонтальные линии
    main_plot: any = None
    deriv_plot: any = None
    inflection_points: any = None

    # Список решений
    solutions: List[Solution] = []
    solution_ids: List[str] = []

    tree: Treeview  # Таблица результатов
    b_start: Button  # Кнопка начала/остановки
    lin_space_label: Label  # Надпись о точности графика

    cached_function: any  # Декодированная функция, что бы каждый раз не вызывать eval
    after_job_id: any = None  # id отложенного вызова функции для её отмены

    def f(self, x):
        return self.cached_function(x)

    def __init__(self, st: float, en: float, div: int, eps: float):
        super().__init__()
        self.st = st
        self.en = en
        self.div = div
        self.eps = eps
        self.lin_x = np.linspace(
            st, en,
            self.lin_space_size)  # Множество точек для построения графика.

        fig = plt.Figure(tight_layout=True)
        self.ax = fig.add_subplot()

        self.ax.axhline(0, color='black')

        self.grid_columnconfigure(0, weight=2)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=1)
        self.fagg = FigureCanvasTkAgg(fig, self)
        self.fagg.get_tk_widget().grid(row=0, column=0, sticky='WNSE')
        self.frame = Frame(self)
        self.frame.grid(row=0, column=1, sticky='WNSE', rowspan=2)

        self.init_sidebar()

        self.solver = HordEquationSolver(st, en, div, eps, self.f)

        self.prepare()

        self.fagg.draw()
        button_frame = Frame(self)
        button_frame.grid(row=1, column=0, sticky='WE')
        self.b_start = Button(button_frame, text='start')
        self.b_start.pack(side='left', anchor='center')
        self.b_start.bind('<Button>', self.start)
        Button(button_frame, text='reset',
               command=self.reset).pack(side='left', anchor='center')

    def init_sidebar(self):
        self.cached_function = eval('lambda x: ' + DEFAULT_EXPRESSION)
        self.expr = StringVar(self)
        self.expr.set(DEFAULT_EXPRESSION)
        self.expr.trace_variable('w', self.var_debounce(self.expression_input))

        # Динамические переменные для входных полей
        start_v = DoubleVar(self, value=self.st)
        end = DoubleVar(self, value=self.en)
        epsilon = DoubleVar(self, value=self.eps)
        divs = IntVar(self, value=self.div)
        lin_space_var = DoubleVar(self,
                                  value=math.log(self.lin_space_size,
                                                 LOG_BASE))
        variables = ((start_v, 'st'), (end, 'en'), (epsilon, 'eps'), (divs,
                                                                      'div'))

        # Функция обертка для сигнализирования о смене переменной.
        def outer(var, var_name):
            def inner(*_args):
                try:
                    self.params_input(var.get(), var_name)
                except Exception:
                    pass

            return inner

        for (v, name) in variables:
            v.trace('w', self.debounce(outer(v, name), 250))

        lin_debouncer = self.debounce(self.modify_lin_space_size, 150)
        lin_space_var.trace(
            'w', lambda *_args: self.modify_lin_space_size_callback(
                lin_space_var.get(), lin_debouncer))

        self.frame.columnconfigure(1, weight=2)
        Label(self.frame, text='Выражение:').grid(column=0,
                                                  row=0,
                                                  columnspan=2,
                                                  sticky='EW')
        Entry(self.frame, textvariable=self.expr).grid(column=0,
                                                       row=1,
                                                       columnspan=2,
                                                       sticky='EW')

        self.frame.rowconfigure(2, minsize=25)
        Label(self.frame, text='Начало').grid(column=0, row=3, sticky='W')
        Entry(self.frame, textvariable=start_v).grid(column=1,
                                                     row=3,
                                                     sticky='EW')

        Label(self.frame, text='Конец').grid(column=0, row=4, sticky='W')
        Entry(self.frame, textvariable=end).grid(column=1, row=4, sticky='EW')

        Label(self.frame, text='Точность').grid(column=0, row=5, sticky='W')
        Entry(self.frame, textvariable=epsilon).grid(column=1,
                                                     row=5,
                                                     sticky='EW')

        Label(self.frame, text='Разделение').grid(column=0, row=6, sticky='W')
        Entry(self.frame, textvariable=divs).grid(column=1, row=6, sticky='EW')

        self.frame.rowconfigure(7, minsize=25)
        self.lin_space_label = Label(
            self.frame,
            text=f'Количество точек графика: {self.lin_space_size}')
        self.lin_space_label.grid(column=0, row=8, columnspan=2, sticky='W')
        w = Scale(self.frame,
                  from_=math.log(5, LOG_BASE),
                  to=math.log(MAX_LINE_SPACE_SIZE, LOG_BASE),
                  resolution=0.1 / LOG_BASE,
                  orient=HORIZONTAL,
                  variable=lin_space_var)
        w.grid(column=0, row=9, columnspan=2, sticky='EW')

        self.tree = Treeview(self.frame)
        self.tree['columns'] = (1, 2, 3, 4, 5)
        self.tree.column('#0', width=35)
        self.tree.column(1, width=130, anchor='center')
        self.tree.column(2, width=80, anchor='center')
        self.tree.column(3, width=80, anchor='center')
        self.tree.column(4, width=80, anchor='center')
        self.tree.column(5, width=80, anchor='center')
        self.tree.heading('#0', text='№')
        self.tree.heading(1, text='Интервал')
        self.tree.heading(2, text='Корень')
        self.tree.heading(3, text='Значение')
        self.tree.heading(4, text='Итераций')
        self.tree.heading(5, text='Ошибка')

        self.tree.grid(column=0, row=10, columnspan=2, sticky='EWSN')

        self.STEP_MODE = IntVar(self, value=0)
        self.SHOW_INFLECTION_POINTS = IntVar(self, value=1)
        self.SHOW_INFLECTION_POINTS.trace(
            'w', lambda *_args: self.redraw_main_plot(True))
        Checkbutton(self.frame,
                    text='Пошаговый режим',
                    variable=self.STEP_MODE).grid(column=0,
                                                  row=11,
                                                  sticky='WS')
        Checkbutton(self.frame, text='Показывать точка перегиба', variable=self.SHOW_INFLECTION_POINTS)\
            .grid(column=0, row=12, sticky='WS')

    def modify_lin_space_size_callback(self, value, callback):
        self.lin_space_label.configure(
            text=f'Количество точек графика: {round(LOG_BASE**value)}')
        callback(value)

    def modify_lin_space_size(self, size):
        self.lin_space_size = round(LOG_BASE**size)
        self.redraw_main_plot(True)

    def redraw_main_plot(self, draw=False):
        if self.st != self.lin_x.min() or self.en != self.lin_x.max(
        ) or self.lin_space_size != len(self.lin_x):
            self.lin_x = np.linspace(self.st, self.en, self.lin_space_size)

        if self.main_plot:
            self.main_plot.remove()
        if self.deriv_plot:
            self.deriv_plot.remove()
            self.deriv_plot = None
        if self.inflection_points:
            self.inflection_points.remove()
            self.inflection_points = None

        v = self.f(self.lin_x)
        self.main_plot = self.ax.plot(self.lin_x,
                                      v,
                                      label='f(x)',
                                      color='tab:blue')[0]

        if self.SHOW_INFLECTION_POINTS.get():
            v2 = np.diff(v)
            v2 = np.insert(v2, 0, v2[0] - (v2[1] - v2[0]))
            v2 /= ((self.en - self.st) / self.lin_space_size)

            v2 = np.diff(v2)
            second_derivative_is_zero = all([x < self.eps for x in v2])
            if not second_derivative_is_zero:
                v2 = np.append(v2, v2[-1])
                v2 /= ((self.en - self.st) / self.lin_space_size)
                inflection_points_x = []
                inflection_points_y = []
                for i in range(1, len(v2)):
                    if v2[i - 1] * v2[i] <= 0:
                        if v[i - 1] == 0:
                            continue
                        n = i - 1 if v2[i - 1] < v2[i] else i
                        x = self.st + (self.en -
                                       self.st) / self.lin_space_size * n
                        y = self.f(x)

                        inflection_points_x.append(x)
                        inflection_points_y.append(y)

                self.inflection_points = self.ax.scatter(inflection_points_x,
                                                         inflection_points_y,
                                                         80,
                                                         marker='x',
                                                         color='violet')

                self.deriv_plot = self.ax.plot(self.lin_x,
                                               v2,
                                               label="f''(x)",
                                               color='tab:green')[0]

        mx_y = max(v)
        mn_y = min(v)
        m = (mx_y - mn_y) / 50
        dx = abs(self.en - self.st) * 0.05
        self.ax.set_ylim(mn_y - m, mx_y + m)
        self.ax.set_xlim(self.st - dx, self.en + dx)

        if draw:
            self.fagg.draw()

    # Первичное отображение, подготавливает все данные для него.
    def prepare(self):
        step = (self.en - self.st) / self.div

        self.redraw_main_plot()

        for plt in self.plots:
            plt.remove()
        self.plots.clear()
        for line in self.h_lines:
            line.remove()
        self.h_lines.clear()

        for i in range(self.div + 1):
            x = (self.st + step * i)
            self.h_lines.append(self.ax.axvline(x, color='black'))

        self.plots = [
            self.ax.plot(self.lin_x,
                         self.solver.hord.k * self.lin_x + self.solver.hord.b,
                         label='lin',
                         color='tab:orange')[0],
            self.ax.scatter([self.solver.p1.x], [self.solver.p1.y],
                            marker='o',
                            color='red'),
            self.ax.scatter([self.solver.p_fixed.x], [self.solver.p_fixed.y],
                            marker='o',
                            color='blue')
        ]
        self.fagg.draw()

    def var_debounce(self,
                     func: Callable[[str], None],
                     t: int = 500) -> Callable:
        def inner(*args):
            func(self.tk.globalgetvar(args[0]))

        return self.debounce(inner, t)

    def debounce(self, func: Callable[..., None], t: int = 500) -> Callable:
        return Debouncer(self, func, t)

    def expression_input(self, value):
        try:
            self.cached_function = eval('lambda x: ' + value)
            self.reset()
        except Exception as e:
            # traceback.print_exc()
            # print(e)
            pass

    def params_input(self, value, var_name):
        self.__setattr__(var_name, value)
        self.reset()

    def reset(self):
        if self.after_job_id:
            self.after_cancel(self.after_job_id)
        self.started = False
        self.done = False
        self.tree.delete(*self.solution_ids)
        self.solutions.clear()
        self.solution_ids.clear()
        self.solver = HordEquationSolver(self.st, self.en, self.div, self.eps,
                                         self.f)
        self.b_start.configure(text='start')
        self.b_start.bind('<Button>', self.start)
        self.prepare()

    def start(self, event):
        if self.STEP_MODE.get():
            self.step_solve()
        else:
            if not self.started or self.paused:
                self.started = True
                self.paused = False
                self.step_solve()
                event.widget.configure(text='stop')
                event.widget.bind('<Button>', self.stop)

    def stop(self, event):
        self.paused = True
        if self.after_job_id:
            self.after_cancel(self.after_job_id)
        event.widget.configure(text='start')
        event.widget.bind('<Button>', self.start)

    # Перерисовка хорды и точек пересечения.
    def redraw_solution(self):
        for pt in self.plots:
            pt.remove()
        self.plots = [
            self.ax.plot(self.lin_x,
                         self.solver.hord.k * self.lin_x + self.solver.hord.b,
                         label='lin',
                         color='tab:orange')[0],
            self.ax.scatter([self.solver.p1.x], [self.solver.p1.y],
                            marker='o',
                            color='red'),
            self.ax.scatter([self.solver.p_fixed.x], [self.solver.p_fixed.y],
                            marker='o',
                            color='blue')
        ]
        self.fagg.draw()
        self.ax.relim()

    def step_solve(self):
        if self.started and not self.solver.done or self.STEP_MODE.get():
            status = self.solver.next_step()
            self.redraw_solution()
            if not status:
                if self.started and not self.paused and not self.STEP_MODE.get(
                ):
                    self.after_job_id = self.after(100, self.step_solve)
            else:
                self.add_solution(self.solver.get_solution())
                if self.solver.next_segment():
                    print('DONE!!!!')
                    self.b_start.configure(text='DONE!!!!')
                    self.b_start.unbind('<Button>')
                else:
                    self.redraw_solution()
                    if not self.STEP_MODE.get():
                        self.after_job_id = self.after(200, self.step_solve)

    def add_solution(self, sol):
        if sol.err == 0:
            interval = f'({sol.interval[0]:.5} : {sol.interval[1]:.5})'
            iid = self.tree.insert('',
                                   'end',
                                   text=str(len(self.solutions) + 1),
                                   values=(interval, f'{sol.x:.7}',
                                           f'{sol.y:.5}', sol.iter, sol.err))
        else:
            iid = self.tree.insert('',
                                   'end',
                                   text=str(len(self.solutions) + 1),
                                   values=('', '', '', '', sol.err))
        self.solutions.append(sol)
        self.solution_ids.append(iid)
コード例 #7
0
class Model:
    def __init__(self, config):
        self.config = config

        self.db_file = self.config['filepaths']['tinydb']

        self.packs = []
        self.current_pack = None

        self.standard_sets = config['sets_standard']
        self.wild_sets = config['sets_wild']

        self.acronyms = dict(config['sets_wild'].items())
        self.acronyms.update(config['sets_standard'].items())

        self.image = None

        # ~~ Image saving view ~~
        # StringVar will hold the name of the current subpage
        self.current_subpage = StringVar()

        # ~~ Card pack subpage ~~
        self.quantities = {}
        self.notes = StringVar()  # defaults to empty string
        self.card_set = StringVar()

        # ~~ Arena subpage ~~
        # Todo: may want to group rewards up so call call model.rewards.gold for example
        self.reward_quantities = {}
        self.reward_dust = IntVar()
        self.reward_gold = IntVar()
        self.reward_packs = IntVar()
        self.arena_wins = IntVar()
        self.arena_losses = IntVar()

        # ~~ End of Season subpage ~~
        self.end_rank = IntVar()
        self.max_rank = IntVar()

        # ~~ Other subpage ~~
        self.output_names = {
            self.config.get(item[0], 'name', fallback=item[0]): item[0]
            for item in self.config.items('output')
        }

        self.other_pages = [
            self.config.get(item[0], 'name', fallback=item[0])
            for item in self.config.items('output') if item[0] not in [
                'packs',
                'arena',
                'rewards',
            ]
        ]
        self.selected_folder = StringVar()

        # ~~ Stats view ~~
        self.viewed_total_quantities = {
        }  # quantities for each rarity, for stats view
        self.viewed_mean_quantities = {}
        self.view_card_set = StringVar()  # card set, as deemed by stats view
        self.viewed_total_packs = StringVar()  # packs for stats view card set

        self.quantities = {rarity: IntVar() for rarity in Hearthstone.rarities}
        self.viewed_total_quantities = {
            rarity: IntVar()
            for rarity in Hearthstone.rarities
        }
        self.viewed_mean_quantities = {
            rarity: StringVar()
            for rarity in Hearthstone.rarities
        }

        self.enchant_value = StringVar()
        self.disenchant_value = StringVar()

        # ~~ Pity view ~~
        # NOTE: hard coding pity values in here
        # G.Epics and G.Legendaries are conservative best estimates
        self.pity_max = dict(
            zip(Hearthstone.rarities[2:], [10, 40, 25, 30, 150, 350]))
        self.pity_card_set = StringVar()
        self.pity_total_packs = StringVar()
        # We ignore common and rare from our pity timers, they are guaranteed almost every pack
        self.pity_current_timers = {
            rarity: StringVar()
            for rarity in Hearthstone.rarities[2:]
        }
        self.pity_card_set.trace_variable('w', self.extract_timers)
        self.pity_advice = StringVar()

        self.find_images()
        self.next_pack()

    def bind_graph_update_function(self, function):
        self.view_card_set.trace_variable('w', function)

    def find_images(self):
        """Finds all the screenshots on the desktop that might be packs, and stores them"""
        desktop_path = self.config['filepaths']['desktop']
        for file in os.listdir(desktop_path):
            if file.endswith(".png") and file.startswith("Hearthstone"):
                # Todo: signature of CardPack might change
                self.packs.append(CardPack(file, desktop_path))
        # reverse so pop can be used to get in order
        self.packs.sort(key=lambda pack: pack.sortkey, reverse=True)

    def next_pack(self):
        # closing images once we're done with them
        if self.current_pack:
            self.image.close()
        # moves onto the next pack in the list, updating image as well
        try:
            self.current_pack = self.packs.pop()
            self.image = Image.open(self.current_pack.full_path)
        except IndexError:
            # no more packs to process
            self.current_pack = None

        # Todo: handle the no-more pack case more deftly
        # could use a BooleanVar flag for 'model has finished'

        self.reset_variables()

    def reset_variables(self):
        # Resetting some variables back to default values
        for rarity in Hearthstone.rarities:
            default = Hearthstone.default_pack[rarity]
            self.quantities[rarity].set(default)
        self.notes.set('')
        # Note: self.card_set is not reset, assumes multiple packs of same set most likely

    # TODO: this model-level validation is the last bastion
    # --> need not be the only one, if the Controller can do it, might be worth
    def is_valid_pack(self):
        # Todo: ideally we'd throw the user some information as to why it fails
        # --> Not core requirement, I will know what's up

        if (self.card_set.get() not in self.standard_sets) and \
                (self.card_set.get() not in self.wild_sets):
            # CARD SET NOT SELECTED
            return False

        total_cards = sum([val.get() for val in self.quantities.values()])
        if total_cards != 5:
            #  NOT ENOUGH CARDS
            return False

        if self.quantities['common'].get() == 5:
            # ALL COMMONS NOT VALID
            return False

        if (self.quantities['legendary'].get() +
                self.quantities['golden_epic'].get() +
                self.quantities['golden_legendary'].get() >
                0) and (self.notes.get() == ''):
            # NOTEWORTHY CARD NOT NOTED
            return False

        return True

    # TODO: test
    # TinyDB file should be updated with the pack data
    # TinyDB should be using the correct table for the data
    # image should be moved to the correct folder, according to the word of the config
    # directories should be made if not present

    # TODO: storing the data to the database, and moving the file
    # should be separate both from each other and from the main 'submit' method
    # imagine we were moving both into web-based databasing

    # Should the correct submission process be the responsibility of Model or Controller?

    def submit(self):
        # commits pack data to tinydb file
        # makes no attempt to validate, assumes you have sorted this

        # Todo: check the current subpage, and modify behaviour accordingly

        current_page = self.current_subpage.get()

        if current_page == "Packs":
            self.submit_cardpack()
        elif current_page == "Arena2":
            self.submit_arena()
        elif current_page == "Season End5":
            self.submit_eos()
        elif current_page == "Other":
            self.submit_other()
        else:
            # TODO raise some form of error, unrecognised subpage
            pass

    def submit_cardpack(self):
        with TinyDB(self.db_file) as db:
            card_table = db.table('card_data')
            card_table.insert({
                'date': self.current_pack.sortkey,
                **{
                    rarity: self.quantities[rarity].get()
                    for rarity in Hearthstone.rarities
                }, 'notes': self.notes.get(),
                'set': self.acronyms[self.card_set.get()],
                'filename': self.current_pack.image_name
            })

        dest_folder = self.fetch_destination('packs')

        destination = os.path.join(dest_folder, self.current_pack.image_name)

        print('\nFile to move: {}'.format(self.current_pack.full_path))
        print('\nDestination set to {}. Continue?'.format(destination))
        input()  # REMOVE, testing hold
        self.image.close()
        shutil.move(self.current_pack.full_path, destination)

        # TODO: check this is needed
        # TODO: move to end of main submit function
        self.extract_data()
        self.next_pack()

    def submit_arena(self):
        pass

    def submit_eos(self):
        pass

    def submit_other(self):
        folder_selection = self.selected_folder.get()
        if folder_selection not in self.output_names:
            # TODO: proper handling
            print('Unknown output folder "{}"'.format(folder_selection))
            return

        folder_name = self.output_names[folder_selection]
        dest_folder = self.fetch_destination(folder_name)
        destination = os.path.join(dest_folder, self.current_pack.image_name)

        print('\nFile to move: {}'.format(self.current_pack.full_path))
        print('\nDestination set to {}. Continue?'.format(destination))
        input()  # REMOVE, testing hold
        self.image.close()
        shutil.move(self.current_pack.full_path, destination)

        # TODO: check this is needed
        # TODO: move to end of main submit function
        self.extract_data()
        self.next_pack()

    def not_pack(self):
        # "Not pack" will likely be removed and made into "skip".
        # Images for things other than packs will have a sub page of the storage screen
        self.next_pack()

    def fetch_destination(self, out_name):
        folder = self.config['output'][out_name]
        path_parts = []

        if self.config.has_section(out_name):
            if self.config.getboolean(out_name,
                                      'yearseperated',
                                      fallback=False):
                path_parts.append(str(self.current_pack.date.year))
            if self.config.getboolean(out_name,
                                      'monthseperated',
                                      fallback=False):
                path_parts.append(month_name[self.current_pack.date.month])
        dest_folder = os.path.join(folder, *path_parts)

        os.makedirs(dest_folder, exist_ok=True
                    )  # create folder, and all intermediary folders, if needed
        return dest_folder

    # Another alias. Can be cleaned up when refactoring if needed
    skip_image = next_pack

    def extract_data(self):
        # called when the view_card_set variable changes
        # looks at the new value, and determines if it is a set, or 'All Sets'
        # performs requires queries on the tinyDB, and updates any variables to these value
        # -->view will then change, as variables are updated

        set_name = self.view_card_set.get()

        if not set_name:
            return
        if set_name == 'Card Set':
            return
        elif set_name == 'All Sets':  # case looking for all packs handled separately
            with TinyDB(self.db_file) as db:
                card_table = db.table('card_data')
                results = card_table.all()
        else:
            acronym = self.acronyms[set_name]
            pack = Query()
            with TinyDB(self.db_file) as db:
                card_table = db.table('card_data')
                results = card_table.search(pack['set'] == acronym)

        total_packs = len(results)

        dates = [
            datetime(*[int(val) for val in result['date'].split('/')])
            for result in results
        ]
        packs = list(range(1, total_packs + 1))

        self.viewed_total_packs.set('{} Packs'.format(total_packs))

        count = Counter()
        for pack_line in results:
            count.update({k: pack_line[k] for k in Hearthstone.rarities})

        for rarity in Hearthstone.rarities:
            self.viewed_total_quantities[rarity].set(count[rarity])
            if total_packs == 0:
                self.viewed_mean_quantities[rarity].set('###')
            else:
                self.viewed_mean_quantities[rarity].set('{:.3f}'.format(
                    float(count[rarity]) / total_packs))

        if total_packs == 0:
            self.enchant_value.set('Average Enchant\n')
            self.disenchant_value.set('Average Disenchant\n')
        else:
            self.enchant_value.set('Average Enchant\n{:.1f}'.format(
                float(value_enchant(count) / total_packs)))
            self.disenchant_value.set('Average Disenchant\n{:.1f}'.format(
                float(value_disenchant(count) / total_packs)))

        return dates, packs

    def extract_timers(self, *callback):
        # called when the pity_card_set variable changes
        # looks at new value, and gets new set
        # performs required db queries and updates variables
        set_name = self.pity_card_set.get()

        if set_name == 'Card Set':
            return
        else:
            acronym = self.acronyms[set_name]
            pack = Query()
            with TinyDB(self.db_file) as db:
                card_table = db.table('card_data')
                results = card_table.search(pack['set'] == acronym)
            self.pity_total_packs.set('{} Packs'.format(len(results)))

        # Using stringvars, so we can return "<current>/<max>"
        results.sort(key=lambda entry: entry['date'], reverse=True)
        for rarity in Hearthstone.rarities[2:]:  # ignore <epics
            # Generator that enumerates the packs, and selects only those that contain rarity
            # then we take the first item with next which produces timer + pack (should we need that)
            # Using len(results) if no pack is found, ie all packs count
            timer, pack = next(
                (i for i in enumerate(results) if i[1][rarity] > 0),
                (len(results), None))
            self.pity_current_timers[rarity].set('{}/{}'.format(
                timer, self.pity_max[rarity]))
コード例 #8
0
class Timer:
    def __init__(self, parent):
        root.geometry("280x100")
        root.title("Таймер выключения пк")
        root.resizable(width=False, height=False)
        self.displaying_counting = Label(root,
                                         text="осталось",
                                         font=("Arial bold", 14))
        self.displaying_counting.place(x=0, y=60)

        self.lbl_sec = Label(root, text="сек", font=("Arial bold", 14))
        self.lbl_sec.place(x=135, y=0)
        self.lbl_min = Label(root, text="мин", font=("Arial bold", 14))
        self.lbl_min.place(x=80, y=0)
        self.lbl_hour = Label(root, text="час", font=("Arial bold", 14))
        self.lbl_hour.place(x=30, y=0)

        self.entry_seconds = Entry(parent, width=2)
        self.entry_seconds.place(x=120, y=5)
        self.entry_minutes = Entry(parent, width=2)
        self.entry_minutes.place(x=65, y=5)
        self.entry_hours = Entry(parent, width=4)
        self.entry_hours.place(x=5, y=5)

        self.imput_str_sec = StringVar()
        self.imput_str_min = StringVar()
        self.imput_str_hour = StringVar()

        callback = root.register(self.only_numeric_input_less_than_60)
        callback_hours = root.register(self.only_numeric_input)
        self.entry_seconds.configure(validate="key",
                                     validatecommand=(callback, "%P"),
                                     textvariable=self.imput_str_sec)
        self.entry_minutes.configure(validate="key",
                                     validatecommand=(callback, "%P"),
                                     textvariable=self.imput_str_min)
        self.entry_hours.configure(validate="key",
                                   validatecommand=(callback_hours, "%P"),
                                   textvariable=self.imput_str_hour)

        self.imput_str_sec.trace_variable("w", self.entry_set_call)
        self.imput_str_min.trace_variable("w", self.entry_set_call)
        self.imput_str_hour.trace_variable("w", self.entry_set_call)

        btn = Button(root,
                     text="начать отсчет",
                     command=self.clicked_start,
                     relief=RAISED,
                     overrelief=FLAT)
        btn.place(x=10, y=30)
        btn2 = Button(root,
                      text="stop",
                      command=self.stop_countdown,
                      relief=RAISED,
                      overrelief=FLAT)
        btn2.place(x=100, y=30)

    def logic(self, seconds, minutes, hours):
        "responsible for the countdown and turns off the computer"
        seconds = int(seconds)
        minutes = int(minutes)
        hours = int(hours)

        s = time.strftime("%S", time.localtime())
        m = time.strftime("%M", time.localtime())
        h = time.strftime("%H", time.localtime())
        day = time.strftime("%d", time.localtime())

        sec_at_the_end = seconds + int(s)
        min_at_the_end = minutes + int(m)
        hour_at_the_end = hours + int(h)
        day_at_the_end = day
        day_at_the_end = int(day_at_the_end)

        while True:
            if sec_at_the_end > 60:
                sec_at_the_end -= 60
                min_at_the_end += 1
            if min_at_the_end > 60:
                min_at_the_end -= 60
                hour_at_the_end += 1
            if hour_at_the_end > 24:
                hour_at_the_end -= 24
                day_at_the_end += 1
            else:
                break

        old_s = int(s)
        self.stop_flag = True
        self.off_pc_flag = False
        if self.stop_flag == True:
            while True:

                if seconds <= 0 and minutes > 0:
                    seconds = 60
                    minutes -= 1
                if minutes <= 0 and hours > 0:
                    minutes = 60
                    hours -= 1

                s = time.strftime("%S", time.localtime())
                m = time.strftime("%M", time.localtime())
                h = time.strftime("%H", time.localtime())
                day = time.strftime("%d", time.localtime())

                s = int(s)
                m = int(m)
                h = int(h)
                day = int(day)

                if s > old_s:
                    seconds -= 1
                old_s = s

                res = "осталось {}".format(hours) + "час {}".format(
                    minutes) + "мин {}".format(int(seconds)) + "сек"
                self.displaying_counting.after(
                    100, self.displaying_counting.configure(text=res))
                if sec_at_the_end <= s and min_at_the_end <= m and hour_at_the_end <= h and day_at_the_end <= day and self.off_pc_flag == True:
                    self.displaying_counting.configure(text="конец")
                    os.system('shutdown -s')
                    break

                if self.stop_flag == False:
                    self.displaying_counting.configure(text="осталось")
                    break
                self.off_pc_flag = True
                root.update()

    def clicked_start(self):
        "logic buttom and displaying a countdown"
        seconds = self.entry_seconds.get()
        minutes = self.entry_minutes.get()
        hours = self.entry_hours.get()

        if seconds == "":
            seconds = 0
        if minutes == "":
            minutes = 0
        if hours == "":
            hours = 0

        self.logic(seconds, minutes, hours)
        root.update()

    def entry_set_call(self, name, index, mode):
        "prohibits entering more than 2 digits, for hours 4"
        sec = self.imput_str_sec.get()
        min = self.imput_str_min.get()
        hour = self.imput_str_hour.get()
        if len(sec) > 2:
            self.imput_str_sec.set(sec[:-1])
        if len(min) > 2:
            self.imput_str_min.set(min[:-1])
        if len(hour) > 4:
            self.imput_str_hour.set(hour[:-1])

    def only_numeric_input(self, P):
        "prohibits entering not numbers"
        if P.isdigit() or P == "":
            return True
        return False

    def only_numeric_input_less_than_60(self, P):
        "prohibits entering letters and numbers greater than 59. for seconds and minutes"
        if P.isdigit() and int(P) < 60 or P == "":
            return True
        return False

    def stop_countdown(self):
        "no explanation is required"
        self.stop_flag = False
コード例 #9
0
"""
Code illustration: 10.01
Tkinter Trace Variable Demo
Tkinter GUI Application Development Blueprints
"""

from tkinter import Tk, Label, Entry, StringVar
root = Tk()

my_variable = StringVar()


def trace_when_my_variable_written(var, indx, mode):
    print("Traced variable {}".format(my_variable.get()))


my_variable.trace_variable("w", trace_when_my_variable_written)

Label(root, textvariable=my_variable).pack(padx=5, pady=5)
Entry(root, textvariable=my_variable).pack(padx=5, pady=5)

root.mainloop()
コード例 #10
0
class PopupFrame(Toplevel): # ОПРЕДЕЛЯЕМ ГЛАВНОЕ ОКНО для РАБОТЫ С ДАННЫМИ;
    def __init__(self):
        super().__init__(root)
        self.init_frame()
        self.view = appl

    def init_frame(self):
        self.resizable(False, False)

        # СТИЛЬ ШРИФТА;
        font_style = ('Consolas', '12')

        # НИЖНЯЯ ПАНЕЛЬ ОШИБОК;
        self.TB_M_ = Frame(self, bg='#EDF0F5', bd=1, relief=FLAT)
        self.LBL_1 = Label(self.TB_M_)
        self.TBbtn = Frame(self.TB_M_)
        self.LBL_2 = Label(self.TB_M_)
        self.TB_M_.pack(fill=X)
        self.LBL_1.pack(side=BOTTOM)
        self.TBbtn.pack(side=BOTTOM)
        self.LBL_2.pack(side=BOTTOM)

        # МЕТКИ, ПОЛЕ и РАСКРЫВАЮЩИЕСЯ СПИСКИ;
        kino = [ [ 'Кино', 'Сериал', 'Мультфильм' ],
                 [ 'Боевик', 'Драма', 'Комедия', 'Приключения', 'Триллер', 'Ужасы', 'Фантастика', 'Эротика' ],
                 [ 'Просмотрено', 'Непросмотрено' ] ]
        
        self.L_name = Label(self.TB_M_, text='Название')
        self.L_mors = Label(self.TB_M_, text='Кино/Сериал/Мультфильм')
        self.L_janr = Label(self.TB_M_, text='Жанр')
        self.L_view = Label(self.TB_M_, text='Просмотр')
        self.count_chars = Label(self.TB_M_, font=('Consolas', '7'))
        
        self.len_mx = StringVar() # Максимальная длина символов в поле;
        self.name__ = Entry(self.TB_M_, width=41, font=font_style, textvariable=self.len_mx)
        self.len_mx.trace_variable('w', self.def_max_count_chars)
        self.mors__ = Combobox(self.TB_M_, values=kino[0], width=39, font=font_style, state='readonly')
        self.janr__ = Combobox(self.TB_M_, values=kino[1], width=39, font=font_style, state='readonly')
        self.view__ = Combobox(self.TB_M_, values=kino[2], width=39, font=font_style, state='readonly')

        self.L_name.pack(side=TOP)
        self.name__.pack(side=TOP)
        self.L_mors.pack(side=TOP)
        self.mors__.pack(side=TOP)
        self.L_janr.pack(side=TOP)
        self.janr__.pack(side=TOP)
        self.L_view.pack(side=TOP)
        self.view__.pack(side=TOP)

        self.name__.focus()
        self.mors__.current(0)
        self.janr__.current(0)
        self.view__.current(0)

        # КНОПКА для ЗАКРЫТИЯ ОКНА;
        self.btn_can = Button(self.TBbtn, text='Закрыть', width=25, command=self.destroy)
        self.btn_can.pack(side=RIGHT)
        
        # УДЕРЖИВАЕМ НАШЕ ДИАЛОГОВОЕ ОКНО 'НА ВЕРХУ';
        self.grab_set()
        self.focus_set()

    def def_max_count_chars(self, name, index, mode):
        msg = self.len_mx.get()
        self.count_chars['text'] = '%d/%d' % (len(self.name__.get()), 40)
        if len(msg) > 40:
            self.len_mx.set(msg[:-1])
            #self.len_mx.set(msg[0:39])
            print(self.len_mx.set(msg[0:39]))
コード例 #11
0
ファイル: hatch_gui.py プロジェクト: Python3pkg/PyHatch
class _Hatch_GUI(object):
    """
    create a Hatch object from
    hatch_supt and to then create a skeleton python project.
    """
    def __init__(self, master):
        self.initComplete = 0
        self.master = master
        self.x, self.y, self.w, self.h = -1, -1, -1, -1

        # bind master to <Configure> in order to handle any resizing, etc.
        # postpone self.master.bind("<Configure>", self.Master_Configure)
        self.master.bind('<Enter>', self.bindConfigure)

        self.master.title("PyHatch GUI")
        self.master.resizable(0, 0)  # Linux may not respect this

        dialogframe = Frame(master, width=810, height=630)
        dialogframe.pack()

        self.Shortdesc_Labelframe = LabelFrame(
            dialogframe,
            text="Short Description (1-liner)",
            height="90",
            width="718")
        self.Shortdesc_Labelframe.place(x=60, y=127)

        helv20 = tkinter.font.Font(family='Helvetica', size=20, weight='bold')

        self.Buildproject_Button = Button(dialogframe,
                                          text="Build Project",
                                          width="15",
                                          font=helv20)
        self.Buildproject_Button.place(x=492, y=10, width=263, height=68)
        self.Buildproject_Button.bind("<ButtonRelease-1>",
                                      self.Buildproject_Button_Click)

        self.Selectdir_Button = Button(dialogframe,
                                       text="<Select Directory>",
                                       width="15")
        self.Selectdir_Button.place(x=72, y=585, width=672, height=31)
        self.Selectdir_Button.bind("<ButtonRelease-1>",
                                   self.Selectdir_Button_Click)

        self.Author_Entry = Entry(dialogframe, width="15")
        self.Author_Entry.place(x=228, y=424, width=187, height=21)
        self.Author_Entry_StringVar = StringVar()
        self.Author_Entry.configure(textvariable=self.Author_Entry_StringVar)
        self.Author_Entry_StringVar.set("John Doe")

        self.Classname_Entry = Entry(dialogframe, width="15")
        self.Classname_Entry.place(x=192, y=73, width=165, height=21)
        self.Classname_Entry_StringVar = StringVar()
        self.Classname_Entry.configure(
            textvariable=self.Classname_Entry_StringVar)
        self.Classname_Entry_StringVar.set("MyClass")

        self.Copyright_Entry = Entry(dialogframe, width="15")
        self.Copyright_Entry.place(x=228, y=478, width=461, height=21)
        self.Copyright_Entry_StringVar = StringVar()
        self.Copyright_Entry.configure(
            textvariable=self.Copyright_Entry_StringVar)
        self.Copyright_Entry_StringVar.set("Copyright (c) 2013 John Doe")

        self.Email_Entry = Entry(dialogframe, relief="sunken", width="15")
        self.Email_Entry.place(x=228, y=505, width=458, height=21)
        self.Email_Entry_StringVar = StringVar()
        self.Email_Entry.configure(textvariable=self.Email_Entry_StringVar)
        self.Email_Entry_StringVar.set("*****@*****.**")

        self.GithubUserName_Entry = Entry(dialogframe,
                                          relief="sunken",
                                          width="15")
        self.GithubUserName_Entry.place(x=228, y=539, width=458, height=21)
        self.GithubUserName_Entry_StringVar = StringVar()
        self.GithubUserName_Entry.configure(
            textvariable=self.GithubUserName_Entry_StringVar)
        self.GithubUserName_Entry_StringVar.set("github_user_name")

        self.Funcname_Entry = Entry(dialogframe, width="15")
        self.Funcname_Entry.place(x=192, y=100, width=157, height=21)
        self.Funcname_Entry_StringVar = StringVar()
        self.Funcname_Entry.configure(
            textvariable=self.Funcname_Entry_StringVar)
        self.Funcname_Entry_StringVar.set("my_function")

        # License values should be correct format
        LICENSE_OPTIONS = tuple(sorted(CLASSIFIER_D.keys()))
        self.License_Entry_StringVar = StringVar()
        self.License_Entry = OptionMenu(dialogframe,
                                        self.License_Entry_StringVar,
                                        *LICENSE_OPTIONS)
        self.License_Entry.place(x=552, y=424, width=184, height=21)
        self.License_Entry_StringVar.set(LICENSE_OPTIONS[0])

        self.Mainpyname_Entry = Entry(dialogframe, width="15")
        self.Mainpyname_Entry.place(x=168, y=37, width=196, height=21)
        self.Mainpyname_Entry_StringVar = StringVar()
        self.Mainpyname_Entry.configure(
            textvariable=self.Mainpyname_Entry_StringVar)
        self.Mainpyname_Entry_StringVar.set("main.py")

        self.Projname_Entry = Entry(dialogframe, width="15")
        self.Projname_Entry.place(x=168, y=10, width=194, height=21)
        self.Projname_Entry_StringVar = StringVar()
        self.Projname_Entry.configure(
            textvariable=self.Projname_Entry_StringVar)
        self.Projname_Entry_StringVar.set("MyProject")

        self.Shortdesc_Entry = Entry(dialogframe, width="15")
        self.Shortdesc_Entry.place(x=72, y=150, width=688, height=48)
        self.Shortdesc_Entry_StringVar = StringVar()
        self.Shortdesc_Entry.configure(
            textvariable=self.Shortdesc_Entry_StringVar)
        self.Shortdesc_Entry_StringVar.set("My project does this")

        # Status must be correct format
        self.Status_Entry_StringVar = StringVar()
        self.Status_Entry = OptionMenu(dialogframe,
                                       self.Status_Entry_StringVar,
                                       *DEV_STATUS_OPTIONS)
        self.Status_Entry.place(x=228, y=451, width=183, height=21)
        self.Status_Entry_StringVar.set(DEV_STATUS_OPTIONS[0])

        self.Version_Entry = Entry(dialogframe, width="15")
        self.Version_Entry.place(x=552, y=451, width=184, height=21)
        self.Version_Entry_StringVar = StringVar()
        self.Version_Entry.configure(textvariable=self.Version_Entry_StringVar)
        self.Version_Entry_StringVar.set("0.1.1")

        self.Author_Label = Label(dialogframe, text="Author", width="15")
        self.Author_Label.place(x=96, y=424, width=112, height=22)

        self.Classname_Label = Label(dialogframe,
                                     text="Class Name",
                                     width="15")
        self.Classname_Label.place(x=60, y=73, width=112, height=22)

        self.Copyright_Label = Label(dialogframe, text="Copyright", width="15")
        self.Copyright_Label.place(x=96, y=478, width=113, height=23)

        self.Email_Label = Label(dialogframe, text="Email", width="15")
        self.Email_Label.place(x=96, y=505, width=113, height=23)

        self.GithubUserName_Label = Label(dialogframe,
                                          text="GithubUserName",
                                          width="15")
        self.GithubUserName_Label.place(x=96, y=539, width=113, height=23)

        self.Funcname_Label = Label(dialogframe,
                                    text="Function Name",
                                    width="15")
        self.Funcname_Label.place(x=60, y=100, width=112, height=22)

        self.License_Label = Label(dialogframe, text="License", width="15")
        self.License_Label.place(x=432, y=424, width=113, height=23)

        self.Longdesc_Label = Label(dialogframe,
                                    text="Paragraph Description",
                                    width="15")
        self.Longdesc_Label.place(x=216, y=220, width=376, height=22)

        self.Mainpyname_Label = Label(dialogframe,
                                      text="Main Python File",
                                      width="15")
        self.Mainpyname_Label.place(x=48, y=37, width=112, height=22)

        self.Projname_Label = Label(dialogframe,
                                    text="Project Name",
                                    width="15")
        self.Projname_Label.place(x=48, y=10, width=112, height=22)

        self.Selectdir_Label = Label(
            dialogframe,
            text="Select the Directory Below Which to Place Your Project",
            width="15")
        self.Selectdir_Label.place(x=156, y=567, width=536, height=24)

        self.Status_Label = Label(dialogframe, text="Status", width="15")
        self.Status_Label.place(x=96, y=451, width=114, height=24)

        self.Version_Label = Label(dialogframe, text="Version", width="15")
        self.Version_Label.place(x=432, y=451, width=113, height=23)

        self.Isclass_Radiobutton = Radiobutton(dialogframe,
                                               text="Class Project",
                                               value="Class Project",
                                               width="15",
                                               anchor=W)
        self.Isclass_Radiobutton.place(x=320, y=73, width=135, height=27)
        self.RadioGroup1_StringVar = StringVar()
        self.RadioGroup1_StringVar.set("Class Project")
        self.RadioGroup1_StringVar_traceName = \
            self.RadioGroup1_StringVar.trace_variable("w",
                                                      self.RadioGroup1_StringVar_Callback)
        self.Isclass_Radiobutton.configure(variable=self.RadioGroup1_StringVar)

        self.Isfunction_Radiobutton = Radiobutton(dialogframe,
                                                  text="Function Project",
                                                  value="Function Project",
                                                  width="15",
                                                  anchor=W)
        self.Isfunction_Radiobutton.place(x=320, y=100, width=135, height=27)
        self.Isfunction_Radiobutton.configure(
            variable=self.RadioGroup1_StringVar)

        lbframe = Frame(dialogframe)
        self.Text_1_frame = lbframe
        scrollbar = Scrollbar(lbframe, orient=VERTICAL)
        self.Text_1 = Text(lbframe,
                           width="40",
                           height="6",
                           yscrollcommand=scrollbar.set)
        scrollbar.config(command=self.Text_1.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.Text_1.pack(side=LEFT, fill=BOTH, expand=1)

        self.Text_1_frame.place(x=72, y=250, width=665, height=160)
        # >>>>>>insert any user code below this comment for section "top_of_init"

        self.dirname = '<Select Directory>'
        self.Funcname_Entry.config(state=DISABLED)

        h = Hatch(projName='MyProject', mainDefinesClass='N')
        if h.author:
            self.Author_Entry_StringVar.set(h.author)
        if h.proj_license:
            self.License_Entry_StringVar.set(h.proj_license)
        if h.proj_copyright:
            self.Copyright_Entry_StringVar.set(h.proj_copyright)
        if h.email:
            self.Email_Entry_StringVar.set(h.email)
        if h.github_user_name:
            self.GithubUserName_Entry_StringVar.set(h.github_user_name)
        del h

    def build_result_dict(self):
        """Takes user inputs from GUI and builds a dictionary of results"""
        # pylint: disable=W0201
        self.result = {}  # return a dictionary of results

        self.result["author"] = self.Author_Entry_StringVar.get()
        self.result["status"] = self.Status_Entry_StringVar.get()
        self.result["proj_license"] = self.License_Entry_StringVar.get()
        self.result["version"] = self.Version_Entry_StringVar.get()
        self.result["proj_copyright"] = self.Copyright_Entry_StringVar.get()
        self.result["email"] = self.Email_Entry_StringVar.get()
        self.result[
            "github_user_name"] = self.GithubUserName_Entry_StringVar.get()

        self.result["main_py_name"] = self.Mainpyname_Entry_StringVar.get()
        self.result["proj_name"] = self.Projname_Entry_StringVar.get()
        self.result["class_name"] = self.Classname_Entry_StringVar.get()
        self.result["func_name"] = self.Funcname_Entry_StringVar.get()

        self.result["short_desc"] = self.Shortdesc_Entry_StringVar.get()
        self.result["para_desc"] = self.Text_1.get(1.0, END)
        self.result["parent_dir"] = self.dirname

        if self.RadioGroup1_StringVar.get() == "Class Project":
            self.result["is_class_project"] = 'Y'
        else:
            self.result["is_class_project"] = 'N'

    def Buildproject_Button_Click(self, event):
        """When clicked, this method gathers all the user inputs
            and builds the project skeleton in the directory specified.
        """

        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613

        # >>>>>>insert any user code below this comment for section "compID=29"
        # replace, delete, or comment-out the following
        #print "executed method Buildproject_Button_Click"

        if not os.path.isdir(self.dirname):
            ShowError(
                title='Need Parent Directory',
                message=
                'You need to choose a directory in which to place your project.'
            )
        else:
            self.build_result_dict()  # builds self.result dict
            r = self.result

            h = Hatch(projName=r["proj_name"],
                      mainDefinesClass=r["is_class_project"],
                      mainPyFileName=r["main_py_name"],
                      mainClassName=r["class_name"],
                      mainFunctionName=r["func_name"],
                      author=r["author"],
                      proj_copyright=r["proj_copyright"],
                      proj_license=r["proj_license"],
                      version=r["version"],
                      email=r["email"],
                      status=r["status"],
                      github_user_name=r["github_user_name"],
                      simpleDesc=r["short_desc"],
                      longDesc=r["para_desc"])

            call_result = h.save_project_below_this_dir(self.dirname)
            if call_result == 'Success':
                if AskYesNo(title='Exit Dialog',
                            message='Do you want to leave this GUI?'):
                    self.master.destroy()
            else:
                ShowWarning( title='Project Build Error',
                             message='Project did NOT build properly.\n'+\
                            'Warning Message = (%s)'%call_result)

    # return a string containing directory name
    def AskDirectory(self, title='Choose Directory', initialdir="."):
        """Simply wraps the tkinter function of the "same" name."""
        dirname = tkinter.filedialog.askdirectory(parent=self.master,
                                                  initialdir=initialdir,
                                                  title=title)
        return dirname  # <-- string

    def Selectdir_Button_Click(self, event):
        #  I don't care what the exception is, if there's a problem, bail
        #     Also, I want to redefine the dirname attribute here
        # pylint: disable=W0702, W0201

        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        """Selects the directory in which to build project."""

        dirname = self.AskDirectory(title='Choose Directory For Nose Tests',
                                    initialdir='.')
        if dirname:
            try:
                dirname = os.path.abspath(dirname)
            except:
                self.dirname = '<Select Directory>'
                return  # let Alarm force dir selection

            self.dirname = dirname

            self.Selectdir_Button.config(text=self.dirname)

    def RadioGroup1_StringVar_Callback(self, varName, index, mode):
        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        """Responds to changes in RadioGroup1_StringVar."""
        if self.RadioGroup1_StringVar.get() == "Class Project":
            self.Funcname_Entry.config(state=DISABLED)
            self.Classname_Entry.config(state=NORMAL)
        else:
            self.Classname_Entry.config(state=DISABLED)
            self.Funcname_Entry.config(state=NORMAL)

    # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "Master_Configure"
    def bindConfigure(self, event):
        """bindConfigure and Master_Configure help stabilize GUI"""
        #  tkinter requires arguments, but I don't use them
        # pylint: disable=W0613
        if not self.initComplete:
            self.master.bind("<Configure>", self.Master_Configure)
            self.initComplete = 1

    def Master_Configure(self, event):
        """bindConfigure and Master_Configure help stabilize GUI"""
        # >>>>>>insert any user code below this comment for section "Master_Configure"
        # replace, delete, or comment-out the following
        if event.widget != self.master:
            if self.w != -1:
                return
        x = int(self.master.winfo_x())
        y = int(self.master.winfo_y())
        w = int(self.master.winfo_width())
        h = int(self.master.winfo_height())
        if (self.x, self.y, self.w, self.h) == (-1, -1, -1, -1):
            self.x, self.y, self.w, self.h = x, y, w, h

        if self.w != w or self.h != h:
            print("Master reconfigured... make resize adjustments")
            self.w = w
            self.h = h
コード例 #12
0
class CurrencyEditor(Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        self.bvar_modified = BooleanVar()
        self.create_currency_ui()

    def create_currency_ui(self):
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.frm_currency = Frame(self)
        self.frm_currency.rowconfigure(2, weight=1)
        self.frm_currency.columnconfigure(0, weight=1)
        self.frm_currency.grid(padx=10, pady=10, sticky='nsew')

        # Tree Frame
        self.frm_tree = Frame(self.frm_currency)
        self.frm_tree.grid(row=2, sticky='nsew')
        self.frm_tree.rowconfigure(0, weight=1)
        self.frm_tree.columnconfigure(0, weight=1)

        self.tree = EditableTreeview(self.frm_tree,
                                     on_cell_update=self.onCellUpdate)
        scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview)
        scrlx = AutoScrollbar(self.frm_tree,
                              command=self.tree.xview,
                              orient=HORIZONTAL)
        self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set)
        self.tree.grid(row=0, column=0, sticky='nsew')
        scrly.grid(row=0, column=1, sticky='nsew')
        scrlx.grid(row=1, column=0, sticky='nsew')

        self.tree.insert('', 0, text='Exalted Orb', values=('90', '85'))

        frm = Frame(self.frm_currency, relief=SOLID, borderwidth=2)
        frm.columnconfigure(10, weight=1)
        frm.grid(row=1, column=0, pady=(0, 5), sticky='nsew')
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        self.search_var = StringVar()
        self.entry_search = PlaceholderEntry(frm,
                                             'Search..',
                                             style='Default.TEntry',
                                             textvariable=self.search_var)

        self.search_var.trace_variable(
            'w',
            lambda a, b, c: self.tree.search(self.entry_search.get_value()))
        self.entry_search.bind(
            '<Return>',
            lambda event: self.tree.search(self.entry_search.get_value(),
                                           find_next=True))
        # self.btn_currency_search = Button(frm, text='Search', command=lambda event: self.tree_currency.search(self.entry_currency_search.get_value(), find_next=True))
        self.btn_apply = Button(frm, text='Apply', command=self.applyChanges)
        self.btn_reload = Button(
            frm,
            text='Reload',
            command=lambda: self.loadCurrency(force_reload=True))

        self.entry_search.grid(row=2, column=0, pady=5, padx=5)
        # self.btn_currency_search.grid(row=2, column=1, pady=5)
        self.btn_apply.grid(row=2, column=2, pady=5)
        # frm.columnconfigure(3, weight=1)
        self.btn_reload.grid(row=2, column=3, sticky='e', pady=5)

        # Confidence Level
        lbl = Label(frm, text="Confidence level:")
        lbl.grid(row=2, column=10, padx=5, sticky='nse', pady=(3, 5))
        self.lbl_confidence_lvl = lbl
        self.var_confidence_lvl = IntVar()
        self.entry_confidence_lvl = ConfidenceScale(
            frm, variable=self.var_confidence_lvl)
        self.entry_confidence_lvl.grid(row=2, column=11, padx=5, pady=5)
        self.var_confidence_lvl.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl))

        # Tree Config
        tree = self.tree
        tree['columns'] = currencyColumns[1:]
        tree.register_column(
            'Override',
            ColEntry(TooltipEntry(tree),
                     func_validate=_validate_price_override))

        def init_tree_column(col):
            col_name = currencyColumns[0] if col == '#0' else col
            tree.heading(col,
                         text=CurrencyColumn[col_name].value,
                         anchor=W,
                         command=lambda col=col: tree.sort_col(col))
            tree.column(col, width=140, stretch=False)

        for col in ('#0', ) + tree['columns']:
            init_tree_column(col)

        tree.heading('#0', anchor=CENTER)
        tree.column('#0', width=250, stretch=False)
        tree.column(CurrencyColumn.Filler.name, stretch=True)

        tree.heading(CurrencyColumn.Rate.name,
                     command=lambda col=CurrencyColumn.Rate.name: tree.
                     sort_col(col, key=float, default=0))
        tree.heading(CurrencyColumn.EffectiveRate.name,
                     command=lambda col=CurrencyColumn.EffectiveRate.name: tree
                     .sort_col(col, key=float, default=0))
        tree.heading(CurrencyColumn.Override.name,
                     command=lambda col=CurrencyColumn.Override.name: tree.
                     sort_col(col, key=self._price_key))

        self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState())

    def _price_key(self, key):
        if key == '':
            return None  # this means it will be ignored while sorting
        try:
            return cm.compilePrice(key, base_price=0)
        except Exception:
            return 0

    def _updateApplyState(self):
        if self.bvar_modified.get():
            self.btn_apply.config(state=NORMAL)
        else:
            self.btn_apply.config(state=DISABLED)

    def loadCurrency(self, force_reload=False):
        if not cm.initialized:
            return
        if not force_reload and self.bvar_modified.get():
            return

        self.var_confidence_lvl.set(cm.confidence_level)

        tree = self.tree
        tree.clear()

        table = {}
        for curr in cm.shorts:
            effective_rate = cm.crates.get(curr, '0')
            table[curr] = (_to_display_rate(cm.rates.get(curr, '')),
                           cm.overrides.get(curr, ''),
                           _to_display_rate(effective_rate))

        for curr in table:
            tree.insert('', END, '', text=curr, values=table[curr])

        tree.sort_col(CurrencyColumn.EffectiveRate.name, key=float, default=0)

        self.bvar_modified.set(False)

    def applyChanges(self, event=None):
        if not self.bvar_modified.get() or not cm.initialized:
            return

        overrides = {}

        for iid in self.tree.get_children():
            #TODO: hide #0 col and move names to a value column
            currency_name_col = '#0'  # CurrencyColumn.Currency.name
            # id = self.tree.set(iid, currency_name_col)
            id = self.tree.item(iid, 'text')
            override = self.tree.set(iid, CurrencyColumn.Override.name)

            if override:
                overrides[id] = override

        # ids = set([self.tree.set(iid, currency_name_col) for iid in self.tree.get_children()])
        ids = set(
            [self.tree.item(iid, 'text') for iid in self.tree.get_children()])

        # preserve unhandled ids configuration
        for key in (set(cm.overrides) - ids):
            overrides[key] = cm.overrides[key]

        cm.confidence_level = self.entry_confidence_lvl.get()

        try:
            cm.compile(overrides=overrides)
            if fm.initialized:
                threading.Thread(target=fm.compileFilters).start()
            self.bvar_modified.set(False)
        except AppException as e:
            messagebox.showerror('Update error',
                                 e,
                                 parent=self.winfo_toplevel())
        except Exception as e:
            logexception()
            messagebox.showerror(
                'Update error',
                'Failed to apply changes, unexpected error:\n{}'.format(e),
                parent=self.winfo_toplevel())

    def onCellUpdate(self, iid, col, old, new):
        if not self.bvar_modified.get() and old != new:
            self.bvar_modified.set(True)

    def on_entry_change(self, entry):
        self.bvar_modified.set(True)
コード例 #13
0
class PricesEditor(Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)

        self.create_prices_ui()
        self.initial_values = {}
        self.table_modified = False

    def create_prices_ui(self):
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.frm_prices = Frame(self)
        self.frm_prices.rowconfigure(2, weight=1)
        self.frm_prices.columnconfigure(0, weight=1)
        self.frm_prices.grid(padx=10, pady=10, sticky='nsew')

        # Button Frame
        frm_btns = Frame(self.frm_prices, relief=SOLID, borderwidth=2)
        frm_btns.grid(row=0, column=0, pady=(0, 5), sticky='nsew')
        frm_btns.columnconfigure(10, weight=1)
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        self.search_var = StringVar()
        self.entry_search = PlaceholderEntry(frm_btns,
                                             'Search..',
                                             style='Default.TEntry',
                                             textvariable=self.search_var)

        self.search_var.trace_variable(
            'w',
            lambda a, b, c: self.tree.search(self.entry_search.get_value()))
        self.entry_search.bind(
            '<Return>',
            lambda event: self.tree.search(self.entry_search.get_value(),
                                           find_next=True))
        self.btn_apply = Button(frm_btns,
                                text='Apply',
                                command=self.applyChanges)
        self.btn_reload = Button(
            frm_btns,
            text='Reload',
            command=lambda: self.loadPrices(force_reload=True))

        self.entry_search.grid(row=2, column=0, pady=5, padx=5)
        self.btn_apply.grid(row=2, column=2, pady=5)
        # frm.columnconfigure(3, weight=1)
        self.btn_reload.grid(row=2, column=3, sticky='e', pady=5)

        self.var_advanced = BooleanVar(False)
        self.var_advanced.trace_variable(
            'w', lambda a, b, c: self._on_view_option_change())
        self.cb_advanced = Checkbutton(frm_btns,
                                       text='Advanced',
                                       variable=self.var_advanced)
        self.cb_advanced.grid(row=2, column=10, sticky='e', padx=10)

        frm_border = Frame(self.frm_prices, relief=SOLID, borderwidth=2)
        frm_border.grid(row=2, column=0, sticky='nsew')
        frm_border.rowconfigure(2, weight=1)
        frm_border.columnconfigure(0, weight=1)
        # Tree Frame
        self.frm_tree = Frame(frm_border)
        self.frm_tree.grid(row=2, column=0, sticky='nsew', padx=5, pady=(0, 0))
        self.frm_tree.rowconfigure(0, weight=1)
        self.frm_tree.columnconfigure(0, weight=1)

        self.tree = EditableTreeview(self.frm_tree,
                                     on_cell_update=self.onCellUpdate)
        scrly = AutoScrollbar(self.frm_tree, command=self.tree.yview)
        scrlx = AutoScrollbar(self.frm_tree,
                              command=self.tree.xview,
                              orient=HORIZONTAL)
        self.tree.config(yscrollcommand=scrly.set, xscrollcommand=scrlx.set)
        self.tree.grid(row=0, column=0, sticky='nsew')
        scrly.grid(row=0, column=1, sticky='nsew')
        scrlx.grid(row=1, column=0, sticky='nsew')

        # Button Frame
        frm = Frame(frm_border)  #, relief=SOLID, borderwidth=1)
        # frm = Frame(self.frm_prices)
        frm.grid(row=0, column=0, sticky='nsew')
        # self.entry_currency = \
        #     Combobox_Autocomplete(frm, list_of_items=['one of many currencies'], startswith_match=False)

        lbl = Label(frm, text='Item value threshold:')
        lbl.grid(row=0, column=0, padx=5, pady=5, sticky='w')
        self.var_threshold = StringVar()
        self.entry_threshold = TooltipEntry(frm,
                                            textvariable=self.var_threshold)
        self.entry_threshold.bind(
            '<FocusOut>', lambda event: self._validate_threshold_entry())
        self.entry_threshold.grid(row=0, column=1, padx=5, pady=5)
        self.var_threshold.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_threshold))

        lbl = Label(frm, text='Budget:')
        lbl.grid(row=0, column=2, padx=5, pady=5)
        self.var_budget = StringVar()
        self.entry_budget = TooltipEntry(frm, textvariable=self.var_budget)
        self.entry_budget.bind('<FocusOut>',
                               lambda event: self._validate_budget_entry())
        self.entry_budget.grid(row=0, column=3, padx=5, pady=5)
        self.var_budget.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_budget))

        lbl = Label(frm, text='Minimum price:')
        lbl.grid(row=0, column=4, padx=5, pady=5)
        self.var_min_price = StringVar()
        self.entry_min_price = TooltipEntry(frm,
                                            textvariable=self.var_min_price)
        self.entry_min_price.bind(
            '<FocusOut>', lambda event: self._validate_min_price_entry())
        self.entry_min_price.grid(row=0, column=5, padx=5, pady=5)
        self.var_min_price.trace(
            'w', lambda a, b, c: self.on_entry_change(self.entry_min_price))

        lbl = Label(frm, text='Default filter override:')
        lbl.grid(row=0, column=6, padx=5, pady=5)
        self.lbl_fprice_override = lbl
        self.var_fprice_override = StringVar()
        self.entry_fprice_override = TooltipEntry(
            frm, textvariable=self.var_fprice_override)
        self.entry_fprice_override.bind(
            '<FocusOut>', lambda event: self._validate_fprice_override_entry())
        self.entry_fprice_override.grid(row=0, column=7, padx=5, pady=5)
        self.var_fprice_override.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_fprice_override))

        # Advanced

        lbl = Label(frm, text='Default item value override:')
        lbl.grid(row=1, column=0, padx=5, pady=(2, 5), sticky='w')
        self.lbl_price_override = lbl
        self.var_price_override = StringVar()
        self.entry_price_override = TooltipEntry(
            frm, textvariable=self.var_price_override)
        self.entry_price_override.bind(
            '<FocusOut>', lambda event: self._validate_price_override_entry())
        self.entry_price_override.grid(row=1, column=1, padx=5, pady=(2, 5))
        self.var_price_override.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_price_override))

        # Confidence Level
        lbl = Label(frm, text="Confidence level:")
        lbl.grid(row=1, column=2, padx=5, pady=(2, 5), sticky='w')
        self.lbl_confidence_lvl = lbl
        self.var_confidence_lvl = IntVar()
        self.entry_confidence_lvl = ConfidenceScale(
            frm, variable=self.var_confidence_lvl)
        self.entry_confidence_lvl.grid(row=1, column=3, padx=5, pady=(2, 5))
        self.var_confidence_lvl.trace(
            'w',
            lambda a, b, c: self.on_entry_change(self.entry_confidence_lvl))

        self.var_5l_filters = BooleanVar(False)
        self.cb_5l_filters = VarCheckbutton(frm,
                                            text='Enable 5L filters',
                                            variable=self.var_5l_filters)
        self.cb_5l_filters.var = self.var_5l_filters
        self.cb_5l_filters.grid(row=1,
                                column=4,
                                padx=5,
                                pady=(2, 5),
                                columnspan=1)
        self.var_5l_filters.trace_variable(
            'w', lambda a, b, c: self.on_entry_change(self.cb_5l_filters))

        # Tree Config
        tree = self.tree

        def init_tree_column(col):
            col_name = pricesColumns[0] if col == '#0' else col
            tree.heading(col,
                         text=PricesColumn[col_name].value,
                         anchor=W,
                         command=lambda col=col: tree.sort_col(col))
            tree.column(col, width=140, stretch=False)

        # self.tree['columns'] = ('ID', 'Item Price', 'Override', 'Filter Price', 'Filter Override', 'Effective Filter Price', 'Filter State Override', '')
        self.tree['columns'] = pricesColumns[1:]

        self.tree.register_column(
            PricesColumn.Override.name,
            ColEntry(TooltipEntry(self.tree),
                     func_validate=_validate_price_override))
        self.tree.register_column(
            PricesColumn.FilterOverride.name,
            ColEntry(TooltipEntry(self.tree),
                     func_validate=_validate_price_override))
        self.tree.register_column(
            PricesColumn.FilterStateOverride.name,
            ColEntry(Combobox(self.tree,
                              values=filterStateOptions,
                              state=READONLY),
                     accept_events=('<<ComboboxSelected>>', '<Return>')))

        for col in (('#0', ) + tree['columns']):
            init_tree_column(col)

        tree.heading('#0', anchor=CENTER)
        tree.column('#0', width=200, stretch=False)
        tree.column(PricesColumn.Filler.name, stretch=True)

        tree.heading(PricesColumn.ItemPrice.name,
                     command=lambda col=PricesColumn.ItemPrice.name: tree.
                     sort_col(col, key=self._price_key))
        tree.heading(PricesColumn.Override.name,
                     command=lambda col=PricesColumn.Override.name: tree.
                     sort_col(col, key=self._price_key))
        tree.heading(PricesColumn.FilterOverride.name,
                     command=lambda col=PricesColumn.FilterOverride.name: tree.
                     sort_col(col, key=self._price_key))

        tree.heading(PricesColumn.FilterPrice.name,
                     command=lambda col=PricesColumn.FilterPrice.name: tree.
                     sort_col(col, key=self._rate_key, default=0))
        tree.heading(PricesColumn.EffectiveFilterPrice.name,
                     command=lambda col=PricesColumn.EffectiveFilterPrice.name:
                     tree.sort_col(col, key=self._rate_key, default=0))

        self.bvar_modified = BooleanVar()
        self.bvar_modified.trace('w', lambda a, b, c: self._updateApplyState())
        self.bvar_modified.set(False)
        self.var_advanced.set(False)

    def _rate_key(self, key):
        if key == 'N/A':
            return 0
        return float(key)

    def _price_key(self, key):
        if key == '':
            return None  # this means it will be ignored while sorting
        try:
            return cm.compilePrice(key, base_price=0)
        except Exception:
            return 0

    def on_entry_change(self, entry):
        val = entry.get()

        if self.initial_values[entry] != val:
            self.bvar_modified.set(True)

    # def on_price_entry_focusout(self, widget):
    #     valid = _validate_price(widget, accept_empty=False)
    #     if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get():
    #         self.bvar_modified.set(True)
    #     return valid
    #
    # def on_override_entry_focusout(self, widget):
    #     valid = _validate_price_override(widget, accept_empty=False)
    #     if valid and not self.bvar_modified.get() and self.initial_values[widget] != widget.get():
    #         self.bvar_modified.set(True)
    #     return valid

    def _validate_threshold_entry(self):
        return _validate_price(self.entry_threshold, accept_empty=False)

    def _validate_budget_entry(self):
        return _validate_price(self.entry_budget, accept_empty=True)

    def _validate_min_price_entry(self):
        return _validate_price(self.entry_min_price, accept_empty=True)

    def _validate_price_override_entry(self):
        return _validate_price_override(self.entry_price_override,
                                        accept_empty=False)

    def _validate_fprice_override_entry(self):
        return _validate_price_override(self.entry_fprice_override,
                                        accept_empty=False)

    def _update_modified(self):
        modified = any(entry.get() != self.initial_values[entry]
                       for entry in self.initial_values) or self.table_modified

        self.bvar_modified.set(modified)

    def _updateApplyState(self):
        if self.bvar_modified.get():
            self.btn_apply.config(state=NORMAL)
        else:
            self.btn_apply.config(state=DISABLED)

    def _validateForm(self):
        if not self._validate_threshold_entry():
            return False
        if not self._validate_budget_entry():
            return False
        if not self._validate_min_price_entry():
            return False
        if not self._validate_price_override_entry():
            return False
        if not self._validate_fprice_override_entry():
            return False
        return True

    def applyChanges(self, event=None):
        if not self.bvar_modified.get() or not fm.initialized:
            return
        if not self._validateForm():
            return

        price_threshold = self.entry_threshold.get()
        default_price_override = self.entry_price_override.get()
        default_fprice_override = self.entry_fprice_override.get()
        budget = self.entry_budget.get()
        min_price = self.entry_min_price.get()
        confidence_lvl = self.entry_confidence_lvl.get(
        ) or fm.DEFAULT_CONFIDENCE_LEVEL
        enable_5l_filters = self.var_5l_filters.get()

        price_overrides = {}
        filter_price_overrides = {}
        filter_state_overrides = {}

        for iid in self.tree.get_children():
            id = self.tree.set(iid, PricesColumn.ID.name)
            iprice = self.tree.set(iid, PricesColumn.Override.name)

            if iprice:
                price_overrides[id] = iprice

            fprice = self.tree.set(iid, PricesColumn.FilterOverride.name)
            if fprice:
                filter_price_overrides[id] = fprice

            fstate = self.tree.set(iid, PricesColumn.FilterStateOverride.name)
            try:
                filter_state_overrides[id] = FilterStateOption[fstate].value
            except KeyError:
                pass

        ids = set([
            self.tree.set(iid, PricesColumn.ID.name)
            for iid in self.tree.get_children()
        ])

        # preserve unhandled ids configuration
        for key in (set(fm.price_overrides) - ids):
            price_overrides[key] = fm.price_overrides[key]

        for key in (set(fm.filter_price_overrides) - ids):
            filter_price_overrides[key] = fm.filter_price_overrides[key]

        for key in (set(fm.filter_state_overrides) - ids):
            filter_state_overrides[key] = fm.filter_state_overrides[key]

        try:
            fm.updateConfig(default_price_override, default_fprice_override,
                            price_threshold, budget, min_price,
                            price_overrides,
                            filter_price_overrides, filter_state_overrides,
                            int(confidence_lvl), enable_5l_filters)
        except AppException as e:
            messagebox.showerror(
                'Validation error',
                'Failed to update configuration:\n{}'.format(e),
                parent=self.winfo_toplevel())
        except Exception as e:
            logexception()
            messagebox.showerror(
                'Update error',
                'Failed to apply changes, unexpected error:\n{}'.format(e),
                parent=self.winfo_toplevel())
        else:
            # SHOULD always work since config is valid, main console will report any failures
            # background thread because schema validating takes a bit of time
            threading.Thread(target=fm.compileFilters).start()
            self._initFormState()

    def loadPrices(self, force_reload=False):
        if not cm.initialized or not fm.initialized:
            return

        if not force_reload:
            self._update_modified()  # in case of reverted changes
            if self.bvar_modified.get():  # dont interrupt user changes
                return

        tree = self.tree
        tree.clear()

        table = {}
        for fltr in fm.autoFilters:
            # effective_rate = cm.crates.get(curr, '')
            # if effective_rate != '':
            #     effective_rate = round(effective_rate, 3)

            fid = fltr.id

            fstate_override = fm.filter_state_overrides.get(fid, '')
            try:
                fstate_override = FilterStateOption(fstate_override).name
            except ValueError:
                fstate_override = ''

            table[fid] = (fltr.title, fid, fm.item_prices[fid],
                          fm.price_overrides.get(fid, ''),
                          _to_display_rate(
                              fm.compiled_item_prices.get(fid, 'N/A')),
                          fm.filter_price_overrides.get(fid, ''),
                          _to_display_rate(
                              fm.compiled_filter_prices.get(fid, 'N/A')),
                          fstate_override)

        for fid in table:
            tree.insert('', END, '', text=table[fid][0], values=table[fid][1:])

        # tree.sort_by('#0', descending=True)
        tree.sort_col('#0', reverse=False)

        self._initFormState()

    # def onItemPriceUpdate(self, iid, col, old, new):
    #     print('IPrice update: iid {}, col {}'.format(iid, col))

    def onCellUpdate(self, iid, col, old, new):
        if old != new:
            self.table_modified = True
            self.bvar_modified.set(True)
            # self._update_modified()

    def _initFormState(self):
        self.table_modified = False
        self.initial_values[self.entry_threshold] = fm.price_threshold
        self.initial_values[self.entry_budget] = fm.budget
        self.initial_values[self.entry_min_price] = fm.default_min_price
        self.initial_values[
            self.entry_price_override] = fm.default_price_override
        self.initial_values[
            self.entry_fprice_override] = fm.default_fprice_override
        self.initial_values[self.entry_confidence_lvl] = fm.confidence_level
        self.initial_values[self.cb_5l_filters] = fm.enable_5l_filters

        self.var_threshold.set(fm.price_threshold)
        self.var_budget.set(fm.budget)
        self.var_min_price.set(fm.default_min_price)
        self.var_price_override.set(fm.default_price_override)
        self.var_fprice_override.set(fm.default_fprice_override)
        self.var_confidence_lvl.set(fm.confidence_level)
        self.var_5l_filters.set(fm.enable_5l_filters)

        self.bvar_modified.set(False)

    def _on_view_option_change(self):
        advanced_widgets = [
            self.entry_price_override, self.lbl_price_override,
            self.lbl_confidence_lvl, self.entry_confidence_lvl,
            self.cb_5l_filters
        ]
        if not self.var_advanced.get():
            for w in advanced_widgets:
                w.grid_remove()
            self.tree.config(displaycolumn=[
                PricesColumn.FilterPrice.name, PricesColumn.FilterOverride.
                name, PricesColumn.EffectiveFilterPrice.name,
                PricesColumn.Filler.name
            ])
        else:
            for w in advanced_widgets:
                w.grid()
            self.tree.config(displaycolumn='#all')
        self.tree.on_entry_close()
コード例 #14
0
ファイル: edit.py プロジェクト: aeromorrison/etm-tk
class SimpleEditor(Toplevel):

    def __init__(self, parent=None, master=None, file=None, line=None, newhsh=None, rephsh=None, options=None, title=None, start=None, modified=False):
        """
        If file is given, open file for editing.
        Otherwise, we are creating a new item and/or replacing an item
        mode:
          1: new: edit newhsh, replace none
          2: replace: edit and replace rephsh
          3: new and replace: edit newhsh, replace rephsh

        :param parent:
        :param file: path to file to be edited
        """
        # self.frame = frame = Frame(parent)
        if master is None:
            master = parent
        self.master = master
        Toplevel.__init__(self, master)
        self.minsize(400, 300)
        self.geometry('500x200')
        self.transient(parent)
        self.configure(background=BGCOLOR, highlightbackground=BGCOLOR)
        self.parent = parent
        self.loop = parent.loop
        self.messages = self.loop.messages
        self.messages = []
        self.mode = None
        self.changed = False

        self.scrollbar = None
        self.listbox = None
        self.autocompletewindow = None
        self.line = None
        self.match = None

        self.file = file
        self.initfile = None
        self.fileinfo = None
        self.repinfo = None
        self.title = title
        self.edithsh = {}
        self.newhsh = newhsh
        self.rephsh = rephsh
        self.value = ''
        self.options = options
        self.tkfixedfont = tkFont.nametofont("TkFixedFont")
        self.tkfixedfont.configure(size=self.options['fontsize_fixed'])
        # self.text_value.trace_variable("w", self.setSaveStatus)
        frame = Frame(self, bd=0, relief=FLAT)
        frame.pack(side="bottom", fill=X, padx=4, pady=0)
        frame.configure(background=BGCOLOR, highlightbackground=BGCOLOR)

        # quit with a warning prompt if modified
        Button(frame, text=_("Cancel"), highlightbackground=BGCOLOR, pady=2, command=self.quit).pack(side=LEFT, padx=4)
        self.bind("<Escape>", self.quit)

        l, c = commandShortcut('q')
        self.bind(c, self.quit)
        self.bind("<Escape>", self.cancel)

        # finish will evaluate the item entry and, if repeating, show reps
        finish = Button(frame, text=FINISH, highlightbackground=BGCOLOR, command=self.onFinish, pady=2)
        # self.bind("<Control-w>", self.onCheck)
        self.bind("<Control-w>", self.onFinish)

        finish.pack(side=RIGHT, padx=4)

        # find
        Button(frame, text='x', command=self.clearFind, highlightbackground=BGCOLOR, padx=8, pady=2).pack(side=LEFT, padx=0)
        self.find_text = StringVar(frame)
        self.e = Entry(frame, textvariable=self.find_text, width=10, highlightbackground=BGCOLOR)
        self.e.pack(side=LEFT, padx=0, expand=1, fill=X)
        self.e.bind("<Return>", self.onFind)
        Button(frame, text='>', command=self.onFind, highlightbackground=BGCOLOR, padx=8, pady=2).pack(side=LEFT, padx=0)

        text = Text(self, wrap="word", bd=2, relief="sunken", padx=3, pady=2, font=self.tkfixedfont, undo=True, width=70)
        text.configure(highlightthickness=0)
        text.tag_configure(FOUND, background="lightskyblue")

        text.pack(side="bottom", padx=4, pady=3, expand=1, fill=BOTH)
        self.text = text

        self.completions = self.loop.options['completions']

        if start is not None:
            # we have the starting text but will need a new uid
            text = start
            if self.rephsh is None:
                self.edithsh = {}
                self.mode = 1
                self.title = CREATENEW
            else:
                self.edithsh = self.rephsh
                self.mode = 2
                self.title = EDITEXISTING

        elif file is not None:
            # we're editing a file - if it's a data file we will add uid's
            # as necessary when saving
            self.mode = 'file'
            if not os.path.isfile(file):
                logger.warn('could not open: {0}'.format(file))
                text = ""
            else:
                with codecs.open(file, 'r', self.options['encoding']['file']) as f:
                    text = f.read()
        else:
            # we are creating a new item and/or replacing an item
            # mode:
            #   1: new
            #   2: replace
            #   3: new and replace
            initfile = ensureMonthly(options=self.options, date=datetime.now())
            # set the mode
            if newhsh is None and rephsh is None:
                # we are creating a new item from scratch and will need
                # a new uid
                self.mode = 1
                self.title = CREATENEW
                self.edithsh = {}
                self.edithsh['i'] = uniqueId()
                text = ''
            elif rephsh is None:  # newhsh is not None
                # we are creating a new item as a copy and will need
                # a new uid
                self.mode = 1
                self.title = CREATENEW
                self.edithsh = self.newhsh
                self.edithsh['i'] = uniqueId()
                if ('fileinfo' in newhsh and newhsh['fileinfo']):
                    initfile = newhsh['fileinfo'][0]
                text, msg = hsh2str(self.edithsh, self.options)
            elif newhsh is None:
                # we are editing and replacing rephsh - no file prompt
                # using existing uid
                self.title = EDITEXISTING
                self.mode = 2
                # self.repinfo = rephsh['fileinfo']
                self.edithsh = self.rephsh
                text, msg = hsh2str(self.edithsh, self.options)
            else:  # neither is None
                # we are changing some instances of a repeating item
                # we will be writing but not editing rephsh using its fileinfo
                # and its existing uid
                # we will be editing and saving newhsh using self.initfile
                # we will need a new uid for newhsh
                self.mode = 3
                self.title = CREATENEW
                self.edithsh = self.newhsh
                self.edithsh['i'] = uniqueId()
                if 'fileinfo' in newhsh and newhsh['fileinfo'][0]:
                    initfile = self.newhsh['fileinfo'][0]
                text, msg = hsh2str(self.edithsh, self.options)
            self.initfile = initfile
            logger.debug('mode: {0}; initfile: {1}; edit: {2}'.format(self.mode, self.initfile, self.edithsh))
        if self.title is not None:
            self.wm_title(self.title)
        self.settext(text)

        # clear the undo buffer
        if not modified:
            self.text.edit_reset()
            self.setmodified(False)
        self.text.bind('<<Modified>>', self.updateSaveStatus)

        self.text.focus_set()
        self.protocol("WM_DELETE_WINDOW", self.quit)
        if parent:
            self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
                                      parent.winfo_rooty() + 50))
        self.configure(background=BGCOLOR)
        l, c = commandShortcut('f')
        self.bind(c, lambda e: self.e.focus_set())
        l, c = commandShortcut('g')
        self.bind(c, lambda e: self.onFind())
        if start:
            self.text.tag_add("sel", "1.1", "1.{0}".format(len(start)))
            self.text.mark_set(INSERT, END)
        elif line:
            self.text.mark_set(INSERT, "{0}.0".format(line))
        else:
            self.text.mark_set(INSERT, END)
        self.text.see(INSERT)
        # l, c = commandShortcut('/')
        logger.debug("/: {0}, {1}".format(l, c))
        self.text.bind("<Control-space>", self.showCompletions)
        self.grab_set()
        self.wait_window(self)

    def settext(self, text=''):
        self.text.delete('1.0', END)
        self.text.insert(INSERT, text)
        self.text.mark_set(INSERT, '1.0')
        self.text.focus()
        logger.debug("modified: {0}".format(self.checkmodified()))

    def gettext(self):
        return self.text.get('1.0', END + '-1c')

    def setCompletions(self, *args):
        match = self.filterValue.get()
        self.matches = matches = [x for x in self.completions if x and x.lower().startswith(match.lower())]
        self.listbox.delete(0, END)
        for item in matches:
            self.listbox.insert(END, item)
        self.listbox.select_set(0)
        self.listbox.see(0)
        self.fltr.focus_set()

    def showCompletions(self, e=None):
        if not self.completions:
            return "break"
        if self.autocompletewindow:
            return "break"
        line = self.text.get("insert linestart", INSERT)
        m = completion_regex.search(line)
        if not m:
            logger.debug("no match in {0}".format(line))
            return "break"

        # set self.match here since it determines the characters to be replaced
        self.match = match = m.groups()[0]
        logger.debug("found match '{0}' in line '{1}'".format(match, line))

        self.autocompletewindow = acw = Toplevel(master=self.text)
        acw.geometry("+%d+%d" % (self.text.winfo_rootx() + 50, self.text.winfo_rooty() + 50))

        self.autocompletewindow.wm_attributes("-topmost", 1)

        self.filterValue = StringVar(self)
        self.filterValue.set(match)
        self.filterValue.trace_variable("w", self.setCompletions)
        self.fltr = Entry(acw, textvariable=self.filterValue)
        self.fltr.pack(side="top", fill="x")
        self.fltr.icursor(END)

        self.listbox = listbox = Listbox(acw, exportselection=False, width=self.loop.options['completions_width'])
        listbox.pack(side="bottom", fill=BOTH, expand=True)

        self.autocompletewindow.bind("<Double-1>", self.completionSelected)
        self.autocompletewindow.bind("<Return>", self.completionSelected)
        self.autocompletewindow.bind("<Escape>", self.hideCompletions)
        self.autocompletewindow.bind("<Up>", self.cursorUp)
        self.autocompletewindow.bind("<Down>", self.cursorDown)
        self.fltr.bind("<Up>", self.cursorUp)
        self.fltr.bind("<Down>", self.cursorDown)
        self.setCompletions()

    def is_active(self):
        return self.autocompletewindow is not None

    def hideCompletions(self, e=None):
        if not self.is_active():
            return
        # destroy widgets
        self.listbox.destroy()
        self.listbox = None
        self.autocompletewindow.destroy()
        self.autocompletewindow = None

    def completionSelected(self, event):
        # Put the selected completion in the text, and close the list
        modified = False
        if self.matches:
            cursel = self.matches[int(self.listbox.curselection()[0])]
        else:
            cursel = self.filterValue.get()
            modified = True

        start = "insert-{0}c".format(len(self.match))
        end = "insert-1c wordend"
        logger.debug("cursel: {0}; match: {1}; start: {2}; insert: {3}".format(
            cursel, self.match, start, INSERT))
        self.text.delete(start, end)
        self.text.insert(INSERT, cursel)
        self.hideCompletions()
        if modified:
            file = FileChoice(self, "append completion to file", prefix=self.loop.options['etmdir'], list=self.loop.options['completion_files']).returnValue()
            if (file and os.path.isfile(file)):
                with codecs.open(file, 'r', self.loop.options['encoding']['file']) as fo:
                    lines = fo.readlines()
                lines.append(cursel)
                lines.sort()
                content = "\n".join([x.strip() for x in lines if x.strip()])
                with codecs.open(file, 'w', self.loop.options['encoding']['file']) as fo:
                    fo.write(content)
            self.completions.append(cursel)
            self.completions.sort()

    def cursorUp(self, event=None):
        cursel = int(self.listbox.curselection()[0])
        # newsel = max(0, cursel=1)
        newsel = max(0, cursel - 1)
        self.listbox.select_clear(cursel)
        self.listbox.select_set(newsel)
        self.listbox.see(newsel)
        return "break"

    def cursorDown(self, event=None):
        cursel = int(self.listbox.curselection()[0])
        newsel = min(len(self.matches) - 1, cursel + 1)
        self.listbox.select_clear(cursel)
        self.listbox.select_set(newsel)
        self.listbox.see(newsel)
        return "break"

    def setmodified(self, bool):
        if bool is not None:
            self.text.edit_modified(bool)

    def checkmodified(self):
        return self.text.edit_modified()

    def updateSaveStatus(self, event=None):
        # Called by <<Modified>>
        if self.checkmodified():
            self.wm_title("{0} (modified)".format(self.title))
        else:
            self.wm_title("{0}".format(self.title))

    def onFinish(self, e=None):
        if self.mode == 'file':
            self.onSave()
        else:
            self.onCheck()

    def onSave(self, e=None, v=0):
        if not self.checkmodified():
            self.quit()
        elif self.file is not None:
            # we are editing a file
            alltext = self.gettext()
            self.loop.safe_save(self.file, alltext)
            self.setmodified(False)
            self.changed = True
            self.quit()
        else:
            # we are editing an item
            if self.mode in [1, 3]:  # new
                dir = self.options['datadir']
                if 's' in self.edithsh and self.edithsh['s']:
                    dt = self.edithsh['s']
                    file = ensureMonthly(self.options, dt.date())
                else:
                    dt = None
                    file = ensureMonthly(self.options)
                dir, initfile = os.path.split(file)
                # we need a filename for the new item
                # make datadir the root
                prefix, tuples = getFileTuples(self.options['datadir'], include=r'*.txt')
                if v == 2:
                    filename = file
                else:
                    ret = FileChoice(self, "etm data files", prefix=prefix, list=tuples, start=file).returnValue()
                    if not ret:
                        return False
                    filename = os.path.join(prefix, ret)
                if not os.path.isfile(filename):
                    return False
                filename = os.path.normpath(filename)
                logger.debug('saving to: {0}'.format(filename))
                self.text.focus_set()
            logger.debug('edithsh: {0}'.format(self.edithsh))
            if self.mode == 1:
                if self.loop.append_item(self.edithsh, filename):
                    logger.debug('append mode: {0}'.format(self.mode))
            elif self.mode == 2:
                if self.loop.replace_item(self.edithsh):
                    logger.debug('replace mode: {0}'.format(self.mode))
            else:  # self.mode == 3
                if self.loop.append_item(self.edithsh, filename):
                    logger.debug('append mode: {0}'.format(self.mode))
                if self.loop.replace_item(self.rephsh):
                    logger.debug('replace mode: {0}'.format(self.mode))

            # update the return value so that when it is not null then modified
            # is false and when modified is true then it is null
            self.setmodified(False)
            self.changed = True
            self.quit()
            return "break"

    def onCheck(self, event=None, showreps=True, showres=True):
        # only called when editing an item and finish is pressed
        self.loop.messages = []
        text = self.gettext()
        msg = []
        reps = []
        if text.startswith("BEGIN:VCALENDAR"):
            text = import_ical(vcal=text)
        logger.debug("text: {0} '{01}'".format(type(text), text))
        if self.edithsh and 'i' in self.edithsh:
            uid = self.edithsh['i']
        else:
            uid = None
        hsh, msg = str2hsh(text, options=self.options, uid=uid)

        if not msg:
            # we have a good hsh
            pre = post = ""
            if 'r' in hsh:
                pre = _("Repeating ")
            elif 's' in hsh:
                dt = hsh['s']
                if not dt.hour and not dt.minute:
                    dtfmt = fmt_date(dt, short=True)
                else:
                    dtfmt = fmt_shortdatetime(hsh['s'], self.options)
                post = _(" scheduled for {0}").format(dtfmt)
            else:  # unscheduled
                pre = _("Unscheduled ")

            prompt = "{0}{1}{2}".format(pre, type2Text[hsh['itemtype']], post)

            if self.edithsh and 'fileinfo' in self.edithsh:
                fileinfo = self.edithsh['fileinfo']
                self.edithsh = hsh
                self.edithsh['fileinfo'] = fileinfo
            else:
                # we have a new item without fileinfo
                self.edithsh = hsh
            # update missing fields
            logger.debug('calling hsh2str with {0}'.format(hsh))
            str, msg = hsh2str(hsh, options=self.options)

        self.loop.messages.extend(msg)
        if self.loop.messages:
            messages = "{0}".format("\n".join(self.loop.messages))
            logger.debug("messages: {0}".format(messages))
            self.messageWindow(MESSAGES, messages, opts=self.options)
            return False

        logger.debug("back from hsh2str with: {0}".format(str))
        if 'r' in hsh:
            showing_all, reps = get_reps(self.loop.options['bef'], hsh)
            if reps:
                if showreps:
                    try:
                        repsfmt = [unicode(x.strftime(rrulefmt)) for x in reps]
                    except:
                        repsfmt = [unicode(x.strftime("%X %x")) for x in reps]
                    logger.debug("{0}: {1}".format(showing_all, repsfmt))
                    if showing_all:
                        reps = ALLREPS
                    else:
                        reps = SOMEREPS
                    prompt = "{0}, {1}:\n  {2}".format(prompt, reps, "\n  ".join(repsfmt))
                    # self.messageWindow(VALID, repetitions, opts=self.options)
            else:
                repetitions = "No repetitions were generated."
                self.loop.messages.append(repetitions)
            if self.loop.messages:
                messages = "{0}".format("\n".join(self.loop.messages))
                logger.debug("messages: {0}".format(messages))
                self.messageWindow(MESSAGES, messages, opts=self.options)
                return False

        if self.checkmodified():
            prompt += "\n\n{0}".format(SAVEANDEXIT)
        else:
            prompt += "\n\n{0}".format(UNCHANGEDEXIT)

        if str != text:
            self.settext(str)
        ans, value = OptionsDialog(parent=self, title=self.title, prompt=prompt, yesno=False, list=True).getValue()
        if ans:
            self.onSave(v=value)
        return

    def clearFind(self, *args):
        self.text.tag_remove(FOUND, "0.0", END)
        self.find_text.set("")

    def onFind(self, *args):
        target = self.find_text.get()
        logger.debug('target: {0}'.format(target))
        if target:
            where = self.text.search(target, INSERT, nocase=1)
        if where:
            pastit = where + ('+%dc' % len(target))
            # self.text.tag_remove(SEL, '1.0', END)
            self.text.tag_add(FOUND, where, pastit)
            self.text.mark_set(INSERT, pastit)
            self.text.see(INSERT)
            self.text.focus()

    def cancel(self, e=None):
        t = self.find_text.get()
        if t.strip():
            self.clearFind()
            return "break"
        if self.autocompletewindow:
            self.hideCompletions()
            return "break"
        if self.text.tag_ranges("sel"):
            self.text.tag_remove('sel', "1.0", END)
            return
        # if self.checkmodified():
        #     return "break"
        logger.debug(('calling quit'))
        self.quit()

    def quit(self, e=None):
        if self.checkmodified():
            ans = self.confirm(parent=self, title=_('Quit'), prompt=_("There are unsaved changes.\nDo you really want to quit?"))
        else:
            ans = True
        if ans:
            if self.master:
                logger.debug('setting focus')
                self.master.focus()
                self.master.focus_set()
                logger.debug('focus set')
            self.destroy()
            logger.debug('done')

    def messageWindow(self, title, prompt, opts=None, height=8, width=52):
        win = Toplevel(self)
        win.title(title)
        win.geometry("+%d+%d" % (self.text.winfo_rootx() + 50, self.text.winfo_rooty() + 50))
        f = Frame(win)
        # pack the button first so that it doesn't disappear with resizing
        b = Button(win, text=_('OK'), width=10, command=win.destroy, default='active', pady=2)
        b.pack(side='bottom', fill=tkinter.NONE, expand=0, pady=0)
        win.bind('<Return>', (lambda e, b=b: b.invoke()))
        win.bind('<Escape>', (lambda e, b=b: b.invoke()))
        tkfixedfont = tkFont.nametofont("TkFixedFont")
        if 'fontsize_fixed' in self.loop.options and self.loop.options['fontsize_fixed']:
            tkfixedfont.configure(size=self.loop.options['fontsize_fixed'])

        t = ReadOnlyText(
            f, wrap="word", padx=2, pady=2, bd=2, relief="sunken",
            font=tkfixedfont,
            height=height,
            width=width,
            takefocus=False)
        t.insert("0.0", prompt)
        t.pack(side='left', fill=tkinter.BOTH, expand=1, padx=0, pady=0)
        if height > 1:
            ysb = ttk.Scrollbar(f, orient='vertical', command=t.yview)
            ysb.pack(side='right', fill=tkinter.Y, expand=0, padx=0, pady=0)
            t.configure(state="disabled", yscroll=ysb.set)
            t.configure(yscroll=ysb.set)
        f.pack(padx=2, pady=2, fill=tkinter.BOTH, expand=1)

        win.focus_set()
        win.grab_set()
        win.transient(self)
        win.wait_window(win)

    def confirm(self, parent=None, title="", prompt="", instance="xyz"):
        ok, value = OptionsDialog(parent=parent, title=_("confirm").format(instance), prompt=prompt).getValue()
        return ok