= How to create a simple text editor (Phoenix) =
'''Keywords :''' Editor, Text, TextCtrl, Stc, StyledTextCtrl.

<<TableOfContents>>

--------
= Demonstrating : =
__'''''Tested''' py3.x, wx4.x and Win10. ''__

Are you ready to use some samples ? ;)

Test, modify, correct, complete, improve and share your discoveries ! (!)

--------
== Sample one ==
{{attachment:img_sample_one.png}}

{{{#!python
# sample_one.py

import sys
import os.path
import wx

# class MyFrame
# class MyApp

#-------------------------------------------------------------------------------

# This is how you pre-establish a file filter so that the
# dialog only shows the extension(s) you want it to.

wildcard = "Text (*.txt)|*.txt|"        \
           "Executable (*.exe)|*.exe|"  \
           "Library (*.dll)|*.dll|"     \
           "Driver (*.sys)|*.sys|"      \
           "ActiveX (*.ocx)|*.ocx|"     \
           "Python (*.py)|*.py|"        \
           "Python (*.pyw)|*.pyw|"      \
           "All (*.*)|*.*"

#-------------------------------------------------------------------------------

class MyFrame(wx.Frame):
    def __init__(self, filename="noname.txt"):
        super(MyFrame, self).__init__(None, size=(400, 300))

        #------------

        # Return icons folder.
        self.icons_dir = wx.GetApp().GetIconsDir()

        #------------

        self.filename = filename
        self.dirname  = "."

        #------------

        # Simplified init method.
        self.CreateInteriorWindowComponents()
        self.CreateExteriorWindowComponents()

        #------------

        self.CenterOnScreen()

    #---------------------------------------------------------------------------

    def SetTitle(self):
        """
        ...
        """

        # MyFrame.SetTitle overrides wx.Frame.SetTitle,
        # so we have to call it using super :
        super(MyFrame, self).SetTitle("Editor %s" % self.filename)


    def CreateInteriorWindowComponents(self):
        """
        Create "interior" window components. In this case
        it is just a simple multiline text control.
        """

        self.control = wx.TextCtrl(self, -1, value="", style=wx.TE_MULTILINE)


    def CreateExteriorWindowComponents(self):
        """
        Create "exterior" window components, such as menu and status bar.
        """

        # Simplified init method.
        self.SetTitle()

        #------------

        frameIcon = wx.Icon(os.path.join(self.icons_dir,
                                         "icon_wxWidgets.ico"),
                            type=wx.BITMAP_TYPE_ICO)
        self.SetIcon(frameIcon)

        #------------

        self.CreateMenu()
        self.CreateStatusBar()
        self.BindEvents()


    def CreateMenu(self):
        """
        Create menu and menu bar.
        """

        menuBar = wx.MenuBar()

        #------------

        fileMenu = wx.Menu()

        for id, label, helpText, handler in \
            [(wx.ID_ABOUT, "&About",
              "Information about this program.", self.OnAbout),
             (None, None, None, None),
             (wx.ID_OPEN, "&Open",
              "Open a new file.", self.OnOpen),
             (wx.ID_SAVE, "&Save",
              "Save the current file.", self.OnSave),
             (wx.ID_SAVEAS, "Save &as",
              "Save the file under a different name.", self.OnSaveAs),
             (None, None, None, None),
             (wx.ID_EXIT, "E&xit",
              "Terminate the program.", self.OnCloseMe)]:

            if id == None:
                fileMenu.AppendSeparator()
            else:
                item = fileMenu.Append(id, label, helpText)

                #------------

                # Bind some events to an events handler.
                self.Bind(wx.EVT_MENU, handler, item)

        #------------

        # Add the fileMenu to the menuBar.
        menuBar.Append(fileMenu, "&File")

        #------------

        # Add the menuBar to the frame.
        self.SetMenuBar(menuBar)


    def BindEvents(self):
        """
        ...
        """

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)


    def DefaultFileDialogOptions(self):
        """
        Return a dictionary with file dialog options that can be
        used in both the save file dialog as well as in the open
        file dialog.
        """

        return dict(message="Choose a file :",
                    defaultDir=self.dirname,
                    wildcard=wildcard)


    def AskUserForFilename(self, **dialogOptions):
        """
        ...
        """

        dialog = wx.FileDialog(self, **dialogOptions)

        if dialog.ShowModal() == wx.ID_OK:
            userProvidedFilename = True
            self.filename = dialog.GetFilename()
            self.dirname = dialog.GetDirectory()
            # Update the window title with the new filename.
            self.SetTitle()
        else:
            userProvidedFilename = False

        dialog.Destroy()

        return userProvidedFilename


    def OnOpen(self, event):
        """
        Open file.
        """

        if self.AskUserForFilename(style=wx.FD_OPEN,
                                   **self.DefaultFileDialogOptions()):
            file = open(os.path.join(self.dirname, self.filename), 'r', encoding='utf-8')
            self.control.SetValue(file.read())
            file.close()


    def OnSave(self, event):
        """
        Save file.
        """

        with open(os.path.join(self.dirname, self.filename), 'w', encoding='utf-8') as file:
            file.write(self.control.GetValue())


    def OnSaveAs(self, event):
        """
        Save file as.
        """

        if self.AskUserForFilename(defaultFile=self.filename, style=wx.FD_SAVE,
                                   **self.DefaultFileDialogOptions()):
            self.OnSave(event)


    def OnAbout(self, event):
        """
        About dialog.
        """

        dialog = wx.MessageDialog(self,
                                  "A sample editor in wxPython.",
                                  "About sample editor",
                                  wx.OK)
        dialog.ShowModal()
        dialog.Destroy()


    def OnCloseMe(self, event):
        """
        Close the main window.
        """

        self.Close(True)


    def OnCloseWindow(self, event):
        """
        Quit and destroy application.
        """

        self.Destroy()

#-------------------------------------------------------------------------------

class MyApp(wx.App):
    """
    ....
    """
    def OnInit(self):

        #------------

        self.installDir = os.path.split(os.path.abspath(sys.argv[0]))[0]

        #------------

        frame = MyFrame()
        self.SetTopWindow(frame)
        frame.Show(True)

        return True

    #---------------------------------------------------------------------------

    def GetInstallDir(self):
        """
        Returns the installation directory for my application.
        """

        return self.installDir


    def GetIconsDir(self):
        """
        Returns the icons directory for my application.
        """

        icons_dir = os.path.join(self.installDir, "icons")
        return icons_dir

#-------------------------------------------------------------------------------

def main():
    app = MyApp(False)
    app.MainLoop()

#-------------------------------------------------------------------------------

if __name__ == "__main__" :
    main()
}}}
--------
== Sample two ==
{{attachment:img_sample_two.png}}

{{{#!python
# sample_two.py

import sys
import os.path
import wx
import wx.stc as stc   # StyledTextCtrl

# class MyFrame
# class MyApp

#-------------------------------------------------------------------------------

# This is how you pre-establish a file filter so that the
# dialog only shows the extension(s) you want it to.

wildcard = "Text (*.txt)|*.txt|"        \
           "Config (*.cfg)|*.cfg|"      \
           "Python (*.py)|*.py|"        \
           "Python (*.pyw)|*.pyw|"      \
           "All (*.*)|*.*"

#-------------------------------------------------------------------------------

class MyFrame(wx.Frame):
    def __init__(self, filename="noname.txt"):
        super(MyFrame, self).__init__(None, size=(400, 300))

        #------------

        # Return icons folder.
        self.icons_dir = wx.GetApp().GetIconsDir()

        #------------

        self.filename = filename
        self.dirname  = "."
        self.flagDirty = False

        #------------

        # Simplified init method.
        self.SetProperties()
        self.CreateMenuBar()
        self.CreateStyledTextControl()
        self.CreateStatusBar()
        self.BindEvents()

    #---------------------------------------------------------------------------

    def SetProperties(self):
        """
        ...
        """

        self.SetTitle()

        #------------

        frameIcon = wx.Icon(os.path.join(self.icons_dir,
                                         "icon_wxWidgets.ico"),
                            type=wx.BITMAP_TYPE_ICO)
        self.SetIcon(frameIcon)


    def SetTitle(self):
        """
        ...
        """

        # MyFrame.SetTitle overrides wx.Frame.SetTitle,
        # so we have to call it using super :
        super(MyFrame, self).SetTitle("Editor %s" % self.filename)


    def CreateMenuBar(self):
        """
        Create menu and menu bar.
        """

        menuBar = wx.MenuBar()

        #------------

        menu_File = wx.Menu()
        menu_Edit = wx.Menu()
        menu_About = wx.Menu()

        #------------
        #------------

        self.menu_File_Open = menu_File.Append(wx.ID_OPEN,
                                               "&Open" + "\t" + "Ctrl+O",
                                               "Open a new file.")
        #------------

        menu_File.AppendSeparator()

        #------------

        self.menu_File_Save = menu_File.Append(wx.ID_SAVE,
                                               "&Save" + "\t" + "Ctrl+S",
                                               "Save the current file.")
        self.menu_File_SaveAs = menu_File.Append(wx.ID_SAVEAS,
                                                 "&Save as" + "\t" + "Ctrl+Shift+S",
                                                 "Save the file under a different name.")

        #------------

        menu_File.AppendSeparator()

        #------------

        self.menu_File_Close = menu_File.Append(wx.ID_EXIT,
                                                "&Exit" + "\t" + "Ctrl+X",
                                                "Exit the program.")
        #------------

        self.menu_Edit_Cut = menu_Edit.Append(wx.ID_CUT,
                                              "&Cut" + "\t" + "Ctrl+X",
                                              "Cut")
        self.menu_Edit_Copy = menu_Edit.Append(wx.ID_COPY,
                                               "&Copy" + "\t" + "Ctrl+C",
                                               "Copy")
        self.menu_Edit_Paste = menu_Edit.Append(wx.ID_PASTE,
                                                "&Paste" + "\t" + "Ctrl+V",
                                                "Paste")
        #------------

        menu_Edit.AppendSeparator()

        #------------

        self.menu_Edit_Undo = menu_Edit.Append(wx.ID_UNDO,
                                               "&Undo" + "\t" + "Ctrl+Z",
                                               "Undo")
        self.menu_Edit_Redo = menu_Edit.Append(wx.ID_REDO,
                                               "&Redo" + "\t" + "Ctrl+Shift+Z",
                                               "Redo")

        #------------

        self.menu_About_Info = menu_About.Append(wx.ID_ABOUT,
                                                 "&About" + "\t" + "Ctrl+I",
                                                 "Information about this program.")

        #------------
        #------------

        menuBar.Append(menu_File, "File")
        menuBar.Append(menu_Edit, "Edit")
        menuBar.Append(menu_About, "About")

        #------------

        self.SetMenuBar(menuBar)


    def CreateStyledTextControl(self):
        """
        ...
        """

        self.St_TextCtrl = stc.StyledTextCtrl(self)
        self.St_TextCtrl.SetWindowStyle(self.St_TextCtrl.GetWindowStyle() | wx.DOUBLE_BORDER)
        self.St_TextCtrl.StyleSetSpec(stc.STC_STYLE_DEFAULT, "size:10,face:Courier New")
        self.St_TextCtrl.SetWrapMode(stc.STC_WRAP_WORD)

        #------------

        layout = wx.BoxSizer(wx.HORIZONTAL)
        layout.Add(self.St_TextCtrl, proportion=1, border=0, flag=wx.ALL|wx.EXPAND)
        self.SetSizer(layout)

        #------------

        self.St_TextCtrl.Bind(stc.EVT_STC_CHANGE, self.OnChangeTxtCtrl)


    def BindEvents(self):
        """
        ...
        """

        self.Bind(wx.EVT_MENU, self.OnOpen, self.menu_File_Open)
        self.Bind(wx.EVT_MENU, self.OnSave, self.menu_File_Save)
        self.Bind(wx.EVT_MENU, self.OnSaveAs, self.menu_File_SaveAs)
        self.Bind(wx.EVT_MENU, self.OnCloseMe, self.menu_File_Close)
        self.Bind(wx.EVT_MENU, self.OnCut, self.menu_Edit_Cut)
        self.Bind(wx.EVT_MENU, self.OnCopy, self.menu_Edit_Copy)
        self.Bind(wx.EVT_MENU, self.OnPaste, self.menu_Edit_Paste)
        self.Bind(wx.EVT_MENU, self.OnUndo, self.menu_Edit_Undo)
        self.Bind(wx.EVT_MENU, self.OnRedo, self.menu_Edit_Redo)
        self.Bind(wx.EVT_MENU, self.OnAbout, self.menu_About_Info)

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)


    def OnChangeTxtCtrl(self, event):
        """
        ...
        """

        lines = self.St_TextCtrl.GetLineCount()
        width = self.St_TextCtrl.TextWidth(stc.STC_STYLE_LINENUMBER, str(lines) + " ")
        self.St_TextCtrl.SetMarginWidth(0, width)

        self.flagDirty = True


    def DefaultFileDialogOptions(self):
        """
        Return a dictionary with file dialog options that can be
        used in both the save file dialog as well as in the open
        file dialog.
        """

        return dict(message="Choose a file :",
                    defaultDir=self.dirname,
                    wildcard=wildcard)


    def AskUserForFilename(self, **dialogOptions):
        """
        ...
        """

        dialog = wx.FileDialog(self, **dialogOptions)

        if dialog.ShowModal() == wx.ID_OK:
            userProvidedFilename = True
            self.filename = dialog.GetFilename()
            self.dirname = dialog.GetDirectory()
            # Update the window title with the new filename.
            self.SetTitle()
        else:
            userProvidedFilename = False

        dialog.Destroy()

        return userProvidedFilename


    def OnOpen(self, event):
        """
        Open file.
        """

        if self.AskUserForFilename(style=wx.FD_OPEN,
                                   **self.DefaultFileDialogOptions()):
            file = open(os.path.join(self.dirname, self.filename), 'r', encoding='utf-8')
            self.St_TextCtrl.SetValue(file.read())
            file.close()


    def OnSave(self, event):
        """
        Save file.
        """

        with open(os.path.join(self.dirname, self.filename), 'w', encoding='utf-8') as file:
            file.write(self.St_TextCtrl.GetValue())


    def OnSaveAs(self, event):
        """
        Save file as.
        """

        if self.AskUserForFilename(defaultFile=self.filename, style=wx.FD_SAVE,
                                   **self.DefaultFileDialogOptions()):
            self.OnSave(event)


    def OnCut(self, event):
        """
        ...
        """

        self.St_TextCtrl.Cut()


    def OnCopy(self, event):
        """
        ...
        """

        self.St_TextCtrl.Copy()


    def OnPaste(self, event):
        """
        ...
        """

        self.St_TextCtrl.Paste()


    def OnUndo(self, event):
        """
        ...
        """

        self.St_TextCtrl.Undo()


    def OnRedo(self, event):
        """
        ...
        """

        self.St_TextCtrl.Redo()


    def OnAbout(self, event):
        """
        About dialog.
        """

        dialog = wx.MessageDialog(self,
                                  "A sample StyledTextCtrl editor in wxPython.",
                                  "About sample editor",
                                  wx.OK)
        dialog.ShowModal()
        dialog.Destroy()


    def OnCloseMe(self, event):
        """
        Close the main window.
        """

        self.Close(True)


    def OnCloseWindow(self, event):
        """
        Quit and destroy application.
        """

        self.Destroy()

#-------------------------------------------------------------------------------

class MyApp(wx.App):
    """
    ....
    """
    def OnInit(self):

        #------------

        self.installDir = os.path.split(os.path.abspath(sys.argv[0]))[0]

        #------------

        frame = MyFrame()
        self.SetTopWindow(frame)
        frame.Show(True)

        return True

    #---------------------------------------------------------------------------

    def GetInstallDir(self):
        """
        Returns the installation directory for my application.
        """

        return self.installDir


    def GetIconsDir(self):
        """
        Returns the icons directory for my application.
        """

        icons_dir = os.path.join(self.installDir, "icons")
        return icons_dir

#-------------------------------------------------------------------------------

def main():
    app = MyApp(False)
    app.MainLoop()

#-------------------------------------------------------------------------------

if __name__ == "__main__" :
    main()
}}}
--------
== Sample three ==
{{attachment:img_sample_three.png}}

Here we have a simple text editor. Notice the use of exceptions. It is a programmer's task to find and handle bugs. This includes doing such nasty things like opening pictures in an editor, simulating all possible situations. If a script raises an unhandled exception, we simply write a code that handles this (info by '''ZetCode / Jan Bodnar''').

{{{#!python
# sample_three.py

"""

Author : Zetcode
Created : Apr., 2007
Updated : Nov. 16, 2020 by Ecco

"""

import wx
import os

# Editor

#---------------------------------------------------------------------------

TBFLAGS = ( wx.TB_HORIZONTAL
            | wx.NO_BORDER
            | wx.TB_FLAT
            #| wx.TB_TEXT
            #| wx.TB_HORZ_LAYOUT
            )

# This is how you pre-establish a file filter so that the dialog
# only shows the extension(s) you want it to.
wildcard = "Python source (*.py)|*.py|"     \
           "Compiled Python (*.pyc)|*.pyc|" \
           "SPAM files (*.spam)|*.spam|"    \
           "Egg file (*.egg)|*.egg|"        \
           "All files (*.*)|*.*"

#---------------------------------------------------------------------------

class Editor(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(400, 300))

        #------------

        icon = wx.Icon("./icons/icon_wxWidgets.ico")
        self.SetIcon(icon)

        #------------

        # Variables.
        self.modify = False
        self.last_name_saved = ''
        self.replace = False

        #------------

        # Setting up menubar.
        menubar = wx.MenuBar()

        file = wx.Menu()
        new = wx.MenuItem(file, 101, '&New\tCtrl+N', 'Creates a new document.')
        new.SetBitmap(wx.Bitmap('./images/stock_new-16.png'))
        file.Append(new)

        open = wx.MenuItem(file, 102, '&Open\tCtrl+O', 'Open an existing file.')
        open.SetBitmap(wx.Bitmap('./images/stock_open-16.png'))
        file.Append(open)
        file.AppendSeparator()

        save = wx.MenuItem(file, 103, '&Save\tCtrl+S', 'Save the file.')
        save.SetBitmap(wx.Bitmap('./images/stock_save-16.png'))
        file.Append(save)

        saveas = wx.MenuItem(file, 104, 'Save &as...\tShift+Ctrl+S', 'Save the file with a different name.')
        saveas.SetBitmap(wx.Bitmap('./images/stock_save_as-16.png'))
        file.Append(saveas)
        file.AppendSeparator()

        quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the application.')
        quit.SetBitmap(wx.Bitmap('./images/stock_exit-16.png'))
        file.Append(quit)

        edit = wx.Menu()
        cut = wx.MenuItem(edit, 106, 'C&ut\tCtrl+X', 'Cut the selection.')
        cut.SetBitmap(wx.Bitmap('./images/stock_cut-16.png'))
        edit.Append(cut)

        copy = wx.MenuItem(edit, 107, '&Copy\tCtrl+C', 'Copy the selection.')
        copy.SetBitmap(wx.Bitmap('./images/stock_copy-16.png'))
        edit.Append(copy)

        paste = wx.MenuItem(edit, 108, '&Paste\tCtrl+V', 'Paste text from clipboard.')
        paste.SetBitmap(wx.Bitmap('./images/stock_paste-16.png'))
        edit.Append(paste)

        delete = wx.MenuItem(edit, 109, '&Delete', 'Delete the selected text.')
        delete.SetBitmap(wx.Bitmap('./images/stock_delete-16.png'))

        edit.Append(delete)
        edit.AppendSeparator()
        edit.Append(110, 'Select &All\tCtrl+A', 'Select the entire text.')

        view = wx.Menu()
        view.Append(111, '&Statusbar', 'Show StatusBar.')

        help = wx.Menu()
        about = wx.MenuItem(help, 112, '&About\tF1', 'About Editor.')
        about.SetBitmap(wx.Bitmap('./images/stock_about-16.png'))
        help.Append(about)

        menubar.Append(file, '&File')
        menubar.Append(edit, '&Edit')
        menubar.Append(view, '&View')
        menubar.Append(help, '&Help')

        self.SetMenuBar(menubar)

        #------------

        # setting up toolbar
        self.toolbar = self.CreateToolBar(TBFLAGS)
        self.toolbar.AddTool(801, "New", wx.Image('./images/stock_new.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'New', "Long help for 'New'", None)
        self.toolbar.AddTool(802, "Open", wx.Image('./images/stock_open.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Open', "Long help for 'Open'", None)
        self.toolbar.AddTool(803, "Save", wx.Image('./images/stock_save.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Save', "Long help for 'Save'", None)
        self.toolbar.AddSeparator()

        self.toolbar.AddTool(804, "Cut", wx.Image('./images/stock_cut.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Cut', "Long help for 'Cut'", None)
        self.toolbar.AddTool(805, "Copy", wx.Image('./images/stock_copy.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Copy', "Long help for 'Copy'", None)
        self.toolbar.AddTool(806, "Paste", wx.Image('./images/stock_paste.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Paste', "Long help for 'Paste'", None)
        self.toolbar.AddSeparator()

        self.toolbar.AddTool(807, "Exit", wx.Image('./images/stock_exit.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(),
                             wx.NullBitmap, wx.ITEM_NORMAL, 'Exit', "Long help for 'Exit'", None)

        self.toolbar.Realize()

        #------------

        self.text = wx.TextCtrl(self, 1000, '', size=(-1, -1), style=wx.TE_MULTILINE | wx.TE_PROCESS_ENTER)
        self.text.SetFocus()

        #------------

        self.text.Bind(wx.EVT_TEXT, self.OnTextChanged, id=1000)
        self.text.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

        self.Bind(wx.EVT_MENU, self.NewApplication, id=101)
        self.Bind(wx.EVT_MENU, self.OnOpenFile, id=102)
        self.Bind(wx.EVT_MENU, self.OnSaveFile, id=103)
        self.Bind(wx.EVT_MENU, self.OnSaveAsFile, id=104)
        self.Bind(wx.EVT_MENU, self.QuitApplication, id=105)
        self.Bind(wx.EVT_MENU, self.OnCut, id=106)
        self.Bind(wx.EVT_MENU, self.OnCopy, id=107)
        self.Bind(wx.EVT_MENU, self.OnPaste, id=108)
        self.Bind(wx.EVT_MENU, self.OnDelete, id=109)
        self.Bind(wx.EVT_MENU, self.OnSelectAll, id=110)
        self.Bind(wx.EVT_MENU, self.ToggleStatusBar, id=111)
        self.Bind(wx.EVT_MENU, self.OnAbout, id=112)

        self.Bind(wx.EVT_TOOL, self.NewApplication, id=801)
        self.Bind(wx.EVT_TOOL, self.OnOpenFile, id=802)
        self.Bind(wx.EVT_TOOL, self.OnSaveFile, id=803)
        self.Bind(wx.EVT_TOOL, self.OnCut, id=804)
        self.Bind(wx.EVT_TOOL, self.OnCopy, id=805)
        self.Bind(wx.EVT_TOOL, self.OnPaste, id=806)
        self.Bind(wx.EVT_TOOL, self.QuitApplication, id=807)

        self.Bind(wx.EVT_CLOSE, self.QuitApplication)

        #------------

        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(self.text, 1, wx.EXPAND, border=0)

        self.SetSizer(vbox)

        #------------

        self.StatusBar()

        #------------

        self.Centre()

        #------------

        self.Show(True)

    #-----------------------------------------------------------------------

    def NewApplication(self, event):
        editor = Editor(None, -1, 'Editor')
        editor.Centre()
        editor.Show()


    def OnOpenFile(self, event):
        file_name = os.path.basename(self.last_name_saved)

        if self.modify:
            dlg = wx.MessageDialog(self,
                                   'Save changes ?',
                                   '',
                                   wx.YES_NO |
                                   wx.YES_DEFAULT |
                                   wx.CANCEL |
                                   wx.ICON_QUESTION)

            val = dlg.ShowModal()
            if val == wx.ID_YES:
                self.OnSaveFile(event)
                self.DoOpenFile()
            elif val == wx.ID_CANCEL:
                dlg.Destroy()
            else:
                self.DoOpenFile()
        else:
            self.DoOpenFile()


    def DoOpenFile(self):
        open_dlg = wx.FileDialog(self,
                                 message="Choose a file",
                                 defaultDir=os.getcwd(),
                                 defaultFile="",
                                 wildcard=wildcard,
                                 style=wx.FD_OPEN |
                                       wx.FD_CHANGE_DIR |
                                       wx.FD_FILE_MUST_EXIST |
                                       wx.FD_PREVIEW)

        if open_dlg.ShowModal() == wx.ID_OK:
            path = open_dlg.GetPath()

            try:
                file = open(path, 'r')
                text = file.read()
                file.close()
                if self.text.GetLastPosition():
                    self.text.Clear()
                self.text.WriteText(text)
                self.last_name_saved = path
                self.statusbar.SetStatusText('', 1)
                self.modify = False

            except IOError as error:
                dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error))
                dlg.ShowModal()

            except UnicodeDecodeError as error:
                dlg = wx.MessageDialog(self, 'Error opening file\n' + str(error))
                dlg.ShowModal()

        open_dlg.Destroy()


    def OnSaveFile(self, event):
        if self.last_name_saved:

            try:
                file = open(self.last_name_saved, 'w')
                text = self.text.GetValue()
                file.write(text)
                file.close()
                self.statusbar.SetStatusText(os.path.basename(self.last_name_saved) + ' Saved', 0)
                self.modify = False
                self.statusbar.SetStatusText('', 1)

            except IOError as error:
                dlg = wx.MessageDialog(self, 'Error saving file\n' + str(error))
                dlg.ShowModal()
        else:
            self.OnSaveAsFile(event)


    def OnSaveAsFile(self, event):
        save_dlg = wx.FileDialog(self, message="Save file as ...",
                                 defaultDir=os.getcwd(),
                                 defaultFile="",
                                 wildcard=wildcard,
                                 style=wx.FD_SAVE |
                                       wx.FD_OVERWRITE_PROMPT)
        save_dlg.SetFilterIndex(0)

        if save_dlg.ShowModal() == wx.ID_OK:
            path = save_dlg.GetPath()

            try:
                file = open(path, 'w')
                text = self.text.GetValue()
                file.write(text)
                file.close()
                self.last_name_saved = os.path.basename(path)
                self.statusbar.SetStatusText(self.last_name_saved + ' Saved', 0)
                self.modify = False
                self.statusbar.SetStatusText('', 1)

            except IOError as error:
                dlg = wx.MessageDialog(self, 'Error saving file\n' + str(error))
                dlg.ShowModal()
        save_dlg.Destroy()


    def OnCut(self, event):
        self.text.Cut()


    def OnCopy(self, event):
        self.text.Copy()


    def OnPaste(self, event):
        self.text.Paste()


    def QuitApplication(self, event):
        if self.modify:
            dlg = wx.MessageDialog(self, 'Save before Exit ?', '', wx.YES_NO | wx.YES_DEFAULT |
                        wx.CANCEL | wx.ICON_QUESTION)
            val = dlg.ShowModal()
            if val == wx.ID_YES:
                self.OnSaveFile(event)
                if not self.modify:
                    wx.Exit()
            elif val == wx.ID_CANCEL:
                dlg.Destroy()
            else:
                self.Destroy()
        else:
            self.Destroy()


    def OnDelete(self, event):
        frm, to = self.text.GetSelection()
        self.text.Remove(frm, to)


    def OnSelectAll(self, event):
        self.text.SelectAll()


    def OnTextChanged(self, event):
        self.modify = True
        self.statusbar.SetStatusText(' Modified', 1)

        event.Skip()


    def OnKeyDown(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_INSERT:
            if not self.replace:
                self.statusbar.SetStatusText('INS', 2)
                self.replace = True
            else:
                self.statusbar.SetStatusText('', 2)
                self.replace = False
        event.Skip()


    def ToggleStatusBar(self, event):
        if self.statusbar.IsShown():
            self.statusbar.Hide()
        else:
            self.statusbar.Show()
        self.SendSizeEvent()


    def StatusBar(self):
        self.statusbar = self.CreateStatusBar()
        self.statusbar.SetFieldsCount(3)
        self.statusbar.SetStatusWidths([-5, -2, -1])


    def OnAbout(self, event):
        dlg = wx.MessageDialog(self,
                               '\tEditor\t\n'\
                               '\n'\
                               'Another Tutorial\n'\
                               'Jan Bodnar 2005-2006\n'\
                               'Updated by Ecco 2020.',
                               'About Editor',
                               wx.OK |
                               wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

#---------------------------------------------------------------------------


app = wx.App()
Editor(None, -1, 'Editor')
app.MainLoop()
}}}
--------
= Download source =
[[attachment:source.zip]]

--------
= Additional Information =
'''Link :'''

https://teratail.com/questions/124859?link=qa_related_pc

- - - - -

https://wiki.wxpython.org/TitleIndex

https://docs.wxpython.org/

--------
= Thanks to =
??? (sample_one.py coding), ??? (sample_two.py coding), Jan Bodnar (sample_three.py coding), the wxPython community...

--------
= About this page =
Date(d/m/y)     Person (bot)    Comments :

19/07/20 - Ecco (Created page and updated examples for wxPython Phoenix).

--------
= Comments =
- blah, blah, blah....