Skip to content

Instantly share code, notes, and snippets.

@theScottyJam
Last active September 27, 2021 05:13

Revisions

  1. theScottyJam revised this gist Sep 5, 2021. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions new.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,8 @@
    #!/usr/bin/env python3

    # https://superuser.com/a/1251013/57284
    # https://gist.github.com/theScottyJam/ae19299c8d71a21c75f2abd96daea9f8

    from pathlib import Path
    import tkinter as tk
    from tkinter import ttk
    @@ -46,6 +49,7 @@ def mk_file(template, filename):

    try:
    shutil.copy2(src_file, dst_file)
    os.utime(dst_file)
    except Exception as e:
    return str(e)

  2. theScottyJam revised this gist Sep 16, 2017. 1 changed file with 11 additions and 0 deletions.
    11 changes: 11 additions & 0 deletions new.py
    Original file line number Diff line number Diff line change
    @@ -33,6 +33,7 @@ def main():
    window.destroy()

    def mk_file(template, filename):
    """ Create a new file using by coping the chosen template to `current directory/filename` """
    src_file = TEMPLATES_DIR/template
    dst_file = target_dir/filename

    @@ -68,6 +69,7 @@ def __init__(self, templates, on_create_file):
    self.selected_template = None

    def show(self):
    """ Creates and displays the file naming dialog """
    self.window = tk.Tk()
    self.window.style = ttk.Style()
    self.window.style.theme_use('default')
    @@ -79,6 +81,7 @@ def show(self):
    self.window.destroy()

    def render(self, window):
    """ Renders the window and attaches event listeners """
    self.render_body(window)
    self.render_buttons(window)

    @@ -96,6 +99,8 @@ def render(self, window):
    window.protocol("WM_DELETE_WINDOW", self.close_window)

    def render_body(self, window):
    """ Renders the main body of the window, which includes the template choosing box
    and the filename textbox """
    body = ttk.Frame(window)

    # listbox
    @@ -130,6 +135,7 @@ def render_body(self, window):
    self.on_select_template()

    def render_buttons(self, window):
    """ Renders the buttons at the bottom of the window """
    button_bar = ttk.Frame(window)

    self.cancel_button = ttk.Button(button_bar, text="Cancel", width=10, command=self.close_window)
    @@ -140,6 +146,7 @@ def render_buttons(self, window):
    button_bar.pack()

    def set_error(self, text):
    """ Displays some text to the user """
    self.err_label.config(text=text)
    self.textbox.selection_range(0, tk.END)
    self.error_is_shown = True
    @@ -148,6 +155,7 @@ def set_error(self, text):
    # Event listeners

    def on_textbox_change(self, *args):
    """ Called when the user changes the file name """
    if not self.ingore_textbox_change == True:
    self.filename_has_changed = True
    self.err_label.config(text='')
    @@ -156,6 +164,7 @@ def on_textbox_change(self, *args):
    self.filename_has_changed = False

    def on_select_template(self, event=None):
    """ Called when the user selects a different template """
    self.selected_template = self.templates[ int(self.listbox.curselection()[0]) ]
    if not self.filename_has_changed:
    self.ingore_textbox_change = True
    @@ -170,6 +179,7 @@ def select_one_down(self, event):
    def select_one_up(self, event):
    self.move_selection(-1)
    def move_selection(self, amount):
    """ Changes the template selection. """
    cur_selection = self.listbox.curselection()[0]
    new_selection = int(cur_selection) + amount
    if new_selection < 0:
    @@ -185,6 +195,7 @@ def close_window(self, event=None):
    self.window.quit()

    def apply_values(self, event=None):
    """ Usually called with OK is pushed """
    message = self.on_create_file(self.selected_template, self.textbox.get())
    if message:
    self.set_error(message)
  3. theScottyJam created this gist Sep 16, 2017.
    195 changes: 195 additions & 0 deletions new.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,195 @@
    #!/usr/bin/env python3

    from pathlib import Path
    import tkinter as tk
    from tkinter import ttk
    import os
    import shutil

    TEMPLATES_DIR = Path('~/Templates').expanduser()

    target_dir = None
    def main():
    global target_dir
    # The following gets the selected item(s) from nautilus.
    selected = os.environ.get('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS', os.curdir)
    # We're only going to use the first if multiple are selected.
    target_dir = Path( selected.split('\n')[0] ).resolve().parent

    templates = [f.name for f in TEMPLATES_DIR.iterdir()]
    templates.sort()

    if templates:
    prompt = FilenamePrompt(templates, mk_file)
    prompt.show()
    else:
    window = tk.Tk()
    window.title('Error')
    label = tk.Label(window, text="Error: You don't have any templates inside ~/Templates")
    label.grid(row=0)
    label.pack(padx=10, pady=10)
    window.protocol("WM_DELETE_WINDOW", window.quit)
    window.mainloop()
    window.destroy()

    def mk_file(template, filename):
    src_file = TEMPLATES_DIR/template
    dst_file = target_dir/filename

    if filename == '':
    return 'Please provide a file name'
    if not dst_file.parent.is_dir(): # If the inputed text has a "/" with an non-existant folder before it.
    return 'That folder does not exist'
    if dst_file.is_file():
    return 'That file already exists'

    try:
    shutil.copy2(src_file, dst_file)
    except Exception as e:
    return str(e)

    class FilenamePrompt():

    def __init__(self, templates, on_create_file):
    """
    templates is a list of strings that will appear in the menu.
    on_create_file is what gets called when the user pushes ok.
    It gets passes the selected template, and the file name the user chose.
    If an error occures, that error should return as a string that will get shown to the user.
    Otherwise, None should be returned.
    """
    self.templates = templates
    self.on_create_file = on_create_file
    self.filename_has_changed = False # Sets to true as soon as the user alters the content of the textbox.
    # This can be set to true if this class wants to alter the textbox value without triggering other consequences.
    self.ingore_textbox_change = False
    self.error_is_shown = False

    self.selected_template = None

    def show(self):
    self.window = tk.Tk()
    self.window.style = ttk.Style()
    self.window.style.theme_use('default')
    self.window.title('New Document')
    self.window.resizable(False, False)
    self.render(self.window)

    self.window.mainloop()
    self.window.destroy()

    def render(self, window):
    self.render_body(window)
    self.render_buttons(window)

    # Tab order
    self.listbox.lower()
    self.textbox.lower()
    self.ok_button.lower()
    self.cancel_button.lower()

    # Event listeners
    window.bind('<Return>', self.apply_values)
    window.bind('<Escape>', self.close_window)
    window.bind('<Down>', self.select_one_down)
    window.bind('<Up>', self.select_one_up)
    window.protocol("WM_DELETE_WINDOW", self.close_window)

    def render_body(self, window):
    body = ttk.Frame(window)

    # listbox
    self.listbox = tk.Listbox(window, selectmode=tk.SINGLE, width=40, selectbackground='#8af', exportselection=False)
    self.listbox.bind('<<ListboxSelect>>', self.on_select_template)
    self.listbox.pack()
    for template in self.templates:
    self.listbox.insert(tk.END, template)
    # The following auto-resizes the listbox based on the content inside.
    self.listbox.config(height=0)

    # textbox
    ttk.Label(body, text="File name: ").grid(row=0)

    self.textbox_var = tk.StringVar()
    self.textbox_var.trace('w', self.on_textbox_change)

    self.textbox = ttk.Entry(body, textvariable=self.textbox_var)
    self.textbox.grid(row=0, column=1)

    body.pack(padx=5, pady=5)


    # error label
    err_label_frame = ttk.Frame(window)
    self.err_label = tk.Label(err_label_frame, fg='#a00')
    self.err_label.grid(row=0)
    err_label_frame.pack()

    # autoselects the first listbox item
    self.listbox.select_set(0)
    self.on_select_template()

    def render_buttons(self, window):
    button_bar = ttk.Frame(window)

    self.cancel_button = ttk.Button(button_bar, text="Cancel", width=10, command=self.close_window)
    self.cancel_button.pack(side=tk.LEFT, padx=5, pady=5)
    self.ok_button = ttk.Button(button_bar, text="OK", width=10, command=self.apply_values, default=tk.ACTIVE)
    self.ok_button.pack(side=tk.LEFT, padx=5, pady=5)

    button_bar.pack()

    def set_error(self, text):
    self.err_label.config(text=text)
    self.textbox.selection_range(0, tk.END)
    self.error_is_shown = True
    self.textbox.focus_set()

    # Event listeners

    def on_textbox_change(self, *args):
    if not self.ingore_textbox_change == True:
    self.filename_has_changed = True
    self.err_label.config(text='')
    if self.textbox.get() == '':
    # We'll allow the textbox content to change if it's currently empty.
    self.filename_has_changed = False

    def on_select_template(self, event=None):
    self.selected_template = self.templates[ int(self.listbox.curselection()[0]) ]
    if not self.filename_has_changed:
    self.ingore_textbox_change = True
    self.textbox_var.set(self.selected_template)
    self.ingore_textbox_change = False

    self.textbox.selection_range(0, tk.END)
    self.window.after_idle(self.textbox.focus)

    def select_one_down(self, event):
    self.move_selection(1)
    def select_one_up(self, event):
    self.move_selection(-1)
    def move_selection(self, amount):
    cur_selection = self.listbox.curselection()[0]
    new_selection = int(cur_selection) + amount
    if new_selection < 0:
    new_selection = 0
    if new_selection >= len(self.templates):
    new_selection = len(self.templates) - 1

    self.listbox.selection_clear(cur_selection)
    self.listbox.select_set(new_selection)
    self.on_select_template()

    def close_window(self, event=None):
    self.window.quit()

    def apply_values(self, event=None):
    message = self.on_create_file(self.selected_template, self.textbox.get())
    if message:
    self.set_error(message)
    return
    self.window.quit()

    if __name__ == '__main__':
    main()