Code for How to Make a Markdown Editor using Tkinter in Python Tutorial


View on Github

markdown_editor.py

# Imports
from tkinter import *
import ctypes
import re

# Increas Dots Per inch so it looks sharper
ctypes.windll.shcore.SetProcessDpiAwareness(True)

# Setup
root = Tk()
root.title('Markdown Editor')
root.geometry('1000x600')

# Setting the Font globaly
root.option_add('*Font', 'Courier 15')


def changes(event=None):
    display['state'] = NORMAL

    # Clear the Display Area
    display.delete(1.0, END)

    # Insert the content of the Edit Area into the Display Area
    text = editor.get('1.0', END)

    # Save Raw Text for later
    textRaw = text

    # Remove Unwanted Characters
    text = ''.join(text.split('#'))
    text = ''.join(text.split('*'))

    display.insert(1.0, text)

    # Loop through each replacement, unpacking it fully
    for pattern, name, fontData, colorData, offset in replacements:

        # Get the location indices of the given pattern
        locations = search_re(pattern, textRaw, offset)

        print(f'{name} at {locations}')

        # Add tags where the search_re function found the pattern
        for start, end in locations:
            display.tag_add(name, start, end)

        # Configure the tag to use the specified font and color
        # to this every time to delete the previous tags
        display.tag_config(name, font=fontData, foreground=colorData)

    display['state'] = DISABLED

def search_re(pattern, text, offset):
    matches = []
    text = text.splitlines()
    for i, line in enumerate(text):
        for match in re.finditer(pattern, line):
            matches.append(
                (f"{i + 1}.{match.start()}", f"{i + 1}.{match.end() - offset}")
            )
    
    return matches

# Convert an RGB tuple to a HEX string using the % Operator
# 02 means print 2 characters
# x means hexadecimal
def rgbToHex(rgb):
    return "#%02x%02x%02x" % rgb

# Style Setup
editorBackground = rgbToHex((40, 40, 40))
editorTextColor = rgbToHex((230, 230, 230))
displayBackground = rgbToHex((60, 60, 60))
displayTextColor = rgbToHex((200, 200, 200))

caretColor = rgbToHex((255, 255, 255))

# Width of the Textareas in characters
width = 10

# Fonts
editorfontName = 'Courier'
displayFontName = 'Calibri'

# Font Sizes
normalSize = 15
h1Size = 40
h2Size = 30
h3Size = 20

# font Colors
h1Color = rgbToHex((240, 240, 240))
h2Color = rgbToHex((200, 200, 200))
h3Color = rgbToHex((160, 160, 160))

# Replacements tell us were to insert tags with the font and colors given
replacements = [
    [
        '^#[a-zA-Z\s\d\?\!\.]+$',
        'Header 1',
        f'{displayFontName} {h1Size}', 
        h1Color,
        0
    ], [
        '^##[a-zA-Z\s\d\?\!\.]+$',
        'Header 2', 
        f'{displayFontName} {h2Size}',
        h2Color,
        0
    ], [
        '^###[a-zA-Z\s\d\?\!\.]+$', 
        'Header 3', 
        f'{displayFontName} {h3Size}', 
        h3Color,
        0        
    ], [
        '\*.+?\*', 
        'Bold', 
        f'{displayFontName} {normalSize} bold', 
        displayTextColor,
        2
    ],
]

# Making the Editor Area
editor = Text(
    root,
    height=5,
    width=width,
    bg=editorBackground,
    fg=editorTextColor,
    border=30,
    relief=FLAT,
    insertbackground=caretColor
)
editor.pack(expand=1, fill=BOTH, side=LEFT)

# Bind <KeyReleas> so every change is registered
editor.bind('<KeyRelease>', changes)
editor.focus_set()

# Insert a starting text
editor.insert(INSERT, """#Heading 1

##Heading 2

###Heading 3
  
This is a *bold* move!


- Markdown Editor -

""")

# Making the Display Area
display = Text(
    root,
    height=5,
    width=width,
    bg=displayBackground,
    fg=displayTextColor,
    border=30,
    relief=FLAT,
    font=f"{displayFontName} {normalSize}",
)
display.pack(expand=1, fill=BOTH, side=LEFT)

# Disable the Display Area so the user cant write in it
# We will have to toggle it so we can insert text
display['state'] = DISABLED

# Starting the Application
changes()
root.mainloop()