Welcome folks today in this blog post we will be building a multiplayer people chat window ui widget
in tkinter
gui desktop app in python. All the full source code of the application is given below.
Get Started
In order to get started you need to install the below library using the pip
command as shown below
pip install tkinter
After installing this library you need to make an chatwindow.py
file and copy paste the following code
chatwindow.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
import os import tkinter as tk import tkinter.ttk as ttk from smilieselect import SmilieSelect class ChatWindow(tk.Toplevel): def __init__(self, master, friend_name, friend_avatar, **kwargs): super().__init__(**kwargs) self.master = master self.title(friend_name) self.geometry('540x640') self.minsize(540, 640) self.right_frame = tk.Frame(self) self.left_frame = tk.Frame(self) self.bottom_frame = tk.Frame(self.left_frame) self.messages_area = tk.Text(self.left_frame, bg="white", fg="black", wrap=tk.WORD, width=30) self.scrollbar = ttk.Scrollbar(self.left_frame, orient='vertical', command=self.messages_area.yview) self.messages_area.configure(yscrollcommand=self.scrollbar.set) self.text_area = tk.Text(self.bottom_frame, bg="white", fg="black", height=3, width=30) self.text_area.smilies = [] self.send_button = ttk.Button(self.bottom_frame, text="Send", command=self.send_message, style="send.TButton") self.smilies_image = tk.PhotoImage(file="smilies/mikulka-smile-cool.png") self.smilie_button = ttk.Button(self.bottom_frame, image=self.smilies_image, command=self.smilie_chooser, style="smilie.TButton") self.profile_picture = tk.PhotoImage(file="images/avatar.png") self.friend_profile_picture = tk.PhotoImage(file=friend_avatar) self.profile_picture_area = tk.Label(self.right_frame, image=self.profile_picture, relief=tk.RIDGE) self.friend_profile_picture_area = tk.Label(self.right_frame, image=self.friend_profile_picture, relief=tk.RIDGE) self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) self.scrollbar.pack(side=tk.LEFT, fill=tk.Y) self.messages_area.pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.messages_area.configure(state='disabled') self.right_frame.pack(side=tk.LEFT, fill=tk.Y) self.profile_picture_area.pack(side=tk.BOTTOM) self.friend_profile_picture_area.pack(side=tk.TOP) self.bottom_frame.pack(side=tk.BOTTOM, fill=tk.X) self.smilie_button.pack(side=tk.LEFT, pady=5) self.text_area.pack(side=tk.LEFT, fill=tk.X, expand=1, pady=5) self.send_button.pack(side=tk.RIGHT, pady=5) self.configure_styles() self.bind_events() def bind_events(self): self.bind("<Return>", self.send_message) self.text_area.bind("<Return>", self.send_message) self.text_area.bind('<Control-s>', self.smilie_chooser) def send_message(self, event=None): message = self.text_area.get(1.0, tk.END) if message.strip() or len(self.text_area.smilies): message = "Me: " + message self.messages_area.configure(state='normal') self.messages_area.insert(tk.END, message) if len(self.text_area.smilies): last_line_no = self.messages_area.index(tk.END) last_line_no = str(last_line_no).split('.')[0] last_line_no = str(int(last_line_no) - 2) for index, file in self.text_area.smilies: char_index = str(index).split('.')[1] char_index = str(int(char_index) + 4) smilile_index = last_line_no + '.' + char_index self.messages_area.image_create(smilile_index, image=file) self.text_area.smilies = [] self.messages_area.configure(state='disabled') self.text_area.delete(1.0, tk.END) return "break" def smilie_chooser(self, event=None): SmilieSelect(self) def add_smilie(self, smilie): smilie_index = self.text_area.index(self.text_area.image_create(tk.END, image=smilie)) self.text_area.smilies.append((smilie_index, smilie)) def receive_message(self, message, smilies): """ Writes message into messages_area :param message: message text :param smilies: list of tuples of (char_index, smilie_file), where char_index is the x index of the smilie's location and smilie_file is the file name only (no path) :return: None """ self.messages_area.configure(state='normal') self.messages_area.insert(tk.END, message) if len(smilies): last_line_no = self.messages_area.index(tk.END) last_line_no = str(last_line_no).split('.')[0] last_line_no = str(int(last_line_no) - 2) for index, file in smilies: smilie_path = os.path.join(SmilieSelect.smilies_dir, file) image = tk.PhotoImage(file=smilie_path) smilie_index = last_line_no + '.' + index self.messages_area.image_create(smilie_index, image=image) self.messages_area.configure(state='disabled') def configure_styles(self): style = ttk.Style() style.configure("send.TButton", background='#dddddd', foreground="black", padding=16) if __name__ == '__main__': w = tk.Tk() c = ChatWindow(w, 'friend', 'images/avatar.png') c.protocol("WM_DELETE_WINDOW", w.destroy) w.mainloop() |
friendslist.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
import tkinter as tk import tkinter.ttk as ttk from chatwindow import ChatWindow class FriendsList(tk.Tk): def __init__(self, **kwargs): super().__init__(**kwargs) self.title('Tk Chat') self.geometry('700x500') self.menu = tk.Menu(self, bg="lightgrey", fg="black", tearoff=0) self.friends_menu = tk.Menu(self.menu, fg="black", bg="lightgrey", tearoff=0) self.friends_menu.add_command(label="Add Friend", command=self.add_friend) self.menu.add_cascade(label="Friends", menu=self.friends_menu) self.configure(menu=self.menu) self.canvas = tk.Canvas(self, bg="white") self.canvas_frame = tk.Frame(self.canvas) self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side=tk.LEFT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, expand=1, fill=tk.BOTH) self.friends_area = self.canvas.create_window((0, 0), window=self.canvas_frame, anchor="nw") self.bind_events() self.load_friends() def bind_events(self): self.bind('<Configure>', self.on_frame_resized) self.canvas.bind('<Configure>', self.friends_width) def friends_width(self, event): canvas_width = event.width self.canvas.itemconfig(self.friends_area, width=canvas_width) def on_frame_resized(self, event=None): self.canvas.configure(scrollregion=self.canvas.bbox("all")) def load_friends(self): friend_frame = ttk.Frame(self.canvas_frame) profile_photo = tk.PhotoImage(file="images/avatar.png") profile_photo_label = ttk.Label(friend_frame, image=profile_photo) profile_photo_label.image = profile_photo friend_name = ttk.Label(friend_frame, text="Jaden Corebyn", anchor=tk.W) message_button = ttk.Button(friend_frame, text="Chat", command=self.open_chat_window) profile_photo_label.pack(side=tk.LEFT) friend_name.pack(side=tk.LEFT) message_button.pack(side=tk.RIGHT) friend_frame.pack(fill=tk.X, expand=1) def add_friend(self): self.load_friends() def open_chat_window(self): cw = ChatWindow(self, 'Jaden Corebyn', 'images/avatar.png') if __name__ == '__main__': f = FriendsList() f.mainloop() |
smilieselect.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
import os import tkinter as tk import tkinter.ttk as ttk class SmilieSelect(tk.Toplevel): smilies_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'smilies/')) def __init__(self, master, **kwargs): super().__init__(**kwargs) self.master = master self.transient(master) self.position_window() smilie_files = [file for file in os.listdir(self.smilies_dir) if file.endswith(".png")] self.smilie_images = [] for file in smilie_files: full_path = os.path.join(self.smilies_dir, file) image = tk.PhotoImage(file=full_path) self.smilie_images.append(image) for index, file in enumerate(self.smilie_images): row, col = divmod(index, 3) button = ttk.Button(self, image=file, command=lambda s=file: self.insert_smilie(s)) button.grid(row=row, column=col, sticky='nsew') for i in range(3): tk.Grid.columnconfigure(self, i, weight=1) tk.Grid.rowconfigure(self, i, weight=1) def position_window(self): master_pos_x = self.master.winfo_x() master_pos_y = self.master.winfo_y() master_width = self.master.winfo_width() master_height = self.master.winfo_height() my_width = 100 my_height = 100 pos_x = (master_pos_x + (master_width // 2)) - (my_width // 2) pos_y = (master_pos_y + (master_height // 2)) - (my_height // 2) geometry = f"{my_width}x{my_height}+{pos_x}+{pos_y}" self.geometry(geometry) def insert_smilie(self, smilie): self.master.add_smilie(smilie) self.destroy() if __name__ == '__main__': w = tk.Tk() s = SmilieSelect(w) w.mainloop() |
If you execute the above python script
by typing the below command
python chatwindow.py