Python Forum
[WxPython] [Tutorial] Notespad - Create a text editor - W.I.P.
Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[WxPython] [Tutorial] Notespad - Create a text editor - W.I.P.
#10
Similar to the other menu items, adding a edit menu with undo, cut, copy, paste and delete
        edit_menu = wx.Menu()
        self.menu_undo = edit_menu.Append(wx.ID_UNDO, '&Undo',
                                    'Undo change')
        edit_menu.AppendSeparator()
        self.menu_cut = edit_menu.Append(wx.ID_CUT, 'Cu&t',
                                    'Cut the selected text')
        self.menu_copy = edit_menu.Append(wx.ID_COPY, '&Copy',
                                    'Copy the selected text')
        self.menu_paste = edit_menu.Append(wx.ID_PASTE, '&Paste',
                                    'Paste from the clipboard')
        self.menu_delete = edit_menu.Append(wx.ID_DELETE, 'De&lete',
                                    'Delete selected text')
        self.menubar.Append(edit_menu, '&Edit')
binds for the new menu items
        self.Bind(wx.EVT_MENU, self.on_menu_undo, self.menu_undo)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_undo_update, self.menu_undo)
        self.Bind(wx.EVT_MENU, self.on_menu_cut, self.menu_cut)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_cut_update, self.menu_cut)
        self.Bind(wx.EVT_MENU, self.on_menu_copy, self.menu_copy)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_copy_update, self.menu_copy)
        self.Bind(wx.EVT_MENU, self.on_menu_paste, self.menu_paste)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_paste_update, self.menu_paste)
        self.Bind(wx.EVT_MENU, self.on_menu_delete, self.menu_delete)
        self.Bind(
            wx.EVT_UPDATE_UI, self.on_menu_delete_update, self.menu_delete)
These have a type of event that has not been used so far wx.EVT_UPDATE_UI
https://wxpython.org/Phoenix/docs/html/w...Event.html Wrote:This class is used for pseudo-events which are called by wxWidgets to give an application the chance to update various user interface elements. .... see link for more details

Here are the event handlers for undo
    def on_menu_undo(self, event):
        self.txt_ctrl.Undo()

    def on_menu_undo_update(self, event):
        event.Enable(self.txt_ctrl.CanUndo())
on_menu_undo is straight forward, on_menu_undo_update will enable or disable the menu item depending on if the text control can be undone or not.

The events for cut, copy and paste are similar to undo

For the delete events
    def on_menu_delete(self, event):
        self.txt_ctrl.Remove(*self.txt_ctrl.GetSelection())

    def on_menu_delete_update(self, event):
        event.Enable(bool(self.txt_ctrl.GetStringSelection()))
on_menu_delete uses the text controls remove method which removes the text starting at the first given position up to (but not including) the character at the last position.
The values for this are obtained by using text controls GetSelection method.

on_menu_delete_update enables or disables the delete menu item by using bool to get a true or false from the result of method GetSelection which gets the text currently selected in the control or if there is no selection, the returned string is empty.

Full Code
from pathlib import Path
import webbrowser

import wx

WILDCARD = 'Text Documents (*.txt)|*.txt|Python Documents (*.py)|*.py'
UNTITLED = 'Untitled'
HELP_URL = 'https://python-forum.io/Thread-WxPython-Tutorial-Notespad-W-I-P'


class Notespad(wx.Frame):

    def __init__(self, *args, **kwargs):
        super(Notespad, self).__init__(*args, **kwargs)
        self.path = None
        self.frame_settings()
        self.create_menus()
        self.create_gui_items()
        self.create_sizers()
        self.add_binds()

    def frame_settings(self):
        self.update_title()
        self.CreateStatusBar()
        self.SetMinSize((600, 400))
        self.CentreOnScreen()

    def create_menus(self):
        self.menubar = wx.MenuBar()
        self.SetMenuBar(self.menubar)
        self.create_file_menu()
        self.create_edit_menu()
        self.create_help_menu()

    def create_file_menu(self):
        file_menu = wx.Menu()
        self.menu_new = file_menu.Append(wx.ID_NEW, '&New',
                                    'Creates a new document')
        self.menu_open = file_menu.Append(wx.ID_OPEN, '&Open',
                                     'Open an existing file')
        self.menu_save = file_menu.Append(wx.ID_SAVE, '&Save',
                                     'Saves the active document')
        self.menu_saveas = file_menu.Append(wx.ID_SAVEAS, 'Save &As',
                                'Saves the active document with a new name')
        file_menu.AppendSeparator()
        self.menu_exit = file_menu.Append(-1, 'E&xit', 'Exit the Application')
        self.menubar.Append(file_menu, '&File')

    def create_edit_menu(self):
        edit_menu = wx.Menu()
        self.menu_undo = edit_menu.Append(wx.ID_UNDO, '&Undo',
                                    'Undo change')
        edit_menu.AppendSeparator()
        self.menu_cut = edit_menu.Append(wx.ID_CUT, 'Cu&t',
                                    'Cut the selected text')
        self.menu_copy = edit_menu.Append(wx.ID_COPY, '&Copy',
                                    'Copy the selected text')
        self.menu_paste = edit_menu.Append(wx.ID_PASTE, '&Paste',
                                    'Paste from the clipboard')
        self.menu_delete = edit_menu.Append(wx.ID_DELETE, 'De&lete',
                                    'Delete selected text')
        self.menubar.Append(edit_menu, '&Edit')

    def create_help_menu(self):
        help_menu = wx.Menu()
        self.menu_help = help_menu.Append(wx.ID_HELP, '&Help',
                                         'Get Help from python-forum.io')
        self.menubar.Append(help_menu, '&Help')

    def create_gui_items(self):
        self.frame_panel = wx.Panel(self)
        self.txt_ctrl = wx.TextCtrl(self.frame_panel,
                                    style=wx.TE_MULTILINE | wx.BORDER_NONE)

    def create_sizers(self):
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.frame_panel, 1, wx.EXPAND)
        p_sizer = wx.BoxSizer(wx.VERTICAL)
        p_sizer.Add(self.txt_ctrl, 1, wx.EXPAND)
        self.frame_panel.SetSizer(p_sizer)
        self.SetSizer(sizer)
        self.Layout()

    def add_binds(self):
        self.add_frame_binds()
        self.add_fle_menu_binds()
        self.add_edit_menu_binds()
        self.add_help_menu_binds()

    def add_frame_binds(self):
        self.Bind(wx.EVT_CLOSE, self.on_close_evt)

    def add_fle_menu_binds(self):
        self.Bind(wx.EVT_MENU, self.on_menu_new, self.menu_new)
        self.Bind(wx.EVT_MENU, self.on_menu_open, self.menu_open)
        self.Bind(wx.EVT_MENU, self.on_menu_save, self.menu_save)
        self.Bind(wx.EVT_MENU, self.on_menu_saveas, self.menu_saveas)
        self.Bind(wx.EVT_MENU, self.on_menu_exit, self.menu_exit)

    def add_edit_menu_binds(self):
        self.Bind(wx.EVT_MENU, self.on_menu_undo, self.menu_undo)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_undo_update, self.menu_undo)
        self.Bind(wx.EVT_MENU, self.on_menu_cut, self.menu_cut)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_cut_update, self.menu_cut)
        self.Bind(wx.EVT_MENU, self.on_menu_copy, self.menu_copy)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_copy_update, self.menu_copy)
        self.Bind(wx.EVT_MENU, self.on_menu_paste, self.menu_paste)
        self.Bind(wx.EVT_UPDATE_UI, self.on_menu_paste_update, self.menu_paste)
        self.Bind(wx.EVT_MENU, self.on_menu_delete, self.menu_delete)
        self.Bind(
            wx.EVT_UPDATE_UI, self.on_menu_delete_update, self.menu_delete)

    def add_help_menu_binds(self):
        self.Bind(wx.EVT_MENU, self.on_menu_help, self.menu_help)

    def on_menu_exit(self, event):
        self.Close()

    def on_menu_new(self, event):
        if self.save_if_modified():
            self.new()

    def on_menu_open(self, event):
        if self.save_if_modified():
            self.open()

    def on_menu_save(self, event):
        self.save()

    def on_menu_saveas(self, event):
        self.save_as()

    def on_menu_undo(self, event):
        self.txt_ctrl.Undo()

    def on_menu_undo_update(self, event):
        event.Enable(self.txt_ctrl.CanUndo())

    def on_menu_cut(self, event):
        self.txt_ctrl.Cut()

    def on_menu_cut_update(self, event):
        event.Enable(self.txt_ctrl.CanCut())

    def on_menu_copy(self, event):
        self.txt_ctrl.Copy()

    def on_menu_copy_update(self, event):
        event.Enable(self.txt_ctrl.CanCopy())

    def on_menu_paste(self, event):
        self.txt_ctrl.Paste()

    def on_menu_paste_update(self, event):
        event.Enable(self.txt_ctrl.CanPaste())

    def on_menu_delete(self, event):
        self.txt_ctrl.Remove(*self.txt_ctrl.GetSelection())

    def on_menu_delete_update(self, event):
        event.Enable(bool(self.txt_ctrl.GetStringSelection()))

    def on_menu_help(self, event):
        self.open_forum_page()

    def on_close_evt(self, event):
        if event.CanVeto():
            if not self.save_if_modified():
                event.Veto()
                return

        self.Destroy()

    def update_title(self):
        name = UNTITLED
        if self.path:
            name = self.path.name
        self.SetTitle(f'{name} - Notespad')

    def save_changes_msg_dialog(self):
        path = self.path or UNTITLED
        dlg = wx.MessageDialog(
            self, (f'Do you want to save changes to {path}?'),
            'Notespad', wx.YES_NO | wx.CANCEL | wx.CENTER)
        dlg.SetYesNoLabels('Save', 'Don\'t Save')
        return dlg.ShowModal()

    def save_if_modified(self):
        saved_or_dont_save = True
        if self.txt_ctrl.IsModified():
            dlg_result = self.save_changes_msg_dialog()
            if dlg_result == wx.ID_CANCEL:
                saved_or_dont_save = False
            elif dlg_result == wx.ID_YES:
                self.save()
                if not self.path:
                    saved_or_dont_save = False

        return saved_or_dont_save

    def new(self):
        self.txt_ctrl.Clear()
        self.path = None
        self.update_title()

    def open(self):
        with wx.FileDialog(self, message='Open', wildcard=WILDCARD,
                           style=wx.FD_OPEN) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                directory, filename = dlg.GetDirectory(), dlg.GetFilename()
                self.path = Path(directory).joinpath(filename)
                self.txt_ctrl.LoadFile(str(self.path))
                self.update_title()

    def save(self):
        if not self.path:
            self.save_as()
        else:  #
            self.txt_ctrl.SaveFile(str(self.path))
            self.update_title()

    def save_as(self):
        with wx.FileDialog(self, message='Save as', wildcard=WILDCARD,
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                directory, filename = dlg.GetDirectory(), dlg.GetFilename()
                self.path = Path(directory).joinpath(filename)
                self.save()

    def open_forum_page(self):
        webbrowser.open(HELP_URL, 2)


if __name__ == '__main__':
    wx_app = wx.App(False)
    frame = Notespad(None)
    frame.Show()
    wx_app.MainLoop()
Reply


Messages In This Thread
RE: [WxPython][Tutorial] Notespad W.I.P. - by Yoriz - Sep-17-2016, 05:17 PM
RE: [Tutorial] Notespad W.I.P. - by Yoriz - Mar-27-2019, 10:23 PM
RE: [Tutorial] Notespad W.I.P. - by Yoriz - Mar-28-2019, 12:08 AM
RE: [Tutorial] Notespad W.I.P. - by Yoriz - Apr-30-2019, 10:30 PM
RE: [Tutorial] Notespad W.I.P. - by Yoriz - Apr-30-2019, 10:53 PM
RE: [Tutorial] Notespad W.I.P. - by Yoriz - May-03-2019, 10:59 PM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020