示例#1
0
class Highlighter(QSyntaxHighlighter):

    def __init__(self, parent):
        QSyntaxHighlighter.__init__(self, parent)

        self.formatter = Formatter()
        self.lexer = PythonLexer()
        self.style = {}
        for token, style in self.formatter.style:
            char_format = QTextCharFormat()
            if style['color']:
                char_format.setForeground(QColor("#" + style['color']))
            if style['bgcolor']:
                char_format.setBackground(QColor("#" + style['bgcolor']))
            if style['bold']:
                char_format.setFontWeight(QFont.Bold)
            if style['italic']:
                char_format.setFontItalic(True)
            if style['underline']:
                char_format.setFontUnderline(True)

            char_format.setFontStyleHint(QFont.Monospace)
            self.style[token] = char_format

    def highlightBlock(self, text):
        position = self.currentBlock().position()
        length = self.currentBlock().length()
        text = str(self.document().toPlainText()[position:position+length])
        tokens = self.lexer.get_tokens(text)
        i = 0
        for token, text in tokens:
            self.setFormat(i, len(text), self.style[token])
            i += len(text)
示例#2
0
class Interface(object):
    SCREEN_WIDTH = 640  # int(window.winfo_screenwidth())
    SCREEN_HEIGHT = 480  # int(window.winfo_screenheight())

    _libraryArray = []
    _ldArray = []
    _deviceArray = []
    advancedMode = 0

    # lists of menu items (not all of these are used)
    commandList = ["if", "elif", "else", "for", "while"]
    advancedCommandList = ["break", "continue"]
    expressionList = ["+", "-", "*", "/", "//", "%", "**"]
    advancedExpressionList = ["<<", ">>", "|", "^", "&", "~", "@"]
    equationList = ["==", "!=", "<", "<=", ">", ">="]
    advancedEquationList = ["is", "is not", "in", "not in"]
    functionList = ["print()", "input()", "str()", "int()", "float()", "round()", "range()", "len()", "min()", "max()"]
    functionList2 = ["time.sleep()", "time.time()", "random.random()", "random.randint()"]
    logicGateList = ["AND", "OR", "XOR", "NOT"]
    advancedLogicGateList = ["NAND", "NOR", "XNOR"]
    variableList = ["i", "j", "k", "l"]

    selectedPart = None
    moveList = []

    fileName = ""
    fullScreen = 0

    def __init__(self, parent):
        self.lexer = PythonLexer()  # Used for syntax highlighting
        self._window = tk.Frame(parent)
        self.helpText = tk.StringVar(self._window)

        # setup window
        self._window.master.geometry(str(self.SCREEN_WIDTH) + "x" + str(self.SCREEN_HEIGHT))
        if self.fullScreen:
            self._window.master.attributes("-fullscreen", True)
        self._window.bind("<F11>", self.swapFullScreen)
        self._window.master.title(ld.windowName)

        # setup library imports (and localisation data of those libraries)
        libReader = configparser.ConfigParser()
        libReader.optionxform = str
        configIni = "config.ini"
        libReader.read(configIni)
        for i in libReader["LIBRARIES"]:
            if libReader["LIBRARIES"][i] == "1":
                self._libraryArray.append(i)
                module_name = i
                file_path = "lib/" + i + "/" + i + ".py"
                spec = importlib.util.spec_from_file_location(module_name, file_path)
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)

                ld_module_name = i + "Localisationdata"
                ld_file_path = "lib/" + i + "/" + ld_module_name + ".py"
                ld_spec = importlib.util.spec_from_file_location(ld_module_name, ld_file_path)
                ld_module = importlib.util.module_from_spec(ld_spec)
                ld_spec.loader.exec_module(ld_module)
                self._ldArray.append(ld_module)
                for objname in dir(module):
                    if type(eval("module." + objname)) is type:
                        self._deviceArray.append(getattr(module, objname)())

        # setup menu bar
        self.menuBar = tk.Menu(self._window)

        # setup file menu
        self.fileMenu = tk.Menu(self.menuBar, tearoff=0)
        for i in ld.fileMenuList:
            self.fileMenu.add_command(label=i, command=lambda item=i: self.fileClick(item))
        self.fileMenu.add_checkbutton(label=ld.advanced, variable=self.advancedMode, onvalue=1, offvalue=0, command=self.setAdvancedMode)
        self.menuBar.add_cascade(label=ld.fileWindowName, menu=self.fileMenu)

        # setup command menu
        self.commandMenu = tk.Menu(self.menuBar, tearoff=0)
        for i in self.commandList:
            self.commandMenu.add_command(label=i, command=lambda item=i: self.commandClick(item))
        self.menuBar.add_cascade(label=ld.commandWindowName, menu=self.commandMenu)

        # setup expression menu
        self.expressionMenu = tk.Menu(self.menuBar, tearoff=0)
        for i in self.expressionList:
            self.expressionMenu.add_command(label=i, command=lambda item=i: self.expressionClick(item))
        self.menuBar.add_cascade(label=ld.expressionWindowName, menu=self.expressionMenu)

        # setup equation menu
        self.equationMenu = tk.Menu(self.menuBar, tearoff=0)
        for i in self.equationList:
            self.equationMenu.add_command(label=i, command=lambda item=i: self.equationClick(item))
        self.menuBar.add_cascade(label=ld.equationWindowName, menu=self.equationMenu)

        # setup standard function menu
        self.functionMenu = tk.Menu(self.menuBar, tearoff=0)
        for i in self.functionList:
            self.functionMenu.add_command(label=i, command=lambda item=i: self.functionClick(item))
        self.menuBar.add_cascade(label=ld.functionWindowName, menu=self.functionMenu)

        # setup standard function menu
        self.functionMenu2 = tk.Menu(self.menuBar, tearoff=0)
        for i in self.functionList2:
            self.functionMenu2.add_command(label=i, command=lambda item=i: self.function2Click(item))
        self.menuBar.add_cascade(label=ld.functionWindow2Name, menu=self.functionMenu2)

        # setup device menu
        # basically, this code imports all selected libraries, checks their code for all public variables (i.e. instances of parts),
        # checks those instance variables for all public methods (i.e. movement directions),
        # translates all names and organises all this data neatly in a cascading menu.
        self.deviceMenu = tk.Menu(self.menuBar, tearoff=0)
        for libraryCounter in range(len(self._libraryArray)):
            library = self._libraryArray[libraryCounter]
            device = self._deviceArray[libraryCounter]
            locData = self._ldArray[libraryCounter]
            specificPartMenu = tk.Menu(self.deviceMenu, tearoff=0)
            for part in vars(device):
                if not part.startswith('_'):
                    specificMoveMenu = tk.Menu(specificPartMenu, tearoff=0)
                    partPointer = getattr(device, part)
                    for movement in dir(partPointer):
                        if callable(getattr(partPointer, movement)) and not movement.startswith("_"):
                            specificMoveMenu.add_command(label=locData.partDictionary[movement], command=lambda lib_=library, device_=device, part_=part, move_=movement: self.moveClick(lib_, device_, part_, move_))
                    specificPartMenu.add_cascade(label=locData.partDictionary[part], menu=specificMoveMenu)
            self.deviceMenu.add_cascade(label=library, menu=specificPartMenu)
        self.menuBar.add_cascade(label=ld.connectedDeviceWindowName, menu=self.deviceMenu)

        # setup textbox
        self.textBox = scrolledtext.ScrolledText(self._window, width=self.SCREEN_WIDTH // 8 - 3, height=self.SCREEN_HEIGHT // 20)
        self.textBox.grid(row=2, columnspan=6)

        # setup run button
        self.runButton = tk.Button(self._window, text=ld.runButtonText, command=self.runCode)
        self.runButton.grid(row=3, sticky='w')

        # setup help text
        self.helpText.set(ld.helpInfo+ld.helpInfoDefault)
        self.helpLabel = tk.Label(self._window, textvar=self.helpText)
        self.helpLabel.grid(sticky=tk.W, row=4, column=0, columnspan=6)

        # start program loop
        self._window.master.config(menu=self.menuBar)
        self._window.grid(row=0, column=0)
        self._window.master.bind("<KeyRelease-Return>", self.recolorize)

    # Toggles fullscreen
    def swapFullScreen(self, *_args):
        self.fullScreen ^= 1
        if self.fullScreen:
            self._window.master.attributes("-fullscreen", True)
        else:
            self._window.master.attributes("-fullscreen", False)

    # Setup what happens when file menu is clicked
    def fileClick(self, item):
        itemId = ld.fileMenuList.index(item)
        if itemId == 0:  # new file
            self.textBox.delete(1.0, tk.END)
            self.fileName = ""
        if itemId == 1:  # open file
            self.openFile()
        if itemId == 2:  # save file
            self.saveFile()
        if itemId == 3:  # save as
            self.saveFile(True)

    # Run the code in the textbox
    def runCode(self):
        exec(self.textBox.get("1.0", tk.END))

    # Open a file
    def openFile(self):
        # Shows file selection popup
        fileOpenPopup = filedialog.askopenfile(parent=self._window, mode="r", initialdir=os.getcwd() + "/saves")
        if fileOpenPopup is None:
            return
        # Opens file to textbox. Old data gets deleted.
        self.fileName = fileOpenPopup.name
        file = open(self.fileName, "r")
        self.textBox.delete(1.0, tk.END)
        self.textBox.insert(1.0, file.read())
        file.close()
        self.recolorize()

    # Saves the file
    def saveFile(self, newName=False):
        # If the file isn't named yet or "Save as..." is clicked, the user gets a popup to choose a filename and location.
        if newName | (self.fileName == ""):
            fileSavePopup = filedialog.asksaveasfile(parent=self._window, mode="w", initialdir=os.getcwd() + "/saves", defaultextension=".py", filetypes=(("python files", "*.py"), ("text files", "*.txt")))
            if fileSavePopup is None:
                return
            self.fileName = fileSavePopup.name
        # Saves textbox to that file
        file = open(self.fileName, "w")
        file.write(self.textBox.get(1.0, tk.END))
        file.close()

    # Setup what happens when command menu is clicked
    def commandClick(self, item):
        startIndex = self.textBox.index(tk.INSERT)
        tabCheckString = self.textBox.get(str(float(startIndex) // 1), startIndex)
        tabCount = tabCheckString.count("\t")
        self.textBox.insert(tk.INSERT, item)
        if item != "else":
            self.textBox.insert(tk.INSERT, "()")
        self.textBox.insert(tk.INSERT, ":\n\t")
        for i in range(tabCount):
            self.textBox.insert(tk.INSERT, "\t")
        if item != "else":
            self.textBox.mark_set(tk.INSERT, startIndex.split(".")[0] + "." + str(int(startIndex.split(".")[1])+len(item)+1))
        return "break"

    # Setup what happens when expression menu is clicked
    def expressionClick(self, item):
        self.textBox.insert(tk.INSERT, item)
        self.helpText.set(ld.helpInfo + ld.expressionExplanationList[self.expressionList.index(item)])
        return "break"

    # Setup what happens when equation menu is clicked
    def equationClick(self, item):
        self.textBox.insert(tk.INSERT, item)
        self.helpText.set(ld.helpInfo + ld.equationExplanationList[self.equationList.index(item)])
        return "break"

    # Setup what happens when regular function menu is clicked
    def functionClick(self, item):
        startIndex = self.textBox.index(tk.INSERT)
        self.textBox.insert(tk.INSERT, item+"\n")
        self.helpText.set(ld.helpInfo + ld.functionExplanationList[self.functionList.index(item)])
        self.recolorize()
        self.textBox.mark_set(tk.INSERT, startIndex.split(".")[0] + "." + str(int(startIndex.split(".")[1])+len(item)-1))
        return "break"

    # Setup what happens when external function menu is clicked
    def function2Click(self, item):
        self.textBox.insert("1.0", "import " + item.split(".")[0]+"\n")
        self.textBox.insert(tk.INSERT, item)
        self.helpText.set(ld.helpInfo + ld.functionExplanationList2[self.functionList2.index(item)])
        return "break"

    # Sets up advanced mode
    def setAdvancedMode(self):
        self.commandMenu.delete(0, 'end')
        self.expressionMenu.delete(0, 'end')
        self.advancedMode ^= 1
        if self.advancedMode:
            for i in self.advancedCommandList:
                self.commandList.append(i)
            for i in self.advancedExpressionList:
                self.expressionList.append(i)
        else:
            for i in self.advancedCommandList:
                while i in self.commandList:
                    self.commandList.remove(i)
            for i in self.advancedExpressionList:
                while i in self.expressionList:
                    self.expressionList.remove(i)
        for i in self.commandList:
            self.commandMenu.add_command(label=i)
        for i in self.expressionList:
            self.expressionMenu.add_command(label=i)

    # Setup what happens when move menu is clicked: prints selected part and direction to textbox as one would use it in code.
    def moveClick(self, library, device, part, movement):
        if "import " + library not in self.textBox.get("1.0", tk.END):
            self.textBox.insert("1.0", "import " + library + "\n")
            self.textBox.insert(tk.INSERT, type(device).__name__.lower() + " = " + library + "." + type(device).__name__ + "()\n")
        self.textBox.insert(tk.INSERT, type(device).__name__.lower() + "." + part + "." + movement + "()\n")
        self.recolorize()

    # Syntax highlighting code. Changes pieces of code to bold/italic according to the style sheet.
    def create_tags(self):
        bold_font = font.Font(self.textBox, self.textBox.cget("font"))
        bold_font.configure(weight=font.BOLD)

        italic_font = font.Font(self.textBox, self.textBox.cget("font"))
        italic_font.configure(slant=font.ITALIC)

        bold_italic_font = font.Font(self.textBox, self.textBox.cget("font"))
        bold_italic_font.configure(weight=font.BOLD, slant=font.ITALIC)

        style = get_style_by_name('colorful')
        for ttype, ndef in style:
            tag_font = None
            if ndef['bold'] and ndef['italic']:
                tag_font = bold_italic_font
            elif ndef['bold']:
                tag_font = bold_font
            elif ndef['italic']:
                tag_font = italic_font

            if ndef['color']:
                foreground = "#%s" % ndef['color']
            else:
                foreground = None

            self.textBox.tag_configure(str(ttype), foreground=foreground, font=tag_font)

    # Syntax highlighting code. Changes color of pieces of code specified in style sheet.
    def recolorize(self, _event=None):
        self.create_tags()
        code = self.textBox.get("1.0", "end-1c")
        tokensource = self.lexer.get_tokens(code)

        start_line = 1
        start_index = 0
        end_line = 1
        end_index = 0
        for ttype, value in tokensource:
            if "\n" in value:
                end_line += value.count("\n")
                end_index = len(value.rsplit("\n", 1)[1])
            else:
                end_index += len(value)

            if value not in (" ", "\n"):
                index1 = "%s.%s" % (start_line, start_index)
                index2 = "%s.%s" % (end_line, end_index)

                for tagname in self.textBox.tag_names(index1):
                    self.textBox.tag_remove(tagname, index1, index2)
                self.textBox.tag_add(str(ttype), index1, index2)

            start_line = end_line
            start_index = end_index
class Editor(tk.Frame):
    def __init__(self, root, *args, **kwargs):
        # Unable to understand why self is necessary as first argument
        tk.Frame.__init__(self, root, *args, **kwargs)
        self.root = root
        self.__filename = None
        self.__saved = tk.BooleanVar()
        self.__modified = tk.BooleanVar()
        self.build_editor()
        self.build_context_menu()
        root.bind_class("Text", "<Control-A>", self.select_all)
        root.bind_class("Text", "<Control-a>", self.select_all)
        self.config_tags()
        self.create_tags()
        self.set_lexer()

    def build_editor(self):
        """Builds the Editor ->ScrolledText"""
        self.textpad = scrolledtext.ScrolledText(self, undo=True)
        self.textpad.pack(expand=tk.YES, fill=tk.BOTH)
        self.textpad.focus_set()

    def cmenupopup(self, event):
        self.contextmenu.tk_popup(event.x_root, event.y_root)

    def build_context_menu(self):
        self.contextmenu = tk.Menu(self, tearoff=0)
        self.contextmenu.add_command(
            label="Cut",
            accelerator="Ctrl+X",
            command=lambda: self.textpad.event_generate("<<Cut>>"))
        self.contextmenu.add_command(
            label="Copy",
            accelerator="Ctrl+C",
            command=lambda: self.textpad.event_generate("<<Copy>>"))
        self.contextmenu.add_command(
            label="Paste",
            accelerator="Ctrl+V",
            command=lambda: self.textpad.event_generate("<<Paste>>"))
        self.contextmenu.add_separator()
        self.contextmenu.add_command(label="Select All",
                                     accelerator="Ctrl+A",
                                     command=self.select_all)
        self.textpad.bind("<Button-3>", self.cmenupopup)

    def set_text_content(self, newcontent):
        """Replace the text content with new content"""
        self.textpad.delete("1.0", "end")
        self.textpad.insert("1.0", newcontent)

    def get_text_content(self):
        """Get the text content"""
        return self.textpad.get("1.0", "end")

    def set_saved(self, state):
        """Set the saved state"""
        self.__saved.set(state)

    def get_saved(self):
        """Get the saved state"""
        return self.__saved.get()

    def set_modified(self, state):
        """Set the modified state"""
        self.__modified.set(state)

    def get_modified(self):
        """Get the modified state"""
        return self.__modified.get()

    def set_filename(self, name):
        """Set the filename"""
        self.__filename = name

    def get_filename(self):
        """Get filename"""
        return self.__filename

    def select_all(self,
                   event=None):  #Don't know why event as argument is required
        """Select all the text content"""
        self.textpad.tag_add("sel", "1.0", "end-1c")

    def has_content(self):
        """Returns True/False if text has content"""
        return (True if (len(get_text_content()) > 1) else False)

    def search_for(self, query, caseinsensitive):
        self.textpad.tag_remove("match", "1.0", "end")
        count = 0
        if query:
            pos = "1.0"
            while True:
                pos = self.textpad.search(query,
                                          pos,
                                          nocase=caseinsensitive,
                                          stopindex=tk.END)
                if not pos:
                    break
                lastpos = pos + '+' + str(len(query)) + 'c'
                self.textpad.tag_add("match", pos, lastpos)
                count += 1
                pos = lastpos
        return count

    def replace_for(self, replace, replacewith, caseinsensitive):
        self.textpad.tag_remove("match", "1.0", "end")
        count = 0
        if replace:
            pos = "1.0"
            while True:
                pos = self.textpad.search(replace,
                                          pos,
                                          nocase=caseinsensitive,
                                          stopindex=tk.END)
                if not pos:
                    break
                lastpos = pos + '+' + str(len(replace)) + 'c'
                self.textpad.delete(pos, lastpos)
                self.textpad.insert(pos, replacewith)
                count += 1
                pos = lastpos
        return count

    def config_tags(self):
        self.textpad.tag_config("Highlight", background="#D1D4D1")
        self.textpad.tag_config("match", foreground="red", background="yellow")

    def event_key(self, event):
        keycode = event.keycode
        char = event.char
        # print("\tkeycode %s - char %s" % (keycode, repr(char)))
        self.recolorize()

    def set_lexer(self):
        self.lexer = PythonLexer()
        # TODO : Guess Lexer from Filename

    #Function to create tags for highlight
    def create_tags(self):
        bold_font = font.Font(self.textpad, self.textpad.cget("font"))
        bold_font.configure(weight=font.BOLD)

        italic_font = font.Font(self.textpad, self.textpad.cget("font"))
        italic_font.configure(slant=font.ITALIC)

        bold_italic_font = font.Font(self.textpad, self.textpad.cget("font"))
        bold_italic_font.configure(weight=font.BOLD, slant=font.ITALIC)

        style = get_style_by_name('default')
        for ttype, ndef in style:
            # print(ttype, ndef)
            tag_font = None
            if ndef['bold'] and ndef['italic']:
                tag_font = bold_italic_font
            elif ndef['bold']:
                tag_font = bold_font
            elif ndef['italic']:
                tag_font = italic_font

            if ndef['color']:
                foreground = "#%s" % ndef['color']
            else:
                foreground = None

            self.textpad.tag_configure(str(ttype),
                                       foreground=foreground,
                                       font=tag_font)

    #Function to recolorize according to tags created
    # Currently this function is very bad as it recolorizes the whole document from
    # start at every key stroke
    def recolorize(self):
        # print("recolorize")
        code = self.textpad.get("1.0", "end-1c")
        tokensource = self.lexer.get_tokens(code)

        start_line = 1
        start_index = 0
        end_line = 1
        end_index = 0
        for ttype, value in tokensource:
            if "\n" in value:
                end_line += value.count("\n")
                end_index = len(value.rsplit("\n", 1)[1])
            else:
                end_index += len(value)

            if value not in (" ", "\n"):
                index1 = "%s.%s" % (start_line, start_index)
                index2 = "%s.%s" % (end_line, end_index)

                for tagname in self.textpad.tag_names(index1):  # FIXME
                    self.textpad.tag_remove(tagname, index1, index2)

                # print(ttype, repr(value), index1, index2)
                self.textpad.tag_add(str(ttype), index1, index2)

            start_line = end_line
            start_index = end_index

    #Function to recolorize
    def removecolors(self):
        for tag in self.tagdefs:
            self.textpad.tag_remove(tag, "1.0", "end")
示例#4
0
class Editor(object):
    currentLanguageName = "Plain Text"
    currentStyleName = "default"
    windowWidth = 100
    windowHeight = 25

    def __init__(self, root, lexer):

        self.root = root
        self.TITLE = "Simple IO"
        self.file_path = None
        self.set_title()

        self.fontSize = 12

        self.lexer = lexer
        self.bootstrap = [self.recolorize]

        frame = Frame(root)
        # Scroll Bar [X and Y]
        self.xscrollbar = Scrollbar(root, orient="horizontal")
        self.yscrollbar = Scrollbar(root, orient="vertical")

        # Textbox (The main text input area)
        self.editor = Text(frame,
                           yscrollcommand=self.yscrollbar.set,
                           xscrollcommand=self.xscrollbar.set,
                           bg="#FFFFFF",
                           fg="#000000",
                           insertbackground="#000000")
        self.editor.pack(side="left", fill="both", expand=1)
        self.editor.config(wrap="none",
                           undo=True,
                           width=self.windowWidth,
                           height=self.windowHeight,
                           font=("Consolas", self.fontSize),
                           tabs=('1c'))
        self.editor.focus()
        self.create_tags()

        # Scroll Bars packing
        self.xscrollbar.pack(side="bottom", fill="x")  # Horizontal Scroll Bar
        self.xscrollbar.config(command=self.editor.xview)
        self.yscrollbar.pack(side="right", fill="y")  # Vertial Scroll Bar
        self.yscrollbar.config(command=self.editor.yview)

        # ## Status Bar ## #
        self.statusText = (("Font Size: " + str(self.fontSize)) + " | " +
                           "Langauge: " + self.currentLanguageName)
        self.status = Label(root,
                            text=self.statusText,
                            relief=tkinter.SUNKEN,
                            anchor='w')
        self.status.pack(side=tkinter.BOTTOM, fill=tkinter.X)

        frame.pack(fill="both", expand=1)

        #instead of closing the window, execute a function. Call file_quit
        root.protocol("WM_DELETE_WINDOW", self.file_quit)

        #create a top level menu
        self.menubar = Menu(root)

        #Menu item: File
        filemenu = Menu(
            self.menubar,
            tearoff=0)  # tearoff = 0 => can't be seperated from window
        filemenu.add_command(label="New",
                             command=self.file_new,
                             accelerator="Ctrl+N")
        filemenu.add_command(label="Open",
                             command=self.file_open,
                             accelerator="Ctrl+O")
        filemenu.add_command(label="Save",
                             command=self.file_save,
                             accelerator="Ctrl+S")
        filemenu.add_command(label="Save As",
                             command=self.file_save_as,
                             accelerator="Ctrl+Alt+S")
        filemenu.add_separator()  # Adds a lines between the above elements
        filemenu.add_command(label="Exit",
                             command=self.file_quit,
                             accelerator="Ctrl+Q")
        self.menubar.add_cascade(label="File", menu=filemenu)

        # Menu item: View
        viewMenu = Menu(self.menubar, tearoff=0)
        viewMenu.add_command(label="Zoom In",
                             command=self.zoom_In,
                             accelerator="Ctrl+")
        viewMenu.add_command(label="Zoom Out",
                             command=self.zoom_Out,
                             accelerator="Ctrl-")
        self.menubar.add_cascade(label="View", menu=viewMenu)

        # Menu item: Color Scheme
        colorMenu = Menu(self.menubar, tearoff=0)
        colorMenu.add_command(
            label="Default", command=lambda: self.changeColorScheme("default"))
        colorMenu.add_command(
            label="Monokai", command=lambda: self.changeColorScheme("monokai"))
        self.menubar.add_cascade(label="Color Scheme", menu=colorMenu)

        # Menu item: Languages
        languageMenu = Menu(self.menubar, tearoff=0)
        languageMenu.add_command(label="Plain Text",
                                 command=self.languageLexerToPlain)
        languageMenu.add_command(label="Python",
                                 command=self.languageLexerToPython)
        self.menubar.add_cascade(label="Language", menu=languageMenu)

        # display the menu
        root.config(menu=self.menubar)

    # If user trys to quit while there is unsaved content
    def save_if_modified(self, event=None):
        print("Checking if current save is modified...")
        if self.editor.edit_modified():  #modified
            print("Current save is modified")
            response = messagebox.askyesnocancel(
                "Save?",
                "This document has been modified. Do you want to save changes?"
            )  #yes = True, no = False, cancel = None
            if response:  #yes/save
                result = self.file_save()
                if result == "saved":  #saved
                    print("Saved")
                    return True
                else:  #save cancelled
                    return None
            else:
                return response  #None = cancel/abort, False = no/discard
        else:  #not modified
            return True

    def updateStatusBar(self, event=None):
        self.statusText = (("Font Size: " + str(self.fontSize)) + " | " +
                           "Langauge: " + self.currentLanguageName)
        self.status.config(text=self.statusText)

# FILE MENU FUNCTIONS
##############################################################################################

# NEW FILE

    def file_new(self, event=None):
        result = self.save_if_modified()
        if result != None:  #None => Aborted or Save cancelled, False => Discarded, True = Saved or Not modified
            self.editor.delete(1.0, "end")
            self.editor.edit_modified(False)
            self.editor.edit_reset()
            self.file_path = None
            self.set_title()

    # OPEN FILE
    def file_open(self, event=None, filepath=None):
        result = self.save_if_modified()
        if result != None:  #None => Aborted or Save cancelled, False => Discarded, True = Saved or Not modified
            if filepath == None:
                filepath = filedialog.askopenfilename()
            if filepath != None and filepath != '':
                with open(filepath, encoding="utf-8") as f:
                    fileContents = f.read()  # Get all the text from file.
                # Set current text to file contents
                self.editor.delete(1.0, "end")
                self.editor.insert(1.0, fileContents)
                self.editor.edit_modified(False)
                self.file_path = filepath

    # SAVE FILE
    def file_save(self, event=None):
        if self.file_path == None:
            result = self.file_save_as()
        else:
            result = self.file_save_as(filepath=self.file_path)
        return result

    # SAVE AS
    def file_save_as(self, event=None, filepath=None):
        if filepath == None:
            filepath = tkinter.filedialog.asksaveasfilename(
                filetypes=(('Text files', '*.txt'), ('Python files',
                                                     '*.py *.pyw'),
                           ('All files', '*.*')))  #defaultextension='.txt'
        try:
            with open(filepath, 'wb') as f:
                text = self.editor.get(1.0, "end-1c")
                f.write(bytes(text, 'UTF-8'))
                self.editor.edit_modified(False)
                self.file_path = filepath
                self.set_title()
                return "saved"
        except FileNotFoundError:
            print('FileNotFoundError')
            return "cancelled"

    # QUIT
    def file_quit(self, event=None):
        result = self.save_if_modified()
        if result != None:  #None => Aborted or Save cancelled, False => Discarded, True = Saved or Not modified
            print("Exiting with code: 0")
            self.root.destroy()  #sys.exit(0)

    # Show the file name on the top of the window
    def set_title(self, event=None):
        if self.file_path != None:
            title = os.path.basename(self.file_path)
        else:
            title = "Untitled"
        self.root.title(title + " - " + self.TITLE)

    def undo(self, event=None):
        self.editor.edit_undo()

    def redo(self, event=None):
        self.editor.edit_redo()

# VIEW MENU FUNCTIONS
###############################################################################################

    def zoom_Out(self, event=None):
        print("Zooming Out")
        if self.fontSize > 9:
            self.fontSize -= 1
            self.editor.config(font=("Helvetica", self.fontSize))
            self.updateStatusBar()

    def zoom_In(self, event=None):
        print("Zooming In")
        if self.fontSize < 50:
            self.fontSize += 1
            self.editor.config(font=("Helvetica", self.fontSize))
            self.updateStatusBar()

# LANGUAGE MENU FUNCTIONS
###############################################################################################

    def languageLexerToPlain(self, event=None):
        print("Setting language to: Plain Text")
        self.lexer = TextLexer()
        self.currentLanguageName = "Plain Text"
        self.create_tags()
        self.recolorize()
        self.updateStatusBar()

    def languageLexerToPython(self, event=None):
        print("Setting language to: Python")
        self.lexer = PythonLexer()
        self.currentLanguageName = "Python"
        self.create_tags()
        self.recolorize()
        self.updateStatusBar()

# COLOR SCHEME MENU FUNCTIONS
###############################################################################################

    def changeColorScheme(self, styleParam):
        self.currentStyleName = styleParam
        print("Changing style to: " + styleParam)
        self.create_tags()
        self.recolorize()

        # Changing the background color
        if styleParam == "default":
            self.editor.config(bg="#FFFFFF",
                               fg="#000000",
                               insertbackground="#000000")
        elif styleParam == "monokai":
            self.editor.config(bg="#272822",
                               fg="#FFFFFF",
                               insertbackground="#FFFFFF")


# EVENTS
###############################################################################################

    def event_KeyPressed(self, event=None):
        self.recolorize()

    def create_tags(self):
        """
            this method creates the tags associated with each distinct style element of the 
            source code 'dressing'
        """
        bold_font = font.Font(self.editor, self.editor.cget("font"))
        bold_font.configure(weight=font.BOLD)
        italic_font = font.Font(self.editor, self.editor.cget("font"))
        italic_font.configure(slant=font.ITALIC)
        bold_italic_font = font.Font(self.editor, self.editor.cget("font"))
        bold_italic_font.configure(weight=font.BOLD, slant=font.ITALIC)
        style = get_style_by_name(self.currentStyleName)

        for ttype, ndef in style:
            tag_font = None

            if ndef['bold'] and ndef['italic']:
                tag_font = bold_italic_font
            elif ndef['bold']:
                tag_font = bold_font
            elif ndef['italic']:
                tag_font = italic_font

            if ndef['color']:
                foreground = "#%s" % ndef['color']
            else:
                foreground = None

            self.editor.tag_configure(str(ttype),
                                      foreground=foreground,
                                      font=tag_font)

    # This methd colors the text by using the tokens in the lexer
    def recolorize(self):
        code = self.editor.get("1.0", "end-1c")
        tokensource = self.lexer.get_tokens(code)
        start_line = 1
        start_index = 0
        end_line = 1
        end_index = 0

        for ttype, value in tokensource:
            if "\n" in value:
                end_line += value.count("\n")
                end_index = len(value.rsplit("\n", 1)[1])
            else:
                end_index += len(value)

            if value not in (" ", "\n"):
                index1 = "%s.%s" % (start_line, start_index)
                index2 = "%s.%s" % (end_line, end_index)

                for tagname in self.editor.tag_names(index1):  # FIXME
                    self.editor.tag_remove(tagname, index1, index2)

                self.editor.tag_add(str(ttype), index1, index2)

            start_line = end_line
            start_index = end_index

    def main(self, event=None):
        # Key bindings
        self.editor.bind("<Control-o>", self.file_open)  # Open File
        self.editor.bind("<Control-O>", self.file_open)  # ^
        self.editor.bind("<Control-S>", self.file_save)  # Save File
        self.editor.bind("<Control-s>", self.file_save)  # ^
        self.editor.bind("<Control-q>", self.file_quit)  # Quit Application
        self.editor.bind("<Control-Q>", self.file_quit)  # ^
        self.editor.bind("<Control-y>", self.redo)  # Redo Action
        self.editor.bind("<Control-Y>", self.redo)  # ^
        self.editor.bind("<Control-Z>", self.undo)  # Undo Action
        self.editor.bind("<Control-z>", self.undo)  # ^
        self.editor.bind("<Control-minus>", self.zoom_Out)
        self.editor.bind("<Control-plus>", self.zoom_In)
        self.editor.bind("<Key>", self.event_KeyPressed)
示例#5
0
class Tktext(object):
    def __init__(self, master, txt):

        self.lexer = PythonLexer()
        self.txt = txt
        # any key colorize
        master.bind('<Key>', self.event_key)
        # paste
        master.bind('<Control-v>', self.event_paste)
        # copy
        master.bind('<Control-c>', self.event_key)
        master.update()
        self.create_tags()

    # paste method
    def paste(self, text):
        if text:
            self.txt.insert(INSERT, text)
            self.txt.tag_remove(SEL, '1.0', END)
            self.txt.see(INSERT)
            self.recolorize()

    # any key colorize
    def event_key(self, event):
        if event.keycode == 17:
            return 'break'
        self.recolorize()

    def event_paste(self, event=None):
        self.recolorize()

    def create_tags(self):
        bold_font = font.Font(self.txt, self.txt.cget("font"))
        bold_font.configure(weight=font.BOLD)

        italic_font = font.Font(self.txt, self.txt.cget("font"))
        italic_font.configure(slant=font.ITALIC)

        bold_italic_font = font.Font(self.txt, self.txt.cget("font"))
        bold_italic_font.configure(weight=font.BOLD, slant=font.ITALIC)

        style = get_style_by_name('sas')

        for ttype, ndef in style:
            # print(ttype, ndef)
            tag_font = None

            if ndef['bold'] and ndef['italic']: tag_font = bold_italic_font
            elif ndef['bold']: tag_font = bold_font
            elif ndef['italic']: tag_font = italic_font

            if ndef['color']: foreground = "#%s" % ndef['color']
            else: foreground = None

            self.txt.tag_configure(str(ttype),
                                   foreground=foreground,
                                   font=tag_font)

    def recolorize(self):
        code = self.txt.get("1.0", "end-1c")
        tokensource = self.lexer.get_tokens(code)

        start_line = 1
        start_index = 0
        end_line = 1
        end_index = 0
        for ttype, value in tokensource:
            if "\n" in value:
                end_line += value.count("\n")
                end_index = len(value.rsplit("\n", 1)[1])
            else:
                end_index += len(value)

            if value not in (" ", "\n"):
                index1 = "%s.%s" % (start_line, start_index)
                index2 = "%s.%s" % (end_line, end_index)

                for tagname in self.txt.tag_names(index1):  # FIXME
                    self.txt.tag_remove(tagname, index1, index2)

                # print(ttype, repr(value), index1, index2)
                self.txt.tag_add(str(ttype), index1, index2)

            start_line = end_line
            start_index = end_index

    def removecolors(self):
        for tag in self.tagdefs:
            self.txt.tag_remove(tag, "1.0", "end")
示例#6
0
class Window:
    def __init__(self, master):
        self.master = master
        self.master.title('프로그래밍 언어 만들기')
        self.master.config(bg='#fff')
        self.master.geometry('1024x768')
        self.lexer = PythonLexer(stripnl=False)
        self.AddWidgets()
        self.create_tags()

        self.current_level = db.get('level', 0)
        self.set_level(self.current_level)

    def set_level(self, level):
        try:
            level = int(level)
        except:
            level = 0
        if level < 0:
            level = 0
        if level >= len(quizs):
            level = len(quizs) - 1
        #if not ? and level >
        self.set_quiz(level)

    def set_quiz(self, level):
        self.current_level = level
        q = quizs[level]
        self.title_var.set("언어 만들기 - " + q.title)
        self.text.delete("1.0", tkinter.END)
        self.text.insert(tkinter.END, db.setdefault('code' + str(level), ''))
        self.recolorize()
        if db['pass' + str(self.current_level)]:
            self.tutstatus.config(text='통과', fg='#adff2f')
        else:
            self.tutstatus.config(text='')

        self.desc.config(state=tkinter.NORMAL)
        self.desc.delete("1.0", tkinter.END)
        self.desc.insert(
            tkinter.END,
            '목표\n' + q.desc.replace('<code>', '').replace('</code>', ''))
        #''.join(''.join(escape(x) for x in y.split('</code>')) for y in q.desc.split('<code>')))
        self.desc.tag_add("BigTitle", "1.0", "2.0")
        self.desc.config(state=tkinter.DISABLED)

        self.examples.config(state=tkinter.NORMAL)
        self.examples.delete("1.0", tkinter.END)
        self.examples.insert(tkinter.END, '예제\n')
        self.examples.tag_add("BigTitle", "1.0", "2.0")
        if q.examples:
            self.examples.insert(
                tkinter.END, '\n   -----   \n'.join(
                    "입력 " + str(idx + 1) + "\n" + x.generate_output()
                    for idx, x in enumerate(q.examples)))
        else:
            self.examples.insert(tkinter.END, '(예제 없음)')
        self.examples.config(state=tkinter.DISABLED)

    def select_all(self, *args):
        self.text.tag_add("sel", "1.0", "end")

    def AddWidgets(self):
        global console
        self.master.configure(bg='white')
        self.master.grid_columnconfigure(0, weight=1, uniform=2)
        self.master.grid_columnconfigure(1, weight=1, uniform=2)
        self.master.grid_rowconfigure(0, minsize=50)
        self.master.grid_rowconfigure(1, weight=1, uniform=1)
        self.master.grid_rowconfigure(2, weight=1, uniform=1)
        self.master.grid_rowconfigure(3, weight=1, uniform=1)
        self.master.grid_rowconfigure(4, weight=1, uniform=1)

        console = scrolledtext.ScrolledText(master=self.master, )
        self.text = scrolledtext.ScrolledText(
            master=self.master,
            bg='#272822',
            undo=True,
            exportselection=True,
            cursor='xterm',
            selectbackground='#ddd',
            insertbackground='#fff',
            insertwidth='3',
        )  #, **self.uiopts)
        self.text.grid(column=1, row=1, rowspan=3, sticky=('nsew'))
        console.grid(column=1, row=4, rowspan=1, sticky=('nsew'))

        for command, key in ('Copy', 'c'), ('Paste', 'v'), ('Cut', 'x'):
            self.text.event_add('<<' + command + '>>', '<Command-' + key + '>')
            self.text.event_add('<<' + command + '>>',
                                '<Command-' + key.upper() + '>')
            self.text.event_add('<<' + command + '>>', '<Control-' + key + '>')
            self.text.event_add('<<' + command + '>>',
                                '<Control-' + key.upper() + '>')
        self.text.bind("<Command-a>", self.select_all)
        self.text.bind("<Command-A>", self.select_all)
        self.text.bind("<Control-a>", self.select_all)
        self.text.bind("<Control-A>", self.select_all)

        self.desc = scrolledtext.ScrolledText(
            self.master
            #,highlightbackground="#7c7cfa", highlightcolor="#7c7cfa", highlightthickness=1, spacing1 = 5, spacing3 = 5, bg='#ddd'
        )
        self.desc.grid(column=0,
                       row=1,
                       rowspan=2,
                       sticky=('nsew'),
                       padx=5,
                       pady=5)
        self.desc.bind("<1>", lambda event: self.desc.focus_set())

        self.examples = scrolledtext.ScrolledText(
            self.master,
            #highlightbackground="#7c7cfa", highlightcolor="#7c7cfa", highlightthickness=1, spacing1 = 5, spacing3 = 5, bg='#eee'
        )
        self.examples.grid(column=0,
                           row=3,
                           rowspan=2,
                           sticky=('nsew'),
                           padx=5,
                           pady=5)
        self.examples.bind("<1>", lambda event: self.examples.focus_set())

        self.navbar = tkinter.Frame(
            self.master,
            height=50,
            bg='#7c7cfa',
            #highlightbackground="red", highlightcolor="red", highlightthickness=1
        )
        self.title_var = tkinter.StringVar()
        self.title1 = tkinter.Label(self.navbar,
                                    textvariable=self.title_var,
                                    bg='#7c7cfa',
                                    fg='white')
        self.title1.configure(font=('monaco', 27))
        self.title_var.set("언어 만들기 - ")
        self.title1.pack(side=tkinter.LEFT, padx=5)

        right_btn = tkinter.Button(self.navbar,
                                   text='→',
                                   highlightbackground='#7c7cfa',
                                   highlightcolor='#7c7cfa')
        right_btn.pack(side=tkinter.RIGHT)
        right_btn.configure(
            command=lambda: self.set_level(self.current_level + 1))
        submit_btn = tkinter.Button(self.navbar,
                                    text="제출",
                                    highlightbackground='#7c7cfa',
                                    highlightcolor='#7c7cfa')
        submit_btn.pack(side=tkinter.RIGHT)
        submit_btn.configure(command=lambda: self.do_submit())

        test_btn = tkinter.Button(self.navbar,
                                  text="테스트",
                                  highlightbackground='#7c7cfa',
                                  highlightcolor='#7c7cfa')
        test_btn.pack(side=tkinter.RIGHT)
        test_btn.configure(command=lambda: self.do_test())

        left_btn = tkinter.Button(self.navbar,
                                  text='←',
                                  highlightbackground='#7c7cfa',
                                  highlightcolor='#7c7cfa')
        left_btn.pack(side=tkinter.RIGHT)
        left_btn.configure(
            command=lambda: self.set_level(self.current_level - 1))

        self.tutstatus = tkinter.Label(self.navbar,
                                       text='',
                                       fg='#adff2f',
                                       bg='#7c7cfa')
        self.tutstatus.pack(side=tkinter.RIGHT)
        self.tutstatus.configure(font=('monaco', 27))

        self.navbar.grid(column=0,
                         row=0,
                         columnspan=2,
                         sticky=tkinter.W + tkinter.E + tkinter.N + tkinter.S)

        #self.examples.insert(tkinter.INSERT,'abc\n'*100)
        #self.desc.insert(tkinter.INSERT,'desc')
        #self.text.insert(tkinter.INSERT, 'code')
        # NORMAL

        self.examples.configure(font=('monaco', 14))
        self.desc.configure(font=('monaco', 14))
        self.text.configure(font=('monaco', 16))

        self.desc.config(state=tkinter.DISABLED)
        self.examples.config(state=tkinter.DISABLED)

        self.text.bind("<BackSpace>", self.do_backspace)

        self.master.bind('<Key>', self.event_key)
        self.text.bind('<Return>', self.autoindent)
        self.text.bind('<Tab>', self.tab2spaces4)
        self.recolorize()

    def do_backspace(self, event):
        lineindex, columnindex = self.text.index("insert").split(".")
        linetext = self.text.get(lineindex + ".0", self.text.index('insert'))
        if int(columnindex) > 0 and all(x == ' ' for x in linetext):
            self.text.delete("insert-%d chars" % min(int(columnindex), 4),
                             "insert")
            return 'break'

    def autoindent(self, event):
        indentation = ""
        lineindex = self.text.index("insert").split(".")[0]
        linetext = self.text.get(lineindex + ".0", lineindex + ".end")

        for character in linetext:
            if character in [" ", "\t"]:
                indentation += character
            else:
                break

        self.text.insert(self.text.index("insert"), "\n" + indentation)
        return "break"

    def tab2spaces4(self, event):
        #idx = self.text.index("insert")
        self.text.insert(self.text.index("insert"), "    ")
        return "break"

        #self.text.insert(tkinter.INSERT, text)
        #self.text.tag_remove(tkinter.SEL, '1.0', tkinter.END)
        #self.text.see(tkinter.INSERT)
    def event_key(self, event):
        keycode = event.keycode
        char = event.char
        self.recolorize()
        self.master.update()

    def event_mouse(self, event):
        pass

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

    def recolorize(self):
        code = self.text.get("1.0", "end-1c")
        tokensource = self.lexer.get_tokens(code)
        start_line = 1
        start_index = 0
        end_line = 1
        end_index = 0

        for ttype, value in tokensource:
            if "\n" in value:
                end_line += value.count("\n")
                end_index = len(value.rsplit("\n", 1)[1])
            else:
                end_index += len(value)

            if value not in (" ", "\n"):
                index1 = "%s.%s" % (start_line, start_index)
                index2 = "%s.%s" % (end_line, end_index)

                for tagname in self.text.tag_names(index1):
                    if tagname == 'sel':
                        continue
                    self.text.tag_remove(tagname, index1, index2)

                self.text.tag_add(str(ttype), index1, index2)

            start_line = end_line
            start_index = end_index

    def do_test(self):
        code = self.text.get("1.0", "end")
        q = quizs[self.current_level]
        db['code' + str(self.current_level)] = code.rstrip('\n')
        save()
        examples = q.examples
        if not examples:
            examples = [Example("")]
        gui.run(code, self.master, str(examples[0]))

    def show_pass(self):
        messagebox.showinfo("성공", "통과하였습니다! 다음 레벨로 진행합니다.")
        self.set_level(self.current_level + 1)

    def show_fail(self, hint):
        messagebox.showerror("실패", "실패하였습니다. 힌트를 참고하여 다시 작성해 보세요.\n\n" + hint)

    def do_submit(self):
        code = self.text.get("1.0", "end")
        q = quizs[self.current_level]
        db['code%s' % self.current_level] = code.rstrip('\n')
        pass_this_time = True
        examples = q.examples
        if not examples:
            examples = [Example("")]
        for idx, e in enumerate(examples):
            has_exception = False
            goal_has_exception = False
            res = 1
            goal = 2
            my_exc = None
            goal_exc = None
            example_input = str(e)
            try:
                res = verify.run(code, example_input)
            except Exception as exc:
                has_exception = True
                my_exc = exc

            try:
                goal = verify.run(q.goal.goal, example_input)
            except Exception as exc:
                goal_has_exception = True
                goal_exc = exc
                raise
            print(goal, res, goal_has_exception, has_exception, my_exc,
                  goal_exc)
            if goal == res or goal_has_exception and has_exception and type(
                    my_exc) is type(goal_exc):
                print('예제 %s 통과' % (idx + 1))
            else:
                print('예제 %s 실패' % (idx + 1))
                pass_this_time = False
        level = db.setdefault('level', 0)
        if pass_this_time:
            if level <= self.current_level:
                db['level'] = self.current_level + 1
            print('통과했습니다!')
            self.tutstatus.config(text='통과', fg='#adff2f')
            if not db.setdefault('pass' + str(self.current_level), 0):
                db['pass' + str(self.current_level)] = 1
                try:
                    res = urlopen('http://ipkn.me:4949/pass/' +
                                  str(self.current_level)).read()
                except:
                    pass

            self.show_pass()
        else:
            print('통과하지 못했습니다.')
            if not db['pass' + str(self.current_level)]:
                self.tutstatus.config(text='실패', fg='#ff2f1f')
                try:
                    res = urlopen('http://ipkn.me:4949/fail/' +
                                  str(self.current_level)).read()
                except:
                    pass
            k = 'fail' + str(self.current_level)
            db[k] = db.get(k, 0) + 1
            self.show_fail(q.hint.generate(db[k]))
        save()

    def create_tags(self):
        bold_font = font.Font(self.text, self.text.cget("font"))
        bold_font.configure(weight=font.BOLD)
        italic_font = font.Font(self.text, self.text.cget("font"))
        italic_font.configure(slant=font.ITALIC)
        bold_italic_font = font.Font(self.text, self.text.cget("font"))
        bold_italic_font.configure(weight=font.BOLD, slant=font.ITALIC)
        style = get_style_by_name('monokai')
        self.desc.tag_configure('BigTitle', font=('monaca', 23))
        self.examples.tag_configure('BigTitle', font=('monaca', 23))

        for ttype, ndef in style:
            tag_font = None

            if ndef['bold'] and ndef['italic']:
                tag_font = bold_italic_font
            elif ndef['bold']:
                tag_font = bold_font
            elif ndef['italic']:
                tag_font = italic_font

            if ndef['color']:
                foreground = "#%s" % ndef['color']
            else:
                foreground = None

            self.text.tag_configure(str(ttype),
                                    foreground=foreground,
                                    font=tag_font)