Handling files

We have already covered the usual handling of files in Python when parsing our yaml configurations. The basic syntax is as follows:

with open("path/to/file/", "r") as file:
file.read()

with open("path/to/file", "w") as file:
file.write("file contents")

This is very easy to do when we have a file path we are defining ourselves. The problem comes when we want the user to be able to open any file, and save a new file to any location on their computer. In this case we do not have the exact path, and it isn't very user-friendly to expect the user to be able to type in the full path either.

Luckily, Tkinter has a module that comes to our aid in this situation: filedialog. The filedialog module comes with a few different methods that allow us to easily get full paths to files, both for opening existing files and saving new ones, all with a user-friendly GUI.

We will be using two functions in this module for our text editor:

  • askopenfilename: This asks the user for an existing file path on their computer. A window will open up displaying the contents of the current folder. The shown files can be filtered by using a keyword argument filetypes.
  • asksaveasfilename: This displays a similar window to askopenfilename but does not enforce that the provided path already exists. If the user selects an existing file, an information box will appear telling the user that the path already exists, and asking them for confirmation to overwrite it.

Let's get straight into using this module in our text editor. Make sure you have the texteditor.py file open and add the import statement at the top:

from tkinter import filedialog

We will begin with the ability to open a file for editing. We want this functionality to appear under our file cascade, and the shortcut will be Ctrl and O:

def file_open(self, event=None):
"""
Ctrl+O
"""
file_to_open = filedialog.askopenfilename()
if file_to_open:
self.open_file = file_to_open

self.text_area.display_file_contents(file_to_open)
self.highlighter.force_highlight()
self.line_numbers.force_update()

The method name will be file_open, since the file_ prefix is needed to get this method to appear in our cascade.

When called by the Menu there will not be an event object passed, but if called by the keyboard shortcut there will be. To accommodate for this we accept an event argument but default it to None, since we do not actually need it.

The first thing we need to do is get the path to the file that the user wishes to open. We do this by calling the askopenfilename function from the filedialog module. This presents the user with a window showing their computer's files and allows them to click on the one they want.

This window also has a Cancel button, which will cause the function to return nothing. Because of this, we only want to perform the rest of the logic if the user actually chose a file, so the rest of this function will go inside an if statement, which checks if askopenfilename returned a non-empty string.

The path to the chosen file is stored as an attribute called open_file, before being passed over to a method of our TextArea widget called display_file_contents, which will handle writing the text contained in the file into itself.

As with some of our edit methods, since this method will change the contents of our TextArea we force the line numbers and syntax highlighting to update afterwards.

Let's hop on over to our textarea.py file and check out what needs to happen in display_file_contents:

def display_file_contents(self, filepath):
with open(filepath, 'r') as file:
self.delete(1.0, tk.END)
self.insert(1.0, file.read())

To add the text from the supplied file path into our TextArea, we first need to open the file using the open method.

Inside this block we first delete the entire contents of the TextArea using the delete method. The range passed to this method is the beginning index (always 1.0) and the END constant. This ensures that everything inside the widget will be deleted.

Now that the widget is empty we can write the contents of the opened file inside. We use the insert method to achieve this. The first argument is the beginning index, indicating that we want this content inserted at the beginning of our TextArea. The content to be written is the text within our file, which we extract using the read method.

You can now see the window created by the askopenfilename method by running texteditor.py and selecting the Open option from the File menu. Try opening the texteditor.py file itself inside the text editor application to see all of the syntax highlighting in action:

With opening a file sorted, the next thing to do is saving files. Once again we will define a method in our texteditor.py file to handle this:

def file_save(self, event=None):
"""
Ctrl+S
"""
current_file = self.open_file if self.open_file else None
if not current_file:
current_file = filedialog.asksaveasfilename()

if current_file:
contents = self.text_area.get(1.0, tk.END)
with open(current_file, 'w') as file:
file.write(contents)

When saving a file, we need to know the location at which to write the new file contents. If we already have a file open, then our open_file attribute will be set and we can assume that the user is updating the same file. If not, we need to ask the user for the destination file path.

We get this information from the user with the asksaveasfilename method of the filedialog module. This will open a similar window to askopenfilename but with a Save button in place of the Open button.

Again, this window features a Cancel button, which will not return a file path, so we need to use an if statement to ensure the user did not cancel the operation.

If the user chose a destination, we need to open that path as a file handler and write the contents of our TextArea widget into it.

We get the contents of our TextArea using the get method, passing this the beginning index and END constant. This ensures that we have all of the content inside it.

The file handler is opened using the open function as before, but instead we pass w as the second argument to open the file in write mode. We then use the write method to insert the contents of our TextArea widget into this file.

That's all there is to saving a file. You can now run the texteditor.py file again and check out the Save option in your File menu. Try saving a new file, then try to save over it again afterwards. You should be presented with a confirmation message box. This is all built in to the filedialog module, we didn't need to do any of it ourselves!

Let's finish off the File menu with a New option, which will clear the currently open file from our TextArea and give the user a blank area to type into:

def file_new(self, event=None):
"""
Ctrl+N
"""
self.text_area.delete(1.0, tk.END)
self.open_file = None
self.line_numbers.force_update()

This method should be self-explanatory, since each line has been encountered recently. We simply delete the entirety of our TextArea widget, remove any reference to our open_file attribute (so that the save option does not overwrite it), and force the line numbers to update.

With that, all options from our File menu are now complete.

We can also quickly add a couple more methods to our Edit menu and finish that off too:

def edit_select_all(self, event=None):
"""
Ctrl+A
"""
self.text_area.event_generate("<Control-a>")

def edit_find_and_replace(self, event=None):
"""
Ctrl+F
"""
self.show_find_window()

Since we have defined select all functionality in our TextArea widget we may as well add this to our Edit menu. We just need to pass along the event like we have done with cut, copy, and paste.

Since find/replace windows usually exist in the Edit menu of other text editors, we can also add a menu option for this in our text editor. The method just needs to call the same method as is bound to Control + F already.

With this, two of our four cascade menus are now filled. The next one to focus on is the Tools menu, which will contain functions that allow the user to customize the editor to their liking, including loading different syntax highlighting files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset