maroph icon

text_editor5.py

maroph | PRO | 12/13/19 01:48:41 PM UTC | 0 ⭐ | 363 👁️ | Never ⏰ | []
Python |

9.62 KB

|

None

|

0 👍

/

0 👎

t#!/usr/bin/env python3
 
# FutureLearn course: Programming with GUIs
# URL: https://www.futurelearn.com/courses/programming-with-guis
#
# Tested with
# Python 3.8.0 on Windows 10 (mainly)
# Python 3.7.3 on Raspbian 10.2
#
# Known bug on Raspbian with Python 3.7.3
# (works under Windows 10 with Python 3.8.0):
# Start the app, click Help/About and than exit the app.
# After closing the info box the app thinks, there is unsaved text
# in the editor.
# I have the same effect also in some other situations on Raspbian 10.2
# and not on Windows 10
# IMHO: this seems to be a problem in the underlying TK implementation.
 
from guizero import App, Box, Combo, MenuBar, Slider, Text, TextBox
from pathlib import Path
import os
import sys
 
app_name = 'Simple Text Editor'
app_version = '5'
app_verdate = '13-DEC-2019'
 
app_version_string = app_name + ' v' + app_version + ' (' + app_verdate + ')'
 
app = App(title=app_name, bg='slategray', width=800, height=600)
 
# remember the last folder in use, default: . - the current folder
folder = '.'
# True: no unsaved text in the editor
text_saved = True
# True: dark mode is enabled
dark_mode = False
 
 
# get the current folder ins use from the complete file path, stored in file_name_text_box.value
def get_folder():
    global file_name_text_box
 
    name = file_name_text_box.value
    if name is None:
        return '.'
    elif len(name) == 0:
        return '.'
    else:
        return Path(name).parent
 
 
# show some version information
def about_editor():
    global app_version_string
 
    app.info('About', app_version_string + "\nA simple Python guizero based text editor")
    return
 
 
# open a file at startup of the editor
# store the file name in file_name_text_box.value
def open_file_at_startup(file_name):
    global text_saved
    global editor
    global file_name_text_box
 
    try:
        with open(file_name, "r") as f:
            editor.value = f.read()
    except Exception as e:
        print('Error in reading file:', e)
        return
 
    file_name_text_box.value = file_name
    text_saved = True
 
 
# open a file, selected in a file selector box, in the editor and
# store the file name in file_name_text_box.value
def open_file():
    global text_saved
    global app
    global editor
    global file_name_text_box
 
    if not text_saved:
        resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
        if not resp:
            return
 
    f = get_folder()
    name = app.select_file('Open file', folder=f, filetypes=[["Text files", "*.txt"], ["Markdown files", "*.md"]], save=False)
    if name is None:
        text_saved = True
        return
    if len(name) == 0:
        text_saved = True
        return
 
    try:
        with open(name, "r") as f:
            editor.value = f.read()
    except Exception as e:
        app.error('Read Error', 'Error in reading file: ' + str(e))
        return
 
    file_name_text_box.value = name
    text_saved = True
 
 
# save the text in the editor the the file name, stored in file_name_text_box.value
def save_file():
    global text_saved
    global file_name_text_box
 
    name = file_name_text_box.value
    if name is None:
        save_as_file()
        return
    if len(name) == 0:
        save_as_file()
        return
 
    try:
        with open(name, "w") as f:
            f.write(editor.value)
    except Exception as e:
        app.error('Write Error', 'Error in reading file: ' + str(e))
        return
 
    text_saved = True
 
 
# save the text in the editor into  a file, selected in a file selector box and
# store the name in file_name_text_box.value
def save_as_file():
    global text_saved
    global app
    global file_name_text_box
 
    f = get_folder()
 
    name = app.select_file('Save file', folder=f, filetypes=[["Text files", "*.txt"], ["Markdown files", "*.md"]], save=True)
    if name is None:
        return
    if len(name) == 0:
        return
 
    try:
        with open(name, "w") as f:
            f.write(editor.value)
    except Exception as e:
        app.error('Write Error', 'Error in reading file: ' + str(e))
        return
 
    file_name_text_box.value = name
    text_saved = True
 
 
# remove all text from the editor and set the file name to '' in file_name_text_box.value
def new_file():
    global text_saved
    global app
    global file_name_text_box
    global editor
    global char_count_text_box
 
    if not text_saved:
        resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
        if not resp:
            return
 
    file_name_text_box.value = ''
    editor.value = ''
    char_count_text_box.value = '0'
    text_saved = True
 
 
# close the editor app
def exit_app():
    global text_saved
    global app
 
    if not text_saved:
        resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
        if not resp:
            return
    app.destroy()
 
 
def change_font():
    global editor
    global font
 
    editor.font = font.value
 
 
def change_text_color():
    global editor
    global text_color
 
    editor.text_color = text_color.value
 
 
def change_text_size():
    global editor
    global size
 
    editor.text_size = size.value
    editor.resize(1, 1)
    editor.resize("fill", "fill")
 
 
# get number of characters in the editor
# observation: even if editor.value is '' the len(editor.value) is 1
def get_number_of_text_chars():
    global editor
 
    chars = len(editor.value)
    if chars > 0:
        return chars -1
    else:
        return 0
 
 
# wil be called by any change in the editor's text area
def count_text_chars():
    global text_saved
    global char_count_text_box
 
    char_count_text_box.value = get_number_of_text_chars()
    text_saved = False
 
 
def toggle_status_line():
    global status_box
 
    if status_box.visible:
        status_box.visible = False
    else:
        status_box.visible = True
 
 
def toggle_char_count():
    global char_count_box
 
    if char_count_box.visible:
        char_count_box.visible = False
    else:
        char_count_box.visible = True
 
 
def toggle_preferences_controls():
    global preferences_controls
 
    if preferences_controls.visible:
        preferences_controls.visible = False
    else:
        preferences_controls.visible = True
 
 
def toggle_dark_mode():
    global dark_mode
    global editor
    global text_color
 
    if dark_mode:
        dark_mode = False
        editor.bg = 'white'
        editor.text_color = text_color.value
        text_color.visible = True
    else:
        dark_mode = True
        editor.bg = '#0f0f0f'
        editor.text_color = 'white'
        text_color.visible = False
 
 
# same handling as if clicked o 'File/Exit'
app.when_closed = exit_app
 
# create the app's menu bar and the related menu items
menubar = MenuBar(app,
                  # menu bar
                  toplevel=["File", "Options", "Help"],
                  # menu items
                  options=[
                      # File
                      [
                          # items in File
                          ["New", new_file],
                          ["Open...", open_file],
                          ["Save", save_file],
                          ["Save as...", save_as_file],
                          ["Exit", exit_app]
                      ],
                      # Options
                      [
                          # items in Options
                          ["Toggle status line", toggle_status_line],
                          ["Toggle char count", toggle_char_count],
                          ["Toggle preferences", toggle_preferences_controls],
                          ["Toggle dark mode", toggle_dark_mode]
                      ],
                      # Help
                      [
                          # items in Help
                          ["About", about_editor]
                      ]
                  ])
 
 
# create the status line, copntains: file name and character count
status_box = Box(app, width='fill', border=True)
file_name_label = Text(status_box, text='File: ', align="left")
file_name_text_box = TextBox(status_box, text='', width='fill', align="left", enabled=False)
file_name_text_box.bg = 'white'
char_count_box = Box(status_box, width='fill', border=True)
char_count_text_box = TextBox(char_count_box, text='', width='20', align="right", enabled=False)
char_count_label = Text(char_count_box, text='Chars: ', align="right")
char_count_text_box.value = '0'
text_saved = True
 
 
# create the preferences line
preferences_controls = Box(app, align="top", width="fill", border=True)
font = Combo(preferences_controls, options=["Courier", "Times New Roman", "Verdana"], align="left", selected='Courier', command=change_font)
size = Slider(preferences_controls,  align="left", command=change_text_size, start=10, end=18)
size.value = 10
text_color = Combo(preferences_controls, options=['black', 'blue', 'red', 'magenta'], align="left", selected='black', command=change_text_color)
 
 
# create the editor
editor = TextBox(app, multiline=True, height="fill", width="fill", text='', scrollbar=True)
editor.bg = 'white'
editor.font = font.value
editor.text_size = size.value
editor.text_color = text_color.value
editor.update_command(count_text_chars)
editor.focus()
 
 
if len(sys.argv) > 1:
    # argument available, assumption: this is the name of an existing file
    # read file into editor before displaying the GUI
    open_file_at_startup(os.path.realpath(sys.argv[1]))
 
 
app.display()

Comments