class Modal(Window): MODAL_WIDTH = 640 MODAL_HEIGHT = 480 def __init__(self, root, title, buttons: dict = None, width=MODAL_WIDTH, height=MODAL_HEIGHT, **args): Window.__init__(self, root, title, width, height, **args) # Header self.frm_header = Frame(self, bg=white, padx=20, pady=20) self.frm_header.pack(side=TOP, fill=X) self.lbl_title = Label(self.frm_header, text=title, bg=white, fg=black, font='-size 20 -weight bold') self.lbl_title.pack(side=LEFT) self.btn_close = IconFrame(self.frm_header, 'resources/icons/ui/cancel.png', 10, black, 50, lambda e: self.destroy(), danger) self.btn_close.pack(side=RIGHT) self.frm_header.update() # Header Bottom Border self.frm_border_bottom = Frame(self, highlightthickness=1, highlightbackground=border) self.frm_border_bottom.pack(side=TOP, fill=X) # Body self.frm_body = Frame(self, bg=background, padx=30, pady=30) self.frm_body.pack(side=TOP, fill=BOTH) # Footer self.frm_footer = Frame(self, bg=silver, padx=15, pady=15) self.frm_footer.pack(side=BOTTOM, fill=X) if buttons != None: for button in buttons: btn = (DangerButton if button.get('mode', 'main') == 'danger' else MainButton)(self.frm_footer, button.get('text', 'Text'), button.get('icon', 'error.png'), button.get('cmnd', None)) btn.pack(side=RIGHT, padx=(10, 0)) # Footer Bottom Border self.frm_border_top = Frame(self, highlightthickness=1, highlightbackground=border) self.frm_border_top.pack(side=BOTTOM, fill=X) self.grab_set_global() self.resizable(False, False)
class TextBox(Frame, Animatable): def __init__(self, master, iconPath, **args): Frame.__init__(self, master, **args) self.configure(bg=black, pady=3) self.icon = IconFrame(self, iconPath, 15, black, 40, bg=black) self.icon.pack(side=LEFT) self.entry = Entry(self, relief=FLAT, font='-size 12 -weight bold', bd=5, fg=black) self.entry.pack(side=LEFT, fill=BOTH, expand=1, padx=(0, 3)) Animatable.__init__(self) def get_text(self): return str(self.entry.get()).lstrip(' ').rstrip(' ') def clear(self): self.entry.delete(0, len(self.entry.get())) def bind_events(self): self.entry.bind('<FocusIn>', lambda e: self.onEnter()) self.entry.bind('<FocusOut>', lambda e: self.onLeave()) def changeFocusColor(self, color): self.stop_transitions() def _set(v): self['bg'] = v _get = lambda: self['bg'] def _set1(v): self.icon['bg'] = v _get1 = lambda: self.icon['bg'] def _set2(v): self.entry['fg'] = v _get2 = lambda: self.entry['fg'] self.icon.set_bgColor(color) self.save_transition(ColorTransition(_set, _get, color)) self.save_transition(ColorTransition(_set1, _get1, color)) self.save_transition(ColorTransition(_set2, _get2, color)) def onEnter(self): self.changeFocusColor(teal) def onLeave(self): self.changeFocusColor(black)
def __init__(self, root, title: str, message: str, messageType: str = 'error', actions: dict = None, **args): # Prepare modal buttons buttons = [{ 'text': 'Okay', 'icon': 'yes.png', 'cmnd': lambda e: self.destroy() }] if actions != None: buttons = [{ 'text': 'No', 'icon': 'no.png', 'cmnd': actions.get('no', lambda e: self.destroy()), 'mode': 'danger' }, { 'text': 'Yes', 'icon': 'yes.png', 'cmnd': actions.get('yes', None) }] Modal.__init__(self, root, title, buttons, self.MODAL_WIDTH, 345, **args) color = black if messageType == self.PROMPT else ( teal if messageType == self.INFO else danger) img_icon = IconFrame(self.frm_body, 'resources/icons/ui/' + messageType + '.png', 6, color, 75) img_icon.pack(side=LEFT) lbl_message = Label(self.frm_body, text=message, bg=background, fg=color, font='-size 15 -weight bold', justify=LEFT, wraplength=self.MODAL_WIDTH - 150) lbl_message.pack(side=LEFT, padx=(10, 0))
class ListItem(Frame): def __init__(self, master, dataObject, bindings, buttons, creationMethod = None, **args): Frame.__init__(self, master, **args) self.buttons = [] self.dataObject = dataObject self.bindings = bindings self.buttonSettings = buttons self.creationMethod = creationMethod (ListItem.create if creationMethod == None else creationMethod)(self) def create(item): self = item # binding values username = '******' if self.bindings == None else self.bindings.get('username', '{username}') self.configure(padx=10, pady=10, relief=SOLID, highlightthickness=1, highlightbackground=border, bg=white) self.img_icon = IconFrame(self, self.bindings.get('image') if self.bindings.get('image') != None else 'resources/icons/ui/face.png', 0, black, 40, highlightbackground=border, highlightthickness=1) self.img_icon.pack(side=LEFT) self.lbl_username = Label(self, text=username, bg=white, font='-size 12 -weight bold', fg=black) self.lbl_username.pack(side=LEFT, fill=Y, padx=5) def correct(call, obj): return lambda e: call(obj) if self.buttonSettings != None: for s in self.buttonSettings: path = 'resources/icons/ui/' + s.get('icon', 'error.png') btn = IconButton(self, s.get('text', 'Button Text'), '-size 10 -weight bold', teal, path, 15, {'fg': white, 'bg': teal}, teal, 30, correct(s.get('cmd', None), self.dataObject), bg=white, highlightthickness=1, highlightbackground=border, padx=7, pady=5) btn.pack(side=RIGHT, padx=(0, 5)) self.buttons.append(btn)
class SessionWindow(Window): # Chat Session Item Styles CHAT_NORMAL = { 'bg': white, 'lbl_username': teal, 'lbl_user': black, 'lbl_content': black, 'lbl_time': gray } CHAT_UNREAD = { 'bg': teal, 'lbl_username': white, 'lbl_user': background, 'lbl_content': background, 'lbl_time': background } # BOOKMARK_TOCHANGE: make it None ACTIVE_USER = None def __init__(self, root, title='Welcome', width=Window.DEFAULT_WIDTH, height=Window.DEFAULT_HEIGHT, **args): Window.__init__(self, root, title, width, height) if SessionWindow.ACTIVE_USER == None: SessionWindow.ACTIVE_USER = args.get('user', None) self.frm_rSection = Frame(self, bg=background) self.frm_rSection.pack_propagate(0) self.frm_vBar = Frame(self, bg=black, padx=15, pady=15) self.frm_hBar = Frame(self.frm_rSection, bg=white, padx=20, pady=20) self.frm_hBarBorder = Frame(self.frm_rSection, height=1, bg=border) self.frm_body = Frame(self.frm_rSection, bg=background, padx=30, pady=30) self.frm_body.pack_propagate(0) self.frm_body.grid_propagate(0) self.frm_rSection.pack(side=RIGHT, fill=BOTH, expand=1) self.frm_vBar.pack(side=LEFT, fill=Y) self.frm_hBar.pack(side=TOP, fill=X) self.frm_hBarBorder.pack(side=TOP, fill=X) self.frm_body.pack(side=BOTTOM, expand=1, fill=BOTH) # vbar buttons self.vBarButtons = {} # Set up the bars self.config_vBar() self.config_hBar() self.created_notif_items = [] def runnable(self): try: while self.time_to_kill != True: Container.threadSession.flush() # declaration of noNewNotifs, noNewMessages = True, True # check for new unseen notifications unseen_notifs = Container.threadSafeFilter(Notification,Notification.recipient == SessionWindow.ACTIVE_USER, Notification.id.notin_(Container.threadSafeFilter(SeenNotification.notificationId.distinct()))).all() for notif in unseen_notifs: if Container.threadSafeFilter(SeenNotification, SeenNotification.notificationId == notif.id, SeenNotification.seerId == SessionWindow.ACTIVE_USER.id).first() == None: self.icn_notification.set_image('resources/icons/ui/bell_ring.png') self.lbl_notif_counter['fg'] = teal self.lbl_notif_counter['text'] = str(len(unseen_notifs)) noNewNotifs = False break # change the icon if there is no new notifications if noNewNotifs: self.icn_notification.set_image('resources/icons/ui/bell_outline.png') self.lbl_notif_counter['text'] = '0' self.lbl_notif_counter['fg'] = black # check for new unseen messages if len (self.getUnreadMessages()) > 0: self.icn_discussion.set_image('resources/icons/ui/discussion.png') self.lbl_discus_counter['fg'] = teal self.lbl_discus_counter['text'] = str (len (self.getUnreadMessages())) noNewMessages = False # change the icon if there is no new messages if noNewMessages: self.icn_discussion.set_image('resources/icons/ui/discussion_outline.png') self.lbl_discus_counter['text'] = '0' self.lbl_discus_counter['fg'] = black # wait for the next frame time.sleep(5) except: pass def hide(self): # thread killer logic will be here self.time_to_kill = True # continute the original work super().hide() def refresh(self): # this should run self.time_to_kill = False # start thread threading.Thread(target=self.runnable).start() def change_session_item_style(self, item, style=CHAT_UNREAD): # Change background for w in [item, item.frm_content, item.lbl_username, item.lbl_user, item.lbl_content, item.lbl_time, item.img_photo]: w.config(bg=style['bg']) # Change foreground for i in style.keys(): if hasattr(item, i): getattr(item, i).config(fg=style[i]) def configure_msg_listitem(self, root, item): li = ListItemFactory.DiscussionListItem(root, item) # set message listItem click for c in [li, li.frm_content, li.lbl_username, li.lbl_user, li.lbl_content, li.lbl_time, li.img_photo]: c.bind('<Button-1>', lambda e: self.windowManager.run_tag('discussion',session= item.session)) # change style for unread messages if item.user != self.ACTIVE_USER and Container.filter(SeenMessage, SeenMessage.messageId == item.id,SeenMessage.seerId == SessionWindow.ACTIVE_USER.id).first() == None: self.change_session_item_style(li,self.CHAT_UNREAD) # return list item return li def configure_notif_listitem(self, root, item): li= ListItemFactory.NotificationListItem(root, item) li.window = self # save notification that are not recieveInv as seen if item.type != NotificationType.INVITED.value and Container.filter(SeenNotification, SeenNotification.notificationId == item.id, SeenNotification.seerId == SessionWindow.ACTIVE_USER.id).first() == None: Container.save(SeenNotification(date= datetime.datetime.now(), seer= SessionWindow.ACTIVE_USER, notification= item)) # append this item to a list self.created_notif_items.append (li) # return return li def clean_notifications(self): notifs = Container.filter(Notification) for notif in notifs: if notif.nature == NotificationNature.INV.value and Container.filter(Invitation, Invitation.id == notif.invitationId).first() == None: Container.deleteObject(notif) elif notif.nature == NotificationNature.INVLINK.value: invitationLink = Container.filter(InvitationLink, InvitationLink.id == notif.invitationId).first() if invitationLink == None or Container.filter(Collaboration, Collaboration.sessionId == invitationLink.sessionId, Collaboration.userId == notif.actorId).first() == None: Container.deleteObject(notif) elif notif.nature == NotificationNature.SHARELINK.value and Container.filter(ShareLink, ShareLink.id == notif.invitationId).first() == None: Container.deleteObject(notif) def config_vBar(self): def callback(tag): return lambda e: self.windowManager.run_tag(tag) for i in SessionWindow.vBarSettings: # retrieve callable cb = callback(i.get('dest')) # instantiate button btn = IconButton(self.frm_vBar, i.get('text', 'Icon Button'), '-size 11 -weight bold', white, f'resources/icons/ui/{i["icon"]}', 0, None, black, 32, cb, bg=black, pady=10) btn.label.pack_forget() btn.pack(side=i.get('dock', TOP), fill=X) # save button self.vBarButtons[i.get('name')] = btn def quit(): self.time_to_kill = True self.windowManager.run_tag('sign') SessionWindow.ACTIVE_USER = None # self.windowManager.quit() self.vBarButtons['btn_quit'].bind_click(lambda e: quit() ) def getLastMessages(self): msgs = [] for i in Container.filter(Session): if i.owner == SessionWindow.ACTIVE_USER or Container.filter(Collaboration, Collaboration.userId == SessionWindow.ACTIVE_USER.id, Collaboration.sessionId == i.id).first() != None: msg = Container.filter(Message, Message.sessionId == i.id).order_by(Message.sentDate.desc()).first() if msg != None: msgs.append(msg) # make the order key def orderKey (e): return e.sentDate # sort msgs.sort(key=orderKey, reverse=True) # return return msgs def getUnreadMessages(self): # first we will get the sessions that this user # has collaborated in sessions = [] # search for collaborations for c in Container.filter(Collaboration, Collaboration.userId == SessionWindow.ACTIVE_USER.id): sessions.append(c.sessionId) # search for owned sessions for s in Container.filter(Session, Session.ownerId == SessionWindow.ACTIVE_USER.id): sessions.append(s.id) # retrieve all received messages allmsgs = [] for s in sessions: for m in Container.filter(Message, Message.sessionId == s): if m not in allmsgs and m != None: allmsgs.append(m) # retrieve seen messages seenmsgs = [] for m in allmsgs: if m.userId == SessionWindow.ACTIVE_USER.id: seenmsgs.append(m) elif len(Container.filter(SeenMessage, SeenMessage.messageId == m.id, SeenMessage.seerId == SessionWindow.ACTIVE_USER.id).all()) > 0: seenmsgs.append(m) # retrieve unseen messages unseenmsgs = [] for m in allmsgs: if m not in seenmsgs: unseenmsgs.append(m) # return return unseenmsgs def config_hBar(self): # Creation of elements # BOOKMARK_DONE: change user profile image self.clean_notifications() # username self.btn_username = IconButton(self.frm_hBar, SessionWindow.ACTIVE_USER.userName, '-size 15', biege, 'resources/icons/ui/face.png' if SessionWindow.ACTIVE_USER.image == None else SessionWindow.ACTIVE_USER.image, 6, None, biege, 40, lambda e: self.windowManager.run_tag('profile'), bg=white) # bell icon self.icn_notification = IconFrame( self.frm_hBar, 'resources/icons/ui/bell_outline.png', 0, None, 32, lambda e: self.show_popup( self.to_window_coords(e.x_root, e.y_root)[0] - 360, self.to_window_coords(e.x_root, e.y_root)[1] + 20, # BOOKMARK_DONE: notification data list Container.filter(Notification,Notification.recipient == SessionWindow.ACTIVE_USER, Notification.id.notin_(Container.filter(SeenNotification.notificationId.distinct(), SeenNotification.notificationId == Notification.id, Notification.type == NotificationType.INVITED.value))).all(), self.configure_notif_listitem ) ) # discussion icon self.icn_discussion = IconFrame( self.frm_hBar, 'resources/icons/ui/discussion_outline.png', 0, None, 32, lambda e: self.show_popup( self.to_window_coords(e.x_root, e.y_root)[0] - 360, self.to_window_coords(e.x_root, e.y_root)[1] + 20, # BOOKMARK_DONE: discussion data list # Container.filter(Message,Message.sessionId == Collaboration.sessionId,or_(Collaboration.userId == SessionWindow.ACTIVE_USER.id, and_(Message.sessionId == Session.id, Session.ownerId == SessionWindow.ACTIVE_USER.id) ),Message.sentDate.in_(Container.filter(func.max(Message.sentDate)).group_by(Message.sessionId))).group_by(Message.sessionId).all(), self.getLastMessages(), self.configure_msg_listitem ) ) # notification counter label self.lbl_notif_counter = Label(self.frm_hBar, fg=black, bg=white, text='0', font='-weight bold -size 9') self.lbl_discus_counter = Label(self.frm_hBar, fg=black, bg=white, text='0', font='-weight bold -size 9') # Positioning elements self.btn_username.pack(side=LEFT) self.lbl_notif_counter.pack(side=RIGHT) self.icn_notification.pack(side=RIGHT) self.lbl_discus_counter.pack(side=RIGHT) self.icn_discussion.pack(side=RIGHT, padx=(0, 5)) # Vertical Bar Settings vBarSettings = [ { 'name': 'btn_home', 'icon': 'home.png', 'text': 'Home', 'dest': 'home' }, { 'name': 'btn_discussion', 'icon': 'discussion_original.png', 'text': 'Discussions', 'dest': 'discussion' }, { 'name': 'btn_profile', 'icon': 'settings.png', 'text': 'Settings', 'dest': 'profile' }, { 'name': 'btn_quit', 'icon': 'logout.png', 'text': 'Sign Out', 'dest': None, 'dock': BOTTOM } ]
class IconButton(Frame, Animatable): def __init__(self, master, text, textFont, textColor, imagePath, imageScale, hoverEff: dict, imgBgColor=None, imgSize=50, btnCommand=None, **args): Frame.__init__(self, master, **args) Animatable.__init__(self) self.hover_theme = hoverEff # Create the icon self.icon = IconFrame(self, imagePath, imageScale, imgBgColor, imgSize) self.icon.pack(side=LEFT) # Create the label self.label = Label(self, text=text, font=textFont, fg=textColor, bg=self['bg']) self.label.pack(side=LEFT, padx=5) # Save the initial theme self.theme = {'bg': self['bg'], 'fg': textColor} # Bind click event if btnCommand != None: self.bind_click(btnCommand) def bind_click(self, command): def prepareCommand(e): # Invoke command command(e) self.bind('<Button-1>', prepareCommand) self.label.bind('<Button-1>', prepareCommand) self.icon.bind('<Button-1>', prepareCommand) def onEnter(self): if self.hover_theme == None: return Animatable.onEnter(self) def set1(v): self['bg'] = v def set2(v): self.label['bg'] = v def set3(v): self.icon['bg'] = v def set4(v): self.label['fg'] = v get1 = lambda: self['bg'] get2 = lambda: self.label['bg'] get3 = lambda: self.icon['bg'] get4 = lambda: self.label['fg'] self.save_transition( ColorTransition(set1, get1, self.hover_theme['bg'])) self.save_transition( ColorTransition(set2, get2, self.hover_theme['bg'])) self.save_transition( ColorTransition(set3, get3, self.hover_theme['bg'])) self.save_transition( ColorTransition(set4, get4, self.hover_theme['fg'])) if 'config' in self.hover_theme: self.configure(**self.hover_theme['config']) def onLeave(self): if self.hover_theme == None: return self.temp_theme = self.hover_theme self.hover_theme = self.theme self.onEnter() self.hover_theme = self.temp_theme