Diff to Previous Revision
--- revision 2 2017-02-23 22:46:27
+++ revision 3 2017-02-23 22:47:16
@@ -1,898 +1,257 @@
-import functools
-from collections import defaultdict
-
-from chatbox import Chatbox, User_Message, Notification_Message, Notification_Of_Private_Message
-from ordered_listbox import Tagged_and_Ordered_Dictbox
+# Author: Miguel Martinez Lopez
+# Uncomment the next line to see my email
+# print("Author's email: %s"%"61706c69636163696f6e616d656469646140676d61696c2e636f6d".decode("hex"))
+
+import datetime
+import collections
try:
- from Tkinter import StringVar, Text, Frame, Button, PanedWindow, Scrollbar, Label, Entry, Menu, TclError
+ from Tkinter import StringVar, Text, Frame, PanedWindow, Scrollbar, Label, Entry
from Tkconstants import *
import ttk
- import tkFont
except ImportError:
- from tkinter import StringVar, Text, Frame, Button, PanedWindow, Scrollbar, Label, Entry, Menu, TclError
+ from tkinter import StringVar, Text, Frame, PanedWindow, Scrollbar, Label, Entry
from tkinter.constants import *
import tkinter.ttk as ttk
- from tkinter import font as tkFont
-
-
-def autoscroll(sbar, first, last):
- """Hide and show scrollbar as needed."""
- first, last = float(first), float(last)
- if first <= 0 and last >= 1:
- sbar.grid_remove()
- else:
- sbar.grid()
- sbar.set(first, last)
-
-def to_tkFont(font_spec):
- if isinstance(font_spec, tkFont.Font):
- return font_spec
-
- font_spec_length = len(font_spec)
+
+User_Message = collections.namedtuple('User_Message', 'nick content')
+Notification_Message = collections.namedtuple('Notification_Message', 'content tag')
+Notification_Message.__new__.__defaults__ = ('notification',)
+
+Notification_Of_Private_Message = collections.namedtuple('Notification_Message', 'content from_ to')
+
+# TODO: Add frame topic
+class Chatbox(object):
+ def __init__(self, master, my_nick=None, command=None, topic=None, entry_controls=None, maximum_lines=None, timestamp_template=None, scrollbar_background=None, scrollbar_troughcolor=None, history_background=None, history_font=None, history_padx=None, history_pady=None, history_width=None, entry_font=None, entry_background=None, entry_foreground=None, label_template=u"{nick}", label_font=None, logging_file=None, tags=None):
+ self.interior = Frame(master, class_="Chatbox")
+
+ self._command = command
+
+ self._is_empty = True
+
+ self._maximum_lines = maximum_lines
+ self._timestamp_template = timestamp_template
+
+ self._command = command
+
+ self._label_template = label_template
+
+ self._logging_file = logging_file
+
+ if logging_file is None:
+ self._log = None
+ else:
+ try:
+ self._log = open(logging_file, "r")
+ except:
+ self._log = None
+
+ top_frame = Frame(self.interior, class_="Top")
+ top_frame.pack(expand=True, fill=BOTH)
+
+ self._textarea = Text(top_frame, state=DISABLED)
+
+ self._vsb = Scrollbar(top_frame, takefocus=0, command=self._textarea.yview)
+ self._vsb.pack(side=RIGHT, fill=Y)
+
+ self._textarea.pack(side=RIGHT, expand=YES, fill=BOTH)
+ self._textarea["yscrollcommand"]=self._vsb.set
+
+ entry_frame = Frame(self.interior, class_="Chatbox_Entry")
+ entry_frame.pack(fill=X, anchor=N)
+
+ if entry_controls is not None:
+ controls_frame = Frame(entry_frame, class_="Controls")
+ controls_frame.pack(fill=X)
+ entry_controls(controls_frame, chatbox=self)
+
+ bottom_of_entry_frame = Frame(entry_frame)
+ self._entry_label = Label(bottom_of_entry_frame)
+ self._entry = Entry(bottom_of_entry_frame)
+ else:
+ self._entry_label = Label(entry_frame)
+ self._entry = Entry(entry_frame)
+
+ self._entry.pack(side=LEFT, expand=YES, fill = X)
+ self._entry.bind("<Return>", self._on_message_sent)
+
+ self._entry.focus()
+
+ if history_background:
+ self._textarea.configure(background=history_background)
+
+ if history_font:
+ self._textarea.configure(font=history_font)
+
+ if history_padx:
+ self._textarea.configure(padx=history_padx)
+
+ if history_width:
+ self._textarea.configure(width=history_width)
+
+ if history_pady:
+ self._textarea.configure(pady=history_pady)
+
+ if scrollbar_background:
+ self._vsb.configure(background = scrollbar_background)
+
+ if scrollbar_troughcolor:
+ self._vsb.configure(troughcolor = scrollbar_troughcolor)
+
+ if entry_font:
+ self._entry.configure(font=entry_font)
+
+ if entry_background:
+ self._entry.configure(background=entry_background)
+
+ if entry_foreground:
+ self._entry.configure(foreground=entry_foreground)
+
+ if label_font:
+ self._entry_label.configure(font=label_font)
+
+ if tags:
+ for tag, tag_config in tags.items():
+ self._textarea.tag_config(tag, **tag_config)
+
+ self.set_nick(my_nick)
+
+ @property
+ def topic(self):
+ return
+
+ @topic.setter
+ def topic(self, topic):
+ return
+
+ def focus_entry(self):
+ self._entry.focus()
+
+ def bind_entry(self, event, handler):
+ self._entry.bind(event, handler)
+
+ def bind_textarea(self, event, handler):
+ self._textarea.bind(event, handler)
+
+ def bind_tag(self, tagName, sequence, func, add=None):
+ self._textarea.tag_bind(tagName, sequence, func, add=add)
+
+ def focus(self):
+ self._entry.focus()
+
+ def user_message(self, nick, content):
+ if self._timestamp_template is None:
+ self._write((u"%s:"%nick, "nick"), " ", (content, "user_message"))
+ else:
+ timestamp = datetime.datetime.now().strftime(self._timestamp_template)
+ self._write((timestamp, "timestamp"), " ", (u"%s:"%nick, "nick"), " ", (content, "user_message"))
+
+ def notification_message(self, content, tag=None):
+ if tag is None:
+ tag = "notification"
+
+ self._write((content, tag))
+
+ notification = notification_message
- if font_spec_length == 1:
- family, = font
- return tkFont.Font(family=family)
- elif font_spec_length == 2:
- family, size = font_spec
- return tkFont.Font(family=family, size=size)
- elif font_spec_length == 3:
- family, size, weight = font_spec
- return tkFont.Font(family=family, size=size, weight=weight)
-
-def _nested_value(obj, *list_of_keys):
- for key in list_of_keys:
- if isinstance(obj, dict):
- if key in obj:
- obj = obj[key]
- else:
- return None
- else:
- return None
- return obj
-
-# TODO: Add frame topic
-class Channel(object):
- def __init__(self, master, connection, messenger, channel_ID, channel_name, my_nick_var, entry_controls, style):
- self._my_nick_var = my_nick_var
- my_nick_var.trace_variable('w', lambda name, index, mode: self._chatbox.set_nick(self._my_nick_var.get()))
-
- self._channel_ID = channel_ID
- self._name = channel_name
- self._messenger = messenger
-
- self.interior = Frame(master, class_="Channel")
- self.interior.channel = self
-
- self._is_closed = False
-
- self._connection = connection
-
- def _build_chatbox(self, master, logging_file, style):
- kwargs = _nested_value(style, "Channel", "Chatbox")
-
- if not kwargs:
- kwargs = {}
-
- scrollbar_style = _nested_value(style, "Scrollbar")
-
- if scrollbar_style:
- for style_name in ("background", "troughcolor"):
- if style_name in scrollbar_style:
- kwargs.setdefault("scrollbar_" +style_name, scrollbar_style[style_name])
-
- kwargs["logging_file"]=logging_file
- kwargs["my_nick"] = self._my_nick_var.get()
- kwargs["command"] = self._on_message_sent
-
- self._chatbox = Chatbox(master, **kwargs)
- return self._chatbox
-
- @property
- def channel_ID(self):
- return self._channel_ID
-
- @property
- def name(self):
- return self._name
-
- @property
- def messenger(self):
- return self._messenger
+ def notification_of_private_message(self, content, from_, to):
+ if self._timestamp_template is None:
+ self.notification_message(u"{from_} -> {to}: {content}".format(from_=from_, to=to, content=content), "notification_of_private_message")
+ else:
+ timestamp = datetime.datetime.now().strftime(self._timestamp_template)
+ self.notification_message(u"{timestamp} {from_} -> {to}: {content}".format(timestamp=timestamp, from_=from_, to=to, content=content), "notification_of_private_message")
+
+ def new_message(self, message):
+ if isinstance(message, User_Message):
+ self.user_message(message.content, message.nick)
+ elif isinstance(message, Notification_Message):
+ self.notification(message.content, message.tag)
+ elif isinstance(message, Notification_Of_Private_Message):
+ self.notification_of_private_message(message.from_, message.to, message.content)
+ else:
+ raise Exception("Bad message")
+
+ def tag(self, tag_name, **kwargs):
+ self._textarea.tag_config(tag_name, **kwargs)
+
+ def clear(self):
+ self._is_empty = True
+ self._textarea.delete('1.0', END)
@property
def logging_file(self):
- return self._chatbox.logging_file
-
- @property
- def is_closed(self):
- return self._is_closed
-
- def bind_entry(self, event, handler):
- self._chatbox.bind_entry(event, handler)
-
- def bind_textarea(self, event, handler):
- self._chatbox.bind_textarea(event, handler)
-
- def bind_tag(self, tagName, sequence, func, add=None):
- self._chatbox.bind_tag(tagName, sequence, func, add=add)
-
- def user_message(self, content, user):
- if self._is_closed:
- raise Exception("Channel is closed: %s"%self._channel_ID)
- else:
- self._chatbox.user_message(content, user)
+ return self._logging_file
+
+ def send(self, content):
+ if self._my_nick is None:
+ raise Exception("Nick not set")
+
+ self.user_message(self._my_nick, content)
+
+ def _filter_text(self, text):
+ return "".join(ch for ch in text if ch <= u"\uFFFF")
- def notification_message(self, content, tag=None):
- if self._is_closed:
- raise Exception("Channel is closed: %s"%self._channel_ID)
- else:
- self._chatbox.notification(content, tag)
-
- def notification_of_private_message(self, content, from_, to):
- if self._is_closed:
- raise Exception("Channel is closed: %s"%self._channel_ID)
- else:
- if to is None:
- to = self._my_nick_var.get()
-
- if from_ is None:
- from_ = self._my_nick_var.get()
-
- self._chatbox.notification_of_private_message(content, from_, to)
-
- def send(self, content):
- if self._is_closed:
- raise Exception("Channel is closed: %s"%self.channel_ID)
- else:
- self._chatbox.send(content)
-
- def _on_message_sent(self, content):
- self._connection.trigger("i-send-a-message", {"channel_ID":self._channel_ID, "channel_name":self._name, "content":content})
-
- def add_messages(self, list_of_messages):
- if self._is_closed:
- raise Exception("Channel is closed: %s"%self.channel_ID)
- else:
- self._history.add_list_of_messages(list_of_messages)
-
- def close(self):
- self._is_closed = True
- self._connection.trigger("channel-closed", {"channel_ID":self._channel_ID, "channel_name":self._name})
-
- def set_nick(self, nick):
- self._my_nick_var.set(nick)
-
- def get_nick(self):
- return self._my_nick_var.get()
-
- def focus_entry(self):
- self._chatbox.focus_entry()
-
-class Public_Channel(Channel):
- def __init__(self, master, connection, messenger, channel_ID, channel_name, my_nick_var, panedwindow_ratio=0.8, topic=None, logging_file=None, maximum_lines=None, entry_controls=None, style=None):
- Channel.__init__(self, master, connection, messenger, channel_ID, channel_name, my_nick_var, entry_controls, style)
-
- self._panedwindow = panedwindow = PanedWindow(self.interior, orient=HORIZONTAL, opaqueresize= False, sashpad =1)
- panedwindow.pack(expand=True, fill=BOTH)
-
- chatbox = self._build_chatbox(panedwindow, logging_file=logging_file, style=style)
- left_pane = chatbox.interior
-
- chatbox.interior.pack(expand=True, fill=BOTH)
-
- self._user_list = User_List(panedwindow, connection, style=style)
- right_pane = self._user_list.interior
-
- panedwindow.add(left_pane)
- panedwindow.add(right_pane)
-
- panedwindow.update_idletasks()
- panedwindow.sash_place(0, int(round(panedwindow.winfo_reqwidth()*panedwindow_ratio)), 1)
-
- self.interior.bind('<Configure>', self._adjust_panedwindow)
-
- def _adjust_panedwindow(self, event):
- x = event.width - self._user_list.interior.winfo_reqwidth()
-
- self.interior.update_idletasks()
- self._panedwindow.sash_place(0, x, 1)
-
- def add_user(self, user):
- self._user_list.add_user(user)
-
- def delete_user(self, user):
- self._user_list.delete_user(user)
-
- def deselect_user(self):
- self._user_list.deselect()
-
- def set_tag(self, user, tag):
- self._user_list.set_tag(user, tag)
-
- def delete_tag(self, user, tag):
- self._user_list.delete_tag(user, tag)
-
- @property
- def users(self):
- return self._user_list.user_names
-
- @property
- def topic(self):
- return self._chatbox.topic
-
- @topic.setter
- def topic(self, topic):
- self._chatbox.topic = topic
-
-class Private_Channel(Channel):
- def __init__(self, master, connection, messenger, channel_ID, channel_name, my_nick_var, logging_file=None, entry_controls=None, style=None):
- Channel.__init__(self, master, connection, messenger, channel_ID, channel_name, my_nick_var, entry_controls, style)
-
- chatbox = self._build_chatbox(self.interior, logging_file=logging_file, style=style)
- chatbox.interior.pack(expand=True, fill=BOTH)
-
-# Modificar envio de mensajes. COntrolar mensajes no leidos
-# NO existe mensajes no leidos
-# Llevar registro de canales que son mostrados
-# Modificar mensaje en entry
-
-class Panel_Of_Channel_Names(object):
-
- def __init__(self, master, connection, style=None, add_at_the_beginning=True):
- self.interior = Frame(master, takefocus=1, class_="Panel_Of_Channel_Names")
-
- self._connection = connection
-
- self.interior.grid_columnconfigure(0, weight=1)
- self.interior.grid_rowconfigure(0, weight=1)
-
- self._vsb = Scrollbar(self.interior, takefocus=0)
- self._vsb.grid(column=1, row=0, sticky=N+S)
-
- #self._treeview = ttk.Treeview(self.interior, yscrollcommand=lambda f, l: autoscroll(self._vsb, f, l), takefocus=0,selectmode="browse", show="headings", columns=("#1"), style="Panel_Of_Channels.Treeview")
- self._treeview = ttk.Treeview(self.interior, yscrollcommand=lambda f, l: autoscroll(self._vsb, f, l), takefocus=0,selectmode="browse", show="headings", columns=("#1"), style="Panel_Of_Channels.Treeview")
- self._treeview.grid(column=0, row=0, sticky=N+S+W+E)
-
- self._treeview.column("#0", stretch= True, anchor="w")
- self._treeview.heading("#0",text="messenger")
-
- self._treeview.column("#1", stretch= True, anchor="w")
- self._treeview.heading("#1",text="channels")
-
- self._treeview.bind("<Double-Button-1>", self._on_select_item)
- self._treeview.bind('<Return>', self._on_select_item)
-
- self._treeview.bind('<Escape>', lambda event: self.deselect())
-
- self._vsb["command"]=self._treeview.yview
-
- if style is not None:
- ttk_style = ttk.Style()
-
- if "Panel_Of_Channel_Names" in style:
- panel_style = style["Panel_Of_Channel_Names"]
-
- if "messenger_column" in panel_style:
- messenger_column_style = panel_style["messenger_column"]
- self._treeview.column("#0", **messenger_column_style)
-
- if "channel_column" in panel_style:
- channel_column_style = panel_style["channel_column"]
- self._treeview.column("#1", **channel_column_style)
-
- if "background" in panel_style:
- background = panel_style["background"]
- ttk_style.configure("Panel_Of_Channels.Treeview", background=background, fieldbackground=background)
-
- if "foreground" in panel_style:
- foreground = panel_style["foreground"]
- ttk_style.configure("Panel_Of_Channels.Treeview", foreground=foreground)
-
- if "font" in panel_style:
- font = panel_style["font"]
-
- ttk_style.configure("Panel_Of_Channels.Treeview", font=font, rowheight=to_tkFont(font).metrics("linespace"))
-
- if "tags" in panel_style:
- style_tags = panel_style["tags"]
- for tag, config_tag in style_tags.items():
- self._treeview.tag_configure(tag, **config_tag)
-
- if "Scrollbar" in style:
- scrollbar_style = style["Scrollbar"]
-
- if "background" in style:
- self._vsb.configure(background=scrollbar_style["background"])
-
- if "throughcolor" in style:
- self._vsb.configure(throughcolor=scrollbar_style["throughcolor"])
-
- self._unread_messages = defaultdict(int)
-
- self._show_messengers = False
- self._add_at_the_beginning = add_at_the_beginning
-
- self._treeview.bind("<Button-3>", self._generate_context_menu_event)
-
- def add_messenger(self, messenger, open=True):
- if not self._show_messengers:
- self._treeview.configure(show="tree headings")
- self._show_messengers = True
-
- messenger_IID = self._messenger_IID(messenger)
-
- self._treeview.insert("", END, messenger_IID, text=messenger)
- self._treeview.item(messenger_IID, open=open)
-
- return messenger_IID
-
- def delete_messenger(self, messenger):
- messenger_IID = self._messenger_IID(messenger)
- self._treeview.delete(messenger_IID)
-
- def _messenger_IID(self, messenger):
- return "+"+messenger
-
- def add_channel_name(self, channel_name, channel_ID, messenger=None, tags=None, image=None):
- if channel_ID[0] == "+":
- raise ValueError("Channel ID can't start with '+'")
-
- kwargs = {}
- if tags is not None:
- kwargs["tags"] = tags
-
- if image is not None:
- kwargs["image"] = image
-
- if messenger is None:
- iid = ""
- else:
- iid = self.add_messenger(messenger)
-
- self._treeview.insert(iid, END, channel_ID, values=(channel_name,), **kwargs)
- self._treeview.update_idletasks()
-
- def delete_channel_name(self, channel_ID):
- if channel_ID[0] == "+":
- raise ValueError("Channel ID can't start with '+'")
-
- self._treeview.delete(channel_ID)
-
- def set_tag(self, channel_ID, tag):
- tags = list(self._treeview.item(channel_ID, option="tags"))
- if not tags:
- tags = []
-
- if not tag in tags:
- tags.append(tag)
-
- self._treeview.item(channel_ID, tags=tags)
-
- def delete_tag(self, channel_ID, tag):
- tags = list(self._treeview.item(channel_ID, option="tags"))
-
- try:
- index = tags.index(tag)
- except ValueError:
- return
-
- tags.pop(index)
- self._treeview.item(channel_ID, tags=tags)
-
- def select_first_on_list(self):
- list_of_items = self._treeview.get_children()
- if len(list_of_items) ==0:
- return
-
- first_item = list_of_items[0]
- self.select(first_item)
-
- def select_next(self):
- selection = self._treeview.selection()
-
- if len(selection) ==0:
- self.select_first_on_list()
- else:
- item = selection[0]
-
- next_item = self._treeview.next(item)
- if next_item != "":
- self.select(next_item)
-
- def select_previous(self):
- selection = self._treeview.selection()
-
- if len(selection) ==0:
- self.select_first_on_list()
- else:
- item = selection[0]
-
- prev_item = self._treeview.prev(item)
- if prev_item != "":
- self.select(prev_item)
-
- def deselect(self):
- selection = self._treeview.selection()
-
- for item in selection:
- self._treeview.selection_remove(item)
-
- def select(self, channel_ID):
- self._treeview.focus_set()
- self._treeview.selection_set((channel_ID,channel_ID))
- self._treeview.focus(channel_ID)
-
- def clear(self):
- self._treeview.delete(*self._treeview.get_children())
-
- def bind(self, event, handler):
- self._treeview.bind(event, handler)
-
- def __iter__(self):
- return self._treeview.get(0, END)
-
- def _on_select_item(self, event):
- selected_items = self._treeview.selection()
- if selected_items == "": return
-
- item_ID = selected_items[0]
- if item_ID[0] == "+":
- self._connection.trigger('messenger-selected',item_ID[1:])
- else:
- self._connection.trigger('channel-selected',item_ID)
-
- def _generate_context_menu_event(self, event):
- # http://stackoverflow.com/questions/12014210/python-tkinter-app-adding-a-right-click-context-menu
- iid = self.tree.identify('item',event.x, event.y)
- # se tiene que capturar el channel ID
- # channel_ID =
-
- self._connection.trigger('panel-of-channel-names-context-menu',{"metadata":{"iid": iid}, "x_root":event.x_root, "y_root":event.y_root})
-
- def notifify_messages_are_unread(self, channel_ID):
- if self._unread_messages[channel_ID] == 0:
- self.set_tag(channel_ID, "unread_messages")
-
- self._unread_messages[channel_ID] += 1
-
- if self._add_at_the_beginning:
- self._treeview.move(channel_ID,self._treeview.parent(channel_ID),0)
-
- def notifify_messages_are_read(self, channel_ID):
- if self._unread_messages[channel_ID] > 0:
- self.delete_tag(channel_ID, "unread_messages")
-
- self._unread_messages[channel_ID] = 0
-
-def validate_channel(func):
- @functools.wraps(func)
- def wrapped(self, channel_ID, *args, **kwargs):
- if channel_ID in self._channels:
- return func(self, channel_ID, *args, **kwargs)
- else:
- raise ValueError("Not a valid channel ID: %s"%channel_ID)
- return wrapped
-
-# Guardar tambien variable nick por cada messenger
-# Se debe retocar la parte del nick
-
-class Messenger(object):
- def __init__(self, name=None, nick=None):
- self.name = name
- self.nick_var = StringVar()
- self.channels = set()
-
- if nick is not None:
- self.set_nick(nick)
-
- def get_nick(self, nick):
- return self.nick_var.get()
-
- def set_nick(self, nick):
- self.nick_var.set(nick)
-
- def add_channel(self, channel_ID):
- self.channels.add(channel_ID)
-
- def delete_channel(self, channel_ID):
- self.channels.remove(channel_ID)
-
- def __iter__(self):
- return self.channels
-
-class Connection(object):
- def __init__(self):
- self._callbacks = defaultdict(list)
-
- def on(self, event_name, callback):
- self._callbacks[event_name].append(callback)
-
- def off(self, event_name, *args):
- if len(args) == 0:
- del self._callbacks[event_name]
- else:
- list_of_callbacks = self._callbacks[event_name]
- for callback in args:
- try:
- index = list_of_callbacks.index(callback)
- except ValueError:
- continue
-
- list_of_callbacks.pop(index)
-
- def trigger(self, event_name, data=None):
- for callback in self._callbacks[event_name]:
- callback(data)
-
-def _menu_builder(f):
-
- def wrapped(data):
-
- menu = Menu(tearoff=0)
- f(menu, data["metadata"])
-
- menu.tk_popup(data["x_root"], data["y_root"], 0)
- menu.destroy()
-
- return wrapped
-
-# Utilizar conexion para notificar mensajes leidos
-
-class Messenger_MegaWidget(object):
- _ID = 0
-
- def __init__(self, master, my_nick, style=None, entry_controls=None):
- self.interior = interior = Frame(master, class_="Chat_MegaWidget")
-
- self._style = style
-
- self._entry_controls = entry_controls
-
- self._connection = Connection()
- self._connection.on("channel-selected", self._on_select_channel)
-
- top_panel = PanedWindow(interior, orient=HORIZONTAL, opaqueresize= False, sashpad =1)
- top_panel.pack(expand=YES, fill=BOTH)
-
- self._notebook = ttk.Notebook(top_panel, style="Channels_Notebook.TNotebook")
- self._notebook.enable_traversal()
- self._notebook.bind('<3>', self._open_tab_context_menu)
-
- if "Tabs" in style:
- notebook_style = style["Tabs"]
- if "width" in notebook_style:
- self._notebook.configure(width=notebook_style["width"])
-
- if "height" in notebook_style:
- self._notebook.configure(height=notebook_style["height"])
-
- if "font" in notebook_style:
- ttk.Style().configure("Channels_Notebook.TNotebook.Tab", font=notebook_style["font"])
-
- self._notebook.bind("<<NotebookTabChanged>>", lambda event: self._on_tab_changed())
-
- self._panel_of_channel_names = Panel_Of_Channel_Names(top_panel, self._connection, style=style)
- self._panel_of_channel_names.interior.pack(expand=True, fill=BOTH)
-
-
- top_panel.add(self._panel_of_channel_names.interior)
- top_panel.add(self._notebook)
-
- self._channels = {}
-
- self._panel_of_channel_names.bind("<Escape>", lambda event: (self.deselect_chats(),self._entry.focus()))
-
- interior.bind_all('<Control-KeyPress-m>', lambda event: self._entry.focus())
- interior.bind_all('<Control-KeyPress-M>', lambda event: self._entry.focus())
-
- self._messengers = {
- None: Messenger(nick=my_nick)
- }
-
- def _open_tab_context_menu(self, event):
- if event.widget.identify(event.x, event.y) == 'label':
- index = event.widget.index('@%d,%d' % (event.x, event.y))
- #obtener aqui el canal
- #tab_rint event.widget.tab(index, 'text')
- self._connection.on("tab-context-menu", {"x_root":event.x_root, "y_root":event.y_root})
- return "break"
-
- def _on_select_channel(self, channel_ID):
- channel = self._channels[channel_ID]
-
- try:
- self._notebook.select(channel.interior)
- except TclError:
- self._notebook.add(channel.interior, text=channel.name)
- self._notebook.select(channel.interior)
-
- channel.focus_entry()
-
- def deselect_channel_on_panel(self):
- self._panel_of_channel_names.deselect()
-
- @validate_channel
- def select_channel_on_panel(self, channel_ID):
- self._panel_of_channel_names.select(channel_ID)
-
- @validate_channel
- def untag_channel(self, channel_ID, tag):
- self._panel_of_channel_names.delete_tag(channel_ID, tag)
-
- @validate_channel
- def tag_channel(self, channel_ID, tag):
- self._panel_of_channel_names.set_tag(channel_ID, tag)
-
- @validate_channel
- def select_channel_on_notebook(self, channel_ID):
- channel = self._channels[channel_ID]
- self._notebook.select(channel.interior)
-
- def add_messenger(self, name):
- if name in self._messengers:
- raise ValueError("Messenger already created: %s"%name)
-
- self._messengers[name] = Messenger(name=name)
- self._panel_of_channel_names.add_messenger(name)
-
- def delete_messenger(self, name):
- if name not in self._messengers:
- raise ValueError("Messenger not exists: %s"%name)
-
- for channel_ID in self._messengers[name]:
- self.close_channel(channel_ID)
-
- del self._messengers[name]
- self._panel_of_channel_names.delete_messenger(name)
-
- def new_channel(self, channel_name, channel_ID=None, messenger=None, start_open=None, tags=None, image=None, public=False):
- if channel_ID is None:
- channel_ID = "channel"+str(self._ID)
- self._ID += 1
- else:
- if channel_ID[0] == "+":
- raise ValueError("Channel ID can not start with '+': %s"%channel_ID)
-
- if channel_ID in self._channels:
- raise ValueError("Channel is already created: %s"%channel_ID)
-
- if messenger in self._messengers:
- messenger_instance = self._messengers[messenger]
- else:
- messenger_instance = self._messengers[messenger] = Messenger(name=messenger)
- self._panel_of_channel_names.add_messenger(messenger)
-
- my_nick_var = messenger_instance.nick_var
-
- if public:
- if start_open is None:
- start_open = True
- channel = Public_Channel(self._notebook, self._connection, messenger_instance, channel_ID, channel_name, my_nick_var, style=self._style, entry_controls=self._entry_controls)
- else:
- if start_open is None:
- start_open = False
-
- channel = Private_Channel(self._notebook, self._connection, messenger_instance, channel_ID, channel_name, my_nick_var, style=self._style, entry_controls=self._entry_controls)
-
- channel.bind_entry("<Control-W>", lambda event, channel_ID=channel.channel_ID: self.hide_channel(channel_ID))
- channel.bind_entry("<Control-w>", lambda event, channel_ID=channel.channel_ID: self.hide_channel(channel_ID))
- channel.bind_entry('<Up>', lambda event: self._panel_of_channel_names.select_previous())
- channel.bind_entry('<Down>', lambda event: self._panel_of_channel_names.select_next())
-
- self._channels[channel_ID] = channel
-
- self._panel_of_channel_names.add_channel_name(channel_name, channel_ID, messenger=messenger, tags=tags, image=image)
- messenger_instance.add_channel(channel_ID)
-
- if start_open:
- self._notebook.add(channel.interior, text=channel_name)
-
- return channel_ID
-
- def on(self, event_name, callback):
- self._connection.on(event_name, callback)
-
- def trigger(self, event_name, data):
- self._connection.trigger(event_name, data)
-
- @validate_channel
- def show_channel(self, channel_ID, select=False):
- channel = self._channels[channel_ID]
- self._notebook.add(channel.interior)
-
- if select:
- self._notebook.select(channel.interior)
-
- def hide_channel(self, channel_ID=None):
- if channel_ID is None:
- channel = self.current_channel
- else:
- if channel_ID in self._channels:
- channel = self._channels[channel_ID]
+ def _write(self, *args):
+ if len(args) == 0: return
+
+ relative_position_of_scrollbar = self._vsb.get()[1]
+
+ self._textarea.config(state=NORMAL)
+
+ if self._is_empty:
+ self._is_empty = False
+ else:
+ self._textarea.insert(END, "\n")
+ if self._log is not None:
+ self._log.write("\n")
+
+ for arg in args:
+ if isinstance(arg, tuple):
+ text, tag = arg
+ # Parsing not allowed characters
+ text = self._filter_text(text)
+ self._textarea.insert(END, text, tag)
else:
- raise ValueError("Not a valid channel ID: %s"%channel_ID)
-
- self._notebook.hide(channel.interior)
-
- @validate_channel
- def close_channel(self, channel_ID=None):
- if channel_ID is None:
- channel = self.current_channel
- channel_ID = channel.channel_ID
- else:
- channel = self._channels[channel_ID]
-
- self._panel_of_channel_names.delete_channel_name(channel_ID)
-
- messenger = channel.messenger
- channel.interior.destroy()
- del self._channels[channel_ID]
- self._messengers[messenger].delete_channel(channel_ID)
-
- channel.close()
-
- def focus_entry(self):
- channel = self.current_channel
- if channel is not None:
- channel.focus_entry_set()
-
- @property
- def my_nick(self):
- return self._messengers[None].get_nick()
-
- @my_nick.setter
- def my_nick(self, new_nick):
- return self._messengers[None].set_nick(new_nick)
-
- def set_nick_on_messenger(self, messenger, nick):
- for channel_ID in self._messengers[messenger]:
- channel = self._channels[channel_ID]
- channel.set_nick(nick)
-
- def send(self, message, channel_ID=None):
- if channel_ID is None:
- channel = self.current_channel
- if self.current_channel is None:
- return
- else:
- channel = self._channels[channel_ID]
-
- channel.send(message)
-
- @validate_channel
- def user_message(self, channel_ID, nick, content):
- channel = self._channels[channel_ID]
- channel.user_message(nick, content)
-
- if channel != self.current_channel:
- self._notifify_messages_are_unread(channel_ID)
-
- @validate_channel
- def notification_message(self, channel_ID, content, notification_type=None):
- channel = self._channels[channel_ID]
- channel.notification_message(content, notification_type)
-
- if channel != self.current_channel:
- self._notifify_messages_are_unread(channel_ID)
-
- @validate_channel
- def notification_of_private_message(self, channel_ID, content, from_=None, to=None):
- channel = self._channels[channel_ID]
- channel.notification_of_private_message(content, from_=from_, to=to)
-
- if channel != self.current_channel:
- self._notifify_messages_are_unread(channel_ID)
-
- def change_nick(self, new_nick, messenger=None):
- self._messengers[messenger].set_nick(new_nick)
-
- @validate_channel
- def __getitem__(self, channel_ID):
- return self._channels[channel_ID]
-
- @validate_channel
- def __delitem__(self, channel_ID):
- self.close_channel(channel_ID)
-
- def __contains__(self, channel_ID):
- return channel_ID in self._channels
-
- @property
- def current_channel(self):
- widget_name = self._notebook.select()
- if widget_name:
- return self.interior.nametowidget(widget_name).channel
- else:
- return None
-
- def _on_tab_changed(self):
- channel = self.current_channel
- channel_ID = channel.channel_ID
-
- channel.focus_entry()
-
- self._notifify_messages_are_read(channel_ID)
-
- def _notifify_messages_are_read(self, channel_ID):
- self._panel_of_channel_names.notifify_messages_are_read(channel_ID)
-
- channel = self._channels[channel_ID]
- self._notebook.tab(channel.interior, text= channel.name)
-
- def _notifify_messages_are_unread(self, channel_ID):
- self._panel_of_channel_names.notifify_messages_are_unread(channel_ID)
-
- channel = self._channels[channel_ID]
- try:
- self._notebook.tab(channel.interior, text= "[*] "+channel.name)
- except TclError:
- pass
-
+ text = arg
+
+ text = self._filter_text(text)
+ self._textarea.insert(END, text)
+
+ if self._log is not None:
+ self._log.write(text)
+
+ if self._maximum_lines is not None:
+ start_line = int(self._textarea.index('end-1c').split('.')[0]) -self._maximum_lines
+
+ if lines_to_delete >= 1:
+ self._textarea.delete('%s.0'%start_line, END)
+
+ self._textarea.config(state=DISABLED)
+
+ if relative_position_of_scrollbar == 1:
+ self._textarea.yview_moveto(1)
+
+ def _on_message_sent(self, event):
+ message = self._entry.get()
+ self._entry.delete(0, END)
+
+ self.send(message)
+
+ if self._command:
+ self._command(message)
+
+ def set_nick(self, my_nick):
+ self._my_nick = my_nick
+
+ if my_nick:
+ text = self._label_template.format(nick=my_nick)
+
+ self._entry_label["text"] = text
+ self._entry_label.pack(side=LEFT,padx=(5,5), before=self._entry)
+ else:
+ self._entry_label.pack_forget()
+
if __name__ == "__main__":
-
- style = {
- "Tabs":{
- "width":600,
- "height": 600,
- "font": ("TkDefaultFont", 11)
- },
- "Scrollbar": {
- "background":"#7f0303",
- "troughcolor" :"#cc2b00"
- },
- "Panel_Of_Channel_Names": {
- "messenger_column": {
- "width": 100
- },
- "channel_column": {
- "width": 100
- },
- "background":"#676760",
- "foreground":"#f7f7f7",
- "font": ("Courier New", 12),
- "tags": {
- "unread_messages": {
- "foreground": "red"
- }
- }
- },
- "Channel": {
- "Chatbox": {
- "timestamp_template": "[%H:%M:%S]",
- "history_background":"#676760",
- "history_font": ("Courier New", 12),
- "label_font": ("TkDefaultFont", 10, "bold"),
- "entry_font": ("Courier New", 12),
- "entry_background": "#676760",
- "entry_foreground":"#f7f7f7",
- "tags": {
- "timestamp": {
- "foreground":"#f7f7f7"
- },
- "nick": {
- "foreground":"#f7f7f7"
- },
- "notification": {
- "foreground":"#f7f7f7"
- },
- "notification_of_private_message": {
- "foreground":"#f7f7f7"
- },
- "user_message": {
- "foreground":"#f7f7f7"
- }
- }
- },
- "User_List": {
- "width": 100,
- "background":"#676760",
- "foreground":"#f7f7f7",
- "font": ("Courier New", 12)
- }
- }
- }
try:
from Tkinter import Tk
@@ -902,16 +261,13 @@
root = Tk()
root.title("Chat megawidget")
- chat = Messenger_MegaWidget(root, my_nick="user123", style=style)
- chat.interior.pack(expand=True, fill=BOTH)
-
-
- channel_ID = chat.new_channel("#barcelona", start_open=True)
-
- chat.new_channel("johny23")
- chat.new_channel("Samatha32")
-
- chat.user_message(channel_ID, "john", "hi everybody")
- for i in range(100): chat.send("hola caracola")
+ def command(txt):
+ print(txt)
+
+ chatbox = Chatbox(root, my_nick="user1", command=command)
+ chatbox.user_message("user2", "hello guys")
+
+ chatbox.send("Hi, you are welcome!")
+ chatbox.interior.pack(expand=True, fill=BOTH)
root.mainloop()