diff --git a/.gitignore b/.gitignore index 894a44cc..4ad64672 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ venv.bak/ # mypy .mypy_cache/ + +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 697c2bf7..ec0dd9b6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ You should be using [Pipenv](https://pipenv.readthedocs.io/en/latest/). Take a l * Setting up for development: `pipenv install --dev` * Running the application (assuming you use our project layout): `pipenv run start` +# Team Name +youthful-yeomen + # Project Information `# TODO` diff --git a/project/__main__.py b/project/__main__.py index e69de29b..8ee70e65 100644 --- a/project/__main__.py +++ b/project/__main__.py @@ -0,0 +1,4 @@ +from notepad import Notepad + +notepad = Notepad(width=600, height=400) +notepad.run() diff --git a/project/notepad.py b/project/notepad.py new file mode 100644 index 00000000..fd1f58bc --- /dev/null +++ b/project/notepad.py @@ -0,0 +1,198 @@ +import os +from tkinter.messagebox import showerror +from tkinter.filedialog import ( + Text, + Menu, + Scrollbar, + N, + E, + S, + W, + Y, + Tk, + askopenfilename, + asksaveasfilename, + RIGHT, + END, + TclError, +) + +from reverse_writer import WordReverser + + + + +class Notepad: + __root = Tk() + + # default window width and height + __thisWidth = 300 + __thisHeight = 300 + __thisTextArea = Text(__root) + __thisMenuBar = Menu(__root) + __thisFileMenu = Menu(__thisMenuBar, tearoff=0) + __thisEditMenu = Menu(__thisMenuBar, tearoff=0) + __thisHelpMenu = Menu(__thisMenuBar, tearoff=0) + + # To add scrollbar + __thisScrollBar = Scrollbar(__thisTextArea) + __file = None + + def __init__(self, **kwargs): + + # Set window size (the default is 300x300) + + try: + self.__thisWidth = kwargs["width"] + except KeyError: + pass + + try: + self.__thisHeight = kwargs["height"] + except KeyError: + pass + + # Set the window text + self.__root.title("Youthful-Yeomen : Untitled") + + # Center the window + screen_width = self.__root.winfo_screenwidth() + screen_height = self.__root.winfo_screenheight() + + # For left-align + left = (screen_width / 2) - (self.__thisWidth / 2) + + # For right-align + top = (screen_height / 2) - (self.__thisHeight / 2) + + # For top and bottom + self.__root.geometry( + "%dx%d+%d+%d" % (self.__thisWidth, self.__thisHeight, left, top) + ) + + # To make the text area auto resizable + self.__root.grid_rowconfigure(0, weight=1) + self.__root.grid_columnconfigure(0, weight=1) + + # Add controls (widget) + self.__thisTextArea.grid(sticky=N + E + S + W) + wr = WordReverser() + self.__thisTextArea.bind("", wr.reverse_word_only) + # To open new file + self.__thisFileMenu.add_command(label="New", command=self.__new_file) + + # To open a already existing file + self.__thisFileMenu.add_command(label="Open", command=self.__open_file) + + # To save current file + self.__thisFileMenu.add_command(label="Save", command=self.__save_file) + + # To create a line in the dialog + self.__thisFileMenu.add_separator() + self.__thisFileMenu.add_command(label="Exit", + command=self.__quit_application) + self.__thisMenuBar.add_cascade(label="File", menu=self.__thisFileMenu) + + # To give a feature of cut + self.__thisEditMenu.add_command(label="Cut", command=self.__cut) + + # to give a feature of copy + self.__thisEditMenu.add_command(label="Copy", command=self.__copy) + + # To give a feature of paste + self.__thisEditMenu.add_command(label="Paste", command=self.__paste) + + # to give a feature of reverse + self.__thisEditMenu.add_command(label="Reverse", command=self.__reverse) + + # To give a feature of editing + self.__thisMenuBar.add_cascade(label="Edit", menu=self.__thisEditMenu) + + self.__root.config(menu=self.__thisMenuBar) + + self.__thisScrollBar.pack(side=RIGHT, fill=Y) + + # Scrollbar will adjust automatically according to the content + self.__thisScrollBar.config(command=self.__thisTextArea.yview) + self.__thisTextArea.config(yscrollcommand=self.__thisScrollBar.set) + + def __quit_application(self): + self.__root.destroy() + + def __open_file(self): + + self.__file = askopenfilename( + defaultextension=".txt", + filetypes=[("All Files", "*.*"), ("Text Documents", "*.txt")], + ) + + if self.__file == "": + + # no file to open + self.__file = None + else: + + self.__root.title( + os.path.basename(self.__file) + " - Youthful-Yeomen") + self.__thisTextArea.delete(1.0, END) + + file = open(self.__file, "r") + reversed_file = file.read()[::-1] + + self.__thisTextArea.insert(1.0, reversed_file) + + file.close() + + def __new_file(self): + self.__root.title("Youthful-Yeomen : Untitled") + self.__file = None + self.__thisTextArea.delete(1.0, END) + + def __save_file(self): + + if self.__file is None: + # Save as new file + self.__file = asksaveasfilename( + initialfile="Untitled.txt", + defaultextension=".txt", + filetypes=[("All Files", "*.*"), ("Text Documents", "*.txt")], + ) + + if self.__file == "": + self.__file = None + else: + + # Try to save the file + file = open(self.__file, "w") + file.write(self.__thisTextArea.get(1.0, END)) + file.close() + + # Change the window title + self.__root.title(os.path.basename(self.__file) + " - Notepad") + + else: + file = open(self.__file, "w") + file.write(self.__thisTextArea.get(1.0, END)) + file.close() + + def __cut(self): + self.__thisTextArea.event_generate("<>") + + def __copy(self): + self.__thisTextArea.event_generate("<>") + + def __paste(self): + self.__thisTextArea.event_generate("<>") + + def __reverse(self): + try: + text = self.__thisTextArea.get('sel.first', 'sel.last') + except TclError: + showerror(title="Error", message="Please select text to reverse") + return + reversed_text = text[::-1] + self.__thisTextArea.insert('sel.first', reversed_text) + self.__thisTextArea.delete('sel.first', 'sel.last') + + def run(self): + self.__root.mainloop() diff --git a/project/reverse_writer.py b/project/reverse_writer.py new file mode 100644 index 00000000..13cb9cdb --- /dev/null +++ b/project/reverse_writer.py @@ -0,0 +1,34 @@ +from tkinter import INSERT + + +class WordReverser: + + def __init__(self): + self.last_idx = '0' + + def reverse_word_only(self, event): + widget = event.widget + current_insert_index = widget.index('insert') + current_line = current_insert_index.split('.')[0] + + # remove the char entered + widget.delete("%s-1c" % INSERT, INSERT) + + # handle the enter key + if event.char == '\r': + widget.insert('end', '\n') + self.last_idx = '0' + elif event.char == ' ': + # we got the word, set the new char index for next word + self.last_idx = current_insert_index.split('.')[1] + # finally put the char in reverse + widget.insert(f"{current_line}.{self.last_idx}", event.char) + + def reverse_words_with_line(self, event): + ta = event.widget + event.widget.delete("%s-1c" % INSERT, INSERT) + if event.char == '\r': + ta.insert('end', '\n') + else: + line = ta.index('insert').split('.')[0] + ta.insert(f"{line}.0", event.char)