class ChatText(Text): def __init__(self, master, **kwargs): self.master = Frame(master) self.font = Font(family="Helvetica", size=DEFAULT_FONT_SIZE) self.font_bold = Font(family="Helvetica", size=DEFAULT_FONT_SIZE, weight=BOLD) Text.__init__(self, self.master, font=self.font, state=DISABLED, **kwargs) self.scrollbar = Scrollbar(self.master, orient=VERTICAL, command=self.yview) self.search_frame = SearchFrame(self.master) self.configure(yscrollcommand=self.scrollbar.set) self.images: dict = {} self.declare_tags() def declare_tags(self): for color in default_colors: self.tag_configure(color, foreground=color, font=self.font_bold) self.tag_configure('search_result', background='lightgreen') def pack(self, **kwargs): self.master.pack(**kwargs) self.search_frame.pack(side=BOTTOM, fill=X) self.scrollbar.pack(side=RIGHT, fill=Y) Text.pack(self, side=LEFT, fill=BOTH, expand=True) def repack(self): self.search_frame.pack_forget() self.scrollbar.pack_forget() Text.pack_forget(self) self.search_frame.pack(side=BOTTOM, fill=X) self.scrollbar.pack(side=RIGHT, fill=Y) Text.pack(self, side=LEFT, fill=BOTH, expand=True) def append_message(self, username: str, fragments: list, color: str, autoscroll: bool = True): self.configure(state=NORMAL) if color not in default_colors: self.tag_configure(color, foreground=color, font=self.font_bold) self.insert(END, username, color) self.insert(END, ' : ') for fragment in fragments: if 'emoticon' in fragment: emote_id = fragment.get('emoticon').get('emoticon_id') image = self.images.get(emote_id) if image is None: image_path = get_emote(emote_id) image = ImageTk.PhotoImage(Image.open(image_path)) self.images[emote_id] = image self.image_create(END, image=image, padx=2, pady=2) else: self.insert(END, fragment.get('text'), fragment.get('tag')) if autoscroll: self.yview_moveto(1.0) self.insert(END, '\n') self.configure(state=DISABLED) def clear(self): self.configure(state=NORMAL) self.delete(1.0, END) self.configure(state=DISABLED)
class SSNI: def __init__(self): self.file_name = 'video_file.txt' self.master = Tk() self.master.withdraw() self.master.title('SSNI') self.master.iconbitmap(self.resource_path('included_files\\icon.ico')) self.first_left_frame = Frame(self.master) self.video_entry = Entry(self.first_left_frame, fg='grey', font=('Courier', 12), width=19, justify='center', highlightthickness=2, highlightbackground='grey') self.video_entry.insert(END, 'Video Name') self.video_entry.pack(pady=10, padx=10, ipady=3) self.add_button = Button( self.first_left_frame, text='ADD', bg='green', fg='white', activebackground='green', activeforeground='white', cursor='hand2', command=lambda: self.add_remove_search_command(button_name='ADD')) self.add_button.pack(pady=5, ipady=3, ipadx=80) self.remove_button = Button( self.first_left_frame, text='REMOVE', bg='green', fg='white', activebackground='green', activeforeground='white', cursor='hand2', command=lambda: self.add_remove_search_command(button_name='REMOVE' )) self.remove_button.pack(pady=5, ipady=3, ipadx=70) self.search_button = Button( self.first_left_frame, text='SEARCH', bg='green', fg='white', activebackground='green', activeforeground='white', cursor='hand2', command=lambda: self.add_remove_search_command(button_name='SEARCH' )) self.search_button.pack(pady=5, ipady=3, ipadx=70) self.rename_window_button = Button(self.first_left_frame, text='RENAME', bg='green', fg='white', activebackground='green', activeforeground='white', cursor='hand2', command=self.rename_window) self.rename_window_button.pack(pady=5, ipady=3, ipadx=69) self.first_left_frame.pack(padx=5, side=LEFT, ipady=2) self.text_area_frame = Frame(self.master) self.text_area = Text(self.text_area_frame, width=27, height=13, state=DISABLED, cursor='arrow') self.scrollbar = Scrollbar(self.text_area_frame, orient="vertical", command=self.text_area.yview) self.text_area.pack(side=LEFT, ipady=1) self.text_area_frame.pack(pady=5, padx=4, anchor='w') self.show_scrollbar() self.text_area.config(state=NORMAL) self.text_area.delete('1.0', END) self.text_area_frame.pack(padx=5, pady=5, side=LEFT) self.master.after(0, self.insert_text_area) self.rename_window_button.bind('<Return>', lambda e: self.rename_window()) self.add_button.bind( '<Return>', lambda e: self.add_remove_search_command(button_name='ADD')) self.remove_button.bind( '<Return>', lambda e: self.add_remove_search_command(button_name='REMOVE')) self.search_button.bind( '<Return>', lambda e: self.add_remove_search_command(button_name='SEARCH')) # Rename_window widgets self.rename_window_frame = Frame(self.master) self.old_name_entry = Entry(self.rename_window_frame, fg='grey', font=('Courier', 12), width=19, justify='center', highlightthickness=2, highlightbackground='grey') self.old_name_entry.insert(END, 'Old Name') self.new_name_entry = Entry(self.rename_window_frame, fg='grey', font=('Courier', 12), width=19, justify='center', highlightthickness=2, highlightbackground='grey') self.new_name_entry.insert(END, 'New Name') self.rename_button = Button(self.rename_window_frame, text='RENAME', bg='green', fg='white', activebackground='green', activeforeground='white', cursor='hand2', command=self.rename_command) self.back_button = Button(self.rename_window_frame, text='BACK', bd=0, fg='blue', font=('Courier', 15, 'bold'), cursor='hand2', command=self.back_command, activeforeground='blue') self.master.bind('<Button-1>', self.key_bindings) self.back_button.bind('<Return>', self.back_command) self.video_entry.bind('<FocusIn>', self.key_bindings) self.old_name_entry.bind('<FocusIn>', self.key_bindings) self.new_name_entry.bind('<FocusIn>', self.key_bindings) self.rename_button.bind('<Return>', self.rename_command) self.master.after(0, self.center_window) self.master.bind_class( 'Button', '<FocusIn>', lambda event, focus_out=True: self.key_bindings(event, focus_out)) self.master.mainloop() def center_window(self): '''Set initial position of the window to the center of the screen''' self.master.update() self.master.resizable(0, 0) screen_width, screen_height = self.master.winfo_screenwidth( ) // 2, self.master.winfo_screenheight() // 2 width, height = self.master.winfo_width(), self.master.winfo_height() self.master.geometry( f'{width}x{height}+{screen_width - width // 2}+{screen_height - height // 2}' ) self.master.deiconify() def key_bindings(self, event, focus_out=False): '''When user clicks in and out of the entry boxes''' widget = event.widget widgets = { self.video_entry: 'Video Name', self.old_name_entry: 'Old Name', self.new_name_entry: 'New Name' } if widget in widgets and widget.get().strip() == widgets[widget]: widget.delete(0, END) widget.config(fg='black') if widget == self.new_name_entry and not self.old_name_entry.get( ).strip(): self.old_name_entry.delete(0, END) self.old_name_entry.config(fg='grey') self.old_name_entry.insert(END, widgets[self.old_name_entry]) elif widget not in widgets or focus_out: for _widget in widgets: if not _widget.get().strip(): _widget.delete(0, END) _widget.config(fg='grey') _widget.insert(END, widgets[_widget]) if widget in [ self.master, self.rename_window_frame, self.first_left_frame ]: self.master.focus() def rename_window(self): '''Rename window when user clicks rename button''' self.first_left_frame.pack_forget() self.text_area_frame.pack_forget() self.old_name_entry.pack(pady=10, padx=10, ipady=3) self.new_name_entry.pack(pady=10, padx=10, ipady=3) self.rename_button.pack(pady=15, ipady=3, ipadx=70) self.back_button.pack() self.rename_window_frame.pack(padx=5, side=LEFT, ipady=2) self.text_area_frame.pack(pady=15, padx=5, anchor='w') def back_command(self, event=None): '''Command when user clicks back button''' self.text_area_frame.pack_forget() self.rename_window_frame.pack_forget() self.first_left_frame.pack(padx=5, side=LEFT, ipady=2) self.text_area_frame.pack(pady=15, padx=5, anchor='w') self.back_button.pack_forget() def show_scrollbar(self): '''Show scrollbar when the character in the text is more than the height of the text widget''' if self.text_area.cget('height') < int( self.text_area.index('end-1c').split('.')[0]): self.scrollbar.pack(side=LEFT, fill='y') self.text_area.config(yscrollcommand=self.scrollbar.set) self.master.after(100, self.hide_scrollbar) else: self.master.after(100, self.show_scrollbar) def hide_scrollbar(self): '''Hide scrollbar when the character in the text is less than the height of the text widget''' if self.text_area.cget('height') >= int( self.text_area.index('end-1c').split('.')[0]): self.scrollbar.pack_forget() self.text_area.config(yscrollcommand=None) self.master.after(100, self.show_scrollbar) def read_file(self): '''Getting everything from file''' if os.path.exists(self.file_name): with open(self.file_name, 'r') as f: return [line.strip('\n') for line in f.readlines()] else: with open(self.file_name, 'w'): pass def sort_file(self): '''Sorting contents of file according to length in ascending order''' contents = self.read_file() contents.sort(key=len) with open(self.file_name, 'w') as f: for content in contents: f.write(f'{content}\n') def insert_text_area(self): '''Insert contents of file in Text widget''' contents = self.read_file() self.sort_file() self.text_area.config(state=NORMAL) if contents: self.text_area.delete('1.0', END) self.text_area.config(fg='black') for index, content in enumerate(contents): if index == 0: self.text_area.insert(END, content) else: self.text_area.insert(END, '\n' + content) else: self.text_area.delete('1.0', END) self.text_area.config(fg='grey') self.text_area.insert('1.0', 'No data yet.') self.text_area.config(state=DISABLED) def add_remove_search_command(self, button_name): '''Commands when user clicks either ADD, REMOVE or SEARCH buttons''' contents = self.read_file() from_entry = self.video_entry.get().strip().title() if not from_entry or from_entry == 'Video Name': messagebox.showerror('Invalid Name', 'No Name given') elif button_name == 'ADD': if from_entry in contents: messagebox.showinfo('Already Exists', f'"{from_entry}" is already in file') else: with open(self.file_name, 'a') as f: f.write(f'{from_entry}\n') messagebox.showinfo('Value Added', f'"{from_entry}" added in file') elif button_name == 'REMOVE': if from_entry in contents: with open(self.file_name, 'w') as f: for content in contents: if from_entry != content: f.write(f'{content}\n') messagebox.showinfo('Value Removed', f'"{from_entry}" reomved from file') else: option = messagebox.askyesno( 'Add Value?', f'"{from_entry}" not in file. Do you want to add it?') if option: self.action_button_command('ADD') elif button_name == 'SEARCH': if from_entry in contents: messagebox.showinfo('Exists', f'"{from_entry}" is in file') else: option = messagebox.askyesno( 'Add Value?', f'"{from_entry}" not in file. Do you want to add it?') if option: self.action_button_command('ADD') self.sort_file() self.insert_text_area() self.video_entry.delete(0, END) self.video_entry.insert(END, 'Video Name') self.video_entry.config(fg='grey') self.master.focus() def rename_command(self, event=None): '''Commands when user clicks RENAME button''' contents = self.read_file() old_name = self.old_name_entry.get().strip().title() new_name = self.new_name_entry.get().strip().title() if not old_name or not new_name or old_name == 'Old Name' or new_name == 'New Name': messagebox.showerror('Invalid Video Name', 'The input video name is invlaid') elif old_name not in contents: messagebox.showerror('Not exists', 'Old Name Not found') elif new_name in contents: messagebox.showerror( 'Already Exists', 'New Name already exists in file. Try another!') else: old_name_index = contents.index(old_name) contents[old_name_index] = new_name with open(self.file_name, 'w') as f: for content in contents: f.write(f'{content}\n') self.insert_text_area() widgets = { self.old_name_entry: 'Old Name', self.new_name_entry: 'New Name' } for widget, text in widgets.items(): widget.delete(0, END) widget.insert(END, text) widget.config(fg='grey') self.master.focus() messagebox.showinfo('Renamed', f'{old_name} renamed to {new_name}') def resource_path(self, relative_path): '''Get absolute path to resource from temporary directory In development: Gets path of files that are used in this script like icons, images or file of any extension from current directory After compiling to .exe with pyinstaller and using --add-data flag: Gets path of files that are used in this script like icons, images or file of any extension from temporary directory''' try: base_path = sys._MEIPASS # PyInstaller creates a temporary directory and stores path of that directory in _MEIPASS. except AttributeError: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path)