Exemplo n.º 1
0
    def render(self, data, node_name):
        label_txt = data.get('label', None)
        font = Font(family="Helvetica", size=12)
        h = font.metrics("linespace") + 6
        if label_txt:
            w = font.measure(label_txt) + 8
        else:
            w = font.measure("....") + 2
        self.config(width=w, height=h)
        marker_options = {
            'fill': data.get('color', 'orange'),
            'outline': 'orange'
        }

        if data.get('circle', False) or data.get('type', 'NOTLAN') == 'LAN':
            self.create_oval(0, 0, w - 1, h - 1, **marker_options)
            self.config(width=w, height=h)
            if label_txt:
                self.create_text((w) / 2, (h) / 2,
                                 text=label_txt,
                                 font=font,
                                 fill="black")
        else:
            self.create_rectangle(0, 0, w, h, **marker_options)
            if label_txt:
                self.create_text(w / 2,
                                 h / 2,
                                 text=label_txt,
                                 font=font,
                                 fill="black")
Exemplo n.º 2
0
    def render(self, data, node_name):
        # Figure out what size the text we want is.
        label_txt = data.get('label', None)
        font = Font(family="Helvetica", size=12)
        h = font.metrics("linespace") + 1

        if label_txt:
            w = font.measure(label_txt) + 2
        else:
            w = font.measure("....") + 2
        self.config(width=w, height=h)
        marker_options = {
            'fill': data.get('color', 'blue'),
            'outline': 'white'
        }

        if data.get('circle', None):
            self.create_oval(0, 0, w, h, **marker_options)
            self.config(width=w, height=h)
            if label_txt:
                self.create_text(w / 2,
                                 h / 2,
                                 text=label_txt,
                                 font=font,
                                 fill="white")
        else:
            self.create_rectangle(0, 0, w, h, **marker_options)
            if label_txt:
                self.create_text(w / 2,
                                 h / 2,
                                 text=label_txt,
                                 font=font,
                                 fill="white")
Exemplo n.º 3
0
 def displayAutomata(self):
     for item in self.canvasitems:
         self.automataCanvas.delete(item)
     if self.selectedButton == 0:
         header = "e-NFA"
         automata = self.nfa
         if self.dotFound:
             image = self.nfaimg
             imagefile = self.nfaimagefile
     elif self.selectedButton == 1:
         header = "DFA"
         automata = self.dfa
         if self.dotFound:
             image = self.dfaimg
             imagefile = self.dfaimagefile
     elif self.selectedButton == 2:
         header = "Minimized DFA"
         automata = self.minDFA
         if self.dotFound:
             image = self.mindfaimg
             imagefile = self.mindfaimagefile
     font = Font(family="times", size=20)
     (w, h) = (font.measure(header), font.metrics("linespace"))
     headerheight = h + 10
     itd = self.automataCanvas.create_text(10,
                                           10,
                                           text=header,
                                           font=font,
                                           anchor=NW)
     self.canvasitems.append(itd)
     [text, linecount] = automata.getPrintText()
     font = Font(family="times", size=13)
     (w, h) = (font.measure(text), font.metrics("linespace"))
     textheight = headerheight + linecount * h + 20
     itd = self.automataCanvas.create_text(10,
                                           headerheight + 10,
                                           text=text,
                                           font=font,
                                           anchor=NW)
     self.canvasitems.append(itd)
     if self.dotFound:
         itd = self.automataCanvas.create_image(10,
                                                textheight,
                                                image=image,
                                                anchor=NW)
         self.canvasitems.append(itd)
         totalwidth = imagefile.size[0] + 10
         totalheight = imagefile.size[1] + textheight + 10
     else:
         totalwidth = self.cwidth + 10
         totalheight = textheight + 10
     if totalheight < self.cheight:
         totalheight = self.cheight
     if totalwidth < self.cwidth:
         totalwidth = self.cwidth
     self.automataCanvas.config(scrollregion=(0, 0, totalwidth,
                                              totalheight))
Exemplo n.º 4
0
    def count_lines(self, s):
        """Count the number of lines in a given text.

        Before calculation, the tab width and line length of the text are
        fetched, so that up-to-date values are used.

        Lines are counted as if the string was wrapped so that lines are never
        over linewidth characters long.

        Tabs are considered tabwidth characters long.
        """
        # Tab width is configurable
        tabwidth = self.editwin.get_tk_tabwidth()

        # Get the Text widget's size
        linewidth = self.editwin.text.winfo_width()
        # Deduct the border and padding
        linewidth -= 2*sum([int(self.editwin.text.cget(opt))
                            for opt in ('border', 'padx')])

        # Get the Text widget's font
        font = Font(self.editwin.text, name=self.editwin.text.cget('font'))
        # Divide the size of the Text widget by the font's width.
        # According to Tk8.5 docs, the Text widget's width is set
        # according to the width of its font's '0' (zero) character,
        # so we will use this as an approximation.
        # see: http://www.tcl.tk/man/tcl8.5/TkCmd/text.htm#M-width
        linewidth //= font.measure('0')

        return count_lines_with_wrapping(s, linewidth, tabwidth)
Exemplo n.º 5
0
        def text_extents(self, style, text):
            """
            The text extents are calculated using tkinter.Font
            """
            with InternalWindow() as window:
                font_type = ""
                if style["bold"]:
                    font_type += "bold"
                if style["italic"]:
                    if len(font_type) > 0:
                        font_type += " "
                    font_type += "italic"

                # Create the new font object.
                font = Font(window, (style["font"], -int(style["size"]*FONT_SCALING), font_type))
                # Query the data
                width = font.measure(text)
                metrics = font.metrics()

            return {
                "width": width / float(FONT_SCALING),
                "height": metrics["linespace"] / float(FONT_SCALING),
                "ascent": metrics["ascent"] / float(FONT_SCALING),
                "descent": metrics["descent"] / float(FONT_SCALING)
            }
Exemplo n.º 6
0
    def __init__(
        self,
        master: Union[tk.Frame, tk.Tk],
        x: int,
        y: int,
        horizontal_anchor: HorizontalAnchor,
        vertical_anchor: VerticalAnchor,
        txt: str,
        font: tkf.Font,
        txt_color: str,
        color: str,
    ):
        super().__init__(
            master=master,
            text=txt,
            font=font,
            padx=0,
            pady=0,
            fg=txt_color,
            bg=color,
            bd=0,
        )

        txt_width = font.measure(txt)
        txt_height = font.metrics("linespace")
        xloc, yloc = self._get_position(
            horizontal_anchor,
            vertical_anchor,
            x,
            y,
            txt_width,
            txt_height
        )
        self.place(x=xloc, y=yloc)
Exemplo n.º 7
0
    def count_lines(self, s):
        """Count the number of lines in a given text.

        Before calculation, the tab width and line length of the text are
        fetched, so that up-to-date values are used.

        Lines are counted as if the string was wrapped so that lines are never
        over linewidth characters long.

        Tabs are considered tabwidth characters long.
        """
        # Tab width is configurable
        tabwidth = self.editwin.get_tk_tabwidth()

        # Get the Text widget's size
        linewidth = self.editwin.text.winfo_width()
        # Deduct the border and padding
        linewidth -= 2*sum([int(self.editwin.text.cget(opt))
                            for opt in ('border', 'padx')])

        # Get the Text widget's font
        font = Font(self.editwin.text, name=self.editwin.text.cget('font'))
        # Divide the size of the Text widget by the font's width.
        # According to Tk8.5 docs, the Text widget's width is set
        # according to the width of its font's '0' (zero) character,
        # so we will use this as an approximation.
        # see: http://www.tcl.tk/man/tcl8.5/TkCmd/text.htm#M-width
        linewidth //= font.measure('0')

        return count_lines_with_wrapping(s, linewidth, tabwidth)
Exemplo n.º 8
0
    def create_widgets(self):
        ''' creates GUI for app '''
        # expand widget to fill the grid
        # self.columnconfigure(1, weight=1, pad=100)
        # self.rowconfigure(1, weight=1, pad=20)

        self.textv = Text(self, relief=SUNKEN)
        self.textv.grid(row=1, column=1, columnspan=2, sticky=E + W)

        efont = Font(family="Monospace", size=14)
        self.textv.configure(font=efont)
        self.textv.config(
            wrap=NONE,  # wrap = "word"
            undo=True,  # Tk 8.4
            width=60,
            tabs=(efont.measure(' ' * 4), ))
        self.textv.focus()

        self.scry = Scrollbar(self, orient=VERTICAL, command=self.textv.yview)
        self.scry.grid(row=1, column=3, sticky=E + N + S)  # use N+S+E
        self.textv['yscrollcommand'] = self.scry.set

        self.scrx = Scrollbar(self,
                              orient=HORIZONTAL,
                              command=self.textv.xview)
        self.scrx.grid(row=2, column=1, columnspan=2, sticky=E + W + N)
        self.textv['xscrollcommand'] = self.scrx.set

        btnSave = Button(self, text='Open', command=self.on_btn_open_clicked)
        btnSave.grid(row=3, column=1)

        btnClose = Button(self, text='Save', command=self.on_btn_save_clicked)
        btnClose.grid(row=3, column=2)
Exemplo n.º 9
0
    def create_widgets(self):
        ''' creates GUI for app '''
        # expand widget to fill the grid
        # self.columnconfigure(1, weight=1, pad=100)
        # self.rowconfigure(1, weight=1, pad=20)

        # customize widget style when using ttk...
        # style = Style()
        # style.configure("TButton", width=10)

        root.geometry("300x300")

        self.txt = Text(self)
        self.txt.grid(row=1, column=1)

        efont = Font(family="Ubuntu Mono", size=14)
        self.txt.configure(font=efont)
        self.txt.config(wrap = "word", # wrap = NONE
               undo = True, # Tk 8.4
               width = 80,
               tabs = (efont.measure(' ' * 4),))
        self.txt.focus()

        self.txt.insert("1.0", "right click to Copy & Paste ...")

        self.popup_menu = Menu(self, tearoff = 0)
        self.popup_menu.add_command(label = "Copy",
                                    command = lambda:self.clipbrd(1))
        self.popup_menu.add_command(label = "Paste",
                                    command = lambda:self.clipbrd(2))
        self.popup_menu.add_separator()
        self.popup_menu.add_command(label = "say bye", command = exit)
        self.txt.bind("<Button-3>",self.do_popup)
Exemplo n.º 10
0
def run():
    global x, first, work_integral, b
    w.configure(background='black')

    k = 70

    f_text = 'Avenir Next Ultra Light'
    font = Font(family=f_text, size=k)
    text = 'The trig function is '
    c = font.measure(text)

    if first:
        work_integral = laTeX('$\\sin{(x+\\pi)}$', 'red', k)

        w.create_text(*A(0, 0),
                      text=text,
                      font=(f_text, k),
                      fill='red',
                      tag='txt',
                      anchor=W)
        w.create_image(*A(c, -2),
                       image=work_integral,
                       anchor=W,
                       tag='integral')
        # w.create_text(*A(137, 0), text='.', font=('Avenir Next Ultra Light', k), fill='red')
        first = False
    else:
        img = w.find_withtag('integral')
        w.coords(img, *A(c, -2))
        txt = w.find_withtag('txt')
        w.itemconfig(txt, text=text)

    w.update()
    # time.sleep(0.001)
    time.sleep(0.5)
Exemplo n.º 11
0
 def update_font(self):
     """Update the sidebar text font, usually after config changes."""
     font = idleConf.GetFont(self.text, 'main', 'EditorWindow')
     tk_font = Font(self.text, font=font)
     char_width = max(tk_font.measure(char) for char in ['>', '.'])
     self.canvas.configure(width=char_width * 3 + 4)
     self._update_font(font)
Exemplo n.º 12
0
class Scroller(object):
    """
    Scrolls through a solution list.
    """
    def __init__(self, wdw, sols):
        """
        Stores the list of solutions in sols
        and defines the layout of the GUI.
        """
        wdw.title('solutions scroller')
        self.sols = sols
        self.cursor = 0
        self.lbl = Label(wdw, text="solution : ")
        self.lbl.grid(row=0, column=0, sticky=E)
        self.ent = Entry(wdw)
        self.ent.grid(row=0, column=1, stick=W)
        self.ent.insert(INSERT, "0 of %d" % len(sols))
        self.myft = Font(family="Courier New", size=12, weight="normal")
        self.mlen = self.myft.measure("M")
        lines = sols[0].split('\n')
        self.width = max([len(line) for line in lines])
        self.display = StringVar()
        self.display.set(self.sols[0])
        self.mess = Message(wdw, textvariable=self.display, \
            font=self.myft, width=self.width*self.mlen, background='white')
        self.mess.grid(row=1, column=0, columnspan=2)
        self.btnext = Button(wdw, command=self.next, text='next')
        self.btnext.grid(row=2, column=1, sticky=W + E)
        self.btprev = Button(wdw, command=self.previous, text='previous')
        self.btprev.grid(row=2, column=0, sticky=W + E)

    def show(self):
        """
        Shows the solution at position self.cursor
        in the message widget and updates the entry widget.
        """
        self.display.set(self.sols[self.cursor])
        self.ent.delete(0, END)
        self.ent.insert(INSERT, '%d of %d' % (self.cursor, len(self.sols)))

    def next(self):
        """
        Increases the cursor by one if possible.
        """
        if self.cursor < len(self.sols) - 1:
            self.cursor = self.cursor + 1
        self.show()

    def previous(self):
        """
        Decreases the cursor by one if possible.
        """
        if self.cursor > 0:
            self.cursor = self.cursor - 1
        self.show()
Exemplo n.º 13
0
class Scroller(object):
    """
    Scrolls through a solution list.
    """
    def __init__(self, wdw, sols):
        """
        Stores the list of solutions in sols
        and defines the layout of the GUI.
        """
        wdw.title('solutions scroller')
        self.sols = sols
        self.cursor = 0
        self.lbl = Label(wdw, text="solution : ")
        self.lbl.grid(row=0, column=0, sticky=E) 
        self.ent = Entry(wdw)
        self.ent.grid(row=0, column=1, stick=W)
        self.ent.insert(INSERT, "0 of %d" % len(sols))
        self.myft = Font(family="Courier New", size=12, weight="normal")
        self.mlen = self.myft.measure("M")
        lines = sols[0].split('\n')
        self.width = max([len(line) for line in lines])
        self.display = StringVar()
        self.display.set(self.sols[0])
        self.mess = Message(wdw, textvariable=self.display, \
            font=self.myft, width=self.width*self.mlen, background='white')
        self.mess.grid(row=1, column=0, columnspan=2)
        self.btnext = Button(wdw, command=self.next, text='next')
        self.btnext.grid(row=2, column=1, sticky=W+E)
        self.btprev = Button(wdw, command=self.previous, text='previous')
        self.btprev.grid(row=2, column=0, sticky=W+E)

    def show(self):
        """
        Shows the solution at position self.cursor
        in the message widget and updates the entry widget.
        """
        self.display.set(self.sols[self.cursor])
        self.ent.delete(0, END)
        self.ent.insert(INSERT, '%d of %d' % (self.cursor, len(self.sols)))

    def next(self):
        """
        Increases the cursor by one if possible.
        """
        if self.cursor < len(self.sols) - 1:
            self.cursor = self.cursor + 1
        self.show()

    def previous(self):
        """
        Decreases the cursor by one if possible.
        """
        if self.cursor > 0:
            self.cursor = self.cursor - 1
        self.show()
Exemplo n.º 14
0
 def label_size(self, label):
     """Calculate label size"""
     font_family, font_size = self.font
     font = Font(family=font_family, size=font_size)
     width = 0
     lines = 0
     for line in label.split('\n'):
         width = max(width, font.measure(line))
         lines += 1
     xscale = turtle.getscreen().xscale
     yscale = turtle.getscreen().yscale
     return width / xscale, font.metrics('linespace') * lines / yscale
Exemplo n.º 15
0
Arquivo: event.py Projeto: SlamaFR/AP1
def taille_texte(chaine, police='Helvetica', taille='24'):
    """
    Donne la largeur et la hauteur en pixel nécessaires pour afficher
    ``chaine`` dans la police et la taille données.

    :param str chaine: chaîne à mesurer
    :param police: police de caractères (défaut : `Helvetica`)
    :param taille: taille de police (défaut 24)
    :return: couple (w, h) constitué de la largeur et la hauteur de la chaîne
        en pixels (int), dans la police et la taille données.
    """
    font = Font(family=police, size=taille)
    return font.measure(chaine), font.metrics("linespace")
Exemplo n.º 16
0
 def __init__(self, master=None): 
     self.theme = THEME_SETUP[THEME]
     self.font = Font(size=FONT_SIZE)
     super().__init__(master,bg=self.theme['bg'],width=400,heigh=200,fg=self.theme['fg'],font=self.font,insertbackground=self.theme['cursor'])
     self.current_open_file = ''
     self.lastSaved = ''
     # self.pack(fill=BOTH,expand=1)
     self.tag_configure("method", foreground="red")
     self.tag_configure("keyword", foreground="blue")
     font = Font(font=self['font'])
     tab = font.measure('    ')
     self.config(tabs=tab)
     self.pack_propagate(False)
     self.grid_propagate(False)
Exemplo n.º 17
0
    def __config_calendar(self):
        cols = self._cal.formatweekheader(3).split()

        self._calendar['columns'] = cols
        self._calendar.tag_configure('header', background='grey90')
        self._calendar.insert('', 'end', values=cols, tag='header')

        # adjust its columns width
        font = Font()
        maxwidth = max(font.measure(col) for col in cols)
        for col in cols:
            self._calendar.column(
                col, width=maxwidth, minwidth=maxwidth, anchor='e'
            )
Exemplo n.º 18
0
    def create_widgets(self):
        ''' creates GUI for app '''
        # expand widget to fill the grid
        # self.columnconfigure(1, weight=1, pad=100)
        # self.rowconfigure(1, weight=1, pad=20)

        # customize widget style when using ttk...
        # style = Style()
        # style.configure("TButton", width=12)

        self.textedit = Text(self)
        self.textedit.grid(row=1, column=1, columnspan=3)

        efont = Font(family="Helvetica", size=14)
        self.textedit.configure(font=efont)
        self.textedit.config(
            wrap=NONE,  # wrap = NONE
            undo=True,  # Tk 8.4
            width=80,
            tabs=(efont.measure(' ' * 4), ))
        self.textedit.focus()
        ## basic handler commands #
        # .get("1.0", END)
        # .delete("1.0", END)
        # .insert("1.0", "New text content ...")

        self.scry = Scrollbar(self,
                              orient=VERTICAL,
                              command=self.textedit.yview)
        self.scry.grid(row=1, column=4, sticky='wns')  # use N+S+E
        self.textedit['yscrollcommand'] = self.scry.set

        self.scrx = Scrollbar(self,
                              orient=HORIZONTAL,
                              command=self.textedit.xview)
        self.scrx.grid(row=2, column=1, columnspan=3, sticky='wen')
        self.textedit['xscrollcommand'] = self.scrx.set

        btnOpen = Button(self, text='Open', command=self.on_btn_open_clicked)
        btnOpen.grid(row=3, column=1, pady=8)

        btnSave = Button(self, text='Save', command=self.on_btn_save_clicked)
        btnSave.grid(row=3, column=2)

        btnClose = Button(self,
                          text='Close',
                          command=self.on_btn_close_clicked)
        btnClose.grid(row=3, column=3)
Exemplo n.º 19
0
def drawText():
    serial.write("CONF?".encode('utf-8'))
    query = getMode(serial.read_until())
    modeText = query[0]
    modifierText = query[1]

    serial.write("READ?".encode('utf-8'))
    data = serial.read_until()
    canvas.itemconfigure(value, text=data)
    canvas.itemconfigure(modifier, text=modifierText)
    canvas.itemconfigure(mode, text=modeText)

    font = Font(family="LcdStd", size=60)
    length = font.measure(data)
    canvas.coords(modifier, length, 38)
    canvas.coords(mode, length, 13)

    serial.write("SYSTEM:LOCAL?".encode('utf-8'))
    root.after(100, drawText)
Exemplo n.º 20
0
    treeState[col]['selected'] = False if direction else True
    treeState[col]['alternate'] = True if direction else False
    if ttk.Style().theme_use() == 'aque':
        tree.heading(col, state='user1')
        treeState[col]['user1'] = True
    else:
        tree.heading(col, image=upArrow if direction else downArrow)
style = ttk.Style()
from tkinter.font import Font
font_ = Font(name=style.lookup('Heading', 'font'), exists=True)
globals().update(locals())
for col in title:
    name = col
    tree.heading(col, text=name, image=noArrow, anchor='w',
        command=lambda col=col: SortBy(tree, col, False))
    tree.column(col, width=font_.measure(name)+noArrow.width()+5)
font_ = Font(name=style.lookup('Treeview', 'font'), exists=True)
for i in data:
    tree.insert('', 'end', values=' '.join(i))
    for n in i:
        len_ = font_.measure(col+'  ')
        if tree.column(col, 'width') < len_:
            tree.column(col, width=len_)

## Code to do the sorting of the tree contents when clicked on
##proc SortBy {tree col direction} {
##    # Determine currently sorted column and its sort direction
##    foreach c {country capital currency} {
##	set s [$tree heading $c state]
##	if {("selected" in $s || "alternate" in $s) && $col ne $c} {
##	    # Sorted column has changed
Exemplo n.º 21
0
    def create_widgets(self):
        ''' creates GUI for app '''
        # expand widget to fill the grid
        # self.columnconfigure(1, weight=1, pad=100)
        # self.rowconfigure(1, weight=1, pad=20)

        # myfont = Font(family='Lucida Console', weight = 'bold', size = 20)

        # customize widget style when using ttk...
        # style = Style()
        # style.configure("TButton", width=10) # global
        # style.configure("my.TButton", width=10) # 'style' option

        ''' ONLY OPTIONS FOR 'grid' FUNCTIONS:
                column  row
                columnspan  rowspan
                ipadx and ipady
                padx and pady
                sticky="nsew"
        -------------------------------------------------------- '''
        self.style = Style()
        self.style.configure("TButton", width=15)

        btn = Button(self, text='Close', command=self.exit)
        btn.grid(row=1, column=1)

        self.vent = StringVar()
        # self.vent.trace("w", self.eventHandler)
        ent = Entry(self, textvariable=self.vent)
        ent.grid(row=2, column=1)

        optionlist = ('aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff')
        self.vopt = StringVar()
        self.vopt.set(optionlist[0])
        opt = OptionMenu(self, self.vopt, *optionlist)
        opt.grid(row=3, column=1)

        self.vcombo = StringVar()
        combo = Combobox(self, textvariable=self.vcombo)
        combo['values'] = ('value1', 'value2', 'value3')
        # COMBO.bind('<<ComboboxSelected>>', self.ONCOMBOSELECT)
        combo.current(0)
        combo.grid(row=4, column=1)

        self.vlbl = StringVar()
        lbl = Label(self, text='', textvariable=self.vlbl)
        lbl.grid(row=5, column=1)
        self.vlbl.set('')

        self.vbar = DoubleVar()
        bar = Scale(self, variable=self.vbar)
        bar.grid(row=6, column=1)
        # str(self.var.get())

        self.vchk = IntVar()
        chk = Checkbutton(self, variable=self.vchk, text='check me out')
        chk.grid(row=7, column=1)

        
        frm = LabelFrame(self, text="label frame",
                            width=100, height=100)
        frm.grid(row=8, column=1, sticky='nsew')

        btn2 = Button(frm, text='Hello', command=self.proc)
        btn2.grid(row=1, column=1)

        self.vlbl2 = StringVar()
        lbl2 = Label(frm, text='Hello', textvariable=self.vlbl2)
        lbl2.grid(row=2, column=1)
        self.vlbl2.set('Hello')

        self.txt = Text(self, width=16)
        self.txt.grid(row=1, column=2, rowspan=6, padx=(5,0), pady=5)
        efont = Font(family="Helvetica", size=14)
        self.txt.configure(font=efont)
        self.txt.config(wrap="word", # wrap=NONE
                           undo=True, # Tk 8.4
                           height=12,
                           insertbackground='#000',   # cursor color
                           tabs=(efont.measure(' ' * 4),))
        self.txt.focus()
        ## basic handler commands #
        # .get("1.0", END)
        # .delete("1.0", END)
        # .insert("1.0", "New text content ...")
        self.sctxt = Scrollbar(self, orient=VERTICAL, command=self.txt.yview)
        self.sctxt.grid(row=1, column=3, rowspan=6, sticky='nsw')  # use nse
        self.txt['yscrollcommand'] = self.sctxt.set

        sep = Separator(self)
        sep.grid(row=7, column=2, columnspan=4, sticky='ew')

        menubar = Menu(root)
        mn_file = Menu(menubar, tearoff=0)
        mn_file.add_command(label="New", command=self.mn_file_new, accelerator="Ctrl-n", underline=1)
        mn_file.add_command(label="Open", command=self.nm_file_open)
        mn_file.add_command(label="Save", command=self.nm_file_save, accelerator="Ctrl-s", underline=1)
        mn_file.add_command(label="Save-As", command=self.nm_file_saveas)
        mn_file.add_separator()
        mn_file.add_command(label="Exit", command=self.nm_file_exit, accelerator="Ctrl-q")
        menubar.add_cascade(label="File", menu=mn_file)
        mn_edit = Menu(menubar, tearoff=0)
        mn_edit.add_command(label="Undo", command=self.mn_edit_undo, accelerator="Ctrl-z")
        mn_edit.add_command(label="Select All", command=self.mn_edit_selall, accelerator="Ctrl-a")
        submenu = Menu(mn_edit, tearoff=False)
        submenu.add_command(label="Copy", command=self.mn_edit_copy, accelerator="Ctrl-c")
        submenu.add_command(label="Paste", command=self.mn_edit_paste, accelerator="Ctrl-v")
        mn_edit.add_cascade(label="Clipboard", menu=submenu, underline=2)
        menubar.add_cascade(label="Edit", menu=mn_edit)
        mn_help = Menu(menubar, tearoff=0)
        mn_help.add_command(label="Help Index", command=self.mn_help_index)
        mn_help.add_command(label="About…", command=self.mn_help_about)
        menubar.add_cascade(label="Help", menu=mn_help)
        root.config(menu=menubar) # display the menu

        self.lst = Listbox(self, width=10)
        self.lst.grid(row=1, column=4, rowspan=6, sticky='nsew')

        self.lst.bind("<<ListboxSelect>>", self.on_select_list)
        for i in range(100):
            self.lst.insert(i, "Item " + str(i))

    #
    # FUNCS TO EDIT LISTBOX CONTENTS
    #
    # def delete_item(self):
    #     if self.listbox.curselection() == ():
    #         return # nothing selected
    #     print("Deleting: " + str(self.listbox.curselection()))
    #     self.listbox.delete(self.listbox.curselection())

    # def insert_item(self):
    #     if self.listbox.curselection() == ():
    #         return # nothing selected
    #     list_item = self.listbox.curselection()
    #     self.listbox.insert(list_item[0], self.txtfld.get())
    #     print("inserted at " + str(list_item[0]))


        self.sclst = Scrollbar(self, orient=VERTICAL, command=self.lst.yview)
        self.sclst.grid(row=1, column=5, rowspan=6, sticky='nsw')  # use nse
        self.lst['yscrollcommand'] = self.sclst.set

        self.vcmbx = StringVar()
        cmbx = Combobox(self, textvariable=self.vcmbx, width=6)
        cmbx['values'] = ('alt', 'scidsand', 'classic', 'scidblue',
                          'scidmint', 'scidgreen', 'default', 'scidpink',
                          'arc', 'scidgrey', 'scidpurple', 'clam', 'smog',
                          'kroc', 'black', 'clearlooks',
                          'radiance', 'blue')
        cmbx.current(0)
        cmbx.grid(row=8, column=2, sticky='ew', padx=4)
        cmbx.bind('<<ComboboxSelected>>', self.comboselected)

        frm2 = Frame(self)
        frm2.grid(row=8, column=3, columnspan=3, sticky='nsew')

        self.vrad = StringVar() # USE ONE VAR PER GROUP OF BUTTONS
        radyes = Radiobutton(frm2, variable=self.vrad, value='Yes', text='Yes')
        radyes.grid(row=1, column=1, sticky='ew', padx=10)
        radno = Radiobutton(frm2, variable=self.vrad, value='No', text='No')
        radno.grid(row=2, column=1, sticky='ew', padx=10)
Exemplo n.º 22
0
# after we figure out how tall the clue will actually be
# we will add the
clue_rect_default_height = 75

x_pad = 20  # the amount to pad before starting the clue number
x_num_pad = 5  # the amount to pad between the clue number and the clue
y_pad = 5  # the amount to pad (top and bottom) between the end
# of the clue box and the bounding box of the clue text

clue_rect_top_y = 0  # zero to start.  bottom_y + 1 in the loop
for key, value in down_clues.items():
    print(key, value)

    clue_number = key
    clue_text = value
    clue_text_length = rfont.measure(clue_text)
    clue_num_length = rfont.measure(clue_number)
    clue_text_height = rfont.metrics("linespace")
# print("clue_text_length:", clue_text_length)
# col_left_x, row_top_y, col_right_x, row_bot_y

# clue_rect_bottom_y = clue_rect_top_y + clue_rect_default_height
# clue_text_line_len = clue_right_x - (clue_left_x + (x_pad*2) + clue_num_length + x_num_pad + clue_top_y + y_pad)

# cr = list_canvas.create_rectangle(clue_left_x, clue_top_y, clue_right_x, clue_bottom_y, fill="light grey")
# cn = list_canvas.create_text(clue_left_x + x_pad, clue_top_y + y_pad, text=clue_number, font=bfont, anchor = "nw")
# ct = list_canvas.create_text(clue_left_x + x_pad + clue_num_length + x_num_pad, clue_top_y + y_pad, text=clue_text, font=rfont, anchor = "nw", width=clue_text_line_len)
# bb = list_canvas.bbox(ct)
# x0, y0, x1, y1 = list_canvas.coords(cr)
# y1 = bb[3] + y_pad
# list_canvas.coords(cr, x0, y0, x1, y1)
Exemplo n.º 23
0
class TextEditor(Frame, CaretObserver, TextObserver):
    def update_text(self, text: str):
        self.update_status_bar()
        self.redisplay()

    def update_caret_location(self, loc: Location):
        self.update_status_bar()
        self.redisplay()

    def __init__(self, text_editor_model: TextEditorModel):
        super().__init__()

        # TODO extract
        self._minimalWidth = 555
        self._minimalHeight = 700
        self._xMargin = 10
        self._numberSpacing = 50
        self._yMargin = 20
        self._font = Font(size=12, family="Purisa")
        self._canvasTextColor = flat_colors.FlatUiColors.CLOUDS
        self._canvasBackgroundColor = flat_colors.FlatUiColors.PETER_RIVER
        self._canvasHighlightColor = flat_colors.FlatUiColors.EMERALD

        # TODO extract
        self._caretShownDelay = 300
        self._caretHiddenDelay = 5000
        self._caretColor = flat_colors.FlatUiColors.MIDNIGHT_BLUE

        self.__processed_selection = []

        self._model = text_editor_model
        self._model._caretObservers.add(self)
        # using an anonymous class:
        # self._model._observers.add(type("Pero", (CaretObserver, object),
        #                                 {"updateCaretLocation": lambda _, __: self.redisplay()})())

        self._clipboardStack = ClipboardStack()

        self._pluginsDirectory = "./plugins/"
        self._pluginsModuleName = "plugins"
        self._plugins = self._load_plugins()

        self.init_menubar()
        self.init_toolbar()
        self.init_statusbar()
        self.init_canvas()

    def _load_plugins(self):
        plugins = []
        for file in os.listdir(self._pluginsDirectory):
            if file.endswith(".py"):
                print(file)
                # try:
                spec = importlib.util.spec_from_file_location(
                    self._pluginsModuleName, self._pluginsDirectory + file)
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                for name, obj in inspect.getmembers(
                        module, lambda x: inspect.isclass(x) and issubclass(x, Plugin) and not inspect.isabstract(x)):
                    plugins.append(obj())
                # except:
                #     pass

        return plugins

    def init_menubar(self):
        menubar = Menu(self.master)
        self.master.config(menu=menubar)

        file_menu = Menu(menubar)
        file_menu.add_command(label="Open", underline=0, command=self.open_file)
        file_menu.add_command(label="Save", underline=0, command=self.save_file)
        file_menu.add_command(label="Exit", underline=0, command=self.on_exit)
        menubar.add_cascade(label="File", underline=0, menu=file_menu)

        edit_menu = Menu(menubar)
        edit_menu.add_command(label="Undo", command=self.undo, state=DISABLED)
        UndoManager.get_instance().attach_undo_observer(
            type("UndoManagerStackObserver1", (UndoManagerStackObserver, object),
                 {"stack_empty": lambda _, e: edit_menu.entryconfig(1, state=DISABLED if e else NORMAL)})())

        edit_menu.add_command(label="Redo", command=self.redo, state=DISABLED)
        UndoManager.get_instance().attach_redo_observer(
            type("UndoManagerStackObserver2", (UndoManagerStackObserver, object),
                 {"stack_empty": lambda _, e: edit_menu.entryconfig(2, state=DISABLED if e else NORMAL)})())

        edit_menu.add_command(label="Cut", command=self.cut, state=DISABLED)
        self._model.attach_selection_observer(
            type("SelectionObserverCut", (SelectionObserver, object),
                 {"update_selection": lambda _, e: edit_menu.entryconfig(3, state=DISABLED if not e else NORMAL)})())

        edit_menu.add_command(label="Copy", command=self.copy, state=DISABLED)
        self._model.attach_selection_observer(
            type("SelectionObserverCopy", (SelectionObserver, object),
                 {"update_selection": lambda _, e: edit_menu.entryconfig(4, state=DISABLED if not e else NORMAL)})())

        edit_menu.add_command(label="Paste", command=self.paste, state=DISABLED)
        self._clipboardStack.attach_clipboard_observer(
            type("ClipboardObserverPaste", (ClipboardObserver, object),
                 {"update_clipboard": lambda _, e: edit_menu.entryconfig(5, state=DISABLED if not e else NORMAL)})())

        edit_menu.add_command(label="Paste and Take", command=self.paste_and_take)
        self._clipboardStack.attach_clipboard_observer(
            type("ClipboardObserverPasteAndTake", (ClipboardObserver, object),
                 {"update_clipboard": lambda _, e: edit_menu.entryconfig(6, state=DISABLED if not e else NORMAL)})())

        edit_menu.add_command(label="Delete selection", command=self.delete_selection, state=DISABLED)
        self._model.attach_selection_observer(
            type("SelectionObserverDelete", (SelectionObserver, object),
                 {"update_selection": lambda _, e: edit_menu.entryconfig(7, state=DISABLED if not e else NORMAL)})())

        edit_menu.add_command(label="Clear document", command=self.clear_document)
        menubar.add_cascade(label="Edit", underline=0, menu=edit_menu)

        move_menu = Menu(menubar)
        move_menu.add_command(label="Caret to document start", command=self.caret_to_start)
        move_menu.add_command(label="Caret to document end", command=self.caret_to_end)
        menubar.add_cascade(label="Move", underline=0, menu=move_menu)

        plugins_menu = Menu(menubar)
        for plugin in self._plugins:
            plugins_menu.add_command(
                label=plugin.get_name(),
                command=lambda p=plugin: p.execute(self._model, UndoManager.get_instance(), self._clipboardStack))

        menubar.add_cascade(label="Plugins", underline=0, menu=plugins_menu)

    def init_toolbar(self):
        toolbar = Frame(self.master)

        undo = Button(toolbar, text="Undo", command=self.undo, state=DISABLED)
        undo.pack(side=LEFT, padx=2, pady=2)
        UndoManager.get_instance().attach_undo_observer(
            type("UndoManagerStackObserver3", (UndoManagerStackObserver, object),
                 {"stack_empty": lambda _, e: undo.config(state=DISABLED if e else NORMAL)})())

        redo = Button(toolbar, text="Redo", command=self.redo, state=DISABLED)
        redo.pack(side=LEFT, padx=2, pady=2)
        UndoManager.get_instance().attach_redo_observer(
            type("UndoManagerStackObserver4", (UndoManagerStackObserver, object),
                 {"stack_empty": lambda _, e: redo.config(state=DISABLED if e else NORMAL)})())

        copy = Button(toolbar, text="Copy", command=self.copy, state=DISABLED)
        copy.pack(side=LEFT, padx=2, pady=2)
        self._model.attach_selection_observer(
            type("SelectionObserverCopyTool", (SelectionObserver, object),
                 {"update_selection": lambda _, e: copy.config(state=DISABLED if not e else NORMAL)})())

        cut = Button(toolbar, text="Cut", command=self.cut, state=DISABLED)
        cut.pack(side=LEFT, padx=2, pady=2)
        self._model.attach_selection_observer(
            type("SelectionObserverCutTool", (SelectionObserver, object),
                 {"update_selection": lambda _, e: cut.config(state=DISABLED if not e else NORMAL)})())

        paste = Button(toolbar, text="Paste", command=self.cut, state=DISABLED)
        paste.pack(side=LEFT, padx=2, pady=2)
        self._clipboardStack.attach_clipboard_observer(
            type("ClipboardObserverPasteTool", (ClipboardObserver, object),
                 {"update_clipboard": lambda _, e: paste.config(state=DISABLED if not e else NORMAL)})())

        toolbar.pack(side=TOP, fill=X)

    def init_statusbar(self):
        self._statusbar = Label(self.master, bd=1, relief=SUNKEN, padx=6, pady=4, anchor=E)
        self._statusbar.pack(side=BOTTOM, fill=X)
        self.update_status_bar()

    def init_canvas(self):
        self.master.title("Lab 3 example")
        self.pack(fill=BOTH, expand=1)

        self._display_caret = True
        self._selecting_active = False
        self._canvas = Canvas(self, bg=self._canvasBackgroundColor,
                              scrollregion=(0, 0, self._minimalWidth, self._minimalHeight))
        self._vbar = Scrollbar(self, orient=VERTICAL)
        self._vbar.pack(side=RIGHT, fill=Y)
        self._vbar.config(command=self._canvas.yview)
        self._hbar = Scrollbar(self, orient=HORIZONTAL)
        self._hbar.pack(side=BOTTOM, fill=X)
        self._hbar.config(command=self._canvas.xview)
        self._canvas.config(xscrollcommand=self._hbar.set, yscrollcommand=self._vbar.set)
        self._canvas.pack(fill=BOTH, expand=1)

        self.redisplay()
        self._canvas.bind_all("<Key>", self.on_key_pressed)
        self._other_dialog_open = False
        self.after(self._caretShownDelay, self.on_timer)

    def redisplay(self, update_selection=False):
        # print("caret loc", self._model.get_caret_location().get(), "||| current row and column",
        #       self._model.find_caret(), "|||| sel", self._model.get_selection_range().get_start(),
        #       self._model.get_selection_range().get_end())
        row_height = self._font.metrics()["linespace"]
        (caret_row, caret_column) = self._model.find_caret()

        height = max(self._minimalHeight, len(
            list(self._model.all_lines())) * row_height + 2 * self._yMargin)
        width = max(self._minimalWidth, self._font.measure(
            max(list(self._model.all_lines()), key=len)) + self._numberSpacing + 2 * self._xMargin)
        self._canvas.delete("all")
        self._canvas.config(scrollregion=(0, 0, width, height))

        # TODO follow caret by scrolling left/right and up/down
        # deltaX =
        # deltaY =

        self._render_selection(update_selection)
        self._render_caret(caret_row, caret_column)

        for (i, line) in enumerate(self._model.all_lines()):
            self._canvas.create_text(self._xMargin, self._yMargin + row_height * i, font=self._font, text=str(i),
                                     anchor=NW, fill=self._canvasTextColor)
            self._canvas.create_text(self._xMargin + self._numberSpacing, self._yMargin + row_height * i,
                                     font=self._font, text=line, anchor=NW, fill=self._canvasTextColor)

    def update_selection(self, right_end_moved: bool):
        """
        Update the current selection in the TextEditorModel. If selecting is inactive, the selection will be reset to
        range [current_caret_location, current_caret_location]. Otherwise, the range will be updated in accordance with
        the new caret position.
        :param right_end_moved: was the caret at the right end of the selection
        """
        # TODO move to an more appropriate place
        new_caret_location = self._model.get_caret_location().get()
        if self._selecting_active:
            selection_before = self._model.get_selection_range()
            if right_end_moved:
                self._model.set_selection_range(LocationRange(selection_before.get_start().get(), new_caret_location))
            else:
                self._model.set_selection_range(LocationRange(new_caret_location, selection_before.get_end().get()))
        else:
            self._model.reset_selection()

    def on_timer(self):
        self._display_caret = not self._display_caret
        self.redisplay(True)
        self.after(self._caretShownDelay if not self._display_caret else self._caretShownDelay, self.on_timer)

    def on_exit(self):
        self.master.destroy()

    def get_x_y_of_line_start(self, row):
        return self._xMargin + self._numberSpacing, self._yMargin + self._font.metrics()["linespace"] * row

    def get_x_y_of_line_end(self, row):
        return (self._xMargin + self._numberSpacing + self._font.measure(self._model.get_line(row)),
                self._yMargin + self._font.metrics()["linespace"] * row)

    def get_x_y_at_caret_location(self, caret_location: Location):
        row, column = self._model.find_location(caret_location)
        return self.get_x_y_at_row_and_column(row, column)

    def get_x_y_at_row_and_column(self, row, column):
        xy_tuple = self.get_x_y_of_line_start(row)
        x, y = xy_tuple[0], xy_tuple[1]
        return x + self._font.measure(self._model.get_line(row)[:column]), y

    def row_height(self):
        return self._font.metrics()["linespace"]

    def _render_caret(self, caret_row, caret_column):
        color = self._caretColor if self._display_caret else self._canvasBackgroundColor
        x_start = self._xMargin + self._numberSpacing + self._font.measure(
            self._model.get_line(caret_row)[:caret_column])
        y_start = self._yMargin + self.row_height() * caret_row + 3
        self._canvas.create_line(x_start - 3, y_start, x_start + 3, y_start, fill=color)
        self._canvas.create_line(x_start, y_start, x_start, y_start + self._font.metrics()["linespace"] - 9, fill=color)
        self._canvas.create_line(x_start - 3, y_start + self._font.metrics()["linespace"] - 9, x_start + 3,
                                 y_start + self._font.metrics()["linespace"] - 9, fill=color)

    def _render_selection(self, update_selection=True):
        selection = self._model.get_selection_range()
        if selection.get_start().get() == selection.get_end().get():
            return

        if update_selection:
            self.__preprocess_selection(selection)

        for e in self.__processed_selection:
            self._canvas.create_rectangle(e[0][0], e[0][1], e[1][0], e[1][1] + self.row_height(),
                                          outline=self._canvasHighlightColor, fill=self._canvasHighlightColor)

    def __preprocess_selection(self, selection: LocationRange):
        sel_start_r, sel_start_c = self._model.find_location(selection.get_start())
        sel_end_r, sel_end_c = self._model.find_location(selection.get_end())

        if sel_start_r > sel_end_r or sel_start_r == sel_end_r and sel_start_c > sel_end_c:
            sel_start_r, sel_start_c, sel_end_r, sel_end_c = sel_end_r, sel_end_c, sel_start_r, sel_start_c

        if sel_start_r == sel_end_r:
            self.__processed_selection = [(self.get_x_y_at_row_and_column(sel_start_r, sel_start_c),
                                           self.get_x_y_at_row_and_column(sel_end_r, sel_end_c))]
        else:
            self.__processed_selection = [(self.get_x_y_at_row_and_column(sel_start_r, sel_start_c),
                                           self.get_x_y_of_line_end(sel_start_r))]
            self.__processed_selection += [(self.get_x_y_of_line_start(row), self.get_x_y_of_line_end(row)) for row in
                                           range(sel_start_r + 1, sel_end_r)]
            self.__processed_selection += [(self.get_x_y_of_line_start(sel_end_r),
                                            self.get_x_y_at_row_and_column(sel_end_r, sel_end_c))]

    def update_status_bar(self):
        ln, col = self._model.find_caret()
        total_rows = self._model.get_lines_count()
        self._statusbar['text'] = "Ln: {}, Col: {}\tTotal rows: {}".format(ln, col, total_rows)

    def on_key_pressed(self, e):
        if self._other_dialog_open:
            return
        keysym = e.keysym
        char = e.char

        # https://stackoverflow.com/questions/19861689/check-if-modifier-key-is-pressed-in-tkinter
        ctrl = (e.state & 0x4) != 0
        alt = (e.state & 0x8) != 0 or (e.state & 0x80) != 0
        shift = (e.state & 0x1) != 0
        self._selecting_active = shift

        # print(e.keysym, e.char, e.keycode, e.char.isprintable(), "%x" % e.state, ctrl, shift, alt)

        if keysym == "Left":
            self._display_caret = True
            caret_before = self._model.get_caret_location().get()
            self._model.move_caret_left()
            self.update_selection(caret_before == self._model.get_selection_range().get_end().get())
        elif keysym == "Right":
            self._display_caret = True
            caret_before = self._model.get_caret_location().get()
            self._model.move_caret_right()
            self.update_selection(caret_before == self._model.get_selection_range().get_end().get())
        elif keysym == "Up":
            self._display_caret = True
            caret_before = self._model.get_caret_location().get()
            self._model.move_caret_up()
            self.update_selection(caret_before == self._model.get_selection_range().get_end().get())
        elif keysym == "Down":
            self._display_caret = True
            caret_before = self._model.get_caret_location().get()
            self._model.move_caret_down()
            self.update_selection(caret_before == self._model.get_selection_range().get_end().get())
        elif keysym == "BackSpace":
            self._display_caret = True
            if self._model.get_selection_range().is_empty():
                action = self._model.execute_delete_before()
                if action is not None:
                    UndoManager.get_instance().push(action)
            else:
                self.delete_selection()
        elif keysym == "Delete":
            self._display_caret = True
            if self._model.get_selection_range().is_empty():
                action = self._model.execute_delete_after()
                if action is not None:
                    UndoManager.get_instance().push(action)
            else:
                self.delete_selection()
        elif keysym == "Return":
            # TODO what if None
            action1 = self.delete_selection(False)
            action2 = self._model.execute_insert_at_caret("\n")
            actions = [a for a in [action1, action2] if a is not None]
            if actions:
                UndoManager.get_instance().push(JumboEditAction(actions))
        elif keysym == "Escape":
            self._display_caret = True
            self.master.destroy()
        elif ctrl and keysym.lower() == "c":
            self.copy()
        elif ctrl and keysym.lower() == "x":
            self.cut()
        elif ctrl and keysym.lower() == "v":
            if shift:
                self.paste_and_take()
            else:
                self.paste()
        elif ctrl and keysym.lower() == "y":
            self.redo()
        elif ctrl and keysym.lower() == "z":
            self.undo()
        elif len(char) and char.isprintable():
            # TODO should LocationRange be immutable? Or how should I pass it around?
            action1 = self.delete_selection(False)
            action2 = self._model.execute_insert_at_caret(char)
            self._model.reset_selection()
            actions = [a for a in [action1, action2] if a is not None]
            if actions:
                UndoManager.get_instance().push(JumboEditAction(actions))

    def open_file(self):
        self._other_dialog_open = True
        try:
            f = filedialog.askopenfile(mode='r', defaultextension=".txt")
            if f is None:
                return
            text = "\n".join(f.readlines())
            self._model.set_text(text)
            f.close()
        finally:
            self._other_dialog_open = False

    def save_file(self):
        self._other_dialog_open = True
        try:
            f = filedialog.asksaveasfile(mode='w', defaultextension=".txt")
            if f is None:
                return
            text = "\n".join(self._model.all_lines())
            f.write(text)
            f.close()
        finally:
            self._other_dialog_open = False

    def undo(self):
        um = UndoManager.get_instance()
        if not um.is_undo_empty():
            um.undo()

    def redo(self):
        um = UndoManager.get_instance()
        if not um.is_redo_empty():
            um.redo()

    def copy(self):
        if not self._model.get_selection_range().is_empty():
            self._clipboardStack.push(self._model.get_selected_text())

    def cut(self):
        if not self._model.get_selection_range().is_empty():
            self._clipboardStack.push(self._model.get_selected_text())
            self.delete_selection()

    def paste(self):
        if self._clipboardStack.has_any():
            self.delete_selection()
            action = self._model.execute_insert_at_caret(self._clipboardStack.peek())
            if action is not None:
                UndoManager.get_instance().push(action)

    def paste_and_take(self):
        if self._clipboardStack.has_any():
            action1 = self.delete_selection(False)
            action2 = self._model.execute_insert_at_caret(self._clipboardStack.pop())
            actions = [a for a in [action1, action2] if a is not None]
            if actions:
                UndoManager.get_instance().push(JumboEditAction(actions))

    def delete_selection(self, push_action=True) -> EditAction:
        if self._model.get_selection_range().is_empty():
            return
        sel_left = self._model.get_selection_range().get_left()
        self._model.move_caret_to(sel_left)

        action = self._model.execute_delete_range(self._model.get_selection_range().clone())
        if push_action and action is not None:
            UndoManager.get_instance().push(action)
        self._model.set_selection_range(LocationRange(sel_left.get(), sel_left.get()))
        return action

    def clear_document(self):
        self._model.set_selection_range(LocationRange(0, self._model.get_caret_max()))
        self.delete_selection()

    def caret_to_start(self):
        self._model.move_caret_to(Location(0))

    def caret_to_end(self):
        self._model.move_caret_to(Location(self._model.get_caret_max()))
Exemplo n.º 24
0
    def Make(self):
        """ Displays all of this Month's expenses. """

        # If the ExpenseFrame exists, it will be destroyed
        try:
            self.outer.destroy()
        except AttributeError as e:
            if debug: print(e.__class__, ':: ', e)
            else: pass

        # outer is created so the delete button frames can be
        # seperated visually from the expense list frame.
        self.outer = tk.Frame(self)
        self.outer.pack()

        self.Title()

        self.ExpenseFrame = tk.Frame(self.outer)
        self.ExpenseFrame.pack(fill='both')

        # Scrollbar for expense list
        scrollbar = tk.Scrollbar(self.ExpenseFrame)
        scrollbar.pack(side='right', fill='y')

        # Columns for expense list
        dataCols = ['Date', 'Expense Type', 'Cost', 'Notes']

        self.tree = ttk.Treeview(self.ExpenseFrame,
                                 columns=dataCols,
                                 show='headings')

        Exp_Attrs = self.master.Budget.expenses.get()

        # maxWidths is used to store the max width of each column in the
        # TreeView object.
        maxWidths = dict()

        # Loop sets max for each column to 0, so each max has starting value
        for col in dataCols:
            maxWidths[col] = 0

        # Defines the font that the TreeView elements will use
        treeFont = Font(self.ExpenseFrame, 'Times', "12")

        # Inserts each expense into the Treeview object
        for values in Exp_Attrs:
            self.tree.insert('', 'end', values=values, tag='expense')

            # This loop finds the width of the largest string in each column.
            for col, item in zip(dataCols, values):
                stringWidth = treeFont.measure(item)
                if stringWidth > maxWidths[col]:
                    maxWidths[col] = stringWidth

        # This loop serves two functions:
        # 1 - Sets the headings in the expense list.
        #     Without this loop, the headings will not actually show.
        #
        # 2 - Sets the width of each column based on the largest string within
        #     each column.
        for col in dataCols:
            self.tree.heading(col, text=col)
            extra = 100
            MAX = maxWidths[col] + extra
            self.tree.column(col, width=MAX)

        # Sets the font of the TreeView elements
        self.tree.tag_configure('expense', font=treeFont)

        # 'yscroll' option must be set to scrollbar set object
        self.tree['yscroll'] = scrollbar.set

        self.tree.pack(side='left', fill='both')

        # Associates scrollbar with the Treeview object
        scrollbar.config(command=self.tree.yview)

        self.CreateDeleteButton()
Exemplo n.º 25
0
class StylableEditor( scrolledtext.ScrolledText ):
    def __init__(self, parent, **kw):
        scrolledtext.ScrolledText.__init__(self, parent, **kw)
        self._myId = None
        self._cbfnc = None
        self['wrap'] = tk.WORD
        self['undo'] = True
        self.bind('<<TextModified>>', self._onModify)
        self.bind( '<KeyPress>', self._onKeyPress )
        self.bind( '<KeyPress-minus>', self._onHyphen )
        self.bind( '<KeyPress-Return>', self._onReturn )
        self._indent:bool = False # indentation mode on/off
        self.bind( '<Control-v>', self._onPaste )
        self.isModified:bool = False
        # define some fonts
        self._textfont = Font( self, self.cget( "font" ) )
        self._boldfont = Font( self, self.cget( "font" ) )
        self._boldfont.configure( weight="bold" )
        self._italicfont = Font( self, self.cget( "font" ) )
        self._italicfont.configure( slant="italic" )
        self._bolditalicfont = Font( self, self.cget( "font" ) )
        self._bolditalicfont.configure( weight="bold", slant="italic" )
        # configure tags
        self.tag_configure( "bold", font=self._boldfont )
        self.tag_configure( "italic", font=self._italicfont )
        self.tag_configure( "bold_italic", font=self._bolditalicfont )
        self.tag_configure( RED, foreground='red' )
        self.tag_configure( GREEN, foreground='green' )
        self.tag_configure( BLUE, foreground='blue' )
        self.tag_configure( "hyper", foreground="blue", underline=1 )
        self.tag_bind( "hyper", "<Enter>", self._enterHyper )
        self.tag_bind( "hyper", "<Leave>", self._leaveHyper )
        self.tag_bind( "hyper", "<Button-1>", self._clickHyper )
        #self.tag_configure( BLACK, foreground='black' )
        self._styleRanges:StyleRanges = StyleRanges( self )

        #text_font = font.nametofont( self.cget( "font" ) )
        bullet_width = self._textfont.measure( "- " )
        em = self._textfont.measure( "m" )
        self.tag_configure( "indented", lmargin1=em, lmargin2=em + bullet_width )

        # https://stackoverflow.com/questions/40617515/python-tkinter-text-modified-callback
        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, command, *args):
        cmd = (self._orig, command) + args
        try:
            result = self.tk.call(cmd)
        except:
            return

        if command in ("insert", "delete", "replace"):
            self.event_generate("<<TextModified>>")

        return result

    def setModifyCallback(self, cbfnc) -> None:
        #the given callback function has to take 1 argument:
        #  -  evt: Event
        self._cbfnc = cbfnc

    def _onModify(self, event ):
        # triggered by <<TextModified>> virtual event.
        # the event arg only contains x and y coordinates.
        # so this method is only used for callback purposes and
        # to set the isModified flag.
        self.isModified = True
        if self._cbfnc:
            self._cbfnc( event )

    def _onKeyPress( self, event ):
        """
        If in indentation mode, check if a char is added to the end of text. If so, expand
        "indented" range appropriate.
        Chars inserted in the mid of text will be taken care of by the Text widget itself.
        """
        if not self._indent: return
        # ignore Backspace and Delete
        if event.keysym in ( "BackSpace", "Delete" ): return
        # ignore insertions in the mid of text
        if not self.index( "end - 1c" ) == self.index( "insert" ): return
        self.insert( "insert", event.char, "indented" )
        return "break"

    def _onHyphen( self, event ) -> None:
        """
        check if the hyphen was entered in column 0.
        If so, we switch to indentation mode,
        where subsequent lines are indented.
        """
        l, c = self.getCaretPosition()
        if c == 0:
            self._indent = True
            print( "indentation mode ON" )

    def _onReturn( self, event ) -> None:
        """Check if in indentation mode. If so, switch it off."""
        print( "_onReturn", event )
        if event.state == 16: # Return pressed without any other key
            self._indent = False
            print( "indentation mode OFF" )

    def _onPaste( self, event ):
        # get the clipboard data, and replace all newlines
        # with the literal string "\n"
        clipboard = self.clipboard_get()
        clipboard = clipboard.replace( "\n", "\\n" )

        # delete the selected text, if any
        try:
            start = self.index( "sel.first" )
            end = self.index( "sel.last" )
            self.delete( start, end )
        except TclError as e:
            # nothing was selected, so paste doesn't need
            # to delete anything
            pass

        # insert the modified clipboard contents
        if self._indent:
            self.insert( "insert", clipboard, "indented" )
        else:
            self.insert( "insert", clipboard )
        #return "break"  # prevents Text widget from inserting text from clipboard.

    def getCaretPosition( self ) -> Tuple[int, int]:
        """returns line and column index of caret"""
        position = self.index( 'insert' )
        l = int( position.split( '.' )[0] )
        c = int( position.split( '.' )[1] )
        return l, c

    def getValue(self) -> any:
        """
        #########comment stackoverflow:############
        It is impossible to remove the final newline that is
        automatically added by the text widget.
        – Bryan Oakley Jan 13 '18 at 14:09

        ==> that's why we do it here:
        """
        s = self.get('1.0', 'end')
        return s[:-1]

    def setValue(self, val: str) -> None:
        self.clear()
        if val:
            self.insert('1.0', val)

    def triggerStyleAction( self, styleAction:StyleAction ) -> None:
        """
        Applies or removes the triggered style to the selected text.
        """
        if styleAction == StyleAction.BOLD:
            self._handleFontStyle( "bold" )
        elif styleAction == StyleAction.ITALIC:
            self._handleFontStyle( "italic" )
        elif styleAction == StyleAction.RED_FOREGROUND:
            self._handleFontColor( RED )
        elif styleAction == StyleAction.BLUE_FOREGROUND:
            self._handleFontColor( BLUE )
        elif styleAction == StyleAction.GREEN_FOREGROUND:
            self._handleFontColor( GREEN )
        elif styleAction == StyleAction.BLACK_FOREGROUND:
            self._handleFontColor( BLACK )
        elif styleAction == StyleAction.MARK_AS_LINK:
            self._toggleLink()

    def _handleFontStyle( self, style:str ) -> None:
        """
        # style may be "bold" or "italic".
        # Basically the style action to take is determined by the first character
        # in the selected range. If style is set, unset it. If not set, set it.
        # It's more complicated if e.g. "bold" is to be set and "italic" is already set:
        #    - compare the ranges of the two styles and take proper action, eg:

                |      italic    |   -> set
                4                15
                        |      bold      |  -> to set according to selection
                        8               20
                ==> from 8 to 15: make italic and bold (tag "bold_italic")
                ==> from 16 to 20: make bold (tag "bold")
        """
        if style in (BOLD, ITALIC, BOLD_ITALIC):
            self._toggleFontStyle( style, self.index( "sel.first" ), self.index( "sel.last" ) )

        return

    def _toggleFontStyle( self, style:str, start:str, stop:str ) -> None:
        if self.isSet( style, start ):
            self._unsetFontStyle( style, start, stop )
        else:
            self._setFontStyle( style, start, stop )

    def isSet( self, style:str, idx:str ) -> bool:
        # seq = self.tag_nextrange( style, idx, idx ) # returns an empty tuple :-o
        # Note: even if style is not set, a tuple of len == 1 will be given.
        # The one and only element of that tuple is the string "None".
        # That contradicts to the tag_nextrange documentation, which pretends to return an empty string.
        #return True if len( seq ) > 1 else False
        stylelist = self.tag_names( idx )
        return True if style in stylelist or ( style in (BOLD, ITALIC) and BOLD_ITALIC in stylelist ) else False

    def _unsetFontStyle( self, style:str, start:str, stop:str ):
        """
        removes the given style within the given range defined by start and stop.
        Takes style "bold_italic" into account: if bold_italic is set and bold is to be removed,
        italic will remain. If italic is to be removed, bold will remain.
        """
        """
        Processing:
        Inspect the different style ranges within start-stop.
            - unset style ranges with matching style
            - ignore style ranges with different style (except BOLD_ITALIC if style to unset is not BOLD_ITALIC)
            - BOLD_ITALIC style ranges: remove BOLD_ITALIC and set ITALIC if style to unset is BOLD or set BOLD 
              if style to unset is ITALIC.
        """
        while self.compare( start, "<", stop ):
            tag = self._styleRanges.getFontStyleAndRange( start, stop )
            if tag.name == style:
                self.tag_remove( style, tag.range.start, tag.range.stop )
            elif tag.name == BOLD_ITALIC:
                self.tag_remove( BOLD_ITALIC, tag.range.start, tag.range.stop )
                self.tag_add( BOLD if style == ITALIC else ITALIC, tag.range.start, tag.range.stop )
            start = tag.range.stop

    def _setFontStyle( self, style: str, start: str, stop: str ):
        """
        applies the given style to the given range.
        start and stop are expected in the form "line.column"
        If bold is already set and italic is to add remove bold and set BOLD_ITALIC.
        If italic is already set and bold is to add remove bold and set BOLD_ITALIC.

        |                                | -> Range to set style
        start                            stop

            |   |         |  |             -> ranges where another style is already set. Replace that style by BOLD_ITALIC
        """
        while self.compare( start, "<", stop ):
            tag = self._styleRanges.getFontStyleAndRange( start, stop )
            if tag.name == "":
                # no style applied at start. Apply style.
                self.tag_add( style, tag.range.start, tag.range.stop )
            else:
                # BOLD, ITALIC or BOLD_ITALIC set
                if tag.name in ( BOLD, ITALIC ):
                    if tag.name != style:
                        # if BOLD or ITALIC, remove it and set BOLD_ITALIC
                        self.tag_remove( tag.name, tag.range.start, tag.range.stop )
                        #print( "_setFontStyle: adding bold_italic from %s to %s" % (tag.range.start, tag.range.stop) )
                        self.tag_add( BOLD_ITALIC, tag.range.start, tag.range.stop )
                # BOLD_ITALIC can be ignored.
            start = tag.range.stop

    def _handleFontColor( self, fontcolor:str ) -> None:
        self._unsetFontColors( "sel.first", "sel.last" )
        if fontcolor != BLACK:
            self._setFontColor( fontcolor, "sel.first", "sel.last" )

    def _setFontColor( self, fontcolor:str, start:str, stop:str ):
        self.tag_add( fontcolor, start, stop )

    def _unsetFontColors( self, start:str, stop:str ):
        for color in (BLUE, RED, GREEN):
            try:
                self.tag_remove( color, start, stop )
            except:
                pass

    def _toggleLink( self ):
        start, stop = self._getUrlBoundaries( "insert" )
        tag_names = self.tag_names( start )
        if "hyper" in tag_names:
            self.tag_remove( "hyper", start, stop )
        else:
            self.tag_add( "hyper", start, stop )

    def _openUrl( self, url ):
        webbrowser.open_new_tab( url )

    def _enterHyper( self, event ):
        self.config( cursor="hand2" )

    def _leaveHyper( self, event ):
        self.config( cursor="" )

    def _clickHyper( self, event ):
        start, stop = self._getUrlBoundaries( CURRENT )
        url = self.get( start, stop )
        self._openUrl( url )

    def _getUrlBoundaries( self, idx:Any ) -> Tuple[Any, Any]:
        """
        Returns the complete URL starting from index "insert".
        """
        idxA = idxB = self.index( idx )
        #search to the left 'til a space, tab or new line
        if self.compare( idxA, ">", "1.0" ):
            idxA = self.index( idxA + "-1c" )
            c = ""
            while self.compare( idxA, ">", "1.0" ):
                c = self.get( idxA, self.index( idxA + "+1c" ) )
                if c in (" ", "\n", "\t"):
                    idxA = self.index( idxA + "+1c" )
                    break
                idxA = self.index( idxA + "-1c" )

        #search to the right 'til a space, tab or new line
        c = ""
        while self.compare( idxB, "<", "end" ):
            c = self.get( idxB, self.index( idxB + "+1c" ) )
            if c in ("", "\n", "\t"):
                break
            idxB = self.index( idxB + "+1c" )

        return (idxA, idxB)


    def testIterate( self, start, stop ):
        idx = self.index( start )
        stop = self.index( stop )
        while self.compare( idx,  "<", stop ):
            print( idx )
            idx = self.index( idx + "+1c" )
        print( "READY." )

    def getStylesAsString( self ) -> str:
        tagstring:str = ""
        tagnames = self.tag_names() # get a tuple of tagnames
        for tagname in tagnames:
            if tagname != "sel":
                ranges = self.tag_ranges( tagname )
                if len( ranges ) > 0:
                    tagstring += ( tagname + ":" )
                    for i in range( 0, len( ranges ), 2 ):
                        start:str = str( ranges[i] )
                        stop:str = str( ranges[i + 1] )
                        tagstring += (start + "," + stop + ";")
                    tagstring = tagstring[:-1] #remove trailing ";"
                    tagstring += "$"
        if len( tagstring ) > 0:
            tagstring = tagstring[:-1] #remove trailing "$"
        #print( "tagstring: ", tagstring )
        return tagstring

    def setStylesFromString( self, stylesstr:str ) -> None:
        if not stylesstr: return

        styles = stylesstr.split( "$" ) # -> bold:1.3,1.6;2.2.,3.4
        for style in styles:
            parts = style.split( ":") #parts[0]: bold  parts[1]:1.3,1.6;2.2,3.4
            ranges = parts[1].split( ";" ) # -> ranges: 1.2,1.6
            for range in ranges:
                startstop = range.split( "," )
                start = startstop[0]
                stop = startstop[1]
                self.tag_add( parts[0], start, stop )

    def resetModified( self ):
        self.isModified = False

    def clear(self) -> None:
        self.delete('1.0', 'end')

    def setFont( self, font ):
        # font example: font = ("Times New Roman", 15)
        pass

    def setSelectionFont( self, font ):
        #font example: font = ("Times New Roman", 15)
        pass

    def setSelectionBold( self, on:bool ) -> None:
        pass

    def setSelectionItalic( self, on:bool ) -> None:
        pass
Exemplo n.º 26
0
    def create_blocks_fst(self):
        text_height = Font.metrics(self.myFont, 'linespace')
        top_line = 20

        # block for Expr
        self.canvas.create_polygon(self.command_block_coords(
            50, top_line, text_height + 15, 110),
                                   fill='violet red',
                                   outline='purple',
                                   tags='expr_block')
        self.canvas.create_polygon(self.inside_block_coords(
            55, top_line + 10, text_height, 90),
                                   fill='light pink',
                                   tags='expr_block')
        top_line += text_height + 25

        # block for return command
        txt_len = Font.measure(self.myFont, 'return')
        self.canvas.create_polygon(self.command_block_coords(
            50, top_line, text_height + 15, 10 + txt_len + 20 + txt_len),
                                   fill='violet red',
                                   outline='purple',
                                   tags='return_block')
        self.canvas.create_text(57,
                                top_line + 8,
                                anchor=NW,
                                text='return',
                                tags='return_block',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            52 + 1.3 * txt_len, top_line + 10, text_height, txt_len),
                                   fill='light pink',
                                   tags='return_block')
        top_line += text_height + 25

        # block for variable assignment command
        txt_len = Font.measure(self.myFont, 'variable')
        txt2_len = Font.measure(self.myFontBold, '=')
        self.canvas.create_polygon(self.command_block_coords(
            50, top_line, text_height + 15,
            10 + txt_len + 15 + txt2_len + 15 + txt_len + 10),
                                   fill='violet red',
                                   outline='purple',
                                   tags='variable_block')
        self.canvas.create_polygon(self.inside_block_coords(
            60, top_line + 10, text_height, txt_len + 5),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='variable_block')
        self.canvas.create_text(67,
                                top_line + 10,
                                anchor=NW,
                                text="variable",
                                tags='variable_block',
                                font=self.myFont)
        self.canvas.create_text(67 + txt_len + 8,
                                top_line + 10,
                                anchor=NW,
                                text="=",
                                tags='variable_block',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            67 + txt_len + 8 + txt2_len + 5, top_line + 10, text_height,
            txt_len),
                                   fill='light pink',
                                   tags='variable_block')
        top_line += text_height + 25

        # block for if statement
        txt_len = Font.measure(self.myFont, 'if')
        self.canvas.create_polygon(self.control_block_coords(
            50, top_line, text_height + 15,
            10 + txt_len + 15 + 8 * txt_len + 15, 25)[0],
                                   fill='orange',
                                   outline='chocolate',
                                   tags='if_block')
        self.canvas.create_polygon(self.control_block_coords(
            50, top_line, text_height + 15,
            10 + txt_len + 10 + 9 * txt_len + 15, 25)[1],
                                   fill='orange',
                                   outline='chocolate',
                                   tags='if_block')
        self.canvas.create_text(67,
                                top_line + 10,
                                anchor=NW,
                                text='if',
                                tags='if_block',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            67 + 2 * txt_len, top_line + 9, text_height, 8 * txt_len),
                                   fill='peachpuff',
                                   tags='if_block')

        top_line += text_height + 75

        # block for while statement
        txt_len = Font.measure(self.myFont, 'while')
        self.canvas.create_polygon(self.control_block_coords(
            50, top_line, text_height + 15, 10 + txt_len + 15 + txt_len + 25,
            25)[0],
                                   fill='orange',
                                   outline='chocolate',
                                   tags='while_block')
        self.canvas.create_polygon(self.control_block_coords(
            50, top_line, text_height + 15, 10 + txt_len + 15 + txt_len + 25,
            25)[1],
                                   fill='orange',
                                   outline='chocolate',
                                   tags='while_block')
        self.canvas.create_text(60,
                                top_line + 10,
                                anchor=NW,
                                text='while',
                                tags='while_block',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            65 + txt_len, top_line + 9, text_height, 55),
                                   fill='peachpuff',
                                   tags='while_block')

        top_line += text_height + 75

        txt_len = Font.measure(self.myFont, 'print()')
        self.canvas.create_polygon(self.inside_block_coords(
            50, top_line, text_height + 4, txt_len + 30 + txt_len),
                                   fill='limegreen',
                                   outline='green',
                                   tags='print')
        self.canvas.create_text(65,
                                top_line + 2,
                                anchor=NW,
                                text="print(",
                                tags='print',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            60 + txt_len, top_line + 2, text_height, txt_len),
                                   fill='lightgreen',
                                   tags='print')
        self.canvas.create_text(70 + 2 * txt_len,
                                top_line + 2,
                                anchor=NW,
                                text=")",
                                tags='print',
                                font=self.myFont)

        top_line += text_height + 15

        # complete print block
        self.canvas.create_polygon(self.command_block_coords(
            50, top_line, 2 * text_height + 5, 135),
                                   fill='violet red',
                                   outline='purple',
                                   tags='print_complete')
        self.canvas.create_polygon(self.inside_block_coords(
            55, top_line + 10, text_height + 4, txt_len + 30 + txt_len),
                                   fill='limegreen',
                                   outline='green',
                                   tags='print_complete')
        self.canvas.create_text(65,
                                top_line + 12,
                                anchor=NW,
                                text="print(",
                                tags='print_complete',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            60 + txt_len, top_line + 12, text_height, txt_len),
                                   fill='lightgreen',
                                   tags='print_complete')
        self.canvas.create_text(70 + 2 * txt_len,
                                top_line + 12,
                                anchor=NW,
                                text=")",
                                tags='print_complete',
                                font=self.myFont)
        top_line += text_height + 25

        # right side column
        # equals statement block
        top_line = 20
        txt_len = Font.measure(self.myFontBold, ' == ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='equals')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.2 * txt_len),
                                   fill='sky blue',
                                   tags='equals')
        self.canvas.create_text(312 + 1.2 * txt_len + 10,
                                top_line + 2,
                                anchor=NW,
                                text="==",
                                tags='equals',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 1.2 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.2 * txt_len),
                                   fill='sky blue',
                                   tags='equals')
        top_line += text_height + 15

        # not equal statement block
        txt_len = Font.measure(self.myFontBold, ' != ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.5 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='not_equal')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.5 * txt_len),
                                   fill='sky blue',
                                   tags='not_equal')
        self.canvas.create_text(312 + 1.5 * txt_len + 10,
                                top_line + 2,
                                anchor=NW,
                                text="!=",
                                tags='not_equal',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 1.5 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.5 * txt_len),
                                   fill='sky blue',
                                   tags='not_equal')
        top_line += text_height + 15

        # grater than block
        txt_len = Font.measure(self.myFontBold, ' > ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='greater')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 2 * txt_len),
                                   fill='sky blue',
                                   tags='greater')
        self.canvas.create_text(312 + 2 * txt_len + 10,
                                top_line + 2,
                                anchor=NW,
                                text=">",
                                tags='greater',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 2 * txt_len + txt_len + 10, top_line + 2, text_height,
            2 * txt_len),
                                   fill='sky blue',
                                   tags='greater')
        top_line += text_height + 15

        # smaller than block
        txt_len = Font.measure(self.myFontBold, ' < ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='smaller')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 2 * txt_len),
                                   fill='sky blue',
                                   tags='smaller')
        self.canvas.create_text(312 + 2 * txt_len + 10,
                                top_line,
                                anchor=NW,
                                text="<",
                                tags='smaller',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 2 * txt_len + txt_len + 10, top_line + 2, text_height,
            2 * txt_len),
                                   fill='sky blue',
                                   tags='smaller')
        top_line += text_height + 15

        # grater or equal block
        txt_len = Font.measure(self.myFontBold, ' >= ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='greater_or_equal')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.2 * txt_len),
                                   fill='sky blue',
                                   tags='greater_or_equal')
        self.canvas.create_text(312 + 1.2 * txt_len + 10,
                                top_line,
                                anchor=NW,
                                text=">=",
                                tags='greater_or_equal',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 1.2 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.2 * txt_len),
                                   fill='sky blue',
                                   tags='greater_or_equal')
        top_line += text_height + 15

        # smaller or equal block
        txt_len = Font.measure(self.myFontBold, ' <= ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='smaller_or_equal')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.2 * txt_len),
                                   fill='sky blue',
                                   tags='smaller_or_equal')
        self.canvas.create_text(312 + 1.2 * txt_len + 10,
                                top_line,
                                anchor=NW,
                                text="<=",
                                tags='smaller_or_equal',
                                font=self.myFontBold)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 1.2 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.2 * txt_len),
                                   fill='sky blue',
                                   tags='smaller_or_equal')
        top_line += text_height + 15

        # or block
        txt_len = Font.measure(self.myFontBold, ' or ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.7 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='or')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.7 * txt_len),
                                   fill='sky blue',
                                   tags='or')
        self.canvas.create_text(312 + 1.7 * txt_len + 10,
                                top_line,
                                anchor=NW,
                                text="or",
                                tags='or',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            310 + 1.7 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.7 * txt_len),
                                   fill='sky blue',
                                   tags='or')
        top_line += text_height + 15

        # and block
        txt_len = Font.measure(self.myFont, 'and ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4,
            15 + 2 * 1.2 * txt_len + txt_len + 15),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='and')
        self.canvas.create_polygon(self.inside_block_coords(
            310, top_line + 2, text_height, 1.2 * txt_len),
                                   fill='sky blue',
                                   tags='and')
        self.canvas.create_text(310 + 1.2 * txt_len + 12,
                                top_line + 2,
                                anchor=NW,
                                text="and",
                                tags='and',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            312 + 1.2 * txt_len + txt_len + 10, top_line + 2, text_height,
            1.2 * txt_len),
                                   fill='sky blue',
                                   tags='and')
        top_line += text_height + 15

        # not block
        txt_len = Font.measure(self.myFont, 'not ')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height + 4, 10 + txt_len + 10 + 1.6 * txt_len),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='not')
        self.canvas.create_text(312,
                                top_line + 2,
                                anchor=NW,
                                text='not',
                                tags='not',
                                font=self.myFont)
        self.canvas.create_polygon(self.inside_block_coords(
            312 + txt_len, top_line + 2, text_height, 1.6 * txt_len),
                                   fill='sky blue',
                                   tags='not')
        top_line += text_height + 15

        # block for variable
        txt_len = Font.measure(self.myFont, 'variable')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height, txt_len + 20),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='variable')
        self.canvas.create_text(315,
                                top_line,
                                anchor=NW,
                                text="variable",
                                tags='variable',
                                font=self.myFont)

        # None block
        txt_len = Font.measure(self.myFont, 'None')
        self.canvas.create_polygon(self.inside_block_coords(
            400, top_line, text_height, txt_len + 20),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='none')
        self.canvas.create_text(415,
                                top_line,
                                anchor=NW,
                                text="None",
                                tags='none',
                                font=self.myFont)

        top_line += text_height + 15

        # block for creating number
        txt_len = Font.measure(self.myFont, 'number')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height, txt_len + 20),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='number')
        self.canvas.create_text(315,
                                top_line,
                                anchor=NW,
                                text="number",
                                tags='number',
                                font=self.myFont)

        # True block
        txt_len = Font.measure(self.myFont, 'True')
        self.canvas.create_polygon(self.inside_block_coords(
            400, top_line, text_height, txt_len + 20),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='true')
        self.canvas.create_text(415,
                                top_line,
                                anchor=NW,
                                text="True",
                                tags='true',
                                font=self.myFont)
        top_line += text_height + 15

        # block for creating string
        txt_len = Font.measure(self.myFont, 'string')
        self.canvas.create_polygon(self.inside_block_coords(
            300, top_line, text_height, txt_len + 20),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='string')
        self.canvas.create_text(315,
                                top_line,
                                anchor=NW,
                                text="string",
                                tags='string',
                                font=self.myFont)

        # False block
        txt_len = Font.measure(self.myFont, 'False')
        self.canvas.create_polygon(self.inside_block_coords(
            400, top_line, text_height, txt_len + 25),
                                   fill='dodger blue',
                                   outline='steel blue',
                                   tags='false')
        self.canvas.create_text(415,
                                top_line,
                                anchor=NW,
                                text="False",
                                tags='false',
                                font=self.myFont)
Exemplo n.º 27
0
class Calendar(ttk.Frame):
    """
    Graphical date selection widget, with callbacks.  To change
    the language, use the ``locale`` library with the appropriate
    settings for the target language.  For instance, to display
    the ``Calendar`` widget in German, you might use::

        locale.setlocale(locale.LC_ALL, 'deu_deu')

    :param parent: the parent frame
    :param callback: the callable to be executed on selection
    :param year: the year as an integer, i.e. `2020`
    :param month: the month as an integer; not zero-indexed; i.e.
    "1" will translate to "January"
    :param day: the day as an integer; not zero-indexed
    :param kwargs: tkinter.frame keyword arguments
    """
    timedelta = datetime.timedelta
    datetime = datetime.datetime

    def __init__(self, parent, callback: callable = None,
                 year: int = None, month: int = None, day: int = None,
                 **kwargs):
        # remove custom options from kw before initializing ttk.Frame
        fwday = calendar.SUNDAY
        now = self.datetime.now()
        year = year if year else now.year
        month = month if month else now.month
        day = day if day else now.day
        locale = kwargs.pop('locale', None)
        sel_bg = kwargs.pop('selectbackground', '#ecffc4')
        sel_fg = kwargs.pop('selectforeground', '#05640e')

        self._date = self.datetime(year, month, day)
        self._selection = None  # no date selected
        self.callback = callback

        super().__init__(parent, **kwargs)

        self._cal = _get_calendar(locale, fwday)

        self.__setup_styles()       # creates custom styles
        self.__place_widgets()      # pack/grid used widgets
        self.__config_calendar()    # adjust calendar columns and setup tags
        # configure a _canvas, and proper bindings, for selecting dates
        self.__setup_selection(sel_bg, sel_fg)

        # store items ids, used for insertion later
        self._items = [
            self._calendar.insert('', 'end', values='') for _ in range(6)
        ]

        # insert dates in the currently empty calendar
        self._build_calendar()

    def __setitem__(self, item, value):
        if item in ('year', 'month'):
            raise AttributeError("attribute '%s' is not writeable" % item)
        elif item == 'selectbackground':
            self._canvas['background'] = value
        elif item == 'selectforeground':
            self._canvas.itemconfigure(self._canvas.text, item=value)
        else:
            ttk.Frame.__setitem__(self, item, value)

    def __getitem__(self, item):
        if item in ('year', 'month'):
            return getattr(self._date, item)
        elif item == 'selectbackground':
            return self._canvas['background']
        elif item == 'selectforeground':
            return self._canvas.itemcget(self._canvas.text, 'fill')
        else:
            r = ttk.tclobjs_to_py(
                {item: ttk.Frame.__getitem__(self, item)}
            )
            return r[item]

    def __setup_styles(self):
        # custom ttk styles
        style = ttk.Style(self.master)

        def arrow_layout(dir):
            return [
                ('Button.focus', {
                    'children': [('Button.%sarrow' % dir, None)]
                })
            ]

        style.layout('L.TButton', arrow_layout('left'))
        style.layout('R.TButton', arrow_layout('right'))

    def __place_widgets(self):
        # header frame and its widgets
        hframe = ttk.Frame(self)
        lbtn = ttk.Button(hframe,
                          style='L.TButton',
                          command=self._prev_month)
        rbtn = ttk.Button(hframe,
                          style='R.TButton',
                          command=self._next_month)
        self._header = ttk.Label(hframe, width=15, anchor='center')
        # the calendar
        self._calendar = ttk.Treeview(self, show='',
                                      selectmode='none', height=7)

        # pack the widgets
        hframe.pack(in_=self, side='top', pady=4, anchor='center')
        lbtn.grid(in_=hframe)
        self._header.grid(in_=hframe, column=1, row=0, padx=12)
        rbtn.grid(in_=hframe, column=2, row=0)
        self._calendar.pack(in_=self, expand=1, fill='both', side='bottom')

    def __config_calendar(self):
        cols = self._cal.formatweekheader(3).split()

        self._calendar['columns'] = cols
        self._calendar.tag_configure('header', background='grey90')
        self._calendar.insert('', 'end', values=cols, tag='header')

        # adjust its columns width
        font = Font()
        maxwidth = max(font.measure(col) for col in cols)
        for col in cols:
            self._calendar.column(
                col, width=maxwidth, minwidth=maxwidth, anchor='e'
            )

    def __setup_selection(self, sel_bg, sel_fg):
        self._font = Font()
        self._canvas = canvas = tk.Canvas(
            self._calendar, background=sel_bg,
            borderwidth=0, highlightthickness=0
        )
        canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w')

        canvas.bind('<ButtonPress-1>', lambda evt: canvas.place_forget())
        self._calendar.bind('<Configure>', lambda evt: canvas.place_forget())
        self._calendar.bind('<ButtonPress-1>', self._pressed)

    def __minsize(self, evt):
        width, height = self._calendar.master.geometry().split('x')
        height = height[:height.index('+')]
        self._calendar.master.minsize(width, height)

    def _build_calendar(self):
        year, month = self._date.year, self._date.month

        # update header text (Month, YEAR)
        header = self._cal.formatmonthname(year, month, 0)
        self._header['text'] = header.title()

        # update calendar shown dates
        cal = self._cal.monthdayscalendar(year, month)
        for indx, item in enumerate(self._items):
            week = cal[indx] if indx < len(cal) else []
            fmt_week = [('%02d' % day) if day else '' for day in week]
            self._calendar.item(item, values=fmt_week)

    def _show_selection(self, text, bbox):
        """
        Configure canvas for a new selection.
        """
        x, y, width, height = bbox

        textw = self._font.measure(text)

        canvas = self._canvas
        canvas.configure(width=width, height=height)
        canvas.coords(canvas.text, width - textw, height / 2 - 1)
        canvas.itemconfigure(canvas.text, text=text)
        canvas.place(in_=self._calendar, x=x, y=y)

    # Callbacks

    def _pressed(self, evt):
        """
        Clicked somewhere in the calendar.
        """
        x, y, widget = evt.x, evt.y, evt.widget
        item = widget.identify_row(y)
        column = widget.identify_column(x)

        if not column or not (item in self._items):
            # clicked in the weekdays row or just outside the columns
            return

        item_values = widget.item(item)['values']
        if not len(item_values):  # row is empty for this month
            return

        text = item_values[int(column[1]) - 1]
        if not text:  # date is empty
            return

        bbox = widget.bbox(item, column)
        if not bbox:  # calendar not visible yet
            return

        # update and then show selection
        text = '%02d' % text
        self._selection = (text, item, column)
        self._show_selection(text, bbox)

        if self.callback is not None:
            self.callback()

    def add_callback(self, callback: callable):
        """
        Adds a callback to call when the user clicks on a date

        :param callback: a callable function
        :return: None
        """
        self.callback = callback

    def _prev_month(self):
        """
        Updated calendar to show the previous month.
        """
        self._canvas.place_forget()

        self._date = self._date - self.timedelta(days=1)
        self._date = self.datetime(self._date.year, self._date.month, 1)
        self._build_calendar()  # reconstruct calendar

    def _next_month(self):
        """
        Update calendar to show the next month.
        """
        self._canvas.place_forget()

        year, month = self._date.year, self._date.month
        self._date = self._date + self.timedelta(
            days=calendar.monthrange(year, month)[1] + 1)
        self._date = self.datetime(self._date.year, self._date.month, 1)
        self._build_calendar()  # reconstruct calendar

    @property
    def selection(self):
        """
        Return a datetime representing the current selected date.
        """
        if not self._selection:
            return None

        year, month = self._date.year, self._date.month
        return self.datetime(year, month, int(self._selection[0]))
Exemplo n.º 28
0
    def address_UI_init(self):
        #            0        1
        #    +------------+--------+
        #    | menu                |
        #    +------------+--------+
        #    | amt Dashper|        |
        #  0 | balance    |  QR    |
        #    | NEEDED     |addr cpy|
        #    +------------+--------+
        #  1 |         SEND        |
        #    +---------------------+
        #  2 | Addr search?        |
        #    +---------------------+
        #  3 | Addrs list          |
        #
        #
        #

        # ====== Dash to send =======
        ds_frame = Frame(self)
        ## ------ amt -------
        Label(ds_frame, text='DASH per address:').grid(row=0, column=0)
        self.amt_per_address = StringVar()
        self.amt_per_address.set('0.00')
        Spinbox(ds_frame,
                textvariable=self.amt_per_address,
                from_=0.000,
                to=1000,
                increment=0.001,
                width=10,
                format='%6.8f').grid(row=0, column=1)

        ## ------ balance
        self.lb_balance = StringVar()
        Label(ds_frame, text='Current Balance:').grid(row=1, column=0)
        Label(ds_frame, textvariable=self.lb_balance).grid(row=1, column=1)

        self.needed_style = Style()
        self.needed_style.configure('BW.TLabel', foreground='green')
        self.lb_needed = StringVar()
        Label(ds_frame, text='DASH needed:').grid(row=2, column=0)
        Label(ds_frame, textvariable=self.lb_needed,
              style='BW.TLabel').grid(row=2, column=1)

        ds_frame.grid(row=0, column=0)

        # ====== QR code =======
        qr_frame = Frame(self)
        self.qr_image = PIL.ImageTk.PhotoImage(make_qr_im(self.address))
        im_label = Label(qr_frame, compound=BOTTOM, image=self.qr_image)
        im_label.image = self.qr_image
        im_label.grid(row=0, column=0, columnspan=3)

        ## ------- addr -------
        self.qr = StringVar()
        self.qr.set(split_addr(self.address))
        qr_label = Label(qr_frame, textvariable=self.qr)
        qr_label.grid(row=1, column=0, columnspan=2)

        ## ------ cpy ---------
        Button(qr_frame,
               text='Copy',
               width=4,
               command=self.address_to_clipboard).grid(row=1, column=2, padx=5)
        qr_frame.grid(row=0, column=1)

        # ------ send
        self.needed_style = Style()
        self.needed_style.configure('C.TButton', font=('Sans', '10', 'bold'))
        self.cb_send = Button(self,
                              text='SEND',
                              style='C.TButton',
                              command=self.send)
        self.cb_send.grid(row=1, column=0, columnspan=2, pady=5)
        self.cb_send.config(state=DISABLED)

        # ------ addresses -------
        fr = Frame(master=self)
        fr.grid(sticky=N + E + S + W, row=2, column=0, columnspan=2)

        # tv_addresses
        style = Style()
        style.configure('Treeview', font=('Consolas', 10))
        consolas = Font(family='Consolas', size=10)
        font_w = consolas.measure('m')
        self.tv_addresses = Treeview(fr,
                                     show='tree headings',
                                     columns=['Balance'])
        self.tv_addresses.column('#0', width=38 * font_w, anchor=W, stretch=0)
        self.tv_addresses.heading('Balance', text='Balance')
        self.tv_addresses.column('Balance', anchor=E)
        self.tv_addresses.heading('#0', text='Address')
        self.tv_addresses.pack(side=LEFT, expand=True, fill='both')

        sb = Scrollbar(fr)
        sb.pack(side=RIGHT, fill='y')

        self.tv_addresses.config(yscrollcommand=sb.set)
        sb.config(command=self.tv_addresses.yview)

        # ------ all columns in place so configure them to auto-size
        Grid.columnconfigure(self, 0, weight=1)
        Grid.columnconfigure(self, 1, weight=1)

        Grid.rowconfigure(self, 0, weight=1)
        Grid.rowconfigure(self, 1, weight=1)
        Grid.rowconfigure(self, 2, weight=1)
        Grid.rowconfigure(self, 4, weight=1)

        # Some events after all variables have been initialised
        self.amt_per_address.trace('w', self.recalc_needed)
Exemplo n.º 29
0
class DNAStrandInterface: 
    ## Valid DNA symbols.
    symbols = 'atcg'
    complSymbols = {
        'a':'t',
        't':'a',
        'c':'g',
        'g':'c'
    }
    def __init__(self, master = None, data1 = "", data2 = ""): 
        
        self.master = master 
        
        self.canResize = 0
        self.helpIsOpen = False

        #text fonts
        self.font = Font(family="Courier", size=22)
        self.widthOfChar = self.font.measure("a")
        self.heightOfChar = self.font.metrics("linespace")


        self.fontHelp = Font(family="Courier", size=12)
        self.widthOfCharHelp = self.fontHelp.measure("a")
        self.heightOfCharHelp = self.fontHelp.metrics("linespace")

        
        self.canvas = Canvas(master)
        self.recenter()
        
        self.dnaMoving = False

        self.x = 0
        self.y = 0
        
        self.getData()
        self.movement() 


    #initial scene to choose method of input data   
    def getData(self):
        self.buttonFile = Button(self.master, text = "Get data from file", command = self.getDataFile)
        self.buttonType = Button(self.master, text = "Type data", command = self.getDataType)
        
        self.buttonFile.place(relx=0.5, rely=0.4, anchor=CENTER)
        self.buttonType.place(relx=0.5, rely=0.6, anchor=CENTER)
        self.canvas.pack()

    #destroys initial scene
    def destroyInitMenu(self):  
        self.buttonFile.destroy()    
        self.buttonType.destroy()

    #get input from file
    #generates error if the path is invalid    
    def getEntryFileData(self):
        path = self.entryFile.get() 
        
        try:
            with open(path, 'r') as f:
                data1 = f.readline()
                if(len(data1) >= 1 and data1[len(data1)-1]=='\n'):
                    data1 = data1[:-1]
                data2 = f.readline()
                if(len(data2) >= 1 and data2[len(data2)-1]=='\n'):
                    data2 = data2[:-1]
                f.close()
                self.destroyDataFile()
                self.setData(data1, data2)
        except IOError as e:
            print("Invalid path")
            self.canvas.destroy()
            exit()

    #destroys data file scene
    def destroyDataFile(self):        
        self.entryFile.destroy()
        self.labelFile.destroy()
        self.buttonGetDataFile.destroy()    
    
    #creates data file scene
    def getDataFile(self):            
        self.destroyInitMenu()
        txt1 = "Path: "
        self.labelFile = Label(master, text=txt1, font = self.font)
        self.entryFile = Entry(self.master) 
        self.buttonGetDataFile = Button(self.master, text = "ENTER", command=self.getEntryFileData)
        
        self.buttonGetDataFile.place(relx=0.5, rely=0.7, anchor=CENTER)
        self.labelFile.place(relx=0.4, rely=0.5, anchor=CENTER)
        self.entryFile.place(relx=0.6, rely=0.5, anchor=CENTER)
        
    #creates data type scene
    def getDataType(self):            
        self.destroyInitMenu()
        txt1 = "DNA strand 1: "
        self.labelDna1 = Label(master, text=txt1, font = self.font)
        txt2 = "DNA strand 2: "
        self.labelDna2 = Label(master, text=txt2, font = self.font)
        self.entryDna1 = Entry(self.master) 
        self.entryDna2 = Entry(self.master) 
        self.buttonGetDataType = Button(self.master, text = "ENTER", command=self.getEntryData)
        self.labelDna1.place(relx=0.4, rely=0.2, anchor=CENTER)
        self.entryDna1.place(relx=0.7, rely=0.2, anchor=CENTER)
        self.labelDna2.place(relx=0.4, rely=0.4, anchor=CENTER)
        self.entryDna2.place(relx=0.7, rely=0.4, anchor=CENTER)
        self.buttonGetDataType.place(relx=0.5, rely=0.7, anchor=CENTER)
        self.canvas.pack()


    #destroys data type scene
    def destroyDataType(self):
        self.entryDna1.destroy()
        self.labelDna1.destroy()
        self.entryDna2.destroy()
        self.labelDna2.destroy()
        self.buttonGetDataType.destroy()

    #gets input from typing
    def getEntryData(self):
        data1 = self.entryDna1.get()
        data2 = self.entryDna2.get()
        self.destroyDataType()
        self.setData(data1, data2)

    #test if given data is valid
    def isValid(self, data):
        if(data == ""):
            return False
        valid = True
        for i in (data):
            flag = False
            for j in(self.symbols):
                if (i == j):
                    flag = True
                    break
            if flag == False:
                valid = False
                break
        return valid

    #generates error if given data is invalid
    def validateData(self, data1, data2):
        try:
            if self.isValid(data1) == False:
                raise ValueError('invalid givenData strand1')
            if self.isValid(data2) == False:
                raise ValueError('invalid givenData strand2')
        except ValueError as error:
                print(str(error))
                self.canvas.destroy()
                exit()


    #sets data1 and data2 and creates dna moving scene
    def setData(self, data1, data2):

        self.canResize = 0
        self.data1 = data1.replace('A','a').replace('T','t').replace('G','g').replace('C','c')
        self.data2 = data2.replace('A','a').replace('T','t').replace('G','g').replace('C','c')
        self.validateData(self.data1, self.data2)
        self.reset()

        #creates strands with the given data
        self.strand1 = self.canvas.create_text( 
                        self.center['x'], self.center['y'], text = self.data1, font = self.font )
        self.strand2 = self.canvas.create_text( 
                        self.center['x'] + self.x, self.center['y'] + self.y + self.widthOfChar, text = self.data2, font = self.font)
        
        #creates help message
        self.helpMSG = self.canvas.create_text( 
                         10 + len("h - help")*self.widthOfCharHelp/2.0, 10, text = "h - help", font = self.fontHelp, fill="red")
        self.helpList = ["Shift-L - shuffle", "Escape - exit", "m - got to position", "    of maximum matches", "right key - move right", "left key - move left", "upper key - move up", "down key - move down", "Tab - reset"] 
        self.helpPosX = [ 0, 0, 0, 0,(25*self.widthOfCharHelp), (25*self.widthOfCharHelp), (25*self.widthOfCharHelp), (25*self.widthOfCharHelp), (25*self.widthOfCharHelp)]
        self.helpPosY = [ 1, 2, 3, 4, 0, 1, 2, 3, 4]
        self.helpTexts = []
        for i in range(len(self.helpList)):
            self.helpTexts.append(self.canvas.create_text( 
                         10 + len(self.helpList[i])*self.widthOfCharHelp/2.0 + self.helpPosX[i], 10 + self.heightOfCharHelp*self.helpPosY[i] , text = "", font = self.fontHelp))

        
        self.recenter() 
        self.canvas.config(width=2560, height=1536)
        self.canvas.pack()
        self.dnaMoving = True
        
        


    #deals with help message
    def help(self, event):

        if(self.dnaMoving):
            msg =""
            if(self.helpIsOpen == True):
                msg = "h - help"
                for i in range(len(self.helpList)):
                    self.canvas.itemconfig(self.helpTexts[i], text = "")
            else:
                msg = "h - close help"
                for i in range(len(self.helpList)):
                    self.canvas.itemconfig(self.helpTexts[i], text = self.helpList[i])
                
            self.canvas.itemconfig(self.helpMSG, text = msg)
            self.canvas.coords(self.helpMSG, 10 + len(msg)*self.widthOfCharHelp/2.0, 10)           
            self.helpIsOpen = not self.helpIsOpen


        

    #recenters components
    def recenter(self):
        self.canvas.pack()
        self.center = {'y' : self.canvas.winfo_height()/2.0, 'x' : self.canvas.winfo_width()/2.0}
        
    #resizes window
    def resize(self, event):
        self.width = event.width  
        self.height = event.height
        
        if(self.canResize >= 1):
            self.canvas.config(width=self.width, height=self.height)
            self.canvas.pack()
            self.canResize = 0
        else:
            self.canResize += 1
        
    #sets strand2 relative position to the starting position
    def reset(self):
        self.x = (len(self.data2) - len(self.data1))*self.widthOfChar/(2.0)
        self.y = self.heightOfChar

    def callReset(self, event):
        if(self.dnaMoving):
            self.reset()

    
    #deals with the strands movements
    def movement(self):
        self.recenter()
        if(self.dnaMoving):
            self.canvas.coords(self.strand1, self.center['x'], self.center['y'])
            self.canvas.coords(self.strand2, self.center['x'] + self.x, self.center['y'] + self.y)
            
            x_strand2 = self.canvas.coords(self.strand2)[0] 
            y_strand2 = self.canvas.coords(self.strand2)[1]

            width_strand2 = len(self.data2)*self.widthOfChar
            height_strand2 = self.heightOfChar

            if(x_strand2 + width_strand2/2 <= 0 or x_strand2 - width_strand2/2 >= self.canvas.winfo_width()):
                self.reset()
            elif(y_strand2 + height_strand2/2 <= 0 or y_strand2 - height_strand2/2 >= self.canvas.winfo_height()):
                self.reset()
            
            self.matches()
  
        self.canvas.after(100, self.movement) 
    
    def left(self, event): 
        self.x += -5
        self.y += 0
    
    def right(self, event): 
        self.x += 5
        self.y += 0
    
    def up(self, event): 
        self.x += 0
        self.y += -5
    
 
    def down(self, event): 
        self.x += 0
        self.y += 5


    #returns if math of c1 and c2 is valid
    def charMatch(self, c1, c2):
        match = False
        if c1 in self.symbols:
            if c2 == self.complSymbols[c1]:
                match = True

        # If c1 is in the complement symbols dictionary and c2 is equal to the complement of c1 the match is valid
 
        return match
    
    def findMatchesWithRightShift(self, shift):       
        myStrand = self.data2
        otherStrand = self.data1

        mat = [False]*len(myStrand)
        
        myIndex = shift # right shift the other is equal to left shift myself
        otherIndex = 0

        # iterates through the letters of the strands testing matches
        i = 0
        while(otherIndex + i < len(otherStrand) and myIndex + i < len(myStrand)):
            c1 = otherStrand[otherIndex + i]
            c2 = myStrand[myIndex + i] 
            if (self.charMatch(c1, c2)== True):
                mat[myIndex + i] = True
            i+=1

        # iterates through the mat array inserting each modified character at its corresponding position in the matches string
        
        matches = ''
        for i in range(len(mat)):
            if (mat[i]==False):
                matches += (myStrand[i]).lower()
            else:
                matches += myStrand[i].upper()
        return matches
    
    def findMatchesWithLeftShift(self, shift):       
        myStrand = self.data2
        otherStrand = self.data1

        mat = [False]*len(myStrand)
        
        myIndex = 0
        otherIndex = shift

        # iterates through the letters of the strands testing matches
        i = 0
        while(otherIndex + i < len(otherStrand) and myIndex + i < len(myStrand)):
            c1 = otherStrand[otherIndex + i]
            c2 = myStrand[myIndex + i] 
            if (self.charMatch(c1, c2) == True):
                mat[myIndex + i] = True
            i+=1

        # iterates through the mat array inserting each modified character at its corresponding position in the matches string
        
        matches = ''
        for i in range(len(mat)):
            if (mat[i]==False):
                matches += (myStrand[i]).lower()
            else:
                matches += myStrand[i].upper()
        return matches
    
    def countMatchesWithRightShift(self, shift):       
        myStrand = self.data2
        otherStrand = self.data1

        count = 0
        
        myIndex = shift # right shift the other is equal to left shift myself
        otherIndex = 0

        # iterates through the letters of the strands testing matches
        i = 0
        while(otherIndex + i < len(otherStrand) and myIndex + i < len(myStrand)):
            c1 = otherStrand[otherIndex + i]
            c2 = myStrand[myIndex + i] 
            if (self.charMatch(c1, c2)== True):
                count += 1
            i+=1

        return count
    
    def countMatchesWithLeftShift(self, shift):       
        myStrand = self.data2
        otherStrand = self.data1

        count = 0
        
        myIndex = 0
        otherIndex = shift

        # iterates through the letters of the strands testing matches
        i = 0
        while(otherIndex + i < len(otherStrand) and myIndex + i < len(myStrand)):
            c1 = otherStrand[otherIndex + i]
            c2 = myStrand[myIndex + i] 
            if (self.charMatch(c1, c2) == True):
                count += 1
            i+=1

        return count

    def findMaxPossibleMatches(self, event):
        if(self.dnaMoving):
            countMax = -1
            posMax = -1

            limitShiftLeft = len(self.data1) - 1
            limitShiftRight = len(self.data2) - 1

            for shift in range(limitShiftLeft):
                matches = self.countMatchesWithLeftShift(shift)
                if (matches > countMax):
                    countMax = matches
                    posMax = shift

            for shift in range(limitShiftRight):
                matches = self.countMatchesWithRightShift(shift)
                if (matches> countMax):
                    countMax = matches
                    posMax = -shift
            
            self.reset()
            self.x += posMax*self.widthOfChar
            
        
    

    # tests matches and indicates them with uppercase letters
    def matches(self):
        self.canvas.pack()
        ini_x_strand2 = self.center['x'] + (len(self.data2) - len(self.data1))*self.widthOfChar/(2.0)
        x_strand2 = self.canvas.coords(self.strand2)[0]

        if(x_strand2 >= ini_x_strand2):
            shift = (int) (x_strand2 - ini_x_strand2) // self.widthOfChar
            self.canvas.itemconfig(self.strand2, text = self.findMatchesWithLeftShift(shift))
        else:
            shift = (int) (ini_x_strand2 - x_strand2) // self.widthOfChar
            self.canvas.itemconfig(self.strand2, text = self.findMatchesWithRightShift(shift))
    
    #shuffles letters of strand2
    def shuffle(self, event):
        if(self.dnaMoving):
            newData = list(self.data2)
            for i in range(0,len(newData) - 1):
                j = randint(i+1, len(newData)-1)
                newData[i], newData[j] = newData[j], newData[i]
            self.data2 = ''.join(newData)
            self.canvas.itemconfig(self.strand2, text = self.data2)
        
    #exits application
    def quit(self, event):
        self.canvas.destroy()
        exit()
Exemplo n.º 30
0
class CanvasElem(object):
    '''CanvasElem keeps track of a canvas element, possibly with text.'''
    
    STD_FONTS = [ {'family':'Bookman Old Style', 'size':14},\
                  {'family':'Century', 'size':14}, \
                  {'family':'Courier', 'size':14}]
    
    #Changes the script-name of the gate to the display-name of the gate
    GATE_MASKS = {'h':'H', 'x':'X', 'y':'Y', 'z':'Z', 'rx':'Rx', 'ry':'Ry',\
                 'rz':'Rz', 's':'S', 'ph':'S','t':'T', 'tdag':'T^', 'measure':'M', 'prepz':'|0>'}
    #All the gates that have a special drawing style
    SPECIAL_GATES = ('cnot', 'cx', 'toffoli', 'swap', 'cphase', 'cz', 'cr','c-x','c-z','class_cx','class_cz')
    SPECIAL_NODES = ('circ','oplus','cross')
    #Determine the radius of the associated nodes of the special gates
    RADII = {'circ':5, 'oplus': 9, 'cross':6 }
    
    #Determine the width of the boxes around the gates
    BORDER_WIDTH = 3
    
    #Determine the margins around the gates.
    MARGINS = (0,0) #(5,5)
    
    def __init__(self, canvas, gate=None, aspect = (-1,-1), bbox=None, font_dict=None,\
                 special_node = None, draw_rect = True):
        self.canvas = canvas
        
        self.aspect = aspect
        self.bbox = bbox

        #Keep track of the actual drawing coordinates
        self.draw_x = -1
        self.draw_y = -1
        self.draw_w = -1
        self.draw_h = -1
        self.text_x = -1
        self.text_y = -1
        
        #Keep track of the min dimensions of the bbox such that the contents can be displayed correctly
        self.min_w = -1
        self.min_h = -1
        
        #Keep track of the attachment points to which other elements can attach themselves.
        self.attachments = {'left':-1, 'right':-1, 'top':-1, 'bottom':-1}
        
        #Keep track of the text within the rectangle
        self.text = None
        #Set the font
        self.font = None
        if font_dict:
            self.font = Font(family=font_dict['family'], size=font_dict['size'])
        else:
            for font_dict in self.STD_FONTS:
                try:
                    self.font = Font(family=font_dict['family'],size=font_dict['size'])
                    if self.font:
                        break
                except Exception as e:
                    pass
            else:
                raise ValueError(f'{self.__str__()} cannot produce any font, none worked!')
        
        #Keep track of the canvas elements
        self.rect_canvas = None
        self.text_canvas = None
        self.special_node = special_node
        self.specials_canvas = []
        
        #Keep track of whether we need to draw the rectangle
        self.draw_rect = draw_rect
        
        #Update the current gate, and the associated text
        self.set_gate(gate)
        
    def find_min_size(self):
        '''Finds the minimum size of the rectangle needed to contain the text'''
        if self.special_node:
            #self.min_w = max(2 * self.RADIUS, self.font.measure('x'), self.font.measure('o'))
            #self.min_h = max(2 * self.RADIUS, self.font.metrics('linespace'))
            self.min_w = self.min_h = 2 * self.RADII[self.special_node]
            
        #If we are an element with actual text inside, compute how large the text is
        elif self.text:
            min_w = self.font.measure(self.text) + self.draw_rect*self.BORDER_WIDTH*2
            min_h = self.font.metrics('linespace') + self.draw_rect*self.BORDER_WIDTH*2
            #Take into account the aspect ratio in self.aspect
            if not -1 in self.aspect:
                #We must have w/h = aspect[0]/aspect[1] => w  = h * aspect[0]/aspect[1]
                #Either stretch w, or stretch h
                if min_h * self.aspect[0]/self.aspect[1] > min_w:
                    self.min_w = int( min_h * self.aspect[0]/self.aspect[1] )
                    self.min_h = int(min_h)
                else:
                    self.min_w = int(min_w)
                    self.min_h = int( min_w * self.aspect[1]/self.aspect[0] )
            else:
                self.min_w = int(min_w)
                self.min_h = int(min_h)
            
            
    def set_bbox(self,bbox):
        self.bbox = bbox
        
    def set_gate(self,gate):
        self.gate = gate
        if self.gate in self.GATE_MASKS.keys():
            self.text = self.GATE_MASKS[self.gate]
        #If the gate is a special gate, suppress the text. Otherwise, set it.
        elif self.gate in self.SPECIAL_GATES:
            self.text = None
        else:
            self.text = self.gate
            
        self.find_min_size()

    def find_draw_coords(self):
        '''Finds the coordinates to draw with, and sets up the attachment points'''
        if self.bbox is None:
            raise ValueError(f'{self.__str__()} cannot find draw coords because bbox is None!')
        
        #Do NOT include margins if we are building a special node
        if self.special_node:
            want_width = self.min_w
            want_height = self.min_h
        else:
            want_width = self.min_w + 2 * self.MARGINS[0]
            want_height = self.min_h + 2 * self.MARGINS[1]

        self.draw_w = want_width if want_width < self.bbox['w'] else self.bbox['w']
        self.draw_h = want_height if want_height < self.bbox['h'] else self.bbox['h']

        self.draw_x = int( self.bbox['x'] + (self.bbox['w'] - self.draw_w)/2 )
        self.draw_y = int( self.bbox['y'] + (self.bbox['h'] - self.draw_h)/2 )

        self.text_x = int( self.draw_x + self.draw_w/2 )
        self.text_y = int( self.draw_y + self.draw_h/2 )

        self.attachments['left'] = self.draw_x
        self.attachments['right'] = self.draw_x + self.draw_w
        self.attachments['top'] = self.draw_y
        self.attachments['bottom'] = self.draw_y + self.draw_h

        
    def draw(self):
        '''Draws the element on the canvas'''
        #First, find the coords at which we should draw.
        self.find_draw_coords()
        
        #If we are a normal node:
        if self.special_node is None:
            #If we should draw a rectangle:
            if self.draw_rect:
                self.rect_canvas = self.canvas.create_rectangle(self.draw_x,self.draw_y,\
                                        self.draw_x+self.draw_w,self.draw_y+self.draw_h,width=self.BORDER_WIDTH)
            
            #If we are a measurement device
            if self.gate == 'measure':
                self.specials_canvas += self.draw_measurement()
            #If we have text:
            elif self.text:
                self.text_canvas = self.canvas.create_text(self.text_x,self.text_y,font=self.font, justify=tk.CENTER,\
                                                          text=self.text)
        
        else: #We are special: we need to draw either a circ, an oplus or a cross
            def node(xy, r, circ=False, fill=False, plus=False, cross=False):
                out = []
                if circ:
                    out.append(self.canvas.create_oval( xy[0]-r, xy[1]-r, xy[0]+r, xy[1]+r, fill='black' if fill else '', width=1.5) )
                if plus:
                    out.append(self.canvas.create_line( xy[0], xy[1]-r, xy[0], xy[1]+r, width=2 ) )
                    out.append(self.canvas.create_line( xy[0]-r, xy[1], xy[0]+r, xy[1], width=2 ) )
                if cross:
                    out.append(self.canvas.create_line( xy[0]-r, xy[1]-r, xy[0]+r, xy[1]+r, width=2.5 ) )
                    out.append(self.canvas.create_line( xy[0]-r, xy[1]+r, xy[0]+r, xy[1]-r, width=2.5 ) )
                return out
            
            mid_x = int( self.bbox['x'] + self.bbox['w']/2 )
            mid_y = int( self.bbox['y'] + self.bbox['h']/2 )
            
            if self.special_node == 'circ':
                self.specials_canvas += node((mid_x,mid_y), self.RADII['circ'], circ=True, fill=True )
            elif self.special_node == 'oplus':
                self.specials_canvas += node((mid_x,mid_y), self.RADII['oplus'], circ=True, plus=True)
            else:
                self.specials_canvas += node((mid_x,mid_y), self.RADII['cross'], cross=True)
                
    def draw_measurement(self):
        '''Draws a measurement device'''
        mid_x = int(self.draw_x + self.draw_w/2)
        mid_y = int(self.draw_y + 3*self.draw_h/5)
        radius = int( (self.draw_w/2) * 7/10 )
        arc = self.canvas.create_arc( mid_x-radius, mid_y-radius, mid_x+radius, mid_y+radius,\
                                     start=0, extent=180, width=2, style=tk.ARC  )
        end_x = int(self.draw_x + self.draw_w * 8.5/10 )
        end_y = int(self.draw_y + self.draw_h * 1.5/10 )
        arrow = self.canvas.create_line(mid_x, mid_y, end_x, end_y, arrow=tk.LAST, width=2 )
        
        return arc, arrow

    def __str__(self):
        return f'R.D. DRAW(x={self.draw_x},y={self.draw_y},w={self.draw_w},h={self.draw_h},text={self.text})'
    
    def __repr__(self):
        return self.__str__()
Exemplo n.º 31
0
class UITabs(Frame):
    def __init__(self, parent, observer):
        Frame.__init__(self, parent)
        self.parent = parent
        self.observer = observer

        self.tabs = []          # list of tab id's
        self.info = {}          # info on each tab
        self.drag = None        # state information when drag in progress
        self.current = None     # id of currently selected tab
        self.mouseover = None   # id of tab the mouse is currently over
        self.tooltip = None     # state information for tooltips
        self.last_x = -1
        self.nextid = 1

        self.define_appearance()
        self.c = Canvas(self, highlightthickness=0, borderwidth=0)
        self.c.grid(column=0, row=0, sticky='nwes')
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        self.c['background'] = self.bg
        self.c['height'] = self.height
        self.bind('<Configure>', self.rebuild)
        self.c.bind('<Motion>', self.mouse_moved)
        self.c.bind('<Leave>', self.leave)
        self.c.bind('<1>', self.mouse_down)
        self.c.bind('<B1-Motion>', self.mouse_dragged)
        self.c.bind('<ButtonRelease-1>', self.mouse_up)

    def add(self, tabid=None, title=None, dirty=False, tooltip=None):
        "Add a new tab"
        if tabid is None:
            while 'tab'+str(self.nextid) in self.tabs:
                self.nextid += 1
            tabid = 'tab'+str(self.nextid)
            self.nextid += 1
        if tabid in self.tabs:
            raise ValueError('tab already added')
        self.tabs.append(tabid)
        self.info[tabid] = {'title': title, 'dirty': dirty, 'tooltip': tooltip}
        self.select(tabid)
        return tabid

    def remove(self, tabid):
        "Remove an existing tab"
        if tabid not in self.tabs:
            raise ValueError('no such tab')
        self.tooltip_clear()
        idx = self.tabs.index(tabid)
        del(self.info[tabid])
        self.tabs.remove(tabid)
        if tabid == self.current:
            if len(self.tabs) == 0:
                self.current = None
            elif idx > len(self.tabs)-1:
                self.current = self.tabs[-1]
            else:
                self.current = self.tabs[idx]
            if self.current is not None:
                self.observer.tab_selected(self, self.current)
        self.update_mouseover(self.last_x)
        self.rebuild()

    def select(self, tabid):
        "Change the currently selected (frontmost) tab."
        if tabid not in self.tabs:
            raise ValueError('no such tab')
        if self.current != tabid:
            self.observer.tab_deselected(self, self.current)
            self.current = tabid
            self.rebuild()
            self.observer.tab_selected(self, self.current)

    def set_title(self, tabid, title):
        "Change the title of a tab"
        if tabid not in self.tabs:
            raise ValueError('no such tab')
        self.info[tabid]['title'] = title
        self.rebuild()

    def set_dirty(self, tabid, dirty=True):
        "Change the saved/unsaved indicator for a tab"
        if tabid not in self.tabs:
            raise ValueError('no such tab')
        self.info[tabid]['dirty'] = dirty
        self.rebuild()

    def set_tooltip(self, tabid, tooltip):
        "Change the tooltip for a tab"
        if tabid not in self.tabs:
            raise ValueError('no such tab')
        self.info[tabid]['tooltip'] = tooltip

    def mouse_moved(self, ev):
        self.last_x = ev.x
        self.update_mouseover(ev.x)

    def leave(self, ev):
        self.mouseover = None
        self.tooltip_clear()
        self.last_x = -1
        self.rebuild()

    def mouse_down(self, ev):
        self.drag_initiate(ev)

    def mouse_dragged(self, ev):
        self.drag_continue(ev)

    def mouse_up(self, ev):
        self.drag_conclude(ev)

    def define_appearance(self):
        self.height = 22
        self.minwidth = 130
        self.maxwidth = 300
        self.addiconwidth = 24
        self.gapwidth = 15
        self.bg = '#c4c4c4'
        self.selbg = '#d3d3d3'
        self.dividerbg = '#b0b0b0'
        self.textcolor = '#424242'
        self.holecolor = '#eeeeee'
        self.titlefont = Font(name='TkTooltipFont', exists=True,
                              root=self.parent)
        self.tooltipfont = Font(name='TkTooltipFont', exists=True,
                                root=self.parent)
        self.plusfont = Font(name='TkMenuFont', exists=True, root=self.parent)

    def calculate_positions(self):
        "Update state info on each tab needed for display, e.g. position"
        self.tabwidth = tabwidth = self.calculate_tabwidth()
        displayed = []
        extra = []
        xpos = 0
        # Step 1: Calculate positions for each tab that will fit on the
        #         display, and note which tabs won't fit
        for t in self.tabs:
            if xpos + tabwidth > self.winfo_width() - self.addiconwidth:
                self.info[t]['visible'] = False
                extra.append(t)
            else:
                self.info[t]['visible'] = True
                self.info[t]['left'] = xpos
                self.info[t]['shift'] = 0
                displayed.append(t)
                xpos += tabwidth
            self.info[t]['tablabel'] = self.tablabel(self.info[t]['title'],
                                                     tabwidth-40)
        # Step 2: If we're in the middle of dragging a tab, potentially
        #         to move it, we indicate a place where it can be dropped
        #         via a gap between tabs. If such a gap has been identified
        #         (see drag_identifygap), open up a space by moving all tabs
        #         to the left of the gap a bit more left, and all tabs to
        #         the right of the gap a bit more right.
        if self.drag is not None and self.drag['state'] == 'inprogress' \
                                 and self.drag['gap'] is not None:
            gap = self.drag['gap']
            tabidx = self.tabs.index(self.drag['tabid'])
            idx = 0
            for t in self.tabs:
                if self.info[t]['visible']:
                    if idx < gap:
                        self.info[t]['shift'] = -self.gapwidth
                    elif idx >= gap:
                        self.info[t]['shift'] = +self.gapwidth
                idx += 1
        # Step 3: If the currently selected tab will not fit on the screen
        #         because there are too many tabs before it on the list,
        #         swap the last displayed tab with the currently selected
        #         one, to ensure it is displayed. Note this doesn't update
        #         the actual order (in self.tabs), just display state info.
        if self.current in extra:
            last = displayed[-1]
            self.info[self.current]['visible'] = True
            self.info[self.current]['left'] = self.info[last]['left']
            self.info[self.current]['shift'] = self.info[last]['shift']
            self.info[last]['visible'] = False
            del(self.info[last]['left'])
            del(self.info[last]['shift'])
            displayed.remove(last)
            extra.insert(0, last)
            extra.remove(self.current)
            displayed.append(self.current)
        self.overflow_tab = displayed[-1] if extra else None
        self.plus_x = xpos

    def calculate_tabwidth(self):
        "Determine width of a tab, factoring in available space, etc."
        fullwidth = self.winfo_width()
        numtabs = len(self.tabs)
        if numtabs == 0:
            return -1
        tabwidth = int((fullwidth - self.addiconwidth) / numtabs)
        if tabwidth < self.minwidth:
            tabwidth = self.minwidth
        if tabwidth > self.maxwidth:
            tabwidth = self.maxwidth
        return tabwidth

    def tablabel(self, title, width):
        "Truncate any long titles that would not fit within the tab label"
        if self.titlefont.measure(title) <= width:
            return title
        dotwidth = self.titlefont.measure('...')
        while True:
            if self.titlefont.measure(title+'...') <= width:
                return title+'...'
            title = title[:-1]

    def update_mouseover(self, x):
        "Determine if the tab our mouse is over has changed, and adjust if so"
        nowover = self.tabunder(x) if x != -1 else None
        if nowover != self.mouseover:
            self.tooltip_clear()
            self.mouseover = nowover
            self.rebuild()
            if nowover is not None and \
                            self.info[nowover]['tooltip'] is not None:
                self.tooltip_schedule()

    def tabunder(self, x):
        "Identify which tab is at a given position"
        for t in self.tabs:
            if self.info[t]['visible'] and self.info[t]['left'] <= x <\
                                self.info[t]['left'] + self.tabwidth:
                return t
        return None

    def rebuild(self, ev=None):
        """
        Update the display to match the current state of the user interface.
        This actually draws all the pieces of the tabs, indicators, etc. on
        the canvas. We take a brute force approach and recreate everything
        from scratch each time we're called, which happens on any significant
        state change.
        """
        self.c.delete('all')
        if self.winfo_width() < self.addiconwidth:
            return
        self.calculate_positions()
        tabwidth = self.tabwidth
        for t in self.tabs:
            if not self.info[t]['visible']:
                continue
            lbl = self.info[t]['tablabel']
            xpos = self.info[t]['left'] + self.info[t]['shift']

            color = self.selbg if t == self.current else self.bg
            rect = self.c.create_rectangle(xpos, 0, xpos + tabwidth - 2,
                            self.height, fill=color, outline=color)
            self.c.tag_bind(rect, '<ButtonRelease-1>',
                            lambda ev=None, t=t: self.select(t))

            self.c.create_line(xpos-1, 0, xpos-1,
                            self.height, fill=self.dividerbg)
            self.c.create_line(xpos+tabwidth-1, 0, xpos+tabwidth-1,
                            self.height, fill=self.dividerbg)
            color = self.textcolor
            if self.drag is not None and self.drag['state'] == 'inprogress' \
                                     and self.drag['tabid'] == t:
                color = self.holecolor
            txt = self.c.create_text(xpos + tabwidth / 2, self.height - 3,
                                   anchor='s', text=lbl, fill=color,
                                   font=self.titlefont)
            self.c.tag_bind(txt, '<ButtonRelease-1>',
                            lambda ev=None, t=t: self.select(t))
            if self.info[t]['dirty']:
                close = self.c.create_oval(xpos+4, self.height-13, xpos+10,
                            self.height-7, fill=self.textcolor,
                            outline=self.textcolor, activefill='red',
                            activeoutline='red')
                self.c.tag_bind(close, '<1>',
                            lambda ev=None, t=t: self.closetab(t, ev))
            elif t == self.mouseover:
                if self.drag is None or self.drag['state'] != 'inprogress':
                    close = self.c.create_text(xpos+8, self.height-3,
                                    anchor='s', text='x', fill=self.textcolor,
                                    activefill='red', font=self.plusfont)
                    self.c.tag_bind(close, '<ButtonRelease-1>',
                                    lambda ev=None, t=t: self.closetab(t, ev))
            if t == self.overflow_tab:
                more = self.c.create_text(xpos + tabwidth - 12,
                            self.height - 5, anchor='s', text='>>',
                            fill=self.textcolor, font=self.titlefont)
                self.c.tag_bind(more, '<1>', self.post_extras_menu)
        plus = self.c.create_text(self.plus_x+self.addiconwidth/2,
                            self.height-3, anchor='s', text='+',
                            fill=self.textcolor, font=self.plusfont)
        self.c.tag_bind(plus, '<ButtonRelease-1>', self.addtab)
        self.drag_createitems()

    def addtab(self, ev=None):
        self.observer.handle_addtab(self)

    def closetab(self, tabid, ev):
        self.observer.handle_closetab(self, tabid)

    def post_extras_menu(self, ev):
        "Show a menu for selecting the tabs that do not fit onscreen"
        self.tooltip_clear()
        self.calculate_positions()
        menu = Menu(self, tearoff=0)
        for t in self.tabs:
            if not self.info[t]['visible']:
                menu.add_command(label=self.info[t]['title'],
                                 command=lambda t=t: self.select(t))
        menu.tk_popup(ev.x_root, ev.y_root)

    def drag_initiate(self, ev):
        tabid = self.tabunder(ev.x)
        if tabid is not None:
            self.drag = {'state': 'pending', 'tabid': tabid,
                         'offsetx': ev.x - (self.info[tabid]['left'] +
                                            self.tabwidth/2),
                         'offsety': ev.y - (self.height - 3),
                         'x': ev.x, 'y': ev.y}

    def drag_continue(self, ev):
        if self.drag is None:
            return
        if self.drag['state'] == 'pending':
            if abs(ev.x - self.drag['x']) > 3 or \
                            abs(ev.y - self.drag['y']) > 3:
                self.drag['state'] = 'inprogress'
                self.drag['x'] = ev.x - self.drag['offsetx']
                self.drag['y'] = ev.y - self.drag['offsety']
                self.drag['gap'] = None
                self.rebuild()
        elif self.drag['state'] == 'inprogress':
            self.drag['x'] = ev.x - self.drag['offsetx']
            self.drag['y'] = ev.y - self.drag['offsety']
            self.c.coords(self.drag['textitem'],
                          (self.drag['x'], self.drag['y']))
            self.drag_identifygap(ev.x, ev.y)

    def drag_conclude(self, ev):
        if self.drag is not None and self.drag['state'] == 'inprogress':
            self.drag_identifygap(ev.x, ev.y)
            gap = self.drag['gap']
            if gap is not None:
                curidx = self.tabs.index(self.drag['tabid'])
                if gap > curidx:
                    gap -= 1
                self.tabs.remove(self.drag['tabid'])
                self.tabs.insert(gap, self.drag['tabid'])
        self.drag = None
        self.rebuild()

    def drag_createitems(self):
        "Called by rebuild to create canvas items for drag in progress"
        if self.drag is not None and self.drag['state'] == 'inprogress':
            if self.drag['gap'] is not None:
                x = self.drag['gap'] * self.tabwidth
                self.c.create_rectangle(x-self.gapwidth, 0, x+self.gapwidth,
                            self.height, fill=self.holecolor,
                            outline=self.holecolor)
            label = self.info[self.drag['tabid']]['tablabel']
            self.drag['textitem'] = self.c.create_text(self.drag['x'],
                            self.drag['y'], text=label, font=self.titlefont,
                            fill=self.textcolor, anchor='s')

    def drag_identifygap(self, x, y):
        gap = None
        for t in self.tabs:
            if not self.info[t]['visible']:
                continue
            left = self.info[t]['left']
            if left <= x <= left + self.tabwidth / 2:
                gap = self.tabs.index(t)
                break
            elif left + self.tabwidth / 2 <= x <= left + self.tabwidth:
                gap = self.tabs.index(t) + 1
                break
        tabidx = self.tabs.index(self.drag['tabid'])
        if gap is not None and (tabidx == gap or tabidx + 1 == gap):
            gap = None
        if y < 0 or y > self.height:
            gap = None
        if gap != self.drag['gap']:
            self.drag['gap'] = gap
            self.rebuild()

    def tooltip_schedule(self):
        self.tooltip_clear()
        self.tooltip = {'window': None,
                        'afterid': self.after(1500, self.tooltip_display)}

    def tooltip_clear(self):
        if self.tooltip is not None:
            if self.tooltip['window'] is not None:
                self.tooltip['window'].destroy()
            if self.tooltip['afterid'] is not None:
                self.after_cancel(self.tooltip['afterid'])
            self.tooltip = None

    def tooltip_display(self):
        self.tooltip['afterid'] = None
        if self.mouseover is None:
            return
        x = self.winfo_rootx() + self.info[self.mouseover]['left'] + 20
        y = self.winfo_rooty() + self.height + 5
        txt = self.info[self.mouseover]['tooltip']
        tw = self.tooltip['window'] = Toplevel(self)
        tw.wm_withdraw()
        tw.wm_geometry("+%d+%d" % (x, y))
        tw.wm_overrideredirect(1)
        try:
            tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
                       "help", "noActivates")
        except TclError:
            pass
        lbl = Label(tw, text=txt, justify=LEFT, background="#ffffe0",
                    borderwidth=0, font=self.tooltipfont)
        if self.tk.call('tk', 'windowingsystem') != 'aqua':
            lbl['borderwidth'] = 1
            lbl['relief'] = 'solid'
        lbl.pack()
        tw.update_idletasks()  # calculate window size to avoid resize flicker
        tw.deiconify()
        tw.lift()  # needed to work around bug in Tk 8.5.18+ (issue #24570)
Exemplo n.º 32
0
    def __init__(self, parent, clue_direction, clue_list, clue_list_box_height,
                 **kwargs):
        super().__init__(parent, **kwargs)
        self.clue_direction = clue_direction

        self.list_label = tk.Label(self,
                                   text=self.clue_direction,
                                   fg="black",
                                   bg="white",
                                   anchor="w")
        self.list_label.pack(side=tk.TOP, fill=tk.X, expand=1)
        self.list_canvas = tk.Canvas(self,
                                     bg='#FFFFFF',
                                     width=240,
                                     height=clue_list_box_height,
                                     scrollregion=(0, 0, 240, 1500))
        #vbar=tk.Scrollbar(self, orient=tk.VERTICAL)
        vbar = tk.Scrollbar(self, orient=tk.VERTICAL, bg="white")
        vbar.pack(side=tk.RIGHT, fill=tk.Y)
        vbar.config(command=self.list_canvas.yview)
        self.list_canvas.config(yscrollcommand=vbar.set)
        # list_canvas.config(width=240,height=300, yscrollcommand=vbar.set)
        self.list_canvas.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
        ##yscrollincrement - increment for vertical scrolling, in pixels,
        ##millimeters '2m', centimeters '2c', or inches '2i'

        bfont = Font(family="Arial", size=10, weight="bold")
        rfont = Font(family="Arial", size=10)

        # Because our list is fixed in width
        # the x coords are going to be constant
        clue_rect_left_x = 0
        clue_rect_right_x = 240  # total width of the clue rectangle

        # default clue rectangle height: this will change
        # after we figure out how tall the clue will actually be.
        clue_rect_default_height = 75

        x_pad = 30  # the amount to pad before starting the clue number
        x_num_pad = 5  # the amount to pad between the clue number and the clue
        y_pad = 5  # the amount to pad (top and bottom) between the end
        # of the clue box and the bounding box of the clue text

        clue_rect_top_y = 0  # zero to start.  bottom_y + 1 in the loop

        for clue_number, clue_text in clue_list:

            clue_num_length = rfont.measure(clue_number)

            clue_rect_bottom_y = clue_rect_top_y + clue_rect_default_height
            clue_text_line_len = clue_rect_right_x - (
                (x_pad * 2) + clue_num_length + x_num_pad)

            clue_num_left_x = clue_rect_left_x + (x_pad - clue_num_length)
            clue_num_top_y = clue_rect_top_y + y_pad
            clue_text_left_x = clue_num_left_x + clue_num_length + x_num_pad
            clue_text_top_y = clue_rect_top_y + y_pad
            clue_tag = self.clue_direction[0] + str(clue_number).zfill(3)
            cr = self.list_canvas.create_rectangle(clue_rect_left_x,
                                                   clue_rect_top_y,
                                                   clue_rect_right_x,
                                                   clue_rect_bottom_y,
                                                   fill="white",
                                                   tags=clue_tag)
            cn = self.list_canvas.create_text(clue_num_left_x,
                                              clue_num_top_y,
                                              text=clue_number,
                                              font=bfont,
                                              anchor="nw",
                                              tags=clue_tag)
            ct = self.list_canvas.create_text(clue_text_left_x,
                                              clue_text_top_y,
                                              text=clue_text,
                                              font=rfont,
                                              anchor="nw",
                                              width=clue_text_line_len,
                                              tags=clue_tag)
            bb = self.list_canvas.bbox(ct)
            x0, y0, x1, y1 = self.list_canvas.coords(cr)
            y1 = bb[3] + y_pad
            self.list_canvas.coords(cr, x0, y0, x1, y1)
            clue_rect_top_y = y1
            self.list_canvas.itemconfig(cr, outline="white")

        self.list_canvas.configure(scrollregion=self.list_canvas.bbox("all"),
                                   yscrollincrement=0)
        # clue_tag = self.clue_direction[0] + str(last_selected_item).zfill(3)
        self.last_clue_tag = clue_tag
        self.set_clue_bg(clue_tag, "grey")

        ##yscrollincrement - increment for vertical scrolling, in pixels,
        ##millimeters '2m', centimeters '2c', or inches '2i'
        # list_canvas.yview_scroll(500, "units")
        #list_canvas.yview_moveto(1)
        self.list_canvas.bind('<Button-1>', self.on_click)
Exemplo n.º 33
0
class ClueListBox(tk.Frame):
    def __init__(self, parent, clue_direction, clue_list, clue_list_box_height, **kwargs):
        super().__init__(parent, **kwargs)
        self.clue_direction = clue_direction

        self.list_label = tk.Label(self, text=self.clue_direction, fg="black", bg="white", anchor="w")
        self.list_canvas=tk.Canvas(self, bg='#FFFFFF', width=240, height=clue_list_box_height, scrollregion=(0,0,240,1500))

        self.vbar=tk.Scrollbar(self, orient=tk.VERTICAL, bg="white")
        self.vbar.config(command=self.list_canvas.yview)
        self.list_canvas.config(yscrollcommand=self.vbar.set)

        self.list_label.pack(side=tk.TOP, fill=tk.X, expand=1)
        self.vbar.pack(side=tk.RIGHT,fill=tk.Y)
        self.list_canvas.pack(side=tk.LEFT,expand=True,fill=tk.BOTH)

        # These might be constants but they could be configurable in the 
        # future. It seems like that might be a big pain for little benefit.
        self.bfont = Font(family="Arial", size=10, weight="bold")
        self.rfont = Font(family="Arial", size=10)
        self.clue_rect_width = 240 # total width of the clue rectangle
        self.x_pad = 30 # the amount to pad before starting the clue number
        self.x_num_pad = 5 # the amount to pad between the clue number and the clue
        self.y_pad = 5 # the amount to pad (top and bottom) between the end 
        # of the clue box and the bounding box of the clue text

        # Because our list is fixed in width
        # the x coords are going to be constant
        clue_rect_left_x = 0 
        clue_rect_right_x = self.clue_rect_width 

        # default clue rectangle height: this will change 
        # after we figure out how tall the clue will actually be.
        self.clue_rect_default_height = 75

        clue_rect_top_y = 0 
        for clue_number, clue_text in clue_list:
            clue_num_length = self.rfont.measure(clue_number)

            clue_rect_bottom_y = clue_rect_top_y + self.clue_rect_default_height 
            clue_text_line_len = clue_rect_right_x - ((self.x_pad*2) + clue_num_length + self.x_num_pad)

            clue_num_left_x = clue_rect_left_x + (self.x_pad - clue_num_length)
            clue_num_top_y = clue_rect_top_y + self.y_pad
            clue_text_left_x = clue_num_left_x + clue_num_length + self.x_num_pad
            clue_text_top_y = clue_rect_top_y + self.y_pad
            clue_tag = self.clue_direction[0] + str(clue_number).zfill(3)
            cr = self.list_canvas.create_rectangle(clue_rect_left_x, clue_rect_top_y, 
                    clue_rect_right_x, clue_rect_bottom_y, fill="white", tags=clue_tag)
            cn = self.list_canvas.create_text(clue_num_left_x, clue_num_top_y, text=clue_number, 
                    font=self.bfont, anchor = "nw", tags=clue_tag)
            ct = self.list_canvas.create_text(clue_text_left_x, clue_text_top_y, 
                    text=clue_text, font=self.rfont, anchor = "nw", width=clue_text_line_len, tags=clue_tag)
            bb = self.list_canvas.bbox(ct)
            x0, y0, x1, y1 = self.list_canvas.coords(cr)
            y1 = bb[3] + self.y_pad
            self.list_canvas.coords(cr, x0, y0, x1, y1)
            clue_rect_top_y = y1
            self.list_canvas.itemconfig(cr, outline="white")

        self.list_canvas.configure(scrollregion = self.list_canvas.bbox("all"), yscrollincrement=0)
        self.current_clue_tag = self.clue_direction[0] + str(1).zfill(3)
        self.last_clue_tag = self.current_clue_tag
        self.set_clue_bg(self.current_clue_tag, "grey")

        ##yscrollincrement - increment for vertical scrolling, in pixels,
        ##millimeters '2m', centimeters '2c', or inches '2i'
        # list_canvas.yview_scroll(500, "units")
        #list_canvas.yview_moveto(1)
        self.list_canvas.bind('<Button-1>', self.on_click)

    def on_click(self, event=None):
        can_x = self.list_canvas.canvasx(event.x)
        can_y = self.list_canvas.canvasy(event.y)
        clicked_item = self.list_canvas.find_closest(can_x, can_y)[0]
        tags = self.list_canvas.gettags(clicked_item)
        for tag in tags:
            if tag[0] == self.clue_direction[0]:
                self.last_clue_tag = self.current_clue_tag
                self.current_clue_tag = tag
                break
        self.set_clue_bg(self.last_clue_tag, "white")
        self.set_clue_bg(self.current_clue_tag, "grey")
        print(self.clue_direction, can_x, can_y, clicked_item, 
            tags)

    def set_clue_bg(self, clue_tag, clue_bg):
        
        clue_canvas_items = self.list_canvas.find_withtag(clue_tag)
        for item in clue_canvas_items:
            if self.list_canvas.type(item) == 'rectangle':
                rect_item = item
                break
        self.list_canvas.itemconfig(rect_item, fill=clue_bg)
Exemplo n.º 34
0
    def __init__(self, dungeon: Dungeon, *args, **kwargs):
        TextInterface.__init__(self, dungeon)
        tk.Tk.__init__(self, *args, **kwargs)
        self.title = "WELCOME TO THE DUNGEON"
        self.bind('<Key>', self.on_keypress)

        n, m = dungeon.n, dungeon.m

        myFont = Font(family="RobotoMono Nerd Font", size=12, weight='normal')
        myInfoFont = Font(family="RobotoMono Nerd Font",
                          size=10,
                          weight='normal')

        cell_size = 40

        self.fg = fg = '#F2F3F4'
        bg = '#3B444B'
        active_fg = '#0095B6'
        active_bg = '#1B242B'

        # ───────────────────────── create the grid ────────────────────────── #
        self.board = tk.Frame(self)
        self.board.pack(padx=1, pady=1)
        self.frames = [None for x in range(n * m)]
        self.cells = [None for x in range(n * m)]
        for x in range(n * m):
            r, c = x // m, x % m

            # ----------- create frames for a fixed size in pixels ----------- #
            self.frames[x] = tk.Frame(self.board,
                                      height=cell_size,
                                      width=cell_size,
                                      bg=bg)
            self.frames[x].pack_propagate(False)
            self.frames[x].grid(row=r, column=c, padx=1, pady=1)

            # ----------- create the labels to display game state ------------ #
            self.cells[x] = tk.Label(self.frames[x],
                                     bg=bg,
                                     fg=fg,
                                     font=myFont,
                                     activeforeground=active_fg,
                                     activebackground=active_bg)
            self.cells[x].pack(expand=True, fill='both')

        # ────────────────────────── infos display ─────────────────────────── #
        f_width = myInfoFont.measure('x')
        w = max(50, int((cell_size * m) / f_width))

        # force_width = tk.Frame(self, width=max(450, cell_size * m))
        # force_width.pack_propagate(False)
        # force_width.pack()

        frame = tk.Frame(self)
        frame.pack()

        self.message_actions = tk.Label(frame,
                                        font=myInfoFont,
                                        width=w,
                                        justify='left')
        self.message_actions.pack(expand=True, fill='x')

        self.message_infos = tk.Label(frame,
                                      font=myInfoFont,
                                      width=w,
                                      justify='left')
        self.message_infos.pack(expand=True, fill='x')

        self.message_caption = tk.Label(frame,
                                        font=myInfoFont,
                                        width=w,
                                        justify='left')
        self.message_caption.pack(expand=True, fill='x')

        # ──────────────────────── display everything ──────────────────────── #
        self.display()
Exemplo n.º 35
0
class T_GUI:
    def __init__(self, root):
        self.language = {
            '阿尔巴尼亚语': 'sq',
            '阿拉伯语': 'ar',
            '阿姆哈拉语': 'am',
            '阿塞拜疆语': 'az',
            '爱尔兰语': 'ga',
            '爱沙尼亚语': 'et',
            '巴斯克语': 'eu',
            '白俄罗斯语': 'be',
            '保加利亚语': 'bg',
            '冰岛语': 'is',
            '波兰语': 'pl',
            '波斯尼亚语': 'bs',
            '波斯语': 'fa',
            '布尔语南非荷兰语': 'af',
            '丹麦语': 'da',
            '德语': 'de',
            '俄语': 'ru',
            '法语': 'fr',
            '菲律宾语': 'tl',
            '弗里西语': 'fy',
            '高棉语': 'km',
            '格鲁吉亚语': 'ka',
            '古吉拉特语': 'gu',
            '哈萨克语': 'kk',
            '海地克里奥尔语': 'ht',
            '韩语': 'ko',
            '豪萨语': 'ha',
            '荷兰语': 'nl',
            '吉尔吉斯语': 'ky',
            '加利西亚语': 'gl',
            '加泰罗尼亚语': 'ca',
            '捷克语': 'cs',
            '卡纳达语': 'kn',
            '科西嘉语': 'co',
            '克罗地亚语': 'hr',
            '库尔德语': 'ku',
            '拉丁语': 'la',
            '拉脱维亚语': 'lv',
            '老挝语': 'lo',
            '立陶宛语': 'lt',
            '卢森堡语': 'lb',
            '罗马尼亚语': 'ro',
            '马尔加什语': 'mg',
            '马耳他语': 'mt',
            '马拉地语': 'mr',
            '马拉雅拉姆语': 'ml',
            '马来语': 'ms',
            '马其顿语': 'mk',
            '毛利语': 'mi',
            '蒙古语': 'mn',
            '孟加拉语': 'bn',
            '缅甸语': 'my',
            '苗语': 'hmn',
            '南非科萨语': 'xh',
            '南非祖鲁语': 'zu',
            '尼泊尔语': 'ne',
            '挪威语': 'no',
            '旁遮普语': 'pa',
            '葡萄牙语': 'pt',
            '普什图语': 'ps',
            '齐切瓦语': 'ny',
            '日语': 'ja',
            '瑞典语': 'sv',
            '萨摩亚语': 'sm',
            '塞尔维亚语': 'sr',
            '塞索托语': 'st',
            '僧伽罗语': 'si',
            '世界语': 'eo',
            '斯洛伐克语': 'sk',
            '斯洛文尼亚语': 'sl',
            '斯瓦希里语': 'sw',
            '苏格兰盖尔语': 'gd',
            '宿务语': 'ceb',
            '索马里语': 'so',
            '塔吉克语': 'tg',
            '泰卢固语': 'te',
            '泰米尔语': 'ta',
            '泰语': 'th',
            '土耳其语': 'tr',
            '威尔士语': 'cy',
            '乌尔都语': 'ur',
            '乌克兰语': 'uk',
            '乌兹别克语': 'uz',
            '希伯来语': 'iw',
            '希腊语': 'el',
            '西班牙语': 'es',
            '夏威夷语': 'haw',
            '信德语': 'sd',
            '匈牙利语': 'hu',
            '修纳语': 'sn',
            '亚美尼亚语': 'hy',
            '伊博语': 'ig',
            '意大利语': 'it',
            '意第绪语': 'yi',
            '印地语': 'hi',
            '印尼巽他语': 'su',
            '印尼语': 'id',
            '印尼爪哇语': 'jw',
            '英语': 'en',
            '约鲁巴语': 'yo',
            '越南语': 'vi',
            '中文繁体': 'zh-TW',
            '中文简体': 'zh-CN'
        }

        self.common_list = [['英语'], ['西班牙语'], ['中文简体'], ['日语']]
        lan_sort_1 = sorted(
            self.language.keys(),
            key=lambda char: lazy_pinyin(char)[0][0])  # 排好序的语种list
        table_len, lan_sort = len(self.language), []
        # 按扭最大宽度,表格横竖比
        button_max_width, table = len(max(lan_sort_1, key=len)) + 2, (ceil(
            table_len / 7), 7)
        for i in range(0, len(lan_sort_1), table[0]):  # 格式化成二维数组,
            lan_sort.append(lan_sort_1[i:i +
                                       table[0]])  # 由于后面会进行行纵交换操作,这里取最大值为列数
        # print(button_max_width)

        var, var_1 = StringVar(), StringVar()
        var.set('自动检测')
        var_1.set(lan_sort_1[-1])

        self.root = root
        self.root.title('书香年华的专用翻译小工具')
        self.font = Font()
        self.frame = ttk.Frame(self.root)
        self.text = Text(self.frame, width=20, height=10)
        self.text_out = Text(self.frame)

        self.button_1 = ttk.Button(self.frame, text='自动检测')
        self.button_2 = ttk.Button(self.frame, text='<=>')
        self.button_3 = ttk.Button(self.frame, text='中文简体')
        self.button_4 = ttk.Button(self.frame, text='翻译')
        self.top_1 = Toplevel(self.button_1)
        self.top_2 = Toplevel(self.button_3)
        self.foc_button = None
        self.top_3 = Toplevel(self.button_1)

        self.tree_1 = ttk.Treeview(self.top_1)
        self.tree_2 = ttk.Treeview(self.top_2)
        self.tree_3 = ttk.Treeview(self.top_3)

        self.canvas_1 = Canvas(self.tree_1)
        self.canvas_2 = Canvas(self.tree_2)
        self.canvas_3 = Canvas(self.tree_3)
        self.canvas_1_text = self.canvas_1.create_text(0, 0)
        self.canvas_2_text = self.canvas_2.create_text(0, 0)
        self.canvas_3_text = self.canvas_3.create_text(0, 0)

        self.sizegrip = ttk.Sizegrip(self.frame)  # 方便调整窗口大小的部件
        # 菜单按钮没写,已完成2018.07.30
        # 位置布局
        self.frame.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W))
        self.text_out.grid(row=0, column=0, columnspan=4, sticky=(N, S, E, W))
        self.button_1.grid(row=1, column=0, sticky=E)
        self.button_2.grid(row=1, column=1)
        self.button_3.grid(row=1, column=2, sticky=W)
        self.button_4.grid(row=1, column=3)
        self.text.grid(row=2, column=0, columnspan=4, sticky=(N, S, E, W))
        self.top_1.grid()
        self.top_2.grid()
        self.top_3.grid()
        self.tree_1.grid()
        self.tree_2.grid()
        self.tree_3.grid()
        self.sizegrip.grid(row=2, column=3, sticky=(S, E))

        self.root.rowconfigure(0, weight=1)
        self.root.columnconfigure(0, weight=1)
        self.frame.columnconfigure(0, weight=2)
        self.frame.columnconfigure(1, weight=1)
        self.frame.columnconfigure(2, weight=1)
        self.frame.columnconfigure(3, weight=4)
        self.frame.rowconfigure(0, weight=3)
        self.frame.rowconfigure(2, weight=1)

        # 字体设置
        self.text_out.tag_configure('font',
                                    foreground='#476042',
                                    font=('Tempus Sans ITC', 35, 'bold'))
        self.text_out.tag_configure('font_normal', font=20)
        self.text_out.tag_configure('font_error',
                                    foreground='red',
                                    font=('Segoe Print', 16, 'bold'))

        self.text.insert(END, '输入框', 'SUNKEN')
        self.text.focus_set()
        self.text.tag_add('sel', 1.0, END)  # 全选文本
        # self.frame.tk_focusFollowsMouse()
        self.text_out.insert(END, '输出框', ('GROOVE', 'font_normal'))
        self.all_var = [self.text.get(0.0, END), var, var_1, True]
        self.text_out.configure(state=DISABLED)

        # 忽略并隐藏窗口
        self.top_1.wm_overrideredirect(True)
        self.top_2.wm_overrideredirect(True)
        self.top_3.wm_overrideredirect(True)
        self.top_1.withdraw()
        self.top_2.withdraw()
        self.top_3.withdraw()

        #  事件0
        self.text_out.bind('<Button-3>', self._right_click)  # 右键复制文本
        self.button_1.bind('<Enter>', self._button_menu)
        self.button_1.bind('<Leave>', self._button_leave)
        self.button_3.bind('<Enter>', self._button_menu)
        self.button_3.bind('<Leave>', self._button_leave)
        # self.top_3.bind('Leave', lambda e: self.top_3.withdraw())
        self.top_1.bind('<FocusOut>', self._lose_focus)  # 失去焦点隐藏窗口
        self.top_2.bind('<FocusOut>', self._lose_focus)
        self.top_3.bind('<FocusOut>', self._lose_focus)
        # 在同一个位置,第一次按是tree部件,第二次是canvas部件
        self.canvas_1.bind('<ButtonPress-1>',
                           lambda evt: self.top_1.withdraw())  # 画布按下时,隐藏窗口
        self.tree_1.bind(
            '<Configure>',
            lambda evt: self.canvas_1.grid_forget())  # place_forget()取消选中状态
        self.tree_1.bind('<ButtonPress-1>', self._tree_pressed)
        self.tree_1.bind('<Leave>',
                         lambda e: self.top_1.withdraw())  # 离开某组件,隐藏窗口
        self.canvas_2.bind('<ButtonPress-1>',
                           lambda evt: self.top_2.withdraw())  # 画布按下时,隐藏窗口
        self.tree_2.bind(
            '<Configure>',
            lambda evt: self.canvas_2.grid_forget())  # place_forget()取消选中状态
        self.tree_2.bind('<ButtonPress-1>', self._tree_pressed)
        self.tree_2.bind('<Leave>',
                         lambda e: self.top_2.withdraw())  # 离开某组件,隐藏窗口
        self.canvas_3.bind('<ButtonPress-1>',
                           lambda evt: self.top_3.withdraw())
        self.tree_3.bind('<Configure>',
                         lambda evt: self.canvas_3.grid_forget())
        self.tree_3.bind('<ButtonPress-1>', self._tree_pressed)
        self.tree_3.bind('<Leave>', lambda e: self.top_3.withdraw())
        # 表格设置
        # print(table, lan_sort)      # 7列 15行
        self.tree_1.configure(show='', selectmode='none', height=table[0])
        self.tree_2.configure(show='', selectmode='none', height=table[0])
        self.tree_3.configure(show='', selectmode='none', height=4)
        self.tree_1['columns'] = tuple([lan_sort[i]
                                        for i in range(table[1])])  # 7行,循环7次
        self.tree_2['columns'] = tuple([lan_sort[i] for i in range(table[1])])
        self.tree_3['columns'] = ('', )  # 列数
        self.items_1 = [
            self.tree_1.insert('', 'end', values='') for _ in range(table[0])
        ]
        self.items_2 = [
            self.tree_2.insert('', 'end', values='') for _ in range(table[0])
        ]
        self.items_3 = [
            self.tree_3.insert('', 'end', values='') for _ in range(4)
        ]
        for i in range(1 + 1):  # 由于后面会进行行列交换,所以这里添的是列数
            self.tree_3.column('#%d' % i, width=button_max_width * 10 + 4)
        for i in range(
                table[1] +
                1):  # 列头不像行头(不纳入计数),默认保留列头(#0),所以得加一,才能显示正确,不然最后一列无法配置width
            self.tree_1.column('#%d' % i, width=button_max_width * 10 + 4)
            self.tree_2.column('#%d' % i, width=button_max_width * 10 + 4)

        if lan_sort[-2] != lan_sort[-1]:  # 填补空白数据,使表格在后续处理中不会丢失数据
            lan_sort[-1].extend([' '] *
                                (len(lan_sort[-2]) - len(lan_sort[-1])))
        table_list = tuple(map(list, zip(*lan_sort)))  # 行列交换
        # common_table_list = tuple(map(list, zip(*self.common_list)))
        # print(self.items_2, self.items_3)
        # table_list = [[row[i] for row in lan_sort] for i in range(len(lan_sort[0]))]
        for index, item in enumerate(self.items_3):
            self.tree_3.item(item, values=self.common_list[index])
            # print(self.common_list[index])
        for index, (item_1,
                    item_2) in enumerate(zip(self.items_1, self.items_2)):
            self.tree_1.item(item_1, values=table_list[index])
            self.tree_2.item(item_2, values=table_list[index])
            # print(item_2, table_list[index])
        table_list[-2][-1] = '自动检测'  # 补充数据
        self.tree_1.item('I00E', values=table_list[-2])

        self.button_1.config(width=(button_max_width - 3) * 2)
        self.button_3.config(width=(button_max_width - 3) * 2)
        self.button_1.configure(command=self._button_menu_1)
        self.button_3.configure(command=self._button_menu_2)
        self.button_2.configure(command=self._swap_key)
        self.button_4.configure(command=self._button_translation)
        # self.root.attributes('-alpha')
        # self.root.attributes('-alpha', 0.8)   # 设置窗口属性,这里是设置透明度
        # 队列
        self.q = queue.LifoQueue(9)
        # self.q.put(list([proxy_main.run, None]))
        # Thread_0(self.q)

        # 这里开线程运行附加程序
        # list_script = (startup_script, '', self._text_1_show)
        # self.q.put(list_script)
        # Thread_0(self.q)
        self._real_translation()

    # def _thread_update(self, e):  # 创建线程
    #     Thread_0(e)

    def _real_update(self, src_text):  # 加入数据到队列
        fun_list = (self._translator_word, src_text, self._text_1_show)
        self.q.put(fun_list)
        # print(threading.activeCount()) 获取线程数
        MyTread_0(self.q)

    def _swap_key(self):  # 交换语种
        a_var, b_var = self.all_var[1].get(), self.all_var[2].get()
        if a_var == '自动检测':
            a_var = '中文简体'
        if a_var == b_var:
            return
        a_var, b_var = b_var, a_var
        # 这里添加判断语种,根据情况是否put数据到队列
        self.button_1.config(text=a_var)
        self.button_3.config(text=b_var)
        self._real_update(self.text.get(0.0, END))
        sleep(0.1)

    def _real_translation(self):
        """实时翻译"""
        src_text = self.text.get(0.0, END)

        if not self._is_change(src_text):
            # print('产生变化')
            self._real_update(src_text)
        # print('dd%s无变化' % src_text)

        self.root.after(2500, self._real_translation)

    def _right_click(self, _):
        """右键事件,可能会添加激活控件再操作,已添加聚焦"""
        self.text_out.focus_set()
        text_txt = self.text_out.get(0.0, END)
        copy(text_txt)

    def _translator_word(self, string_var):  # 翻译
        """翻译"""
        # print('数据来了')
        if self._is_change(string_var) or string_var.strip(
        ) == '':  # 交换语种后与显示语种相同直接返回,字符为空也返回
            return

        # print('有谁', len(string_var.strip()), bool(string_var.strip() == ''))
        # return
        self.all_var[1].set(self.button_1['text'])
        self.all_var[2].set(self.button_3['text'])
        self.all_var[0] = string_var
        self._text_1_show('   \n IN translation...\n', 3.5, 3.5,
                          ('GROOVE', 'font'))
        des_language, src_language = self.all_var[2].get(
        ), self.all_var[1].get()
        src_1 = 'auto' if src_language == '自动检测' else self.language[
            src_language]
        # try:
        # print(string_var, '开始翻译了')
        des_string = google_tran(string_var,
                                 des_1=self.language[des_language],
                                 src_1=src_1).text
        self._text_1_show(des_string, style=('GROOVE', 'font_normal'))
        # except Exception as e:      # 异常处理
        #     self._text_1_show(e)

    def _is_change(self, src_text):
        """判断文本是否变化,判断语种是否改变, 无变化则返回真"""
        if src_text.strip() == self.all_var[0].strip() and self.all_var[1].get(
        ) == self.button_1['text']:
            if self.all_var[2].get() == self.button_3['text']:
                return True
        else:
            return False

    def _button_translation(self):
        """翻译按钮"""
        sleep(0.1)
        src_text = self.text.get(0.0, END)
        # print(src_text)
        if not self._is_change(src_text):
            self._real_update(src_text)
            # self.all_var[0] = src_text

    def _button_menu(self, event):
        button = event.widget
        sleep(0.6)
        in_button = button.winfo_pointerxy()
        sleep(0.2)
        out_button = button.winfo_pointerxy()
        if in_button != out_button:  # 在button中停留太短时间不展示菜单
            return
        self.foc_button = button
        menu_3_geometry = button.winfo_geometry()
        # print(menu_3_geometry, '在里面')
        if self.top_1.state() == 'withdrawn' and self.top_2.state(
        ) == 'withdrawn':
            x3, y3 = self._position(menu_3_geometry, self.top_3)
            self._set_top_windows(self.top_3, x3, y3)
            # sleep(2000)
            # self.top_3.withdraw()
            # print('正常', self.top_1.state(), self.top_2.state())

    def _button_menu_1(self):
        """按键菜单"""
        self.foc_button = self.button_1
        menu_1_geometry = self.button_1.winfo_geometry()
        x1, y1 = self._position(menu_1_geometry)
        self._set_top_windows(self.top_1, x1, y1)
        # root x + button x
        # print(self.top_1.winfo_geometry())

    def _button_menu_2(self):
        self.foc_button = self.button_3
        menu_2_geometry = self.button_3.winfo_geometry()
        x1, y1 = self._position(menu_2_geometry)
        self._set_top_windows(self.top_2, x1, y1)
        # print(x1, y1)
        # root x + button x

    def _set_top_windows(self, top, x1, y1):  # 设置窗口状态
        if top is self.top_1:
            self.button_1.state(['!pressed'])
        elif top is self.top_2:
            self.button_3.state(['!pressed'])
        top.geometry('+%i+%i' % (x1, y1))
        top.focus_force()
        top.deiconify()

    def _text_1_show(self,
                     snap_string,
                     x=0.0,
                     y=END,
                     style=tuple('GROOVE')):  # 刷新输出框
        self.text_out.configure(state=NORMAL)
        self.text_out.delete(x, y)
        self.text_out.grid(row=0, column=0, columnspan=4)
        self.text_out.insert(y, snap_string, style)
        self.text_out.configure(state=DISABLED)

    def _set_font(self, snap_string='   \n IN translation...\n'):  # 字体设置
        self._text_1_show(snap_string, x=3.5, y=3.5, style=('GROOVE', 'font'))

    def _lose_focus(self, event):  # 表格失去焦点
        if event.widget == self.top_1:
            self.top_1.withdraw()
            self.tree_1.state(['!pressed'])
        else:
            self.top_2.withdraw()
            self.tree_2.state(['!pressed'])

    def _position(self, widget_geometry, top_3=None):
        """计算语种表格位置"""
        menu_geometry = widget_geometry  # 按扭geometry
        root_geometry, top_geometry = self.root.winfo_geometry(
        ), self.top_1.winfo_geometry()
        menu_position = [int(i) for i in split('[+x]', menu_geometry)]

        root_position, top_position = [int(i) for i in split('[+x]', root_geometry)], \
                                      [int(i) for i in split('[+x]', top_geometry)]
        child_window_x = root_position[2] + menu_position[2] + 9
        child_window_y = root_position[3] + menu_position[3] - top_position[
            1] + 31
        if top_3:
            top_3_geometry = top_3.winfo_geometry()
            top_position = [int(i) for i in split('[+x]', top_3_geometry)]
            # print(top_position)
            child_window_y = root_position[3] + menu_position[
                3] - top_position[1] + 31
            return child_window_x, child_window_y
        return child_window_x, child_window_y

    def _button_leave(self, e):
        x, y, widget_geometry = e.x, e.y, e.widget.winfo_geometry()
        widget_geometry = [int(i) for i in split('[+x]', widget_geometry)]
        if not 0 < x < widget_geometry[1] and not y < 0:
            if self.top_3.state() == 'withdraw':
                sleep(1)
            self.top_3.withdraw()
        # print(x, y, widget_geometry)

    def _tree_pressed(self, event):
        # self.top_3.withdraw()
        x, y, widget = event.x, event.y, event.widget
        item = widget.identify('item', 0, y)
        column = widget.identify('column', x, 0)
        tree_widget = widget.winfo_toplevel()
        item_values = widget.item(item)['values']

        tree_widget.withdraw()
        if not len(item_values):  # 点击到空白表格(行),返回
            return
        text = item_values[int(column[1]) - 1]  # 默认tkinter保留列头(#0),所以列数得减一
        if not text:  # 数据为空,返回
            return
        # button_widget = tree_widget.winfo_toplevel()
        # print(tree_widget, item_values, text)
        self.foc_button.config(text=text)
        bbox = widget.bbox(item, column)
        self._reveal_selection(text, bbox, widget)

    def _reveal_selection(self, text, bbox, tree):
        x, y, width, height = bbox
        # print(bbox)
        if tree == self.tree_1:
            canvas_ = self.canvas_1
            canvas_text = self.canvas_1_text
        elif tree == self.tree_2:
            canvas_ = self.canvas_2
            canvas_text = self.canvas_2_text
        else:
            canvas_ = self.canvas_3
            canvas_text = self.canvas_3_text
        self.font.measure(text)
        canvas_['background'] = 'SystemHighlight'
        canvas_.configure(width=width, height=height)
        canvas_.coords(canvas_text, width / 2 - 1, height / 2 - 1)  # 居中显示
        canvas_.itemconfigure(canvas_text, text=text)
        canvas_.place(in_=tree, x=x, y=y)
Exemplo n.º 36
0
class CodeTree(Treeview):
    def __init__(self, master):
        Treeview.__init__(self, master, show='tree', selectmode='none',
                          style='flat.Treeview', padding=4)

        self.font = Font(self, font="TkDefaultFont 9")

        self.tag_configure('class', image='img_c')
        self.tag_configure('def', image='img_f')
        self.tag_configure('_def', image="img_hf")
        self.tag_configure('#', image='img_sep')
        self.tag_configure('cell', image='img_cell')
        self.callback = None
        self.cells = []

        self.bind('<1>', self._on_click)
        self.bind('<<TreeviewSelect>>', self._on_select)

    def _on_click(self, event):
        if 'indicator' not in self.identify_element(event.x, event.y):
            self.selection_remove(*self.selection())
            self.selection_set(self.identify_row(event.y))

    def set_callback(self, fct):
        self.callback = fct

    def _on_select(self, event):
        sel = self.selection()
        if self.callback is not None and sel:
            self.callback(*self.item(sel[0], 'values'))

    def _get_opened(self):
        opened = []

        def rec(item):
            if self.item(item, 'open'):
                opened.append(self.item(item, 'tags'))
                for child in self.get_children(item):
                    rec(child)

        for item in self.get_children():
            rec(item)
        return opened

    def populate(self, text, reset):
        if reset:
            opened = []
        else:
            opened = self._get_opened()
        self.delete(*self.get_children())
        tokens = tokenize.tokenize(BytesIO(text.encode()).readline)
        names = set()
        self.cells.clear()
        max_length = 20
        tree = Tree('', [], -1)
        tree_index = 0
        while True:
            try:
                token = tokens.send(None)
            except StopIteration:
                break
            except (tokenize.TokenError, IndentationError):
                continue
            add = False
            if token.type == tokenize.NAME and token.string in ['class', 'def']:
                obj_type = token.string
                indent = token.start[1] + 4
                token = tokens.send(None)
                name = token.string
                names.add(name)
                if name[0] == '_' and obj_type == 'def':
                    obj_type = '_def'
                add = True
            elif token.type == tokenize.COMMENT:
                if token.string[:5] == '# ---' or 'TODO' in token.string:
                    obj_type = '#'
                    indent = token.start[1] + 4
                    name = token.string[1:]
                    add = True
                else:
                    match = re.match(r'^# In(\[.*\].*)$', token.string)
                    if match:
                        obj_type = 'cell'
                        indent = 0
                        name = match.groups()[0].strip()
                        add = True
                        self.cells.append(token.start[0])
                    else:
                        match = re.match(r'^# ?%% ?(.*)$', token.string)
                        if match:
                            obj_type = 'cell'
                            indent = 0
                            name = match.groups()[0].strip()
                            add = True
                            self.cells.append(token.start[0])

            if add:
                tree_index += 1
                parent = tree.insert('I-%i' % tree_index, indent)
                max_length = max(max_length, self.font.measure(name) + 20 + (indent//4 + 1) * 20)
                tags = (obj_type, name)
                self.insert(parent, 'end', f'I-{tree_index}', text=name,
                            tags=tags, open=tags in opened,
                            values=('%i.%i' % token.start, '%i.%i' % token.end))

        self.column('#0', width=max_length, minwidth=max_length)
        for item in self.get_children(''):
            self.item(item, open=True)
        return names