maroph

text_editor5.py

Dec 13th, 2019
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.62 KB | None | 0 0
  1. t#!/usr/bin/env python3
  2.  
  3. # FutureLearn course: Programming with GUIs
  4. # URL: https://www.futurelearn.com/courses/programming-with-guis
  5. #
  6. # Tested with
  7. # Python 3.8.0 on Windows 10 (mainly)
  8. # Python 3.7.3 on Raspbian 10.2
  9. #
  10. # Known bug on Raspbian with Python 3.7.3
  11. # (works under Windows 10 with Python 3.8.0):
  12. # Start the app, click Help/About and than exit the app.
  13. # After closing the info box the app thinks, there is unsaved text
  14. # in the editor.
  15. # I have the same effect also in some other situations on Raspbian 10.2
  16. # and not on Windows 10
  17. # IMHO: this seems to be a problem in the underlying TK implementation.
  18.  
  19. from guizero import App, Box, Combo, MenuBar, Slider, Text, TextBox
  20. from pathlib import Path
  21. import os
  22. import sys
  23.  
  24. app_name = 'Simple Text Editor'
  25. app_version = '5'
  26. app_verdate = '13-DEC-2019'
  27.  
  28. app_version_string = app_name + ' v' + app_version + ' (' + app_verdate + ')'
  29.  
  30. app = App(title=app_name, bg='slategray', width=800, height=600)
  31.  
  32. # remember the last folder in use, default: . - the current folder
  33. folder = '.'
  34. # True: no unsaved text in the editor
  35. text_saved = True
  36. # True: dark mode is enabled
  37. dark_mode = False
  38.  
  39.  
  40. # get the current folder ins use from the complete file path, stored in file_name_text_box.value
  41. def get_folder():
  42.     global file_name_text_box
  43.  
  44.     name = file_name_text_box.value
  45.     if name is None:
  46.         return '.'
  47.     elif len(name) == 0:
  48.         return '.'
  49.     else:
  50.         return Path(name).parent
  51.  
  52.  
  53. # show some version information
  54. def about_editor():
  55.     global app_version_string
  56.  
  57.     app.info('About', app_version_string + "\nA simple Python guizero based text editor")
  58.     return
  59.  
  60.  
  61. # open a file at startup of the editor
  62. # store the file name in file_name_text_box.value
  63. def open_file_at_startup(file_name):
  64.     global text_saved
  65.     global editor
  66.     global file_name_text_box
  67.  
  68.     try:
  69.         with open(file_name, "r") as f:
  70.             editor.value = f.read()
  71.     except Exception as e:
  72.         print('Error in reading file:', e)
  73.         return
  74.  
  75.     file_name_text_box.value = file_name
  76.     text_saved = True
  77.  
  78.  
  79. # open a file, selected in a file selector box, in the editor and
  80. # store the file name in file_name_text_box.value
  81. def open_file():
  82.     global text_saved
  83.     global app
  84.     global editor
  85.     global file_name_text_box
  86.  
  87.     if not text_saved:
  88.         resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
  89.         if not resp:
  90.             return
  91.  
  92.     f = get_folder()
  93.     name = app.select_file('Open file', folder=f, filetypes=[["Text files", "*.txt"], ["Markdown files", "*.md"]], save=False)
  94.     if name is None:
  95.         text_saved = True
  96.         return
  97.     if len(name) == 0:
  98.         text_saved = True
  99.         return
  100.  
  101.     try:
  102.         with open(name, "r") as f:
  103.             editor.value = f.read()
  104.     except Exception as e:
  105.         app.error('Read Error', 'Error in reading file: ' + str(e))
  106.         return
  107.  
  108.     file_name_text_box.value = name
  109.     text_saved = True
  110.  
  111.  
  112. # save the text in the editor the the file name, stored in file_name_text_box.value
  113. def save_file():
  114.     global text_saved
  115.     global file_name_text_box
  116.  
  117.     name = file_name_text_box.value
  118.     if name is None:
  119.         save_as_file()
  120.         return
  121.     if len(name) == 0:
  122.         save_as_file()
  123.         return
  124.  
  125.     try:
  126.         with open(name, "w") as f:
  127.             f.write(editor.value)
  128.     except Exception as e:
  129.         app.error('Write Error', 'Error in reading file: ' + str(e))
  130.         return
  131.  
  132.     text_saved = True
  133.  
  134.  
  135. # save the text in the editor into  a file, selected in a file selector box and
  136. # store the name in file_name_text_box.value
  137. def save_as_file():
  138.     global text_saved
  139.     global app
  140.     global file_name_text_box
  141.  
  142.     f = get_folder()
  143.  
  144.     name = app.select_file('Save file', folder=f, filetypes=[["Text files", "*.txt"], ["Markdown files", "*.md"]], save=True)
  145.     if name is None:
  146.         return
  147.     if len(name) == 0:
  148.         return
  149.  
  150.     try:
  151.         with open(name, "w") as f:
  152.             f.write(editor.value)
  153.     except Exception as e:
  154.         app.error('Write Error', 'Error in reading file: ' + str(e))
  155.         return
  156.  
  157.     file_name_text_box.value = name
  158.     text_saved = True
  159.  
  160.  
  161. # remove all text from the editor and set the file name to '' in file_name_text_box.value
  162. def new_file():
  163.     global text_saved
  164.     global app
  165.     global file_name_text_box
  166.     global editor
  167.     global char_count_text_box
  168.  
  169.     if not text_saved:
  170.         resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
  171.         if not resp:
  172.             return
  173.  
  174.     file_name_text_box.value = ''
  175.     editor.value = ''
  176.     char_count_text_box.value = '0'
  177.     text_saved = True
  178.  
  179.  
  180. # close the editor app
  181. def exit_app():
  182.     global text_saved
  183.     global app
  184.  
  185.     if not text_saved:
  186.         resp = app.yesno('Warning', 'You have unsaved text in the editor - proceed?')
  187.         if not resp:
  188.             return
  189.     app.destroy()
  190.  
  191.  
  192. def change_font():
  193.     global editor
  194.     global font
  195.  
  196.     editor.font = font.value
  197.  
  198.  
  199. def change_text_color():
  200.     global editor
  201.     global text_color
  202.  
  203.     editor.text_color = text_color.value
  204.  
  205.  
  206. def change_text_size():
  207.     global editor
  208.     global size
  209.  
  210.     editor.text_size = size.value
  211.     editor.resize(1, 1)
  212.     editor.resize("fill", "fill")
  213.  
  214.  
  215. # get number of characters in the editor
  216. # observation: even if editor.value is '' the len(editor.value) is 1
  217. def get_number_of_text_chars():
  218.     global editor
  219.  
  220.     chars = len(editor.value)
  221.     if chars > 0:
  222.         return chars -1
  223.     else:
  224.         return 0
  225.  
  226.  
  227. # wil be called by any change in the editor's text area
  228. def count_text_chars():
  229.     global text_saved
  230.     global char_count_text_box
  231.  
  232.     char_count_text_box.value = get_number_of_text_chars()
  233.     text_saved = False
  234.  
  235.  
  236. def toggle_status_line():
  237.     global status_box
  238.  
  239.     if status_box.visible:
  240.         status_box.visible = False
  241.     else:
  242.         status_box.visible = True
  243.  
  244.  
  245. def toggle_char_count():
  246.     global char_count_box
  247.  
  248.     if char_count_box.visible:
  249.         char_count_box.visible = False
  250.     else:
  251.         char_count_box.visible = True
  252.  
  253.  
  254. def toggle_preferences_controls():
  255.     global preferences_controls
  256.  
  257.     if preferences_controls.visible:
  258.         preferences_controls.visible = False
  259.     else:
  260.         preferences_controls.visible = True
  261.  
  262.  
  263. def toggle_dark_mode():
  264.     global dark_mode
  265.     global editor
  266.     global text_color
  267.  
  268.     if dark_mode:
  269.         dark_mode = False
  270.         editor.bg = 'white'
  271.         editor.text_color = text_color.value
  272.         text_color.visible = True
  273.     else:
  274.         dark_mode = True
  275.         editor.bg = '#0f0f0f'
  276.         editor.text_color = 'white'
  277.         text_color.visible = False
  278.  
  279.  
  280. # same handling as if clicked o 'File/Exit'
  281. app.when_closed = exit_app
  282.  
  283. # create the app's menu bar and the related menu items
  284. menubar = MenuBar(app,
  285.                   # menu bar
  286.                   toplevel=["File", "Options", "Help"],
  287.                   # menu items
  288.                   options=[
  289.                       # File
  290.                       [
  291.                           # items in File
  292.                           ["New", new_file],
  293.                           ["Open...", open_file],
  294.                           ["Save", save_file],
  295.                           ["Save as...", save_as_file],
  296.                           ["Exit", exit_app]
  297.                       ],
  298.                       # Options
  299.                       [
  300.                           # items in Options
  301.                           ["Toggle status line", toggle_status_line],
  302.                           ["Toggle char count", toggle_char_count],
  303.                           ["Toggle preferences", toggle_preferences_controls],
  304.                           ["Toggle dark mode", toggle_dark_mode]
  305.                       ],
  306.                       # Help
  307.                       [
  308.                           # items in Help
  309.                           ["About", about_editor]
  310.                       ]
  311.                   ])
  312.  
  313.  
  314. # create the status line, copntains: file name and character count
  315. status_box = Box(app, width='fill', border=True)
  316. file_name_label = Text(status_box, text='File: ', align="left")
  317. file_name_text_box = TextBox(status_box, text='', width='fill', align="left", enabled=False)
  318. file_name_text_box.bg = 'white'
  319. char_count_box = Box(status_box, width='fill', border=True)
  320. char_count_text_box = TextBox(char_count_box, text='', width='20', align="right", enabled=False)
  321. char_count_label = Text(char_count_box, text='Chars: ', align="right")
  322. char_count_text_box.value = '0'
  323. text_saved = True
  324.  
  325.  
  326. # create the preferences line
  327. preferences_controls = Box(app, align="top", width="fill", border=True)
  328. font = Combo(preferences_controls, options=["Courier", "Times New Roman", "Verdana"], align="left", selected='Courier', command=change_font)
  329. size = Slider(preferences_controls,  align="left", command=change_text_size, start=10, end=18)
  330. size.value = 10
  331. text_color = Combo(preferences_controls, options=['black', 'blue', 'red', 'magenta'], align="left", selected='black', command=change_text_color)
  332.  
  333.  
  334. # create the editor
  335. editor = TextBox(app, multiline=True, height="fill", width="fill", text='', scrollbar=True)
  336. editor.bg = 'white'
  337. editor.font = font.value
  338. editor.text_size = size.value
  339. editor.text_color = text_color.value
  340. editor.update_command(count_text_chars)
  341. editor.focus()
  342.  
  343.  
  344. if len(sys.argv) > 1:
  345.     # argument available, assumption: this is the name of an existing file
  346.     # read file into editor before displaying the GUI
  347.     open_file_at_startup(os.path.realpath(sys.argv[1]))
  348.  
  349.  
  350. app.display()
Advertisement
Add Comment
Please, Sign In to add comment