コード例 #1
0
class GetResourceApp(BaseWin):
    def __init__(self, tk_win):
        BaseWin.__init__(self, tk_win)

        self.input_url = StringVar()
        self.input_url.set('https://www.meijubie.com/movie/index44655.html')

        self.processing = False
        self.selectProcessor = StringVar()

        # 处理请求的个数
        self.current_name = ''

    def set_init_window(self):

        self.tk_win.grid_rowconfigure(1, weight=1)
        self.tk_win.grid_columnconfigure(0, weight=1)

        labelframe = LabelFrame(text="输入")
        labelframe.grid_columnconfigure(1, weight=1)
        labelframe.grid(column=0, row=0, padx=10, pady=10, sticky=EW)

        Label(labelframe, text="目标网址", justify=LEFT, width=10).grid(row=0,
                                                                    column=0)
        entry = Entry(labelframe, textvariable=self.input_url)
        entry.grid(row=0, column=1, sticky=EW, padx=5)
        entry.bind('<Key-Return>', self.getResourceLink)

        self.button = Button(labelframe,
                             text="获取",
                             command=self.getResourceLink,
                             width=10)
        self.button.grid(row=0, column=2, sticky=E, padx=5)

        Label(labelframe, text="处理器", justify=LEFT, width=10).grid(row=1,
                                                                   column=0)
        select_process = ttk.Combobox(labelframe,
                                      textvariable=self.selectProcessor,
                                      state="readonly",
                                      values=PROCESS_FUN_NAME_LIST,
                                      width=90)
        select_process.set("请选择网址处理器")
        select_process.grid(row=1,
                            column=1,
                            sticky=EW,
                            columnspan=2,
                            padx=5,
                            pady=5)
        if len(PROCESS_FUN_NAME_LIST) > 0:
            select_process.set(PROCESS_FUN_NAME_LIST[0])

        output_frame = LabelFrame(text="链接结果")
        output_frame.grid(column=0, row=1, sticky=NSEW, padx=10)

        self.copy_btn = Button(output_frame,
                               text="复制到剪贴板",
                               command=lambda: self.copy(self.output_txt))
        self.copy_btn.config(state=DISABLED)
        self.copy_btn.pack()

        self.output_txt = ScrolledText(output_frame)
        self.output_txt.pack(side=TOP,
                             expand=TRUE,
                             fill=BOTH,
                             padx=10,
                             pady=10)
        self.output_txt.bind(
            "<Button-3>",
            lambda x: self.rightKey(x, self.output_txt))  # 绑定右键鼠标事件
        self.output_txt.bind("<<Selection>>", self.on_text_selection)  # 绑定选择事件

        # self.output_txt.grid(column=0, columnspan=4)
        # self.vbar = ttk.Scrollbar(output_frame, orient=VERTICAL, command=self.output_txt.yview)
        # self.output_txt.configure(yscrollcommand=self.vbar.set)

    def on_text_selection(self, event=NONE):
        # try:
        # selection = self.output_txt.get(SEL_FIRST, SEL_LAST)
        # except Exception:
        #     selection = NONE
        # if selection is not NONE and len(selection) > 0:
        if self.output_txt.tag_ranges("sel"):
            self.copy_btn.config(state=NORMAL)
        else:
            self.copy_btn.config(state=DISABLED)

    def getResourceLink(self, event=NONE):
        if self.processing:
            return
        self.processing = True
        # 创建进度条
        self.gress_bar = GressBar()
        # 禁用按钮
        self.button.state = DISABLED
        th = threading.Thread(target=self.doGetResourceLink)
        th.setDaemon(True)
        th.start()
        # 启动进度条
        self.gress_bar.start()
        pass

    def doGetResourceLink(self):
        self.current_name = time.strftime("%Y-%m-%d %H:%M:%S",
                                          time.localtime())
        self.output_txt.insert(
            END,
            '#########################Begin:%s##########################\n' %
            self.current_name)
        request_url = self.input_url.get()
        if not request_url:
            self.output_txt.insert(END, '请求的网址为空\n')
            self.end_process()
            return
        self.output_txt.insert(END, request_url + '\n')
        self.output_txt.insert(END, '\n')

        processor = PROCESS_FUN_DICT[self.selectProcessor.get()]
        if processor is None:
            self.output_txt.insert(END, '未找到对应的网站处理器\n')
            self.end_process()
            return

        last_time = time.time_ns()
        try:
            results = processor.getDownloadLink(request_url)
        except Exception as e:
            self.output_txt.insert(END, '%s\n' % e.args)
            self.end_process()
            return

        index_start = self.output_txt.index("end-1c linestart")
        for item in results:
            self.output_txt.insert(END, item + '\n')
        index_end = self.output_txt.index("end-1c linestart")
        self.output_txt.insert(
            END, '\n耗时=%d毫秒\n' % ((time.time_ns() - last_time) / 1E6))

        self.end_process(index_end, index_start)

    def end_process(self, index_end=END, index_start=END):
        self.output_txt.insert(
            END,
            '###########################End:%s##########################\n' %
            self.current_name)
        self.gress_bar.quit()
        self.button.state = NORMAL
        self.processing = False
        # 移除之前的选择
        self.output_txt.tag_remove(SEL, '1.0', END)
        # 设置选中状态
        print("\nstart=%s,end=%s\n" % (index_start, index_end))
        self.output_txt.tag_add(SEL, index_start, index_end)
        self.output_txt.focus_set()
コード例 #2
0
class myPanel(tkinter.Tk):
    def __init__(self):
        tkinter.Tk.__init__(self, 'lsx')
        self.withdraw()  # 先withdraw隐藏再deiconify显示可使setCenter不会导致页面闪烁

        self.title('电话簿v1.05 (联系作者:QQ11313213)')  # TODO 是否有用

        self.keys_var = tkinter.StringVar()
        self.tex1 = ScrolledText(self)
        ent1 = tkinter.Entry(self, textvariable=self.keys_var, width=125)
        ent1.pack(padx=2,
                  pady=2,
                  fill=tkinter.constants.BOTH,
                  side=tkinter.constants.TOP)
        self.tex1.pack(padx=2,
                       pady=2,
                       fill=tkinter.constants.BOTH,
                       expand=True)

        self.menu = tkinter.Menu(self, tearoff=0)
        self.menu.add_command(label='复制', command=self.copyItem)
        self.menu.add_separator()
        self.menu.add_command(label='来源')
        self.menu.add_separator()
        self.menu.add_command(label='刷新', command=readContacts2)
        self.menu.add_command(label='前后文', command=self.location)
        self.menu.add_separator()
        self.menu.add_command(label='导入文件', command=ImportFiles)
        self.menu.add_command(label='新增和更改', command=UpdateFile)

        self.menu0 = tkinter.Menu(self, tearoff=0)
        self.menu0.add_command(label='刷新', command=readContacts2)
        self.menu0.add_separator()
        self.menu0.add_command(label='导入文件', command=ImportFiles)
        self.menu0.add_command(label='新增和更改', command=UpdateFile)
        self.menu0.add_separator()

        submenu = [tkinter.Menu(self, tearoff=0)]
        self.menu0.add_cascade(label='Designed by Lsx. ', menu=submenu[0])

        for key, value in [['Name', 'Li Shixian'], ['Mail', '*****@*****.**'],
                           ['Website', 'github.com/znsoooo/contacts'],
                           ['Wechat', 'Xian_2'], ['Donate', 'xxxx']]:
            submenu.append(tkinter.Menu(self, tearoff=0))
            submenu.append(tkinter.Menu(self, tearoff=0))
            submenu[-1].add_command(label=value)
            submenu[0].add_cascade(label=key, menu=submenu[-1])
        self.img_wechat = tkinter.PhotoImage(
            data=Image.img1)  # 没有self会导致显示图片为空白
        self.img_donate = tkinter.PhotoImage(data=Image.img2)
        submenu[8].entryconfig(0, image=self.img_wechat)
        submenu[10].entryconfig(0, image=self.img_donate)
        submenu[0].add_separator()
        submenu[0].add_command(label='All Rights Reserved.', command=bonus)

        setCenter(self)
        self.deiconify()

        ent1.focus()
        ent1.bind('<KeyRelease>', self.onKeyRelease)
        self.tex1.bind('<ButtonRelease-3>', self.onRightClick)

    def select(self, row):
        self.tex1.mark_set('insert', '%d.0' % row)
        self.tex1.tag_remove('sel', '0.0', 'end')
        self.tex1.tag_add('sel', '%d.0' % row, '%d.0' % (row + 1))

    def location(self, row=0):
        if not row:
            row = self.current_index + 1
        self.onKeyRelease(keys='')
        self.select(row)
        self.tex1.see('%d.0' % row)

    def copyItem(self):
        text = self.tex1.selection_get()
        self.clipboard_clear()
        self.clipboard_append(text[:-1])  # 去掉文末换行符

    def onRightClick(self, evt=0):
        self.tex1.focus()  # 当焦点在ent1中时
        self.current = int(self.tex1.index('current').split('.')[0])
        if len(self.index):
            self.current_index = self.index[self.current - 1]

            line_last = 0
            for line, file in file_list:
                if line > self.current_index:
                    break
                else:
                    line_last = line
            self.menu.entryconfig(2,
                                  label='来源: %s (line:%s)' %
                                  (file, self.current_index - line_last + 1))
            self.menu.entryconfig(
                2,
                command=lambda: os.popen('explorer /select, %s\\%s\\%s' %
                                         (os.getcwd(), DATA_FOLDER, file)))

            self.select(self.current)
            self.menu.post(evt.x_root, evt.y_root)
        else:
            self.menu0.post(evt.x_root, evt.y_root)

        return self.current

    def onKeyRelease(self, evt=0, keys=None):
        if keys is None:
            keys = self.keys_var.get()
        keys = keys.lower().split(' ')
        ss_new = []
        self.index = []
        for n, s in enumerate(ss):
            ok = True
            for key in keys:
                if key not in s[1]:
                    ok = False
            if ok:
                ss_new.append(s[0])
                self.index.append(n)  # TODO 提出搜索部分到独立的函数

        self.tex1.config(state='normal')
        self.tex1.delete('1.0', 'end')
        self.tex1.insert('1.0', '\n'.join(ss_new))
        self.tex1.config(state='disabled')  # 禁止编辑
        self.title('电话簿v1.05 (联系作者:QQ11313213) - %s结果' %
                   len(ss_new))  # title更改耗时短可以做到'同时'更改的效果

        return ss_new
コード例 #3
0
class Application(tkinter.Tk):
    def __init__(self):
        """Initialize widgets, methods."""

        tkinter.Tk.__init__(self)
        self.grid()

        fontoptions = families(self)
        font = Font(family="Verdana", size=10)

        menubar = tkinter.Menu(self)
        fileMenu = tkinter.Menu(menubar, tearoff=0)
        editMenu = tkinter.Menu(menubar, tearoff=0)
        fsubmenu = tkinter.Menu(editMenu, tearoff=0)
        ssubmenu = tkinter.Menu(editMenu, tearoff=0)

        # adds fonts to the font submenu and associates lambda functions
        for option in fontoptions:
            fsubmenu.add_command(label=option, command = lambda: font.configure(family=option))
        # adds values to the size submenu and associates lambda functions
        for value in range(1,31):
            ssubmenu.add_command(label=str(value), command = lambda: font.configure(size=value))

        # adds commands to the menus
        menubar.add_cascade(label="File",underline=0, menu=fileMenu)
        menubar.add_cascade(label="Edit",underline=0, menu=editMenu)
        fileMenu.add_command(label="New", underline=1,command=self.new, accelerator="Ctrl+N")
        fileMenu.add_command(label="Open", command=self.open, accelerator="Ctrl+O")
        fileMenu.add_command(label="Save", command=self.save, accelerator="Ctrl+S")
        fileMenu.add_command(label="Exit", underline=1,command=exit, accelerator="Ctrl+Q")                      
        editMenu.add_command(label="Copy", command=self.copy, accelerator="Ctrl+C")
        editMenu.add_command(label="Cut", command=self.cut, accelerator="Ctrl+X")
        editMenu.add_command(label="Paste", command=self.paste, accelerator="Ctrl+V")
        editMenu.add_cascade(label="Font", underline=0, menu=fsubmenu)
        editMenu.add_cascade(label="Size", underline=0, menu=ssubmenu)
        editMenu.add_command(label="Color", command=self.color)
        editMenu.add_command(label="Bold", command=self.bold, accelerator="Ctrl+B")
        editMenu.add_command(label="Italic", command=self.italic, accelerator="Ctrl+I")
        editMenu.add_command(label="Underline", command=self.underline, accelerator="Ctrl+U")
        editMenu.add_command(label="Overstrike", command=self.overstrike, accelerator="Ctrl+T")
        editMenu.add_command(label="Undo", command=self.undo, accelerator="Ctrl+Z")
        editMenu.add_command(label="Redo", command=self.redo, accelerator="Ctrl+Y")
        self.config(menu=menubar)


        self.bind_all("<Control-n>", self.new)
        self.bind_all("<Control-o>", self.open)
        self.bind_all("<Control-s>", self.save)
        self.bind_all("<Control-q>", self.exit)
        self.bind_all("<Control-b>", self.bold)
        self.bind_all("<Control-i>", self.italic)
        self.bind_all("<Control-u>", self.underline)
        self.bind_all("<Control-T>", self.overstrike)
        self.bind_all("<Control-z>", self.undo)
        self.bind_all("<Control-y>", self.redo)

        self.text = ScrolledText(self, state='normal', height=80, wrap='word', font = font, pady=2, padx=3, undo=True)
        self.text.grid(column=0, row=0, sticky='NSEW')

        # Frame configuration
        self.grid_columnconfigure(0, weight=1)
        self.resizable(True, True)



    def new(self, *args):
        """Creates a new window."""
        app = Application()
        app.title('Python Text Editor')
        app.option_add('*tearOff', False)
        app.mainloop()

    def color(self):
        """Changes selected text color."""
        try:
            (rgb, hx) = tkinter.colorchooser.askcolor()
            self.text.tag_add('color', 'sel.first', 'sel.last')
            self.text.tag_configure('color', foreground=hx)
        except TclError:
            pass

    def bold(self, *args):
        """Toggles bold for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "bold" in current_tags:
                self.text.tag_remove("bold", "sel.first", "sel.last")
            else:
                self.text.tag_add("bold", "sel.first", "sel.last")
                bold_font = Font(self.text, self.text.cget("font"))
                bold_font.configure(weight="bold")
                self.text.tag_configure("bold", font=bold_font)
        except TclError:
            pass

    def italic(self, *args):
        """Toggles italic for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "italic" in current_tags:
                self.text.tag_remove("italic", "sel.first", "sel.last")
            else:
                self.text.tag_add("italic", "sel.first", "sel.last")
                italic_font = Font(self.text, self.text.cget("font"))
                italic_font.configure(slant="italic")
                self.text.tag_configure("italic", font=italic_font)
        except TclError:
            pass

    def underline(self, *args):
        """Toggles underline for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "underline" in current_tags:
                self.text.tag_remove("underline", "sel.first", "sel.last")
            else:
                self.text.tag_add("underline", "sel.first", "sel.last")
                underline_font = Font(self.text, self.text.cget("font"))
                underline_font.configure(underline=1)
                self.text.tag_configure("underline", font=underline_font)
        except TclError:
            pass

    def overstrike(self, *args):
        """Toggles overstrike for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "overstrike" in current_tags:
                self.text.tag_remove("overstrike", "sel.first", "sel.last")
            else:
                self.text.tag_add("overstrike", "sel.first", "sel.last")
                overstrike_font = Font(self.text, self.text.cget("font"))
                overstrike_font.configure(overstrike=1)
                self.text.tag_configure("overstrike", font=overstrike_font)
        except TclError:
            pass

    def undo(self, *args):
        """Undo function"""
        try:
            self.text.edit_undo()
        except TclError:
            pass

    def redo(self, *args):
        """Redo function"""
        try:
            self.text.edit_redo()
        except TclError:
            pass

    def copy(self, *args):
        """Copy text"""
        self.clipboard_clear()
        self.clipboard_append(self.text.selection_get())

    def cut(self, *args):
        """Cut text"""
        self.copy
        self.text.delete("sel.first", "sel.last")

    def paste(self, *args):
        """Paste text"""
        insertion = self.selection_get(selection = "CLIPBOARD")
        self.text.insert(0.0, insertion)

    def open(self, *args):
        """Opens a file dialog to open a plain text file."""
        filename = tkinter.filedialog.askopenfilename()

        with open(filename) as f:
            text = f.read()

        self.text.delete("1.0", "end")
        self.text.insert('insert', text)

    def save(self, *args):
        try:
            """Opens a file dialog to save the text in plain text format."""
            text = self.text.get("1.0", "end")
            filename = tkinter.filedialog.asksaveasfilename()

            with open(filename, 'w') as f:
                f.write(text)
        except FileNotFoundError:
            pass

    def exit(self, *args):
        """Exits the program."""
        self.quit()
コード例 #4
0
class textEditor:
    def __init__(self):
        self.root = Tk()
        self.root.title('My Text Editor')

        self.text = ScrolledText(self.root, width=100, height=50)

        # if using .grid(), the area of text will be on the left when in full screen mode.
        self.text.pack()

        menubar = Menu(self.root)

        filemenu = Menu(menubar)
        menubar.add_cascade(label="File", menu=filemenu)
        filemenu.add_command(label="New", command=self.new_file)
        filemenu.add_command(label="Open", command=self.open_file)
        filemenu.add_command(label="Save", command=self.save_file)
        filemenu.add_separator()
        filemenu.add_command(label="Exit", command=self.exit)

        editmenu = Menu(menubar)
        menubar.add_cascade(label="Edit", menu=editmenu)

        fontmenu = Menu(menubar)
        menubar.add_cascade(label="Font", menu=fontmenu)
        fontmenu.add_command(label="Courier", command=self.font_courier)
        fontmenu.add_command(label="Helvetica", command=self.font_helvetica)
        fontmenu.add_command(label="Times", command=self.font_times)
        fontmenu.add_command(label="Roman", command=self.font_roman)

        menubar.add_command(label="Find", command=self.find_pattern)

        self.root.config(menu=menubar)

        self.root.mainloop()

    def save_file(self):
        data = self.text.get("1.0", "end-1c")
        savelocation = filedialog.asksaveasfilename(defaultextension='.txt',
                                                    title='Save Text')
        with open(savelocation, "w+") as f:
            try:
                f.write(data)
            except:
                messagebox.showerror(title="Oops!",
                                     message="Unable to save it...")

    def font_times(self):
        self.text.config(font=("times", 12))

    def font_courier(self):
        self.text.config(font=("Courier", 15))

    def font_helvetica(self):
        self.text.config(font=("Helvetica", 12))

    def font_roman(self):
        self.text.config(font=("Times New Roman", 12, "bold"))

    def open_file(self):
        f = filedialog.askopenfile(mode='r')
        t = f.read()
        self.text.delete(0.0, END)
        self.text.insert(0.0, t)

    def new_file(self):
        if len(self.text.get('1.0', END + '-1c')) > 0:
            ask_for_save = messagebox.askquestion(
                'save', 'do you want to save the file')
            if ask_for_save == 'yes':
                self.save_file()
            else:
                self.text.delete(0.0, END)
        self.root.title('My Text Editor')

    def exit(self):
        ask_for_exit = messagebox.askquestion(
            "Exit", "Are you sure you want to exit?")
        if ask_for_exit == 'yes':
            self.root.destroy()

    def handle_click(self):
        self.text.tag_config('Found', background='white', foreground='red')

    def find_pattern(self):
        self.text.tag_remove("Found", '1.0', END)
        find = simpledialog.askstring("Find....", "Enter text:")
        # prevent frozen because of inputing nothing in find.
        if not find:
            return
        idx = '0.0'
        if find:
            idx = '1.0'
        while 1:
            idx = self.text.search(find, idx, nocase=1, stopindex=END)
            if not idx:
                break
            lastidx = '%s+%dc' % (idx, len(find))
            self.text.tag_add('Found', idx, lastidx)
            idx = lastidx
        self.text.bind("<1>", self.handle_click())

        data = self.text.get('1.0', END)
        occurance = data.upper().count(find.upper())

        if occurance > 1:
            label = messagebox.showinfo(
                "Find", find + " has occurances " + str(occurance) + " times.")
        elif occurance == 1:
            label = messagebox.showinfo(
                "Find",
                find + " has just occurances " + str(occurance) + " time.")
        else:
            label = messagebox.showinfo("Find", "No results")
class textEditor():
    alltabs = None

    def __init__(self,
                 window,
                 labelFrame,
                 tabs,
                 vocab,
                 startWithSameLetter,
                 tabsOpen,
                 file_path=""):
        self.window = window
        # record the directory path of the file
        self.file_path = file_path
        if file_path:
            self.file_name = self.file_path
        else:
            # if the file path doesn't exist, name it accordingly
            self.file_name = 'Untitled'
        # record the necessary passed-in parameters
        self.labelFrame = labelFrame
        self.tabsOpen = tabsOpen
        self.tabs = tabs
        self.vocab = vocab
        self.startWithSameLetter = startWithSameLetter
        # create the main gui elements in the respective tab frame
        self.notepad = ScrolledText(self.labelFrame, font=("Calibri", 15))
        editorbox = self.notepad
        self.var = tk.IntVar()
        self.autoCorrectOption = tk.Checkbutton(self.labelFrame, \
                                                text="Enable Auto-Correct", variable=self.var, command=self.switchSpellChecker)
        self.autoComplete_suggestions = tk.Listbox(self.labelFrame)
        self.autoCorrect_suggestions = tk.Listbox(self.labelFrame)
        myFont = Font(family="Calibri", size=15)
        self.autoComplete_suggestions.configure(font=myFont)
        self.autoCorrect_suggestions.configure(font=myFont)

        # create funtionality bars inside tab frame
        self.createMenuBar()
        self.createToolBar(self.labelFrame)

        self.autoCorrectOption.grid(row=1, column=9)
        self.notepad.config(undo=True)
        self.notepad.config(height=900)
        self.notepad.grid(row=2, column=0, columnspan=11, sticky="WE")
        self.window.protocol("WM_DELETE_WINDOW",
                             lambda: newFileTab.closeCheck(self.tabsOpen))
        # add pre-set markup on the entire text widget in the tab frame
        self.notepad.tag_configure("misspelling",
                                   foreground="red",
                                   underline=True)

        # bind all navigation to checking the spelling of the word
        self.nav_click = self.notepad.bind("<ButtonRelease-1>",
                                           self.spellChecker)
        self.nav_up = self.notepad.bind("<Up>", self.spellChecker)
        self.nav_down = self.notepad.bind("<Down>", self.spellChecker)
        self.nav_left = self.notepad.bind("<Left>", self.spellChecker)
        self.nav_right = self.notepad.bind("<Right>", self.spellChecker)

        # check each word's spelling after typed and mark it up
        self.notepad.bind("<space>", self.markUp)
        self.notepad.bind(".", self.markUp)

        # keep calling autocomplete while user is writing
        for letter in "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM":
            self.notepad.bind("<KeyRelease-" + letter + ">", self.autoComplete)
        self.notepad.bind("<KeyRelease-BackSpace>", self.autoComplete)

        # bind file shortcuts
        self.notepad.bind('<Control-s>', newFileTab.saveToFile)
        self.notepad.bind('<Control-o>', newFileTab.openFile)
        self.notepad.bind('<Control-n>', newFileTab.createFile)
        self.notepad.bind('<Control-c>', newFileTab.copySelected)
        self.notepad.bind('<Control-x>', newFileTab.cutSelected)
        self.notepad.bind('<Control-v>', newFileTab.pasteClipboard)

    # this function creates the top menu bar including all functionalities
    def createMenuBar(self):
        menuBar = Menu(self.window)
        # create drop-down options for the file menu
        fileMenu = tk.Menu(menuBar, tearoff=0)
        fileMenu.add_command(
            label="New Document",
            command=lambda: newFileTab.createFile(self.tabsOpen))
        fileMenu.add_command(
            label="Open Local File",
            command=lambda: newFileTab.openFile(self.tabsOpen))
        fileMenu.add_command(
            label="Save file",
            command=lambda: newFileTab.saveToFile(self.tabsOpen))
        fileMenu.add_separator()
        fileMenu.add_command(
            label="Close File",
            command=lambda: newFileTab.closeFile(self.tabsOpen))
        fileMenu.add_command(label="Exit",
                             command=lambda: newFileTab.quit(self.tabsOpen))
        menuBar.add_cascade(label="File", menu=fileMenu)

        # create drop-down options for the edit menu
        editMenu = tk.Menu(menuBar, tearoff=0)
        editMenu.add_command(
            label="Undo", command=lambda: newFileTab.undoEdit(self.tabsOpen))
        editMenu.add_command(
            label="Redo", command=lambda: newFileTab.redoEdit(self.tabsOpen))
        editMenu.add_command(
            label="Copy",
            command=lambda: newFileTab.copySelected(self.tabsOpen))
        editMenu.add_command(
            label="Cut", command=lambda: newFileTab.cutSelected(self.tabsOpen))
        editMenu.add_command(
            label="Paste",
            command=lambda: newFileTab.pasteClipboard(self.tabsOpen))
        menuBar.add_cascade(label="Edit", menu=editMenu)

        self.window.config(menu=menuBar)

    '''icon pics retrieved from:
    https://icons-for-free.com/folder+open+icon-1320161390409087972/'''

    # this function creates the tool bar with clickable icon shortcuts for the functionalities
    def createToolBar(self, labelFrame):
        # add icon for handling creating new files
        new_img = tk.PhotoImage(file="newicon.png")
        new_img = new_img.zoom(1)
        new_img = new_img.subsample(15)

        # add icon for handling opening local files
        open_img = tk.PhotoImage(file="openicon.png")
        open_img = open_img.zoom(1)
        open_img = open_img.subsample(15)

        # add icon for handling saving files
        save_img = tk.PhotoImage(file="saveicon.png")
        save_img = save_img.zoom(1)
        save_img = save_img.subsample(4)

        # add icon for handling copying from files
        copy_img = tk.PhotoImage(file="copyicon.png")
        copy_img = copy_img.zoom(1)
        copy_img = copy_img.subsample(4)

        # add icon for handling cutting from files
        cut_img = tk.PhotoImage(file="cuticon.png")
        cut_img = cut_img.zoom(1)
        cut_img = cut_img.subsample(4)

        # add icon for handling cutting from clipboard
        paste_img = tk.PhotoImage(file="pasteicon.png")
        paste_img = paste_img.zoom(1)
        paste_img = paste_img.subsample(4)

        # add icon for handling undo edits
        undo_img = tk.PhotoImage(file="undoicon.png")
        undo_img = undo_img.zoom(1)
        undo_img = undo_img.subsample(4)

        # add icon for handling redo edits
        redo_img = tk.PhotoImage(file="redoicon.png")
        redo_img = redo_img.zoom(1)
        redo_img = redo_img.subsample(4)

        # add icon for handling closing current file tab
        close_img = tk.PhotoImage(file="closeicon.png")
        close_img = close_img.zoom(1)
        close_img = close_img.subsample(4)

        # create all respective buttons and configure them to their appropriate icons and function calls
        new_button = tk.Button(
            labelFrame,
            image=new_img,
            command=lambda: newFileTab.createFile(self.tabsOpen))
        open_button = tk.Button(
            labelFrame,
            image=open_img,
            command=lambda: newFileTab.openFile(self.tabsOpen))
        save_button = tk.Button(
            labelFrame,
            image=save_img,
            command=lambda: newFileTab.saveToFile(self.tabsOpen))
        copy_button = tk.Button(
            labelFrame,
            image=copy_img,
            command=lambda: newFileTab.copySelected(self.tabsOpen))
        cut_button = tk.Button(
            labelFrame,
            image=cut_img,
            command=lambda: newFileTab.cutSelected(self.tabsOpen))
        paste_button = tk.Button(
            labelFrame,
            image=paste_img,
            command=lambda: newFileTab.pasteClipboard(self.tabsOpen))
        undo_button = tk.Button(
            labelFrame,
            image=undo_img,
            command=lambda: newFileTab.undoEdit(self.tabsOpen))
        redo_button = tk.Button(
            labelFrame,
            image=redo_img,
            command=lambda: newFileTab.redoEdit(self.tabsOpen))
        close_button = tk.Button(
            labelFrame,
            image=close_img,
            command=lambda: newFileTab.closeFile(self.tabsOpen))

        new_button.image = new_img
        open_button.image = open_img
        save_button.image = save_img
        copy_button.image = copy_img
        cut_button.image = cut_img
        paste_button.image = paste_img
        undo_button.image = undo_img
        redo_button.image = redo_img
        close_button.image = close_img

        # grid the buttons appropriately onto the tab frame
        new_button.grid(row=1, column=1)
        open_button.grid(row=1, column=2)
        save_button.grid(row=1, column=3)
        copy_button.grid(row=1, column=4)
        cut_button.grid(row=1, column=5)
        paste_button.grid(row=1, column=6)
        undo_button.grid(row=1, column=7)
        redo_button.grid(row=1, column=8)
        close_button.grid(row=1, column=10)

    # this function takes automatically the first choice from the suggestion
    # box and replaces it with the word that is underlined as misspelt
    def autoCorrect(self, event):
        lastWord = self.getLastWord()
        if self.spellCheckerList(lastWord):
            # get first suggestiosn from listbox contents
            bestSuggestion = self.spellCheckerList(lastWord)[0]
            # configure and find first and last index of word to be replaced
            start = self.notepad.get('1.0', tk.END).index(lastWord)
            end = start + len(lastWord)
            line_num = int(float(self.notepad.index(tk.CURRENT)))
            start_i = str(line_num) + '.' + str(start)
            end_i = str(line_num) + '.' + str(end)
            # delete the misspelled word by the best suggestion in text widget
            self.notepad.delete(start_i, end_i)
            self.notepad.insert(start_i, bestSuggestion)

    # this function unbinds the arrows with the list from the dictionary
    # so that the list dosen't appear when pressing the auto-correct option
    def switchSpellChecker(self):
        self.notepad.unbind('<ButtonRelease-1>')
        self.notepad.unbind('<Up>')
        self.notepad.unbind('<Down>')
        self.notepad.unbind('<Left>')
        self.notepad.unbind('<Right>')
        self.notepad.unbind("<space>")
        # if the autocorrect option is pressed
        if self.var.get():
            # replace the spellchecker bindings to autocorrect
            self.notepad.bind("<space>", self.autoCorrect)
            self.notepad.bind(".", self.autoCorrect)
        # if it is not pressed
        else:
            # rebind the orginal keys for the spellchecker listbox functionality
            self.notepad.bind("<ButtonRelease-1>", self.spellChecker)
            self.notepad.bind("<Up>", self.spellChecker)
            self.notepad.bind("<Down>", self.spellChecker)
            self.notepad.bind("<Left>", self.spellChecker)
            self.notepad.bind("<Right>", self.spellChecker)

            # check each word's spelling after typed and mark it up
            self.notepad.bind("<space>", self.isSpeltCorrect)
            self.notepad.bind(".", self.isSpeltCorrect)

    # this function gets the last word that was typed by the user
    def getLastWord(self):
        # split all input text at all white space characters (e.g. space, tab, enter)
        wordsList = re.split("\s+", self.notepad.get("1.0", tk.END))
        # remove last empty string
        wordsList.remove('')
        # last word is at the last index of the words list
        lastWord = wordsList[len(wordsList) - 1]
        # remove unnecessary punctuations next to the last word
        lastWord_stripped = lastWord.translate(
            str.maketrans('', '', string.punctuation))
        return lastWord_stripped.lower()

    # here we edit the words that are misspelt after
    # getting the words from the screen and correct their form
    def spellCheckerList(self, word_to_check):
        edits = {}
        # if there's no word selected or a space is selected
        if word_to_check == "" or word_to_check == " ":
            return
        # if the word is misspelt, record its respective edit distances and frequencies
        elif not self.isSpeltCorrect(word_to_check):
            # compute for min edit distance from each word in dictionary
            for word in self.vocab:
                edits_num = self.minEditDistance(word_to_check, word)
                # record all words corresponding to edits numbers 1 and 2 in a dictionary
                if edits_num <= 2:
                    # if there is a key in the dictionary corresponding to the same edit distance
                    if edits_num in edits:
                        # add it to its list of values
                        edits[edits_num].append(word)
                    else:
                        # if not, create a new key for the number of edits and add it
                        edits[edits_num] = [word]

        # record and sort frequencies of words corresponding for 1 edit and 2 edits
        freqs1 = []
        freqs2 = []
        # sorting words with edit distance 1 one based on frequency
        if 1 in edits:
            for similar_word in edits.get(1):
                # record frequency of each word with the same edit distance
                freq = self.vocab.get(similar_word)
                freqs1.append(freq)
        # sorting words with edit distance 1 one based on frequency
        if 2 in edits:
            for similar_word in edits.get(2):
                # record frequency of each word with the same edit distance
                freq = self.vocab.get(similar_word)
                freqs2.append(freq)
        # rearrange frequencies individually
        freqs1.sort()
        freqs2.sort()
        # combine the two frequency lists in order of 1 then 2 to get appropriate suggestions list
        # the smallest edit distance if the first priority, then its frequency
        freqs = freqs1 + freqs2
        suggestions = []
        for f in freqs:
            for word in self.vocab:
                # get words based on their corresponding frequencies in order
                if self.vocab.get(word) == f:
                    # add each corresponding word to the suggestions list
                    suggestions.append(word)
        return suggestions

    '''STILL TO DO, CONSIDER CAPITALIZATION IN SPELLCHECKER and replacement word/pop-up lists'''

    # this function checks if the word is spelt correctly or not
    # it returns a boolean value based on this condition
    def isSpeltCorrect(self, word):
        if word in self.vocab:
            return True
        return False

    # this functions recognizes the word and finds similar words
    # depending on their frequency, this will be then added to the
    # suggestion list. this function updates after typing each charachter
    def autoCompleteList(self, e):
        typed_word = self.getCurrWord(e).lower()
        if typed_word == "":
            return
        freqs = []
        suggestions = []
        inp_length = len(typed_word)
        for word in self.vocab:
            # check for english words that start with the same characters
            if word[:inp_length].lower() == typed_word:
                print('hi')
                # record the frequency ranks of such words
                freq = self.vocab.get(word)
                freqs.append(freq)
        # order frequencies
        freqs.sort()
        for f in freqs:
            for word in self.vocab:
                # get words based on their corresponding frequencies in order
                if self.vocab.get(word) == f:
                    suggestions.append(word)
        return suggestions

    # this function takes the list of words suggested if any and
    # inserts them on the screen in the autocomplete suggestions listbox
    def autoComplete(self, event):
        self.autoComplete_suggestions.destroy()
        self.autoComplete_suggestions = tk.Listbox(window)
        myFont = Font(family="Calibri", size=15)
        self.autoComplete_suggestions.configure(font=myFont)
        word = self.getCurrWord(event).lower()
        # ignore autocomplete call if the word is empty
        if not word:
            return
        # if there is one character typed
        if len(word) == 1:
            # use pre-loaded dictionary to get suggestiosn into listbox
            suggestions = self.startWithSameLetter.get(word)
            i = 0
            # add the first 10 word suggestions, as long as they exist
            while i < 11 and i < len(suggestions):
                for l in suggestions:
                    # add them to the suggestions listbox in order
                    self.autoComplete_suggestions.insert(i, l + " ")
                    i += 1
        else:
            # if typed portion is a part of a valid word
            if self.autoCompleteList(event):
                # get autocomplete list and append its first 10 values into the listbox
                suggestions = self.autoCompleteList(event)[:10]
                for i in range(len(suggestions)):
                    self.autoComplete_suggestions.insert(
                        i, suggestions[i] + " ")
            # if not, indicate lack of matches on listbox
            else:
                self.autoComplete_suggestions.insert(0, "No matches found.")

        # remove duplicate words in the suggestions listbox and the typed word
        if word in self.autoComplete_suggestions.get(0, tk.END):
            index = self.autoComplete_suggestions.get(0, tk.END).index(word)
            # delete duplicate word from suggestions listbox
            self.autoComplete_suggestions.delete(index)
            # if there are more suggestions available after 10, add the next one
            if len(self.autoCompleteList(event)) >= 11:
                self.autoComplete_suggestions.insert(
                    10,
                    self.autoCompleteList(event)[10] + " ")

        # place the listbox where the typing cursor is
        (x, y, w, h) = self.notepad.bbox('insert')
        self.autoComplete_suggestions.place(x=x + 140,
                                            y=y + 200,
                                            anchor="center")
        self.autoComplete_suggestions.bind('<<ListboxSelect>>',
                                           self.autoCompleteClickSelect)

    # this function also draws a list box with all the suggested words that
    # could replace the misspelt word.
    def spellChecker(self, event):
        self.autoComplete_suggestions.destroy()
        self.autoCorrect_suggestions.destroy()
        self.autoCorrect_suggestions = tk.Listbox(self.labelFrame)
        myFont = Font(family="Calibri", size=15)
        self.autoCorrect_suggestions.configure(font=myFont)
        # if the selected word is the one being currently typed
        # autocomplete it and don't spellcheck it (word not fully typed yet)
        '''if self.getCurrWord(event) and self.getNavigWord(event):
            self.autoComplete(event)
            return'''
        word = self.getNavigWord(event)
        # if the suggestions listbox is not empty, clear it
        if len(self.autoCorrect_suggestions.get(0, tk.END)) != 0:
            self.autoCorrect_suggestions.delete(0, tk.END)
        # exit spell checker if the word is spelt correctly
        if self.isSpeltCorrect(word):
            return
        # if current word is not empty and is spelled incorrectly
        elif len(self.notepad.get('1.0', 'end-1c')) != 0:
            if self.spellCheckerList(word):
                # append first 10 suggestions into listbox
                suggestions = self.spellCheckerList(word)[:10]
                for i in range(len(suggestions)):
                    self.autoCorrect_suggestions.insert(i, suggestions[i])
            else:
                # if not close matches from min edit function, display appropriate message
                self.autoCorrect_suggestions.insert(0, "No matches found.")
                self.autoCorrect_suggestions.insert(1,
                                                    "Add word to dictionary")

        if len(word) != 1:
            # place the listbox where the cursor is
            (x, y, w, h) = self.notepad.bbox('insert')
            self.autoCorrect_suggestions.place(x=x + 115,
                                               y=y + 160,
                                               anchor="center")

        self.autoComplete_suggestions = tk.Listbox(self.labelFrame)
        myFont = Font(family="Calibri", size=15)
        self.autoComplete_suggestions.configure(font=myFont)
        self.autoCorrect_suggestions.bind('<<ListboxSelect>>',
                                          self.autoCorrectClickSelect)

    # this function takes the selection that the user made from the suggestion box
    # and overwrites the word in he screen
    def autoCorrectClickSelect(self, event):
        selected_word = self.autoCorrect_suggestions.get(
            self.autoCorrect_suggestions.curselection())
        # get the entire word the cursor is on
        navigWord = self.getNavigWord(event)
        if selected_word == "No matches found.":
            self.autoCorrect_suggestions.destroy()
            return
        elif selected_word == "Add word to dictionary":
            self.vocab[navigWord] = len(self.vocab) + 1
        else:
            start = self.notepad.get('1.0', tk.END).index(navigWord)
            end = start + len(navigWord)
            line_num = int(float(self.notepad.index(tk.CURRENT)))
            # configure start and end indices of the word to be corrected syntax correctly
            start_i = str(line_num) + '.' + str(start)
            end_i = str(line_num) + '.' + str(end)
            # delete the misspelled word and replace it by the correct one selected from the listbox
            self.notepad.delete(start_i, end_i)
            self.notepad.insert(start_i, selected_word)

        if self.autoCorrect_suggestions.winfo_exists:
            self.autoCorrect_suggestions.destroy()

    # this function takes the selection that the user made from the suggestion box
    # and overwrites the word in the screen for the autocomplete option
    def autoCompleteClickSelect(self, event):
        if self.autoComplete_suggestions.curselection():
            selected_word = self.autoComplete_suggestions.get(
                self.autoComplete_suggestions.curselection())

            if selected_word == "No matches found.":
                self.autoComplete_suggestions.destroy()
                return
            # get the partial word currently being typed
            currWord = self.getCurrWord(event).lower()
            # configure start and end indices of the word to be corrected syntax correctly
            start = self.notepad.get('1.0', tk.END).index(currWord)
            end = start + len(currWord)
            line_num = int(float(self.notepad.index(tk.CURRENT)))
            start_i = str(line_num) + '.' + str(start)
            end_i = str(line_num) + '.' + str(end)
            # delete the misspelled word and replace it by the correct one selected from the listbox
            self.notepad.delete(start_i, end_i)
            self.notepad.insert(start_i, selected_word)
            self.autoComplete_suggestions.destroy()

    # this function underlines the word that is misspelt and
    # colors it with red
    def markUp(self, misspelt_word):
        lastWord = self.getLastWord()
        # if word contains numbers of special characters, don't mark it up
        if not lastWord.isalpha():
            return
        self.autoComplete_suggestions.destroy()
        # search for starting index of the misspelt word
        index = self.notepad.search(r'\s',
                                    "insert",
                                    backwards=True,
                                    regexp=True)
        if index == "":
            index = "1.0"
        else:
            index = self.notepad.index("%s+1c" % index)
        word = self.notepad.get(index, "insert").translate(
            str.maketrans('', '', string.punctuation))
        # if word spelled correctly, remove pre-set misspelling tag
        if word.lower() in self.vocab:
            self.notepad.tag_remove("misspelling", index,
                                    "%s+%dc" % (index, len(word)))
        else:
            self.notepad.tag_add("misspelling", index,
                                 "%s+%dc" % (index, len(word)))

    '''modfiied code from: 
    https://stackoverflow.com/questions/3732605/add-advanced-features-to-a-tkinter-text-widget'''

    # This function finds the minimum edit distance using a modified version of the Levistein algorithm
    # This is my own implementation of the algorithm
    def minEditDistance(self, misspelt_word, vocab_word):
        rows = len(misspelt_word) + 1
        columns = len(vocab_word) + 1
        matrix = []
        # split list of lists based on rows
        # initialize values for column contents for each row
        for i in range(rows):
            matrix.append([])
            for j in range(columns):
                matrix[i].append(-1)
        # empty string row
        first_row = []
        for n in range(columns):
            first_row.append(n)
        matrix = [first_row] + matrix[1:]
        # add first column values in matrix
        n = 0
        for i in range(rows):
            matrix[i][0] = n
            n += 1
        # for each letter of the misspelt word
        for r in range(rows - 1):
            # go through each letter in the vocab word
            for c in range(columns - 1):
                # if the letters are the same
                if vocab_word[c] == misspelt_word[r]:
                    # copy down the value at the relative left diagonal position in the matrix
                    # into the corresponding matrix position of the current string comparison
                    matrix[r + 1][c + 1] = matrix[r][c]
                # if letters are different
                else:
                    # take the minimum value of the three upper left diagonals to the current position
                    adj_min = min(matrix[r][c], matrix[r][c + 1],
                                  matrix[r + 1][c])
                    # add 1 to get the minimum additional edit to transform the two strings parts so far
                    # add resulting value into corresponding matrix position
                    matrix[r + 1][c + 1] = adj_min + 1
        # minimum number of edits is the last computed value of the matrix
        minEdits = matrix[rows - 1][columns - 1]
        return minEdits

    # this function gets the word that the cursor is hovering over in the text widget
    # and returns it.
    def getNavigWord(self, event):
        start = self.notepad.index("insert wordstart")
        end = self.notepad.index("insert wordend")
        nav_word = self.notepad.get(start, end)
        # remove unnecessary punctuations next to the typed word
        nav_word_stripped = nav_word.translate(
            str.maketrans('', '', string.punctuation))
        return nav_word_stripped.lower()

    # this function gets the word that is being modified currently from the user
    # and returns it.
    def getCurrWord(self, event):
        all_typed = self.notepad.get("1.0", "end")
        i = all_typed.rfind(" ")
        curr_word = all_typed[i + 1:].strip()
        # remove unnecessary punctuations next to the typed word
        curr_word_stripped = curr_word.translate(
            str.maketrans('', '', string.punctuation))
        return curr_word_stripped.lower()
コード例 #6
0
ファイル: TextEditor.py プロジェクト: Drax-il/TextEditor
class Application(tkinter.Tk):
    def __init__(self):
        """Initialize widgets, methods."""

        tkinter.Tk.__init__(self)
        self.grid()

        fontoptions = families(self)
        font = Font(family="Verdana", size=10)

        menubar = tkinter.Menu(self)
        fileMenu = tkinter.Menu(menubar, tearoff=0)
        editMenu = tkinter.Menu(menubar, tearoff=0)
        fsubmenu = tkinter.Menu(editMenu, tearoff=0)
        ssubmenu = tkinter.Menu(editMenu, tearoff=0)

        # adds fonts to the font submenu and associates lambda functions
        for option in fontoptions:
            fsubmenu.add_command(label=option, command = lambda: font.configure(family=option))
        # adds values to the size submenu and associates lambda functions
        for value in range(1,31):
            ssubmenu.add_command(label=str(value), command = lambda: font.configure(size=value))

        # adds commands to the menus
        menubar.add_cascade(label="File",underline=0, menu=fileMenu)
        menubar.add_cascade(label="Edit",underline=0, menu=editMenu)
        fileMenu.add_command(label="New", underline=1,
                             command=self.new, accelerator="Ctrl+N")
        fileMenu.add_command(label="Open", command=self.open, accelerator="Ctrl+O")
        fileMenu.add_command(label="Save", command=self.save, accelerator="Ctrl+S")
        fileMenu.add_command(label="Exit", underline=1,
                             command=exit, accelerator="Ctrl+Q")
        editMenu.add_command(label="Copy", command=self.copy, accelerator="Ctrl+C")
        editMenu.add_command(label="Cut", command=self.cut, accelerator="Ctrl+X")
        editMenu.add_command(label="Paste", command=self.paste, accelerator="Ctrl+V")
        editMenu.add_cascade(label="Font", underline=0, menu=fsubmenu)
        editMenu.add_cascade(label="Size", underline=0, menu=ssubmenu)
        editMenu.add_command(label="Color", command=self.color)
        editMenu.add_command(label="Bold", command=self.bold, accelerator="Ctrl+B")
        editMenu.add_command(label="Italic", command=self.italic, accelerator="Ctrl+I")
        editMenu.add_command(label="Underline", command=self.underline, accelerator="Ctrl+U")
        editMenu.add_command(label="Overstrike", command=self.overstrike, accelerator="Ctrl+T")
        editMenu.add_command(label="Undo", command=self.undo, accelerator="Ctrl+Z")
        editMenu.add_command(label="Redo", command=self.redo, accelerator="Ctrl+Y")
        self.config(menu=menubar)

        """Accelerator bindings. The cut, copy, and paste functions are not
        bound to keyboard shortcuts because Windows already binds them, so if
        Tkinter bound them as well whenever you typed ctrl+v the text would be
        pasted twice."""
        self.bind_all("<Control-n>", self.new)
        self.bind_all("<Control-o>", self.open)
        self.bind_all("<Control-s>", self.save)
        self.bind_all("<Control-q>", self.exit)
        self.bind_all("<Control-b>", self.bold)
        self.bind_all("<Control-i>", self.italic)
        self.bind_all("<Control-u>", self.underline)
        self.bind_all("<Control-T>", self.overstrike)
        self.bind_all("<Control-z>", self.undo)
        self.bind_all("<Control-y>", self.redo)

        self.text = ScrolledText(self, state='normal', height=30, wrap='word', font = font, pady=2, padx=3, undo=True)
        self.text.grid(column=0, row=0, sticky='NSEW')

        # Frame configuration
        self.grid_columnconfigure(0, weight=1)
        self.resizable(True, True)

    """Command functions. *args is included because the keyboard bindings pass
    two arguments to the functions, while clicking in the menu passes only 1."""

    def new(self, *args):
        """Creates a new window."""
        app = Application()
        app.title('Python Text Editor')
        app.option_add('*tearOff', False)
        app.mainloop()

    def color(self):
        """Changes selected text color."""
        try:
            (rgb, hx) = tkinter.colorchooser.askcolor()
            self.text.tag_add('color', 'sel.first', 'sel.last')
            self.text.tag_configure('color', foreground=hx)
        except TclError:
            pass

    def bold(self, *args):
        """Toggles bold for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "bold" in current_tags:
                self.text.tag_remove("bold", "sel.first", "sel.last")
            else:
                self.text.tag_add("bold", "sel.first", "sel.last")
                bold_font = Font(self.text, self.text.cget("font"))
                bold_font.configure(weight="bold")
                self.text.tag_configure("bold", font=bold_font)
        except TclError:
            pass

    def italic(self, *args):
        """Toggles italic for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "italic" in current_tags:
                self.text.tag_remove("italic", "sel.first", "sel.last")
            else:
                self.text.tag_add("italic", "sel.first", "sel.last")
                italic_font = Font(self.text, self.text.cget("font"))
                italic_font.configure(slant="italic")
                self.text.tag_configure("italic", font=italic_font)
        except TclError:
            pass

    def underline(self, *args):
        """Toggles underline for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "underline" in current_tags:
                self.text.tag_remove("underline", "sel.first", "sel.last")
            else:
                self.text.tag_add("underline", "sel.first", "sel.last")
                underline_font = Font(self.text, self.text.cget("font"))
                underline_font.configure(underline=1)
                self.text.tag_configure("underline", font=underline_font)
        except TclError:
            pass

    def overstrike(self, *args):
        """Toggles overstrike for selected text."""
        try:
            current_tags = self.text.tag_names("sel.first")
            if "overstrike" in current_tags:
                self.text.tag_remove("overstrike", "sel.first", "sel.last")
            else:
                self.text.tag_add("overstrike", "sel.first", "sel.last")
                overstrike_font = Font(self.text, self.text.cget("font"))
                overstrike_font.configure(overstrike=1)
                self.text.tag_configure("overstrike", font=overstrike_font)
        except TclError:
            pass

    def undo(self, *args):
        """Undo function"""
        try:
            self.text.edit_undo()
        except TclError:
            pass

    def redo(self, *args):
        """Redo function"""
        try:
            self.text.edit_redo()
        except TclError:
            pass

    def copy(self, *args):
        """Copy text"""
        self.clipboard_clear()
        self.clipboard_append(self.text.selection_get())

    def cut(self, *args):
        """Cut text"""
        self.copy
        self.text.delete("sel.first", "sel.last")

    def paste(self, *args):
        """Paste text"""
        insertion = self.selection_get(selection = "CLIPBOARD")
        self.text.insert(0.0, insertion)

    def open(self, *args):
        """Opens a file dialog to open a plain text file."""
        filename = tkinter.filedialog.askopenfilename()

        with open(filename) as f:
            text = f.read()

        self.text.delete("1.0", "end")
        self.text.insert('insert', text)

    def save(self, *args):
        try:
            """Opens a file dialog to save the text in plain text format."""
            text = self.text.get("1.0", "end")
            filename = tkinter.filedialog.asksaveasfilename()

            with open(filename, 'w') as f:
                f.write(text)
        except FileNotFoundError:
            pass

    def exit(self, *args):
        """Exits the program."""
        self.quit()
コード例 #7
0
ファイル: messages.py プロジェクト: ehmurray8/KytonUI
class LogView(ttk.Frame):
    """
    Widget for the program log messages, ttk Frame implementation.

    :ivar List[MessageType] message_types: the MessageTypes to include in the filter combobox
    :ivar List[MessageType] all_types: a list of all the message types
    :ivar List[MessageDelay] message_delays: a list of all of the message delays
    :ivar Dict[str: collections.deque] messages: mapping of message type title string to dequeues of fixed
                                                 size containing messages' body
    :ivar Dict[str: float] message_time: message body without timestamp mapped to time in s
    :ivar Dict[str: MessageDelay] message_time_filer: message body without timestamp mapped to MessageDelay
    :ivar float first_click_time: the time the header text was first clicked (Used for developer messages)
    :ivar int num_clicks: the number of times the header text was clicked within the specified time (Used for dev msgs)
    :ivar bool showing: True if developer messages are in the filter, False otherwise
    :ivar MessageType current_filter: the current message type to filter messages
    :ivar ttk.Combobox filter_box: the combobox tkinter widget used for selecting a filter level
    :ivar ttk.ScrolledText log_view: the scrolled text tkinter widget used for displaying log messages
    """
    def __init__(self, container: ttk.Frame, **kwargs):
        """
        Setup the log view widgets, and the data structures for storing messages.

        :param container: parent frame
        :param kwargs: additional arguments for the Frame
        """
        super().__init__(container, **kwargs)
        self.message_types = [
            MessageType.INFO, MessageType.WARNING, MessageType.ERROR
        ]
        self.all_types = self.message_types + [
            MessageType.CRITICAL, MessageType.DEVELOPER
        ]
        self.message_delays = [
            MessageDelay.FIRST, MessageDelay.SHORT, MessageDelay.LONG,
            MessageDelay.MAX
        ]

        self.messages = {}
        self.message_time = SizeDict(maxsize=1000)  # type: Dict[str, float]
        self.message_time_filter = SizeDict(
            maxsize=1000)  # type: Dict[str, MessageDelay]

        self.first_click_time = time.time()
        self.num_clicks = 0
        self.showing = False

        self.current_filter = MessageType.INFO
        for t in self.all_types:
            self.messages[t.name.title()] = collections.deque(maxlen=25000)
        self.pack()
        header_frame = ttk.Frame(self)
        header_frame.pack(expand=True, fill=tk.BOTH)
        header_lbl = ttk.Label(header_frame, text="Program Log")
        header_lbl.pack(anchor=tk.W, side=tk.LEFT)
        header_lbl.bind("<Button-1>", self.click_handler)

        self.filter_box = ttk.Combobox(
            header_frame,
            values=[_type.name.title() for _type in self.message_types])
        self.filter_box.config(state="readonly")
        self.filter_box.set(MessageType.INFO.name.title())
        self.filter_box.pack(anchor=tk.E, side=tk.RIGHT)

        self.filter_box.bind("<<ComboboxSelected>>", self.filter_msg)
        self.filter_box.pack(anchor=tk.E)
        self.log_view = ScrolledText(self,
                                     wrap=tk.WORD,
                                     background=LOG_BACKGROUND_COLOR)
        for t in self.all_types:
            self.log_view.tag_configure(t.name,
                                        font=Font(family="Helvetica", size=10),
                                        foreground=t.color)
        self.log_view.pack(expand=True, fill=tk.BOTH)
        ttk.Button(self, text="Export",
                   command=self.export).pack(anchor=tk.CENTER)

    def click_handler(self, _):
        """Click handler for the Program Log header to allow the user to view developer messages."""
        if time.time() - self.first_click_time > 15.:
            self.first_click_time = time.time()
            self.num_clicks = 0
        else:
            self.num_clicks += 1

        if self.num_clicks == 10:
            self.num_clicks = 0
            if not self.showing:
                self.message_types.append(MessageType.DEVELOPER)
            else:
                self.message_types.remove(MessageType.DEVELOPER)
            self.showing = not self.showing
            self.filter_box.config(
                values=[_type.name.title() for _type in self.message_types])

    def export(self):
        """Export the messages to a log text file in the log folder."""
        t = time.time()
        timestamp = datetime.datetime.fromtimestamp(t).strftime(
            "%Y%m%dT%H%M%S")
        if not os.path.isdir("log"):
            os.mkdir("log")
        with open(os.path.join("log", "{}_log.txt".format(str(timestamp))),
                  "w") as f:
            _type = MessageType.INFO.filter_num
            if MessageType.DEVELOPER in self.message_types:
                _type = MessageType.DEVELOPER.filter_num
            msgs = self.get_msgs(_type)
            msgs_sorted = sort_messages(msgs)
            for t, (text, tag) in msgs_sorted:
                f.write(text)

    def clear(self):
        """Clear the log view scrolled text."""
        self.log_view.config(state='normal')
        for t in self.all_types:
            try:
                self.log_view.tag_remove(t.name, "1.0", tk.END)
            except tk.TclError:
                pass
        self.log_view.delete("1.0", tk.END)

    def add_msg(self, msg: Message):
        """
        Add the msg to the log view, and the message store data structures.

        :param msg: message to add to the log view
        """
        self.message_time[msg.msg] = msg.time
        self.messages[msg.type.name.title()].append((msg.time, msg.text))
        if msg.type.filter_num <= self.current_filter.filter_num:
            self.write_msg(msg.text, msg.type.name)

    def write_msg(self, text: str, tag: str, start: str = "1.0"):
        """
        Write the text to the Scrolled Text tkinter widget, color it based on the tag, add it to the beginning
        if start is '1.0', if start tk.END add it to the end.

        :param text: text to write to the scrolled text view
        :param tag: name of the tag that defines the style for the text, based on MessageType
        :param start: the position to start writing the text
        """
        self.log_view.config(state='normal')
        self.log_view.insert(start, text)
        self.highlight_pattern(text, tag)
        self.log_view.config(state='disabled')

    def highlight_pattern(self,
                          pattern: str,
                          tag: str,
                          start: str = "1.0",
                          end: str = "end",
                          regexp: bool = False):
        """
        Apply the given tag to all text that matches the given pattern.

        :param pattern: pattern to match when looking to format the text using the tag.
        :param tag: tkinter scrolled text tag corresponding to a tk font
        :param start: where to start looking for the pattern from
        :param end: where to end looking for the pattern
        :param regexp: If True, pattern will be treated as a Tcl regular expression
        """
        start = self.log_view.index(start)
        end = self.log_view.index(end)
        self.log_view.mark_set("matchStart", start)
        self.log_view.mark_set("matchEnd", start)
        self.log_view.mark_set("searchLimit", end)

        count = tk.IntVar()
        while True:
            index = self.log_view.search(pattern,
                                         "matchEnd",
                                         "searchLimit",
                                         count=count,
                                         regexp=regexp)
            if index == "":
                break
            if count.get() == 0:
                break
            self.log_view.mark_set("matchStart", index)
            self.log_view.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
            self.log_view.tag_add(tag, "matchStart", "matchEnd")

    def get_msgs(self, filter_num: int) -> collections.OrderedDict:
        """
        Get all of the messages that have a filter number less than or equal the filter number.

        :param filter_num: the number used for filtering messages
        :return: time mapped to (text, tag) for all filtered messages
        """
        msgs = {}
        for _type in self.all_types:
            if _type.filter_num <= filter_num:
                for msg in self.messages[_type.name.title()]:
                    msgs[msg[0]] = (msg[1], _type.name)
        return collections.OrderedDict(msgs.items(), key=lambda t: t[0])

    def filter_msg(self, _):
        """Filters messages when the filter combobox value is changed."""
        for t in self.all_types:
            selection = self.filter_box.current()
            if t.name.title() == self.message_types[selection].name.title():
                self.current_filter = t
        msgs = self.get_msgs(self.current_filter.filter_num)
        self.clear()
        msgs_sorted = sort_messages(msgs)
        for t, (text, tag) in msgs_sorted:
            self.write_msg(text, tag, start=tk.END)
コード例 #8
0
ファイル: GUI.py プロジェクト: yogeshsinghgit/Tkinter-Notepad
class PyNote:
    def __init__(self,root):
        self.root = root
        self.root.title('Untitled - PyNote')
        self.root.configure( bg='lightgray')
        self.root.geometry('950x600')
        self.root.protocol("WM_DELETE_WINDOW", self.callback)
        self.root.minsize(width=700, height=400)

        # important stuffs ...
        self.font_style = ('arial',14)
        self.filename = None
        self.file_saved = True
        self.index=0

        # widgets ,........
        self.textarea = ScrolledText(self.root,font=self.font_style,undo=True, wrap = WORD)
        #self.textarea.bindtags(('Text','post-class-bindings', '.', 'all'))
        self.textarea.pack(fill=BOTH,expand=True)
        self.shortcut_binding()

        self.status = StringVar()
        self.pos = StringVar()

        self.status.set('PyNote - ({})'.format('Untitled File'))
        # important stuffs ...
        font_style = ('arial', 13)
        self.label = Label(self.root, textvariable=self.status, fg='black', bg='lightgray', anchor=SW, font=font_style)
        self.label.pack(side=LEFT,fill=BOTH)


        # cursor postion label...
        self.cursor_pos_lbl = Label(self.root, textvariable=self.pos, fg='black', bg='lightgray', anchor=NW, font=font_style)
        self.cursor_pos_lbl.pack(side=RIGHT)

        # code for popup menu bar ....
        # creating menu-bar ...
        self.m = Menu(root, tearoff=0)
        self.m.add_command(label="Refresh", command=self.refresh)
        self.m.add_command(label = "Select All",command=self.select_all)
        self.m.add_command(label="Cut", command=lambda: self.textarea.event_generate("<<Cut>>"))
        self.m.add_command(label="Copy", command=lambda: self.textarea.event_generate("<<Copy>>"))
        self.m.add_command(label = "Paste", command=lambda: self.textarea.event_generate("<<Paste>>"))
        self.m.add_command(label="Delete", command=lambda: self.textarea.delete(SEL_FIRST, SEL_LAST))
        self.m.add_command(label="Add Date/Time", command=self.add_time_date)

        # Calling functions and other class methods or creating objects ......
        self.menubar = Menubar(self)


    # function definations .................

    def set_title(self,name= None):
        if name:
            self.root.title(name + " - PyNote")
        else:
            self.root.title('Untitled - PyNote')



    def new_file(self, event = None):
        self.textarea.delete(1.0, END)
        # call the save command if any text is written on text area ..
        self.filename = None
        self.set_title()
        self.update_status('PyNote - ({})'.format('Untitled File'))


    def open_file(self, *args):
        self.filename = filedialog.askopenfilename( defaultextension = ".txt",
                                                    filetypes = [("All Files" , "*.*"),
                                                                 ("Text Files" , "*.txt"),
                                                                 ("Python Scripts" , "*.py"),
                                                                 ('HTML Docs.' , "*.html"),
                                                                 ("CSS Docs.","*.css")])
        if self.filename:
            self.textarea.delete(1.0,END)
            with open(self.filename , "r") as f :
                self.textarea.insert(1.0,f.read())
            self.update_status('File is Opened /  ' + self.filename)
        self.set_title(self.filename)





    def save_file(self, *args):
        if self.filename:
            try:
                textarea_data = self.textarea.get(1.0,END)
                with open(self.filename, 'w') as f:
                    f.write(textarea_data)
                self.update_status('File is Saved Again ')
                self.file_saved = True

            except Exception as e:
                messagebox.showerror('PyNote -Says ','Error Occurs '+ str(e))
        else:
            self.save_as_file()


    def save_as_file(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(initialfile = 'Untitled.txt',
                                                    defaultextension=".txt",
                                                    filetypes=[("All Files", "*.*"),
                                                               ("Text Files", "*.txt"),
                                                               ("Python Scripts", "*.py"),
                                                               ('HTML Docs.', "*.html"),
                                                               ("CSS Docs.", "*.css")]
                                                    )
            textarea_data =  self.textarea.get(1.0,END)
            with open(new_file , 'w') as f:
                f.write(textarea_data)
            self.filename = new_file
            self.set_title(self.filename)
            self.file_saved = True
            self.update_status('File is Saved As '+ self.filename)
        except Exception as e:
            messagebox.showerror('PyNote -Says ','Error Occurs '+ str(e))


    def about_us(self):
        messagebox.showinfo('About - Us','''
        PyNote is a Text Editor which is looks like a Notepad \n
        PyNote is Build as a Demo Project for those Who are \n
        interested in building Awesome GUI Projects using Tkinter\n
        _________________________________________________________\n
        PyNote Developer : Yogesh Singh \n
        Build For : Dynamic Coding \n''')

    def about_pynote(self):
        messagebox.showinfo('About - PyNote','''
        Current Version : 0.0 \n
        ''')


    def shortcut_binding(self):
        self.textarea.bind('<Control-n>',self.new_file)
        self.textarea.bind('<Control-Key-o>',self.open_file)
        self.textarea.bind('<Control-s>',self.save_file)
        self.textarea.bind('<Control-S>',self.save_as_file)
        self.textarea.bind('<Control-q>',self.close_App)
        self.textarea.bind('<Key>', self.text_area_cursor)
        #self.textarea.bind('<KeyPress>', self.cursor_pos)

        self.root.bind("<Button-3>", self.do_popup)

    def do_popup(self, event):
        try:
            self.m.tk_popup(event.x_root, event.y_root)
        finally:
            self.m.grab_release()

    def find_word_window(self):
        top = Toplevel(self.root)
        top.geometry('400x130')
        top.title('PyNote - Find Text ')
        top.resizable(0,0)

        # important stuffs ...........
        find_var = StringVar()
        self.total_var =StringVar()

        # --------------------------find window widgets ----------------------
        self.find_entry = ttk.Entry(top,width=20,font=('times',12), textvariable = find_var)
        self.find_entry.focus_set()
        self.find_entry.bind('<Return>',self.find)
        self.find_entry.place(x=10,y=25)

        find_btn = Button(top,text='Find',width=10,bd=2,relief=RIDGE,command=self.find)
        find_btn.place(x=200,y=25)

        clear_btn = Button(top, text='Clear', width=10, bd=2, relief=RIDGE,command=lambda:find_var.set(''))
        clear_btn.place(x=300, y=25)

        self.total_world_count = Label(top,font=('arial',13,'bold'))
        self.total_world_count.place(x=20,y=80)

        top.mainloop()


    def find(self, *args):
        # remove tag 'found' from index 1 to END
        self.textarea.tag_remove('found', '1.0', END)

        # returns to widget currently in focus
        s = self.find_entry.get()

        if (s):
            idx = '1.0'
            self.count=0
            while 1:
                # searches for desried string from index 1
                idx = self.textarea.search(s, idx, nocase=1,
                                  stopindex=END)
                self.count +=1

                if not idx: break

                # last index sum of current index and
                # length of text
                lastidx = '% s+% dc' % (idx, len(s))

                # overwrite 'Found' at idx
                self.textarea.tag_add('found', idx, lastidx)
                idx = lastidx

            # mark located string as red
            self.textarea.tag_config('found', foreground='green', background='yellow')
            #self.textarea.focus_set()
            self.total_world_count['text'] = 'Total Word Count is : ' + str(self.count -1 )



    def refresh(self):

        self.textarea.tag_delete("found")


    def find_replace_window(self):
        top = Toplevel(self.root)
        top.geometry('400x130')
        top.title('PyNote - Find Text ')
        top.focus_force()
        top.resizable(0, 0)

        # important stuffs ...........
        find_var = StringVar()
        replace_var = StringVar()

        # --------------------------find replace window widgets ----------------------
        self.find_entry = ttk.Entry(top, width=20, font=('times', 12), textvariable=find_var)
        self.find_entry.focus_set()
        self.find_entry.bind('<Return>', self.find)
        self.find_entry.place(x=10, y=25)

        self.replace_entry = ttk.Entry(top, width=20, font=('times', 12), textvariable=replace_var)
        #self.replace_entry.focus_set()
        self.replace_entry.bind('<Return>', self.find_replace)
        self.replace_entry.place(x=10, y=60)

        find_btn = Button(top, text='Find', width=10, bd=2, relief=RIDGE, command=self.find)
        find_btn.place(x=200, y=25)

        replace_btn = Button(top, text='Replace', width=10, bd=2, relief=RIDGE,
                             command=self.find_replace)
        replace_btn.place(x=200, y=60)

        clear_btn = Button(top, text='Clear All', width=10,height=3, bd=2, relief=RIDGE, command=lambda: [find_var.set(''),replace_var.set('')])
        clear_btn.place(x=300, y=26)

        self.total_world_count = Label(top, text='',font=('arial', 13, 'bold'))
        self.total_world_count.place(x=20, y=100)

        top.mainloop()


    def find_replace(self, *args):
        find = self.find_entry.get()
        replace = self.replace_entry.get()

        if(find and replace):
            idx = '1.0'
            while 1:
                # searches for desired string from index 1
                idx = self.textarea.search(find, idx, nocase=1,
                                  stopindex=END)
                print(idx)
                if not idx: break

                # last index sum of current index and
                # length of text
                lastidx = '% s+% dc' % (idx, len(find))

                self.textarea.delete(idx, lastidx)
                self.textarea.insert(idx, replace)

                lastidx = '% s+% dc' % (idx, len(replace))

                # overwrite 'Found' at idx
                self.textarea.tag_add('found', idx, lastidx)
                idx = lastidx

            self.total_world_count['text'] = '" {} " is replaced with " {} "'.format(find,replace)
        self.replace_entry.focus_set()

    def close_App(self, *args):
        print(self.textarea.get(1.0,END))
        if self.textarea.get(1.0,END) != '' and self.file_saved == True:
            if messagebox.askyesno('PyNote Says','Do you really want to exit'):
                self.root.quit()
        else:
            val = messagebox.askyesnocancel('PyNote - Says', 'Save File Before Exit App. ')
            if val:
                self.save_file()
                self.root.destroy()
            elif val == False:
                self.root.destroy()

    def text_area_cursor(self, *args):
        #print('inside function ')
        self.file_saved = False
        if self.filename:
            self.update_status('PyNote - Currently Working on : ({})'.format(self.filename))
        else:
            self.update_status('Untitled File')

        pos = self.textarea.index(INSERT)
        line , column = pos.split('.')
        column = int(column) + 1
        #print('Current line is : ',line)
        #print('Current Column is : ', column)
        self.pos.set('Line : {} Column : {}'.format(line,column))

    def cursor_pos(self, *args):
        pos = self.textarea.index(INSERT)
        line, column = pos.split('.')
        column = int(column) + 1
        self.pos.set('Line : {} Column : {}'.format(line, column))



    def update_status(self,data=''):
        self.status.set('PyNote - ({})'.format(data))


    def select_font(self):
        font= askfont()
        # font is "" if the user has cancelled
        if font:
            # spaces in the family name need to be escaped
            font['family'] = font['family'].replace(' ', '\ ')
            font_str = "%(family)s %(size)i %(weight)s %(slant)s" % font
            if font['underline']:
                font_str += ' underline'
            if font['overstrike']:
                font_str += ' overstrike'

            self.font_style = font_str
            self.textarea.configure(font=self.font_style)


    def add_time(self):
        now = datetime.datetime.now()
        now = now.strftime(' Time: %I:%M:%S:%p ')
        self.textarea.insert(END, now)


    def add_date(self):
        now = datetime.datetime.now()
        now = now.strftime(' %Y-%m-%d ')
        self.textarea.insert(END, now)


    def add_time_date(self):
        now = datetime.datetime.now()
        now = now.strftime(' Date: %Y-%m-%d Time: %I:%M:%S:%p ')
        self.textarea.insert(END,now)

    # Select all the text in textbox
    def select_all(self, *args):
        self.textarea.tag_add(SEL, "1.0", END)
        self.textarea.mark_set(INSERT, "1.0")
        self.textarea.see(INSERT)

    def callback(self):
        if(self.file_saved) and (self.textarea.get(1.0,END)) != '':
            if messagebox.askokcancel("Quit", "Do you really wish to quit?"):
                self.root.destroy()
        else:
            val = messagebox.askyesnocancel('PyNote - Says','Save File Before Exit App. ')
            if val:
                self.save_file()
            elif val== False:
                self.root.destroy()
コード例 #9
0
class NoteEditor:
    def __init__(self):
        self.id = None
        self.page_name = None
        self.font_name = 'arial'
        self.font_size = 12
        self.font_weight = tk.NORMAL
        self.editor = None
        self.file_io = FileIO()
        self.syntax_file_io = FileIO()

    def create_editor(self, master):
        self.editor = ScrolledText(master,
                                   undo=True,
                                   autoseparators=True,
                                   maxundo=-1)

        # Styling of text area
        self.set_editor_font(None, None)

        self.editor.pack(side="left")
        self.editor.focus()
        self.editor.pack(fill="both", expand=True)

        # Configuring style tags
        self.editor.tag_config("BACKGROUND", background="yellow")
        self.editor.tag_configure("HIGHLIGHT", foreground="red")
        self.editor['wrap'] = tk.NONE

        self.editor.bind('<Button-3>', self.rClicker, add='')

    def set_editor_font(self, font_name, font_size, font_weight=None):
        if font_name is not None:
            self.font_name = font_name

        if font_size is not None and int(font_size) > 0:
            self.font_size = font_size

        if font_weight is not None:
            self.font_weight = font_weight

        self.editor['font'] = Font(family=self.font_name,
                                   size=self.font_size,
                                   weight=self.font_weight)

    def set_editor_bgcolor(self, hex_color):
        self.editor['background'] = hex_color

    def set_editor_fgcolor(self, hex_color):
        self.editor['foreground'] = hex_color

    def set_emphasis(self, on):
        if on == 1:
            bold_font = Font(family=self.font_name,
                             size=self.font_size,
                             weight="bold")
            self.editor.tag_configure("BOLDFONT", font=bold_font)
            if self.editor.tag_ranges(tk.SEL):
                self.editor.tag_add("BOLDFONT", tk.SEL_FIRST, tk.SEL_LAST)
            else:
                self.editor.tag_add("BOLDFONT", "1.0", tk.END)
        else:
            self.editor.tag_remove("BOLDFONT", "1.0", tk.END)

    def toggle_wrap(self, on):
        if on == 1:
            self.editor['wrap'] = tk.WORD
        else:
            self.editor['wrap'] = tk.NONE

    def search_forward(self, text):
        located_start = self.editor.search(text,
                                           tk.INSERT,
                                           stopindex=tk.END,
                                           forwards=True,
                                           nocase=True)
        located_end = '{}+{}c'.format(located_start, len(text))
        if located_start is '' or located_end is '':
            return False

        self.select_editor_location(located_start, located_end)

        # Start position is moved after current found location.
        self.editor.mark_set(tk.INSERT, located_end)
        return True

    def search_backward(self, text):
        located_start = self.editor.search(text,
                                           tk.INSERT,
                                           stopindex='1.0',
                                           backwards=True,
                                           nocase=True)
        located_end = '{}+{}c'.format(located_start, len(text))
        if located_start is '' or located_end is '':
            return False

        self.select_editor_location(located_start, located_end)

        # Start position is moved after current found location.
        self.editor.mark_set(tk.INSERT, located_start)
        return True

    def replace_selected_text(self, new_text):
        self.editor.delete('sel.first', 'sel.last')
        self.editor.insert('insert', new_text)

    def select_editor_location(self, selection_start, selection_end):
        print('Found location start: ', selection_start)
        print('Found location end: ', selection_end)
        selection_start_float = float(selection_start)
        self.editor.tag_remove(tk.SEL, "1.0", 'end')
        self.editor.tag_add(tk.SEL, selection_start, selection_end)
        self.editor.focus_force()
        self.editor.see(selection_start_float)

    def is_dirty(self):
        return self.editor.edit_modified()

    def rClicker(self, e):
        ''' right click context menu for all Tk Entry and Text widgets
        '''

        try:

            def rClick_Copy(e, apnd=0):
                e.widget.event_generate('<Control-c>')

            def rClick_Cut(e):
                e.widget.event_generate('<Control-x>')

            def rClick_Paste(e):
                e.widget.event_generate('<Control-v>')

            def rClick_Highlight_Keyword(e):
                self.highlight_syntax(True)

            e.widget.focus()

            nclst = [
                (' Cut', lambda e=e: rClick_Cut(e)),
                (' Copy', lambda e=e: rClick_Copy(e)),
                (' Paste', lambda e=e: rClick_Paste(e)),
                (' Highlight Keyword',
                 lambda e=e: rClick_Highlight_Keyword(e)),
            ]

            rmenu = tk.Menu(None, tearoff=0, takefocus=0)

            for (txt, cmd) in nclst:
                rmenu.add_command(label=txt, command=cmd)

            rmenu.tk_popup(e.x_root + 40, e.y_root + 10, entry="0")

        except tk.TclError:
            print
            ' - rClick menu, something wrong'
            pass

        return "break"

    def highlight_syntax(self, enable=True):
        syntax_file = Path(Path(
            self.file_io.file_name).suffix[1:]).with_suffix('.hs')
        hs_config_data = Configuration.get_hs_configuration(syntax_file)
        if hs_config_data is None:
            print('No syntax file ', syntax_file)
            return
        #print(hs_config_data)
        keywords = hs_config_data['keyword']['tags']
        keyword_fgcolor = hs_config_data['keyword']['color']
        constant_fgcolor = hs_config_data['constant']['color']

        numbers = re.findall(r'\d{1,3}', self.file_io.file_data)
        self.editor.tag_config('tg_kw', foreground=keyword_fgcolor)
        self.editor.tag_config('tg_num', foreground=constant_fgcolor)
        #keywords = ['package', 'public', 'private', 'abstract', 'internal', 'new', 'static', 'final', 'long', 'extends',
        #            'class', 'import', 'null', 'for', 'if', 'return', 'int', 'char', 'float', 'double', 'implements']
        for keyword in keywords:
            self.editor.mark_set(tk.INSERT, '1.0')
            while True:
                located_end = self.highlight_keyword(keyword + ' ', 'tg_kw')
                if located_end == 0:
                    break
                self.editor.mark_set(tk.INSERT, located_end)

        self.editor.mark_set(tk.INSERT, '1.0')
        for each_number in numbers:
            located_end = self.highlight_keyword(each_number, 'tg_num')
            if located_end != 0:
                self.editor.mark_set(tk.INSERT, located_end)

        print("Syntax highlight executed.")

    def highlight_keyword(self, text, tag):
        located_start = self.editor.search(text,
                                           tk.INSERT,
                                           stopindex=tk.END,
                                           forwards=True,
                                           nocase=False)
        located_end = '{}+{}c'.format(located_start, len(text))
        print(located_start, ',', located_end)
        print('keyword', text)
        if located_start is '' or located_end is '':
            return 0

        self.editor.tag_add(tag, located_start, located_end)
        return located_end