def open_text(self): if self.selected_text_title != "[]": text_root = tk.Tk() popup = tk.Menu(text_root, tearoff=0) textWindow = ScrolledText(text_root, wrap="word") uw_list = self.uw_lb.get(0, tk.END) text_root.bind( "<Button-1>", lambda eff: meaning_part( eff, T=textWindow, unknown_words_list=uw_list, popup=popup) ) textWindow.insert(1.0, general_dict["Text"][self.selected_text_title]) textWindow.tag_config("A", foreground="red", background="white") textWindow.pack(expand=True, fill=tk.BOTH) for word in general_dict["Unknown"][self.selected_text_title]: word = " " + word + " " start = 1.0 search_pos = textWindow.search(word, start, stopindex=tk.END) while search_pos: length = len(word) row, col = search_pos.split('.') end = int(col) + length if str(end)[-1] == "0": end += 1 end = row + '.' + str(end) textWindow.tag_add("A", search_pos, float(end)) start = end search_pos = textWindow.search(word, start, stopindex=tk.END)
def open_text(self): if self.selected_text_title != "[]": text_root = tk.Tk() textWindow = ScrolledText(text_root, wrap="word") textWindow.insert(1.0, general_dict["Text"][self.selected_text_title]) textWindow.tag_config("A", foreground="red", background="white") textWindow.pack(expand=True, fill=tk.BOTH) for word in general_dict["Unknown"][self.selected_text_title]: word = " " + word + " " start = 1.0 search_pos = textWindow.search(word, start, stopindex=tk.END) while search_pos: length = len(word) row, col = search_pos.split('.') end = int(col) + length if str(end)[-1] == "0": end += 1 end = row + '.' + str(end) textWindow.tag_add("A", search_pos, float(end)) start = end search_pos = textWindow.search(word, start, stopindex=tk.END)
class CopyableMessageWindow(basicWindow): """ Creates a modeless (non-modal) message window that allows the user to copy the presented text. """ def __init__(self, topLevel, message, title, align, buttons, makeModal): self.guiRoot = topLevel basicWindow.__init__(self, topLevel, title, resizable=True, topMost=False) linesInMessage = len(message.splitlines()) if linesInMessage > 17: height = 22 else: height = linesInMessage + 5 self.messageText = ScrolledText(self.mainFrame, relief='groove', wrap='word', height=height) self.messageText.insert('1.0', '\n' + message) self.messageText.tag_add('All', '1.0', 'end') self.messageText.tag_config('All', justify=align) self.messageText.pack(fill='both', expand=1) # Add the buttons self.buttonsFrame = Tk.Frame(self.mainFrame) ttk.Button(self.buttonsFrame, text='Close', command=self.close).pack(side='right', padx=5) if buttons: for button in buttons: buttonText, buttonCommand = button ttk.Button(self.buttonsFrame, text=buttonText, command=buttonCommand).pack(side='right', padx=5) ttk.Button(self.buttonsFrame, text='Copy text to Clipboard', command=self.copyText).pack(side='right', padx=5) self.buttonsFrame.pack(pady=3) if makeModal: self.window.grab_set() self.guiRoot.wait_window(self.window) # Button functions def copyText(self): self.guiRoot.clipboard_clear() self.guiRoot.clipboard_append( self.messageText.get('1.0', 'end').strip())
class CopyableMsg(basicWindow): """ Creates a modeless (non-modal) message window that allows the user to easily copy the given text. If buttons are provided, they should be an iterable of tuples of the form (buttonText, buttonCallback). """ def __init__(self, root, message='', title='', alignment='center', buttons=None): super(CopyableMsg, self).__init__(root, title, resizable=True, topMost=False) self.root = root if alignment == 'left': height = 22 # Expecting a longer, more formal message else: height = 14 # Add the main text display of the window self.messageText = ScrolledText(self.window, relief='groove', wrap='word', height=height) self.messageText.insert('1.0', message) self.messageText.tag_add('All', '1.0', 'end') self.messageText.tag_config('All', justify=alignment) self.messageText.pack(fill='both', expand=1) # Add the buttons self.buttonsFrame = Tkinter.Frame(self.window) ttk.Button(self.buttonsFrame, text='Close', command=self.close).pack(side='right', padx=5) if buttons: for buttonText, buttonCommand in buttons: ttk.Button(self.buttonsFrame, text=buttonText, command=buttonCommand).pack(side='right', padx=5) ttk.Button(self.buttonsFrame, text='Copy text to Clipboard', command=self.copyToClipboard).pack(side='right', padx=5) self.buttonsFrame.pack(pady=3) def copyToClipboard(self): self.root.clipboard_clear() self.root.clipboard_append(self.messageText.get('1.0', 'end'))
def open_text(self): #in the usage of tags there were 3 problems faced, 1-the markings such as dot, comma but it is solved # 2-Uppercases are not matching, this is not solved # 3-Words that are shorter than 4 letters take the next words first letter if self.selected_text_title != "[]": text_root = tk.Tk() text_root.title(self.selected_text_title) popup = tk.Menu(text_root, tearoff=0) textWindow = ScrolledText(text_root, wrap = "word") uw_list = self.uw_lb.get(0,tk.END) text_root.bind("<Button-1>", lambda eff:meaning_part(eff, T=textWindow, unknown_words_list=uw_list, popup=popup)) textWindow.insert(1.0, general_dict["Text"][self.selected_text_title]) textWindow.tag_config("A", foreground="red", background="white") textWindow.pack(expand = True, fill = tk.BOTH) for pre_word in uw_list:#general_dict["Unknown"][self.selected_text_title]: word = " " + pre_word + " " start = 1.0 search_pos = textWindow.search(word, start, stopindex=tk.END) if not search_pos: for x in '"^+%&/()=?_-*,;:.!>#${{[]}\'': word = " " + pre_word + x search_pos = textWindow.search(word, start, stopindex=tk.END) if search_pos: break if not search_pos: for x in '"^+%&/()=?_-*,;:.!>#${{[]}\'': word = x + pre_word + " " search_pos = textWindow.search(word, start, stopindex=tk.END) if search_pos: break #print word, search_pos while search_pos: length = len(word) row, col = search_pos.split('.') end = int(col) + length #if str(end)[-1]=="0": # end += 1 print end, col end = row + '.' + str(end) print search_pos, end, word textWindow.tag_add("A", search_pos, float(end)) start = end search_pos = textWindow.search(word, start, stopindex=tk.END)
class myPanel(tkinter.Tk): def __init__(self): tkinter.Tk.__init__(self, 'lsx') try: self.ss = self.clipboard_get() except: self.ss = '' self.cnt = 0 self.slices = paragraph(self.ss, qrlen) self.string = None if not os.path.exists('config.ini'): x = - 100 + 0.2 * self.winfo_screenwidth() # 100: 补偿初始Tk界面为200x200 y = - 100 + 0.7 * self.winfo_screenheight() xy = '+%d+%d'%(x,y) with open('config.ini','w') as f: f.write(xy) with open('config.ini','r') as f: xy = f.read() self.geometry(xy) self.minsize(450,206) self.qrc = tkinter.Label(self) self.ent = ScrolledText(self, width=1, height=15) self.ent.insert(1.0, self.ss) self.ent.mark_set('insert','0.0') # 程序首次运行时不使用see函数,否则会有未知原因半行内容沉没在Text窗体上面一点点 self.ent.focus() text = self.slices[self.cnt] basic = (len(self.slices),len(self.ss)) info = 'Page: %s, Length: %s'%basic if zh_cn: info = '共%s页, %s字'%basic self.withdraw() # withdraw/deiconify 阻止页面闪烁 self.update_idletasks() self.ent.pack(padx=2, pady=2, fill='both', expand=True, side='right') # RIGHT为了在Label隐藏再显示后所在位置一致 self.qrc.pack() self.setQrcode(text, info) self.deiconify() self.qrc.bind('<Button-2>',self.onExit) self.qrc.bind('<Button-1>',self.fliping) self.qrc.bind('<Button-3>',self.fliping) self.qrc.bind('<Double-Button-1>',self.setting) self.qrc.bind('<Double-ButtonRelease-3>',self.openFile) # 没有Release会在窗口打开之后的鼠标抬起时唤起右键菜单 self.ent.bind('<Escape>',self.onExit) self.ent.bind('<F1>',self.openFile) self.ent.bind('<F2>',self.fliping) self.ent.bind('<F3>',self.fliping) self.ent.bind('<F4>',self.setting) self.ent.bind('<KeyRelease>',self.refresh) self.ent.bind('<ButtonRelease-1>',self.selected) self.bind('<Destroy>',lambda evt:self.onExit(evt,close=True)) def setQrcode(self, string, info): if self.string != string: self.string = string if string == '': self.qrc.forget() else: global img img = qrmake(string) self.qrc.config(image=img) self.qrc.pack() log(string, info) self.title('Text Helper v1.06 (%s)'%info) if zh_cn: self.title('文本助手v1.06 (%s)'%info) def refresh(self, evt): if evt.keysym in ['Control_L', 'Return', 'BackSpace']: ss2 = self.ent.get('0.0', 'end')[:-1] if self.ss != ss2: self.ss = ss2 self.cnt = 0 self.slices = paragraph(self.ss, qrlen) text = self.slices[self.cnt] basic = (len(self.slices),len(self.ss)) info = 'Page: %s, Length: %s'%basic if zh_cn: info = '共%s页, %s字'%basic self.setQrcode(text, info) def fliping(self, evt): if evt.num == 1 or evt.keysym == 'F3': self.cnt = min(self.cnt+1,len(self.slices)-1) else: self.cnt = max(self.cnt-1,0) cur1 = getIndex(''.join(self.slices[:self.cnt])) cur2 = getIndex(''.join(self.slices[:self.cnt+1])) self.setCursor(cur1, cur2) text = self.slices[self.cnt] basic = (self.cnt+1,len(self.slices),len(text),len(self.ss)) info = 'Page: %s/%s, Sel: %s/%s'%basic if zh_cn: info = '第%s/%s页, 共%s/%s字'%basic self.setQrcode(text, info) def selected(self, evt): if self.ent.tag_ranges('sel'): text = self.ent.selection_get() info = 'Sel: %s/%s'%(len(text),len(self.ss)) if zh_cn: info = '选中%s/%s字'%(len(text),len(self.ss)) self.setQrcode(text, info) def setCursor(self, cur1, cur2): self.ent.mark_set('insert', cur1) self.ent.tag_remove('sel','0.0','end') self.ent.tag_add('sel',cur1,cur2) self.ent.see(cur1) def setting(self, evt): max_page = len(self.slices) num = tkinter.simpledialog.askinteger('Goto','Go to page (1-%s):'%max_page, initialvalue=self.cnt+1) self.ent.focus() if num: self.cnt = max(1,min(max_page,num)) - 1 text = self.slices[self.cnt] basic = (self.cnt+1,len(self.slices),len(text),len(self.ss)) info = 'Page: %s/%s, Sel: %s/%s'%basic if zh_cn: info = '第%s/%s页, 共%s/%s字'%basic self.setQrcode(text, info) def openFile(self, evt): path = tkinter.filedialog.askopenfilename() if path != '': filename = os.path.basename(path) with open(path,'rb') as f: s = f.read() try: try: res = s.decode() except: try: res = s.decode('gbk') except: raise except: s = base64.urlsafe_b64encode(filename.encode()+b'\n'+s).decode() total = int((len(s)-1)/(qrlen-10)+1) res = '' for i in range(total): res += '%04d%04d.%s,'%(total, i+1, s[(qrlen-10)*i:(qrlen-10)*(i+1)]) self.ss = res self.cnt = 0 self.slices = paragraph(self.ss, qrlen) self.ent.delete(1.0, 'end') self.ent.insert(1.0, res) text = self.slices[self.cnt] basic = (len(self.slices),len(self.ss)) info = 'Page: %s, Length: %s'%basic if zh_cn: info = '共%s页, %s字'%basic self.setQrcode(text, info) def onExit(self, evt, close=False): xy = '+%d+%d'%(self.winfo_x(),self.winfo_y()) with open('config.ini','w') as f: f.write(xy) if not close: self.destroy()
class Tkui(base.BaseUI): """ This is a ui class which handles the complete Tk user interface. """ def __init__(self): """ Initializes.""" base.BaseUI.__init__(self) # internal ui queue self._event_queue = Queue.Queue() # map of session -> (bold, foreground, background) self._currcolors = {} # ses -> string self._unfinishedcolor = {} self._viewhistory = 0 self._do_i_echo = 1 # holds a map of window names -> window references self._windows = {} # instantiate all the widgets self._tk = Tk() self._tk.geometry("800x600") self.settitle() if os.name == "posix": fnt = tkFont.Font(family="Courier", size=12) else: fnt = tkFont.Font(family="Fixedsys", size=12) self._entry = CommandEntry( self._tk, self, fg="white", bg="black", insertbackground="yellow", font=fnt, insertwidth="2" ) self._entry.pack(side="bottom", fill="both") self._topframe = Frame(self._tk) self._topframe.pack(side="top", fill="both", expand=1) self._txt = ScrolledText(self._topframe, fg="white", bg="black", font=fnt, height=20) self._txt.pack(side="bottom", fill="both", expand=1) self._txt.bind("<KeyPress>", self._ignoreThis) self._txtbuffer = ScrolledText(self._topframe, fg="white", bg="black", font=fnt, height=20) self._txtbuffer.bind("<KeyPress-Escape>", self.escape) self._txtbuffer.bind("<KeyPress>", self._ignoreThis) self._entry.focus_set() self._initColorTags() self.dequeue() exported.hook_register("config_change_hook", self.configChangeHandler) exported.hook_register("to_user_hook", self.write) # FIXME - fix this explanation. this is just terrible. tc = config.BoolConfig( "saveinputhighlight", 0, 1, "Allows you to change the behavior of the command entry. When " "saveinputhighlight is off, we discard whatever is on the entry " "line. When it is on, we will retain the contents allowing you " "to press the enter key to do whatever you typed again.", ) exported.add_config("saveinputhighlight", tc) self._quit = 0 def runui(self): global HELP_TEXT exported.add_help("tkui", HELP_TEXT) exported.write_message('For tk help type "#help tkui".') exported.add_command("colorcheck", colorcheck_cmd) # run the tk mainloop here self._tk.mainloop() def wantMainThread(self): # The tkui needs the main thread of execution so we return # a 1 here. return 1 def quit(self): if not self._quit: self._quit = 1 self._topframe.quit() def dequeue(self): qsize = self._event_queue.qsize() if qsize > 10: qsize = 10 for i in range(qsize): ev = self._event_queue.get_nowait() ev.execute(self) self._tk.after(25, self.dequeue) def settitle(self, title=""): """ Sets the title bar to the Lyntin title plus the given string. @param title: the title to set @type title: string """ if title: title = constants.LYNTINTITLE + title else: title = constants.LYNTINTITLE self._event_queue.put(_TitleEvent(self._tk, title)) def removeWindow(self, windowname): """ This removes a NamedWindow from our list of NamedWindows. @param windowname: the name of the window to write to @type windowname: string """ if self._windows.has_key(windowname): del self._windows[windowname] def writeWindow(self, windowname, message): """ This writes to the window named "windowname". If the window does not exist, we spin one off. It handles ansi text and messages just like writing to the main window. @param windowname: the name of the window to write to @type windowname: string @param message: the message to write to the window @type message: string or Message instance """ self._event_queue.put(_WriteWindowEvent(windowname, message)) def writeWindow_internal(self, windowname, message): if not self._windows.has_key(windowname): self._windows[windowname] = NamedWindow(windowname, self, self._tk) self._windows[windowname].write(message) def _ignoreThis(self, tkevent): """ This catches keypresses from the history buffer.""" # kludge so that ctrl-c doesn't get caught allowing windows # users to copy the buffer.... if tkevent.keycode == 17 or tkevent.keycode == 67: return self._entry.focus() if tkevent.char: # we do this little song and dance so as to pass events # we don't want to deal with to the entry widget essentially # by creating a new event and tossing it in the event list. # it only sort of works--but it's the best code we've got # so far. args = ("event", "generate", self._entry, "<KeyPress>") args = args + ("-rootx", tkevent.x_root) args = args + ("-rooty", tkevent.y_root) args = args + ("-keycode", tkevent.keycode) args = args + ("-keysym", tkevent.keysym) self._tk.tk.call(args) return "break" def pageUp(self): """ Handles prior (Page-Up) events.""" if self._viewhistory == 0: self._txtbuffer.pack(side="top", fill="both", expand=1) self._viewhistory = 1 self._txtbuffer.delete("1.0", "end") lotofstuff = self._txt.get("1.0", "end") self._txtbuffer.insert("end", lotofstuff) for t in self._txt.tag_names(): taux = None tst = 0 for e in self._txt.tag_ranges(t): if tst == 0: taux = e tst = 1 else: tst = 0 self._txtbuffer.tag_add(t, str(taux), str(e)) self._txtbuffer.yview("moveto", "1") if os.name != "posix": self._txtbuffer.yview("scroll", "20", "units") self._tk.update_idletasks() self._txt.yview("moveto", "1.0") if os.name != "posix": self._txt.yview("scroll", "220", "units") else: # yscroll up stuff self._txtbuffer.yview("scroll", "-15", "units") def pageDown(self): """ Handles next (Page-Down) events.""" if self._viewhistory == 1: # yscroll down stuff self._txtbuffer.yview("scroll", "15", "units") def escape(self, tkevent): """ Handles escape (Escape) events.""" if self._viewhistory == 1: self._txtbuffer.forget() self._viewhistory = 0 else: self._entry.clearInput() def configChangeHandler(self, args): """ This handles config changes including mudecho. """ name = args["name"] newvalue = args["newvalue"] if name == "mudecho": if newvalue == 1: # echo on self._do_i_echo = 1 self._entry.configure(show="") else: # echo off self._do_i_echo = 0 self._entry.configure(show="*") def _yadjust(self): """Handles y scrolling after text insertion.""" self._txt.yview("moveto", "1") # if os.name != 'posix': self._txt.yview("scroll", "20", "units") def _clipText(self): """ Scrolls the text buffer up so that the new text written at the bottom of the text buffer can be seen. """ temp = self._txt.index("end") ind = temp.find(".") temp = temp[:ind] if temp.isdigit() and int(temp) > 800: self._txt.delete("1.0", "100.end") def write(self, args): """ This writes text to the text buffer for viewing by the user. This is overridden from the 'base.BaseUI'. """ self._event_queue.put(_OutputEvent(args)) def write_internal(self, args): mess = args["message"] if type(mess) == types.StringType: mess = message.Message(mess, message.LTDATA) line = mess.data ses = mess.session if line == "" or self.showTextForSession(ses) == 0: return color, leftover = buffer_write(mess, self._txt, self._currcolors, self._unfinishedcolor) if mess.type == message.MUDDATA: self._unfinishedcolor[ses] = leftover self._currcolors[ses] = color self._clipText() self._yadjust() def convertColor(self, name): """ Tk has this really weird color palatte. So I switched to using color names in most cases and rgb values in cases where I couldn't find a good color name. This method allows me to specify either an rgb or a color name and it converts the color names to rgb. @param name: either an rgb value or a name @type name: string @returns: the rgb color value @rtype: string """ if name.startswith("#"): return name rgb = self._tk._getints(self._tk.tk.call("winfo", "rgb", self._txt, name)) rgb = "#%02x%02x%02x" % (rgb[0] / 256, rgb[1] / 256, rgb[2] / 256) print name, "converted to: ", rgb return rgb def _initColorTags(self): """ Sets up Tk tags for the text widget (fg/bg/u).""" for ck in fg_color_codes.keys(): color = self.convertColor(fg_color_codes[ck]) self._txt.tag_config(ck, foreground=color) self._txtbuffer.tag_config(ck, foreground=color) for ck in bg_color_codes.keys(): self._txt.tag_config(ck, background=bg_color_codes[ck]) self._txtbuffer.tag_config(ck, background=bg_color_codes[ck]) self._txt.tag_config("u", underline=1) self._txtbuffer.tag_config("u", underline=1) def colorCheck(self): """ Goes through and displays all the combinations of fg and bg with the text string involved. Purely for debugging purposes. """ fgkeys = ["30", "31", "32", "33", "34", "35", "36", "37"] bgkeys = ["40", "41", "42", "43", "44", "45", "46", "47"] self._txt.insert("end", "color check:\n") for bg in bgkeys: for fg in fgkeys: self._txt.insert("end", str(fg), (fg, bg)) self._txt.insert("end", str("b" + fg), ("b" + fg, bg)) self._txt.insert("end", "\n") for fg in fgkeys: self._txt.insert("end", str(fg), (fg, "b" + bg)) self._txt.insert("end", str("b" + fg), ("b" + fg, "b" + bg)) self._txt.insert("end", "\n") self._txt.insert("end", "\n") self._txt.insert("end", "\n")
class Tkui(base.BaseUI): """ This is a ui class which handles the complete Tk user interface. """ def __init__(self): """ Initializes.""" base.BaseUI.__init__(self) # internal ui queue self._event_queue = Queue.Queue() # map of session -> (bold, foreground, background) self._currcolors = {} # ses -> string self._unfinishedcolor = {} self._viewhistory = 0 self._do_i_echo = 1 # holds a map of window names -> window references self._windows = {} # instantiate all the widgets self._tk = Tk() self._tk.geometry("800x600") self.settitle() if os.name == 'posix': fnt = tkFont.Font(family="Courier", size=12) else: fnt = tkFont.Font(family="Fixedsys", size=12) self._entry = CommandEntry(self._tk, self, fg='white', bg='black', insertbackground='yellow', font=fnt, insertwidth='2') self._entry.pack(side='bottom', fill='both') self._topframe = Frame(self._tk) self._topframe.pack(side='top', fill='both', expand=1) self._txt = ScrolledText(self._topframe, fg='white', bg='black', font=fnt, height=20) self._txt.pack(side='bottom', fill='both', expand=1) self._txt.bind("<KeyPress>", self._ignoreThis) self._txtbuffer = ScrolledText(self._topframe, fg='white', bg='black', font=fnt, height=20) self._txtbuffer.bind("<KeyPress-Escape>", self.escape) self._txtbuffer.bind("<KeyPress>", self._ignoreThis) self._entry.focus_set() self._initColorTags() self.dequeue() exported.hook_register("config_change_hook", self.configChangeHandler) exported.hook_register("to_user_hook", self.write) # FIXME - fix this explanation. this is just terrible. tc = config.BoolConfig( "saveinputhighlight", 0, 1, "Allows you to change the behavior of the command entry. When " "saveinputhighlight is off, we discard whatever is on the entry " "line. When it is on, we will retain the contents allowing you " "to press the enter key to do whatever you typed again.") exported.add_config("saveinputhighlight", tc) self._quit = 0 def runui(self): global HELP_TEXT exported.add_help("tkui", HELP_TEXT) exported.write_message("For tk help type \"#help tkui\".") exported.add_command("colorcheck", colorcheck_cmd) # run the tk mainloop here self._tk.mainloop() def wantMainThread(self): # The tkui needs the main thread of execution so we return # a 1 here. return 1 def quit(self): if not self._quit: self._quit = 1 self._topframe.quit() def dequeue(self): qsize = self._event_queue.qsize() if qsize > 10: qsize = 10 for i in range(qsize): ev = self._event_queue.get_nowait() ev.execute(self) self._tk.after(25, self.dequeue) def settitle(self, title=""): """ Sets the title bar to the Lyntin title plus the given string. @param title: the title to set @type title: string """ if title: title = constants.LYNTINTITLE + title else: title = constants.LYNTINTITLE self._event_queue.put(_TitleEvent(self._tk, title)) def removeWindow(self, windowname): """ This removes a NamedWindow from our list of NamedWindows. @param windowname: the name of the window to write to @type windowname: string """ if self._windows.has_key(windowname): del self._windows[windowname] def writeWindow(self, windowname, message): """ This writes to the window named "windowname". If the window does not exist, we spin one off. It handles ansi text and messages just like writing to the main window. @param windowname: the name of the window to write to @type windowname: string @param message: the message to write to the window @type message: string or Message instance """ self._event_queue.put(_WriteWindowEvent(windowname, message)) def writeWindow_internal(self, windowname, message): if not self._windows.has_key(windowname): self._windows[windowname] = NamedWindow(windowname, self, self._tk) self._windows[windowname].write(message) def _ignoreThis(self, tkevent): """ This catches keypresses from the history buffer.""" # kludge so that ctrl-c doesn't get caught allowing windows # users to copy the buffer.... if tkevent.keycode == 17 or tkevent.keycode == 67: return self._entry.focus() if tkevent.char: # we do this little song and dance so as to pass events # we don't want to deal with to the entry widget essentially # by creating a new event and tossing it in the event list. # it only sort of works--but it's the best code we've got # so far. args = ('event', 'generate', self._entry, "<KeyPress>") args = args + ('-rootx', tkevent.x_root) args = args + ('-rooty', tkevent.y_root) args = args + ('-keycode', tkevent.keycode) args = args + ('-keysym', tkevent.keysym) self._tk.tk.call(args) return "break" def pageUp(self): """ Handles prior (Page-Up) events.""" if self._viewhistory == 0: self._txtbuffer.pack(side='top', fill='both', expand=1) self._viewhistory = 1 self._txtbuffer.delete("1.0", "end") lotofstuff = self._txt.get('1.0', 'end') self._txtbuffer.insert('end', lotofstuff) for t in self._txt.tag_names(): taux = None tst = 0 for e in self._txt.tag_ranges(t): if tst == 0: taux = e tst = 1 else: tst = 0 self._txtbuffer.tag_add(t, str(taux), str(e)) self._txtbuffer.yview('moveto', '1') if os.name != 'posix': self._txtbuffer.yview('scroll', '20', 'units') self._tk.update_idletasks() self._txt.yview('moveto', '1.0') if os.name != 'posix': self._txt.yview('scroll', '220', 'units') else: # yscroll up stuff self._txtbuffer.yview('scroll', '-15', 'units') def pageDown(self): """ Handles next (Page-Down) events.""" if self._viewhistory == 1: # yscroll down stuff self._txtbuffer.yview('scroll', '15', 'units') def escape(self, tkevent): """ Handles escape (Escape) events.""" if self._viewhistory == 1: self._txtbuffer.forget() self._viewhistory = 0 else: self._entry.clearInput() def configChangeHandler(self, args): """ This handles config changes including mudecho. """ name = args["name"] newvalue = args["newvalue"] if name == "mudecho": if newvalue == 1: # echo on self._do_i_echo = 1 self._entry.configure(show='') else: # echo off self._do_i_echo = 0 self._entry.configure(show='*') def _yadjust(self): """Handles y scrolling after text insertion.""" self._txt.yview('moveto', '1') # if os.name != 'posix': self._txt.yview('scroll', '20', 'units') def _clipText(self): """ Scrolls the text buffer up so that the new text written at the bottom of the text buffer can be seen. """ temp = self._txt.index("end") ind = temp.find(".") temp = temp[:ind] if (temp.isdigit() and int(temp) > 800): self._txt.delete("1.0", "100.end") def write(self, args): """ This writes text to the text buffer for viewing by the user. This is overridden from the 'base.BaseUI'. """ self._event_queue.put(_OutputEvent(args)) def write_internal(self, args): mess = args["message"] if type(mess) == types.StringType: mess = message.Message(mess, message.LTDATA) line = mess.data ses = mess.session if line == '' or self.showTextForSession(ses) == 0: return color, leftover = buffer_write(mess, self._txt, self._currcolors, self._unfinishedcolor) if mess.type == message.MUDDATA: self._unfinishedcolor[ses] = leftover self._currcolors[ses] = color self._clipText() self._yadjust() def convertColor(self, name): """ Tk has this really weird color palatte. So I switched to using color names in most cases and rgb values in cases where I couldn't find a good color name. This method allows me to specify either an rgb or a color name and it converts the color names to rgb. @param name: either an rgb value or a name @type name: string @returns: the rgb color value @rtype: string """ if name.startswith("#"): return name rgb = self._tk._getints( self._tk.tk.call('winfo', 'rgb', self._txt, name)) rgb = "#%02x%02x%02x" % (rgb[0] / 256, rgb[1] / 256, rgb[2] / 256) print name, "converted to: ", rgb return rgb def _initColorTags(self): """ Sets up Tk tags for the text widget (fg/bg/u).""" for ck in fg_color_codes.keys(): color = self.convertColor(fg_color_codes[ck]) self._txt.tag_config(ck, foreground=color) self._txtbuffer.tag_config(ck, foreground=color) for ck in bg_color_codes.keys(): self._txt.tag_config(ck, background=bg_color_codes[ck]) self._txtbuffer.tag_config(ck, background=bg_color_codes[ck]) self._txt.tag_config("u", underline=1) self._txtbuffer.tag_config("u", underline=1) def colorCheck(self): """ Goes through and displays all the combinations of fg and bg with the text string involved. Purely for debugging purposes. """ fgkeys = ['30', '31', '32', '33', '34', '35', '36', '37'] bgkeys = ['40', '41', '42', '43', '44', '45', '46', '47'] self._txt.insert('end', 'color check:\n') for bg in bgkeys: for fg in fgkeys: self._txt.insert('end', str(fg), (fg, bg)) self._txt.insert('end', str("b" + fg), ("b" + fg, bg)) self._txt.insert('end', '\n') for fg in fgkeys: self._txt.insert('end', str(fg), (fg, "b" + bg)) self._txt.insert('end', str("b" + fg), ("b" + fg, "b" + bg)) self._txt.insert('end', '\n') self._txt.insert('end', '\n') self._txt.insert('end', '\n')
class TermWindow(Frame): EOL = None ## # constructor method def __init__(self, master=None, **cnf): apply(Frame.__init__, (self, master), cnf) self.__create_widgets__() self.rxWaitPattern = "" self.localEcho = False self.logFile = None self.inQueue = Queue.Queue() self.outQueue = Queue.Queue() self.update_thread_safe() def update_thread_safe(self): while not self.inQueue.empty(): element = self.inQueue.get_nowait() if element is not None: msg, tag = element self.__display__(msg, tag) self.st_trm.update_idletasks() self.st_trm.after(100, self.update_thread_safe) ## # def __create_widgets__(self): dfl_font = 'Courier 10' # the title frame of the terminal self.f_title = Frame(self) self.f_title.pack(fill=BOTH) self.f_title.configure(FRAMEBORDER) self.shVar = StringVar() # show/hide button self.b_sh = Button(self.f_title, textvariable=self.shVar, font=dfl_font) self.b_sh.pack(side=RIGHT) self.b_sh['command'] = self.show_hide_cont # clear screen button self.b_cls = Button(self.f_title, text="CLS", font=dfl_font, underline=0) self.b_cls.pack(side=RIGHT) self.b_cls['command'] = self.clear_screen # echo on/off button self.al_echo = Label(self.f_title, text="ECHO", relief=RAISED, font=dfl_font, padx='3m', pady='1m', underline=0) self.al_echo.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_echo.bind("<Button-1>", self.__echo_handler__) # log on/off button self.al_log = Label(self.f_title, text="LOG", relief=RAISED, font=dfl_font, padx='3m', pady='1m', underline=0) self.al_log.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_log.bind("<Button-1>", self.__log_handler__) # device connect button self.al_connect = Label(self.f_title, text="CONNECT", relief=RAISED, font=dfl_font, padx='3m', pady='1m', underline=1) self.al_connect.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_connect.bind("<Button-1>", self.__connect_handler__) self.mb_macros = Menubutton(self.f_title, text="Macros", relief=RAISED) self.mb_macros.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.mb_macros.menu = Menu(self.mb_macros, tearoff=0) self.mb_macros["menu"] = self.mb_macros.menu # title of terminal window self.tVar = StringVar() self.l_title = Label(self.f_title, text="foo", font=dfl_font) self.l_title['textvariable'] = self.tVar self.l_title.pack(side=LEFT, expand=1, fill=X) self.l_title['width'] = 42 self.update_title("------ XXX ------") # frame for scrolled text window # (this frame handle needs to be kept fo show_hide_cont()) self.f_cont = Frame(self) # IO data scrolled text window self.st_trm = ScrolledText(self.f_cont, height=10, state=DISABLED, wrap=NONE) self.st_trm.pack(expand=1, fill=BOTH) self.st_trm['font'] = dfl_font self.st_trm.tag_config('E', foreground="blue") self.st_trm.tag_config('M', foreground="magenta") tframe = Frame(self.f_cont) tframe.pack(expand=0, fill=X) self.cmdVar = StringVar() self.ent_trm = Entry(tframe, textvariable=self.cmdVar, font=dfl_font) self.ent_trm.pack(side=LEFT, expand=1, fill=X) self.ent_trm.bind("<Control-l>", self.__log_handler__) self.ent_trm.bind("<Control-e>", self.__echo_handler__) self.ent_trm.bind("<Control-o>", self.__connect_handler__) self.ent_trm.bind("<Control-c>", self.clear_screen) self.ent_trm.bind("<Control-x>", self.show_hide_cont) self.ent_trm.bind("<Control-m>", lambda *args: self.do_macro("M")) self.ent_trm.bind("<KeyPress>", self.__input_handler__) self.gui_elements = [ self.b_sh, self.b_cls, self.al_echo, self.al_log, self.al_connect, self.mb_macros, self.l_title, self.st_trm, self.ent_trm ] self.show_cont() def add_macro(self, id, title, text=None, function=None, params=None): if text: cmd = lambda *args: self.do_macro(text) if function: user_func = eval(function) if params: params = eval(str(params)) else: params = {} cmd = lambda *args: user_func(self, DEVICES, **params) mb = self.mb_macros.menu.add_command(label=title, command=cmd) def _configure_(self, **args): for e in self.gui_elements: e.configure(args) def __input_handler__(self, *args): for i in args: self.terminal_device_write(i.char) def terminal_device_write(self, *args): for i in args: if self.localEcho: self.display(i, "E") if len(i): if i == "\r" or i == "\n": self.device.write(self.EOL) self.display(self.EOL, "E") self.cmdVar.set("") else: self.device.write(i) self.st_trm.update_idletasks() def __echo_handler__(self, *args): if self.localEcho: self.localEcho = False self.al_echo['relief'] = RAISED self.message("Local Echo OFF") else: self.localEcho = True self.al_echo['relief'] = SUNKEN self.message("Local Echo ON") def __log_handler__(self, *args): try: do_open = self.logFile.closed logname = self.logFile.name except: do_open = True logname = "" if do_open: if self.device.logname: logname = self.device.logname self.message("logfile from config file %s " % logname) else: fd = FileDialog(self) logname = fd.go(logname) try: if self.device.logmode not in "wa": self.device.logmode = "a" _print(3, "force _a_ppend") self.logFile = open(logname, self.device.logmode) self.al_log['relief'] = SUNKEN self.message("Logging ON: %s" % self.logFile.name) except: self.message("Error open logfile: %s" % logname) else: self.message("Logging OFF: %s" % self.logFile.name) self.logFile.flush() self.logFile.close() self.al_log['relief'] = RAISED def __connect_handler__(self, *args): if self.device.isConnected: self.device.disconnect() self.al_connect['relief'] = RAISED self.message("Disconnected") else: try: self.device.connect() self.al_connect['relief'] = SUNKEN self.message("Connected") self.al_connect['fg'] = "black" except: self.device.isConnected = False self.message(str(sys.exc_info()[1])) self.al_connect['fg'] = "red" def clear_screen(self, *args): self.st_trm['state'] = NORMAL self.st_trm.delete("0.0", END) self.st_trm['state'] = DISABLED def set_device(self, device): self.device = device self.device.configure(TxQueue=self.outQueue, RxQueue=self.inQueue) self.update_title(self.device) if self.device.log: self.__log_handler__() if self.device.auto_connect: self.__connect_handler__() def update_title(self, title): self.tVar.set(title) def show_cont(self): self.shVar.set("X") self.f_cont.pack(expand=1, fill=BOTH) def hide_cont(self): self.shVar.set("+") self.f_cont.pack_forget() def show_hide_cont(self, *args): if self.shVar.get() == "X": self.hide_cont() else: self.show_cont() def do_macro(self, *args): if self.localEcho: self.display(args[0] + "\n", "E") self.device.write(args[0] + "\n") def write(self, data): self.outQueue.put((data, None)) def message(self, text, tag='M'): msg = "[%s:%s:%s]\n" % (time.asctime(), self.device.name, text) if self.st_trm.index(AtInsert()).find(".0") < 1: msg = "\n" + msg self.inQueue.put((msg, tag)) def display(self, text, tag=None): self.inQueue.put((text, tag)) def __display__(self, msg, tag=None): self.st_trm['state'] = NORMAL here = self.st_trm.index(AtInsert()) for d in re.split("([\r\v\t\n])", msg): if len(d): if d != '\r': self.st_trm.insert(END, d) if tag: self.st_trm.tag_add(tag, here, AtInsert()) self.st_trm.see(END) self.st_trm['state'] = DISABLED try: self.logFile.write(msg) self.logFile.flush() except: pass
def run(self): # help_window() #def help_window(): from ScrolledText import ScrolledText def gotosettings(*args): text.see(tk.END) text.see(setting_index) def gotoinfo(*args): text.see(tk.END) text.see(info_index) def gotoprereq(*args): text.see(tk.END) text.see(prereq_index) def gotolc(*args): text.see(tk.END) text.see(lc_index) def gototop(*args): text.see(tk.END) text.see(top_index) def gotospec(*args): text.see(tk.END) text.see(spec_index) def show_tcross_cursor(*args): text.config(cursor="tcross") def show_arrow_cursor(*args): text.config(cursor="arrow") self.help_top = tk.Toplevel() self.help_top.title("Help") text = ScrolledText(self.help_top, bg='white') text.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.Y) bold_font = tkFont.Font(text, text.cget("font")) bold_font.configure(weight='bold') txt0 = """ Contents General Information Settings Panel Prerequisites Panel Lightcurve Panel Spectrum Panel \n""" text.insert(tk.END, txt0) # Define tags text.tag_add('settings', "%s-15l+4c" % tk.INSERT, "%s-15l+18c" % tk.INSERT) text.tag_bind('settings', '<Enter>', show_arrow_cursor) text.tag_bind('settings', '<Leave>', show_tcross_cursor) text.tag_config('settings', foreground='blue', underline=1) text.tag_bind('settings', '<Button-1>', gotosettings) text.tag_add('info', "%s-16l+4c" % tk.INSERT, "%s-16l+24c" % tk.INSERT) text.tag_bind('info', '<Enter>', show_arrow_cursor) text.tag_bind('info', '<Leave>', show_tcross_cursor) text.tag_config('info', foreground='blue', underline=1) text.tag_bind('info', '<Button-1>', gotoinfo) text.tag_add('prereq', "%s-14l+4c" % tk.INSERT, "%s-14l+24c" % tk.INSERT) text.tag_bind('prereq', '<Enter>', show_arrow_cursor) text.tag_bind('prereq', '<Leave>', show_tcross_cursor) text.tag_config('prereq', foreground='blue', underline=1) text.tag_bind('prereq', '<Button-1>', gotoprereq) text.tag_add('lc', "%s-13l+4c" % tk.INSERT, "%s-13l+20c" % tk.INSERT) text.tag_bind('lc', '<Enter>', show_arrow_cursor) text.tag_bind('lc', '<Leave>', show_tcross_cursor) text.tag_config('lc', foreground='blue', underline=1) text.tag_bind('lc', '<Button-1>', gotolc) text.tag_add('spec', "%s-12l+4c" % tk.INSERT, "%s-12l+18c" % tk.INSERT) text.tag_bind('spec', '<Enter>', show_arrow_cursor) text.tag_bind('spec', '<Leave>', show_tcross_cursor) text.tag_config('spec', foreground='blue', underline=1) text.tag_bind('spec', '<Button-1>', gotospec) setting_index = '70.0' info_index = '20.0' prereq_index = '100.0' lc_index = '140.0' spec_index = '167.0' top_index = '0.0' text.insert(tk.END, "General information\n") text.tag_add('tag', "%s-1l" % tk.INSERT, tk.INSERT) text.tag_config('tag', font=bold_font, underline=1, justify=tk.CENTER) text.insert(tk.END, "\n") txt1 = """ Latspec is designed to analyze FERMI/LAT data. The tool allows to perform all stages of analysis including event filtering, imaging analysis, spectrum and lightcurve generation and spectral modeling using single graphical interface. The core idea behind the Latspec data analysis is to use ancillary response to account for the PSF spillover, allowing to use small extraction radii to minimize contamination from nearby sources. The tool gives reliable results for well isolated and relatively bright source. However, standard likelihood is recommended in the case of a faint source in a crouded field. Even in this case, Latspec can be helpful to examine your data and to do "quick-and-dirty" analysis. The program allows user to process the LAT data, starting from the data set, extracted from the LAT data server, apply all necessary filters, specify regions and produce intermediary data products necessary for spectral and lightcurve analysis. For source and background region selection the program utilizes DS9 image viewer. The LAT data to be analysed along with some common analysis parameters can be set using the "Settings" panel. Preparation tasks (filtered event list, galactic cube and image generation) are performed at the "Prerequisites" panel. """ text.insert(tk.END, txt1) text.tag_add('prereq', "%s-1l+0c" % tk.INSERT, "%s-1l+15c" % tk.INSERT) text.tag_add('settings', "%s-3l+42c" % tk.INSERT, "%s-3l+52c" % tk.INSERT) txt11 = """ For spectral analysis the program extracts count spectra for specified source and background regions, creates necessary response files and optionally fits spectral model to the data. The application also provides interface to Xspec spectral fitting package for spectral modeling. Lightcurve is calculated using Xspec spectral fitting, i.e. spectrum is extracted and spectral fit is performed for each time interval. Details on how to generate and analyze spectra and lightcurves tasks are given below in "Spectrum" and "Lightcurve" sections respectively. """ text.insert(tk.END, txt11) text.tag_add('lc', "%s-2l+62c" % tk.INSERT, "%s-2l+75c" % tk.INSERT) text.tag_add('spec', "%s-2l+47c" % tk.INSERT, "%s-2l+56c" % tk.INSERT) txt2 = """ The main window of the program has three frames: top, middle (Log) and the bottom frame. The top frame is where analysis is performed by using four panels: "Settings", "Prerequisites", "Lightcurve" and "Spectrum". Buttons at the bottom of the top frame are used to switch between analysis panels. The log window displays various information about analysis steps taken, errors occured, warning, etc. Text lines are color coded for convenience. Namely, the output of different tasks performed is shown in black, errors in red, hints for the next step to take is in blue. The bottom row has four buttons. The "Save log" writes the contents of the log window in a file <basename>.log and the "Clear Log" button clears the log window. "Help" button shows user this help and the "Quit" button quits Latspec. Quitting Latspec may take some noticable time (few seconds) as the program has to perform some clean up steps: shut down all active analysis threads, close any open windows, etc. """ text.insert(tk.END, txt2) text.insert( tk.END, " Back to top" ) text.tag_add('back', "%s-0l-11c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) text.tag_bind('back', '<Enter>', show_arrow_cursor) text.tag_bind('back', '<Leave>', show_tcross_cursor) text.tag_config('back', foreground='red', underline=1) text.tag_bind('back', '<Button-1>', gototop) text.insert(tk.END, "\n") text.insert(tk.END, "Settings Panel\n") text.tag_add('tag', "%s-1l" % tk.INSERT, tk.INSERT) text.insert(tk.END, "\n") txt3 = """ There are four sections in this panel:"Data", "Catalog", "Basename" and "Parameters". The "Data" section has the "..." button which allows to change the data directory. The "Data info" button prints a short synopsis of the data in the Log window. The "Catalog" section allows to specify a 2FGL catalog file using the "..." button. You need to specify catalog only the first time you start the program. Latspec stores information about the latest data and catalog in the resource file (~/.latspecrc) and will load them automatically next time you start the program. The "Flux threshold" entry shows the lower flux limit (given in the FLUX1000 column of 2FGL catalog) which used to display the catalog sources in the DS9 imaging tool (see "Prerequisites" panel section below). Namely, when you increase/decrease the flux threshold, less/more sources appear on the image and in the 2FGL source dropdown menu list in the "Image" section of the "Prerequisites" panel. """ text.insert(tk.END, txt3) text.tag_add('prereq', "%s-1l+0c" % tk.INSERT, "%s-1l+15c" % tk.INSERT) txt31 = """ The "Basename" entry specifes the analysis "basename". It is used as a prefix to all intermediary files created during analysis (i.e. <basename>_ltcube.fits for galactic cube,<basename>_roi.fits for image,etc). The default basename is "latspec". For each analysed source Latspec creates individual directory named <basename>_<sourceid> under the current data directory, where <sourceid> is either source 2FGL catalog name, if 2FGL catalog is specified, or sky location ID ra<RA>_dec<DEC>_rad<radius>. For example, for 3C 273 the directory name will be set to latspec_2FGLJ1229.1+0202 or latspec_r187.35_dec2.00_r3.00 respectiviely. All data products file names, created in the current session will have the same <basename>_<sourceID> structure prefix. The "Parameters" subpanel allows to set or modify parameters binsz, dcostheta, thetamax, zmax and set the IRFS used by FERMI Science Tools (gtltcube,gtpsf, gtrspgen,gtbin,etc.). """ text.insert(tk.END, txt31) text.insert( tk.END, " Back to top" ) text.tag_add('back', "%s-0l-11c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) text.tag_bind('back', '<Enter>', show_arrow_cursor) text.tag_bind('back', '<Leave>', show_tcross_cursor) text.tag_config('back', foreground='red', underline=1) text.tag_bind('back', '<Button-1>', gototop) text.insert(tk.END, "\n") text.insert(tk.END, "\n") text.insert(tk.END, "\n") text.insert(tk.END, "Prerequisites Panel\n") text.tag_add('tag', "%s-1l" % tk.INSERT, tk.INSERT) # text.tag_config('tag',foreground='blue',underline=1,justify=tk.CENTER) text.insert(tk.END, "\n") text.insert(tk.END, "\n") txt4 = """ This panels has three subpanels:"Events","Ltcube" and "Image" used to create or specify filtered event file, galactic cube and image file. These tasks are achieved by clicking on the "Filter Events","Run GTLTcube" and "Extract Image" buttons correspondingly. The Latspec then executes a pipeline of Science Tools to create a specific data product. When the product is created, its name shows up on the left side of a subpanel. Clicking on a button before the pipeline will terminate it and the product will NOT be created. The output event list, cube or image files will have names with the <basename>_ prefix. The "Ltcube" subpanel has an additional button "Load Cube" to specify a pre- existing galactic cube file. The "Image" and "Regions" subpanels present a set of components to extract the image of the region and to specify the source and background regions. The entire region image is extracted by clicking the "Extract image" button. When image is available (i.e. the name of the image file is shown next to the "Extract image" button), the source and background extraction regions can be set using any of the following three ways. First, region coordinates and radii can directly set in the corresponding entries of the "Regions" panel. Second, if catalog is specified, the source region can placed over a catalog source by choosing its name in the "2FGL Source" menu. Third, you can launch DS9 and modify regions in DS9. Namely, clicking on "Run DS9" will launch DS9, load the image, preset the source and background region selection and show the catalog sources in the DS9 window. Modifying the regions in the DS9 image will update the numbers in the "Regions" panel. In parallel, catalog source identification is preformed and, if the center of the current source region is idetified with a catalog source, its name appears in the catalog source menu. (It is worth noting that source names that are used here are taken from ASSOC1 column of the catalog or from the SOURCE column if ASSOC1 is empty. Also, the source identification is done with ANY 2FGL source, while the dropdown menu list shows only sources with 2FGL flux above the flux threshold, set in the "Settings" panel). """ text.insert(tk.END, txt4) text.tag_add('settings', "%s-1l-4c" % tk.INSERT, "%s-1l+6c" % tk.INSERT) txt41 = """ After you are done with producing event/cube/image files and selecting your extraction regions, you can proceed with spectral analysis in the "Spectrum" panel or timing analysis in the "Lightcurve" panel. """ text.insert(tk.END, txt41) text.tag_add('lc', "%s-1l+0c" % tk.INSERT, "%s-1l+12c" % tk.INSERT) text.tag_add('spec', "%s-2l+34c" % tk.INSERT, "%s-2l+44c" % tk.INSERT) text.insert( tk.END, "\n Back to top" ) text.tag_add('back', "%s-0l-11c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) text.tag_bind('back', '<Enter>', show_arrow_cursor) text.tag_bind('back', '<Leave>', show_tcross_cursor) text.tag_config('back', foreground='red', underline=1) text.tag_bind('back', '<Button-1>', gototop) text.insert(tk.END, "\n") text.insert(tk.END, "\n") text.insert(tk.END, "Lightcurve Panel\n") text.tag_add('tag', "%s-1l" % tk.INSERT, tk.INSERT) txt5 = """ The lightcurve calculation is implemented as follows. Photon flux in each time bin is calcilated via spectrum fitting, accounting for PSF spillover. The spectrum is fit with pure power law. The photon flux with its errors is calculated using cflux model (see XSPEC documentaion). The "Lightcurve" binning dropdown menue sets the bin size of the lightcurve to month, week or day (Only these three options for time interval are available). The energy range sets the limits for the photon flux calculation in the XSPEC fits. In the "Powerlaw index" entry the user can specify the starting value of the index and wheather it should be kept fixed during spectral fits. These last two sttings should be modified only in rare cases of a very high statistics (or if you really know what you are doing). The "Show lightcurve" button opens up the QPP plot of the lightcurve. In case the powerlaw index was free during the lightcurve extraction, the index evolution is also plotted. "Save lightcurve" button opens a dialog window which allows you to save the lightcurve file in a location different from the default one (i.e. source directory). """ text.insert(tk.END, txt5) text.insert(tk.END, "\n") text.insert(tk.END, "\n") text.insert( tk.END, " Back to top\n" ) text.tag_add('back', "%s-0l-12c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) text.tag_bind('back', '<Enter>', show_arrow_cursor) text.tag_bind('back', '<Leave>', show_tcross_cursor) text.tag_config('back', foreground='red', underline=1) text.tag_bind('back', '<Button-1>', gototop) text.insert(tk.END, "Spectrum Panel\n") text.tag_add('tag', "%s-1l" % tk.INSERT, tk.INSERT) text.insert(tk.END, "\n") txt5 = """ This panel presents a set of tools to extract and analyse Fermi/LAT energy spectrum. First, you need to extract spectrum and all corresponding response files by clicking on the "Extract Spectrum" button. You can specify the time ranges in the "Time Ranges" window. The spectral files are saved in the product directory, shown in the top line of the panel. After extraction is finished you have an option to examine the spectrum and background versus channels by pressing the "Plot spectrum/background" button. When first calculated, spectral files are stored in a source directory with the file names, which can not be changed or modified within Latspec. To save spectral and response files in location different from the source directory use "Save spectrum" button. "Save spectrum" opens a save file dialog window where you can specify the location and name of the files for spectral products to be saved to. Specifically, when using "Save spectrum" dialog you need to select a directory where you want your files to be stored and in the "Save as" window you need to specify <spectrum_name> string WITHOUT ANY EXTENSIONS. The files will then be saved as <spectrum_name>_src.pha, <spectrum_name>_bkg.pha, <spectrum_name>.rsp,etc. On the "Xspec" subpanel the user can perform the full Xspec fit using one of three models: PowerLaw, LogParabola and Power Law with Exponential Cutoff. The fitting model can be changed using the dropdown menu. If the source is identified as a 2FGL catalog source, the model is preselected by the catalog model. The fit is performed in the energy range specified by the "Fit Energy Range". Moreover, one can apply any Xspec model or execute any Xspec command in the Xspec prompt, designated by "xspec>".Any Xspec output appears in the Log window. """ text.insert(tk.END, txt5) text.insert( tk.END, " Back to top\n" ) text.tag_add('back', "%s-0l-12c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) text.tag_bind('back', '<Enter>', show_arrow_cursor) text.tag_bind('back', '<Leave>', show_tcross_cursor) text.tag_config('back', foreground='red', underline=1) text.tag_bind('back', '<Button-1>', gototop) text.tag_config('tag', font=bold_font) text.tag_config('tag', underline=1) text.tag_config('tag', justify=tk.CENTER) txt_bot = """ Nikolai Shaposhnikov (UMD/CRESST/GSFC/FSSC) [email protected] """ text.insert(tk.END, txt_bot) text.tag_add('bot', "%s-2l-0c" % tk.INSERT, "%s-0l-0c" % tk.INSERT) # text.tag_config('bot',foreground='green',underline=0) button = tk.Button(self.help_top, text="Dismiss", command=self.stop) button.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.Y, padx=20) text["state"] = tk.DISABLED
def confirm_change(self, old_line, new_line, old_tuple=(), new_tuple=(), filepath=""): self.confirm_frame = Tkinter.Toplevel(self.parent_frame, padx=10, pady=10) # self.confirm_frame.grid(padx=10,pady=10) self.confirm_frame.protocol("WM_DELETE_WINDOW", self.confirm_decline) # set screen position... ##x = config.get('dialog_x', (self.parent_frame.winfo_rootx() + 50)) ##y = config.get('dialog_y', (self.parent_frame.winfo_rooty() + 50)) x = self.parent_frame.winfo_rootx() + 50 y = self.parent_frame.winfo_rooty() + 50 if x and y: self.confirm_frame.geometry("+%s+%s" % (x, y)) # bind enter to ok and escape to cancel buttons... self.confirm_frame.bind("<Return>", self.confirm_accept) self.confirm_frame.bind("<Escape>", self.confirm_decline) self.confirm_frame.bind("<Configure>", self.save_pos) # make the new dialog a part of the parent... self.confirm_frame.transient(self.parent_frame) # focus onto the dialog... self.confirm_frame.focus_set() label = Tkinter.Label(self.confirm_frame, text=filepath) label.pack() label = Tkinter.Label(self.confirm_frame, text="Change:") # label.grid(row=row_i, column=0, sticky=W) label.pack() # entry = Tkinter.Text(self.confirm_frame, width=75, height=5) # entry = ScrolledText(self.confirm_frame, width=75, height=5) entry = ScrolledText(self.confirm_frame, height=5, wrap=WORD, pady=2, padx=3) entry.insert(Tkinter.INSERT, old_line.encode("utf-8")) # highlight the text to be changed... if len(old_tuple) == 2: entry.tag_add("found", "1.%s" % (old_tuple[0]), "1.%s+%sc" % (old_tuple[0], old_tuple[1] - old_tuple[0])) entry.tag_config("found", foreground="red") entry.config(state=DISABLED) entry.pack(fill=BOTH, expand=Y) label = Tkinter.Label(self.confirm_frame, text="To:") label.pack() self.new_entry = ScrolledText(self.confirm_frame, height=5, wrap=WORD, pady=2, padx=3) self.new_entry.insert(Tkinter.INSERT, new_line.encode("utf-8")) # highlight the text to be changed... if len(new_tuple) == 2: self.new_entry.tag_add( "found", "1.%s" % (new_tuple[0]), "1.%s+%sc" % (new_tuple[0], new_tuple[1] - new_tuple[0]) ) self.new_entry.tag_config("found", foreground="red") self.new_entry.config(state=DISABLED) self.new_entry.pack(fill=BOTH, expand=Y) btnDisplay = Tkinter.Button(self.confirm_frame, text="Yes", command=self.confirm_accept, default=ACTIVE) # btnDisplay.grid(row=row_i, column=0) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="No", command=self.confirm_decline) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="Cancel", command=self.confirm_cancel) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="Yes to All", command=self.confirm_silence) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) self.confirm_frame.update() try: self.parent_frame.wait_window(self.confirm_frame) except Tkinter.TclError: # sometimes the wait_window fails, I'm not sure why, but it seems to be # safe to just ignore it *shrug* pass self.confirm_frame = None
class Dialog(ui.Dialog): def __init__(self, frame=None): self.parent_frame = frame # set the parent frame self.confirm_frame = None self.make_change = False self.new_line = u"" self.status_text = u"" self.make_silent = False self.cancel = False # set screen position... x = 0 y = 0 ##x = config.get('status_x',0) ##y = config.get('status_y',0) ##if x and y: ## self.parent_frame.geometry("+%d+%d" % (int(x),int(y))) # create a status message widget and bind it to the parent... # self.status_text = ScrolledText(self.parent_frame, height=20, width=80, state=DISABLED) # self.status_text.pack() self.status_text = ScrolledText(self.parent_frame, wrap=WORD, pady=2, padx=3, state=DISABLED) self.status_text.pack(fill=BOTH, expand=Y) self.parent_frame.protocol("WM_DELETE_WINDOW", self.destroy) self.parent_frame.bind("<Escape>", self.destroy) self.parent_frame.bind("<Configure>", self.save_pos) self.status_text.update() def confirm_change(self, old_line, new_line, old_tuple=(), new_tuple=(), filepath=""): self.confirm_frame = Tkinter.Toplevel(self.parent_frame, padx=10, pady=10) # self.confirm_frame.grid(padx=10,pady=10) self.confirm_frame.protocol("WM_DELETE_WINDOW", self.confirm_decline) # set screen position... ##x = config.get('dialog_x', (self.parent_frame.winfo_rootx() + 50)) ##y = config.get('dialog_y', (self.parent_frame.winfo_rooty() + 50)) x = self.parent_frame.winfo_rootx() + 50 y = self.parent_frame.winfo_rooty() + 50 if x and y: self.confirm_frame.geometry("+%s+%s" % (x, y)) # bind enter to ok and escape to cancel buttons... self.confirm_frame.bind("<Return>", self.confirm_accept) self.confirm_frame.bind("<Escape>", self.confirm_decline) self.confirm_frame.bind("<Configure>", self.save_pos) # make the new dialog a part of the parent... self.confirm_frame.transient(self.parent_frame) # focus onto the dialog... self.confirm_frame.focus_set() label = Tkinter.Label(self.confirm_frame, text=filepath) label.pack() label = Tkinter.Label(self.confirm_frame, text="Change:") # label.grid(row=row_i, column=0, sticky=W) label.pack() # entry = Tkinter.Text(self.confirm_frame, width=75, height=5) # entry = ScrolledText(self.confirm_frame, width=75, height=5) entry = ScrolledText(self.confirm_frame, height=5, wrap=WORD, pady=2, padx=3) entry.insert(Tkinter.INSERT, old_line.encode("utf-8")) # highlight the text to be changed... if len(old_tuple) == 2: entry.tag_add("found", "1.%s" % (old_tuple[0]), "1.%s+%sc" % (old_tuple[0], old_tuple[1] - old_tuple[0])) entry.tag_config("found", foreground="red") entry.config(state=DISABLED) entry.pack(fill=BOTH, expand=Y) label = Tkinter.Label(self.confirm_frame, text="To:") label.pack() self.new_entry = ScrolledText(self.confirm_frame, height=5, wrap=WORD, pady=2, padx=3) self.new_entry.insert(Tkinter.INSERT, new_line.encode("utf-8")) # highlight the text to be changed... if len(new_tuple) == 2: self.new_entry.tag_add( "found", "1.%s" % (new_tuple[0]), "1.%s+%sc" % (new_tuple[0], new_tuple[1] - new_tuple[0]) ) self.new_entry.tag_config("found", foreground="red") self.new_entry.config(state=DISABLED) self.new_entry.pack(fill=BOTH, expand=Y) btnDisplay = Tkinter.Button(self.confirm_frame, text="Yes", command=self.confirm_accept, default=ACTIVE) # btnDisplay.grid(row=row_i, column=0) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="No", command=self.confirm_decline) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="Cancel", command=self.confirm_cancel) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) btnDisplay = Tkinter.Button(self.confirm_frame, text="Yes to All", command=self.confirm_silence) # btnDisplay.grid(row=row_i, column=1) btnDisplay.pack(side=LEFT, padx=5, pady=5) self.confirm_frame.update() try: self.parent_frame.wait_window(self.confirm_frame) except Tkinter.TclError: # sometimes the wait_window fails, I'm not sure why, but it seems to be # safe to just ignore it *shrug* pass self.confirm_frame = None def confirm_silence(self): self.make_change = True self.make_silent = True self.clean_up() def confirm_cancel(self): self.make_change = False self.cancel = True self.clean_up() def confirm_accept(self): # self.new_line = self.new_entry.get(1.0, END) self.make_change = True self.clean_up() def confirm_decline(self): self.make_change = False self.clean_up() def clean_up(self, event=None): self.save_pos() # print self.screen_pos_x,self.screen_pos_y self.parent_frame.focus_set() self.confirm_frame.destroy() def destroy(self, event=None): self.parent_frame.destroy() def save_pos(self, event=None): return ## save the screen position of the dialog box # if self.confirm_frame: # try: # config.add('dialog_x',(self.confirm_frame.winfo_rootx() - 4)) # config.add('dialog_y',(self.confirm_frame.winfo_rooty() - 30)) # except: # pass ## save the status box's position # if self.parent_frame: # try: # config.add('status_x',self.parent_frame.winfo_rootx() - 4) # config.add('status_y',self.parent_frame.winfo_rooty() - 30) # except: # pass def update(self, msg): # if the window no longer exists, its text can't be updated try: self.status_text.config(state=NORMAL) # Add the new message self.status_text.insert(END, msg.encode("utf-8") + os.linesep) # Scroll down to the bottom again self.status_text.see(END) # Make the display uneditable self.status_text.config(state=DISABLED) self.status_text.update() except: pass
class TextDemo(Demo): label = 'Text widget displaying source with cheap syntax highlighting:\n'+\ '(Move mouse over text and watch indent-structure highlighting.)' font = ('Courier', 10, 'normal') bold = ('Courier', 10, 'bold') Highlights = {'#.*': Options(foreground='red'), r'\'.*?\'': Options(foreground='yellow'), r'\bdef\b\s.*:':Options(foreground='blue', spacing1=2), r'\bclass\b\s.*\n':Options(background='pink', spacing1=5), r'\b(class|def|for|in|import|from|break|continue)\b': Options(font=bold) } def __init__(self, master): Demo.__init__(self, master) self.text = ScrolledText(self, width=80, height=20, font=self.font, background='gray65', spacing1=1, spacing2=1, tabs='24') self.text.pack(side=TOP, expand=YES, fill=BOTH) content = open(sys.argv[0], 'r').read() self.text.insert(AtEnd(), content) reg = re.compile('([\t ]*).*\n') pos = 0 indentTags = [] while 1: match = reg.search(content, pos) if not match: break indent = match.end(1)-match.start(1) if match.end(0)-match.start(0) == 1: indent = len(indentTags) tagb = 'Tagb%08d' % match.start(0) tagc = 'Tage%08d' % match.start(0) self.text.tag_configure(tagc, background='', relief=FLAT, borderwidth=2) self.text.tag_add(tagb, Char( match.start(0)), Char(match.end(0))) self.text.tag_bind(tagb, '<Enter>', lambda e,self=self,tagc=tagc: self.Enter(tagc)) self.text.tag_bind(tagb, '<Leave>', lambda e,self=self,tagc=tagc: self.Leave(tagc)) del indentTags[indent:] indentTags.extend( (indent-len(indentTags))*[None] ) indentTags.append(tagc) for tag in indentTags: if tag: self.text.tag_add(tag, Char(match.start(0)), Char(match.end(0))) pos = match.end(0) for key,kw in self.Highlights.items(): self.text.tag_configure(key, cnf=kw) reg = re.compile(key) pos = 0 while 1: match = reg.search(content, pos) if not match: break self.text.tag_add(key, Char(match.start(0)),Char(match.end(0))) pos = match.end(0) def Enter(self, tag): self.text.tag_raise(tag) self.text.tag_configure(tag, background='gray80', relief=RAISED) def Leave(self, tag): self.text.tag_configure(tag, background='', relief=FLAT)
class TermWindow(Frame): ## # constructor method def __init__(self, master = None, **cnf): apply(Frame.__init__, (self, master), cnf) self.__create_widgets__() self.rxThread = None self.rxWaitEvent = threading.Event() self.rxWaitPattern = "" self.localEcho = False self.logFile = None self.textQueue = Queue.Queue() self.update_thread_safe() def update_thread_safe(self): try: while 1: element = self.textQueue.get_nowait() if element is not None: msg,tag = element self.__display__(msg,tag) self.st_trm.update_idletasks() except Queue.Empty: pass self.st_trm.after(100, self.update_thread_safe) ## # worker function for threaded reading from the input of # the assigned device # def __thrd_reader__(self): self.waitbuf = "" while 1: try: x = self.device.read() if len(x): cmd = self.cmdVar.get() if len(cmd): self.cmdVar.set("") self.write(x) if self.rxWaitEvent.isSet(): self.waitbuf += x if self.waitbuf.find(self.rxWaitPattern) > -1: self.rxWaitEvent.clear() while not self.rxWaitEvent.isSet(): pass self.rxWaitEvent.clear() except: traceback.print_exc() time.sleep(1) else: # this infinitesimal sleep keeps the GUI update alive time.sleep(.01) def waitfor(self, pattern, maxwait=10): self.waitbuf = "" self.rxWaitPattern = pattern self.rxWaitEvent.set() self.maxwait = 10 while self.rxWaitEvent.isSet() and self.maxwait > 0: time.sleep(1) self.maxwait -= 1 if self.maxwait == 0: self.message("PatternNotFound") self.rxWaitEvent.set() ## # def __create_widgets__(self): dfl_font = 'Courier 10' # the title frame of the terminal self.f_title = Frame(self) self.f_title.pack(fill=BOTH) self.f_title.configure(FRAMEBORDER) self.shVar = StringVar() # show/hide button self.b_sh = Button(self.f_title, textvariable=self.shVar, font=dfl_font) self.b_sh.pack(side=RIGHT) self.b_sh['command'] = self.show_hide_cont # clear screen button self.b_cls = Button(self.f_title, text="CLS", font=dfl_font, underline=0) self.b_cls.pack(side=RIGHT) self.b_cls['command'] = self.clear_screen # echo on/off button self.al_echo = Label(self.f_title, text = "ECHO", relief = RAISED, font = dfl_font, padx='3m', pady='1m', underline=0) self.al_echo.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_echo.bind("<Button-1>", self.__echo_handler__) # log on/off button self.al_log = Label(self.f_title, text = "LOG", relief = RAISED, font = dfl_font, padx='3m', pady='1m', underline=0) self.al_log.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_log.bind("<Button-1>", self.__log_handler__) # device connect button self.al_connect = Label(self.f_title, text = "CONNECT", relief = RAISED, font = dfl_font, padx='3m', pady='1m', underline=1) self.al_connect.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.al_connect.bind("<Button-1>", self.__connect_handler__) self.mb_macros = Menubutton(self.f_title, text = "Macros", relief=RAISED) self.mb_macros.pack(side=RIGHT, padx=1, pady=1, fill=BOTH) self.mb_macros.menu = Menu(self.mb_macros, tearoff = 0) self.mb_macros["menu"] = self.mb_macros.menu # title of terminal window self.tVar = StringVar() self.l_title = Label(self.f_title, text="foo", font=dfl_font) self.l_title['textvariable'] = self.tVar self.l_title.pack(side=LEFT, expand=1, fill=X) self.l_title['width'] = 42 self.update_title("------ XXX ------") # frame for scrolled text window # (this frame handle needs to be kept fo show_hide_cont()) self.f_cont = Frame(self) # IO data scrolled text window self.st_trm = ScrolledText(self.f_cont, height=10, state=DISABLED, wrap=NONE) self.st_trm.pack(expand=1,fill=BOTH) self.st_trm['font'] = dfl_font self.st_trm.tag_config('E', foreground="blue") self.st_trm.tag_config('M', foreground="magenta") tframe = Frame(self.f_cont) tframe.pack(expand = 0, fill = X) self.cmdVar = StringVar() self.ent_trm = Entry(tframe, textvariable=self.cmdVar, font=dfl_font) self.ent_trm.pack(side=LEFT, expand =1, fill = X) self.ent_trm.bind("<Control-l>", self.__log_handler__) self.ent_trm.bind("<Control-e>", self.__echo_handler__) self.ent_trm.bind("<Control-o>", self.__connect_handler__) self.ent_trm.bind("<Control-c>", self.clear_screen) self.ent_trm.bind("<Control-x>", self.show_hide_cont) self.ent_trm.bind("<Control-m>", lambda *args: self.do_macro("M")) self.ent_trm.bind("<KeyPress>", self.__input_handler__) self.gui_elements = [ self.b_sh, self.b_cls, self.al_echo, self.al_log, self.al_connect, self.mb_macros, self.l_title, self.st_trm, self.ent_trm] self.show_cont() def add_macro(self, id, title, text = None, function = None, params = None): if text: cmd = lambda *args: self.do_macro(text) if function: user_func = eval(function) params = eval(str(params)) cmd = lambda *args: user_func(DEVICES, **params) mb = self.mb_macros.menu.add_command(label = title, command = cmd) def _configure_(self,**args): for e in self.gui_elements: e.configure(args) def __input_handler__(self, *args): for i in args: if self.localEcho: self.display(i.char, "E") if len(i.char): if i.char == "\r": self.device.write("\r\n") self.cmdVar.set("") else: self.device.write(i.char) def __echo_handler__(self, *args): if self.localEcho: self.localEcho = False self.al_echo['relief'] = RAISED self.message("Local Echo OFF") else: self.localEcho = True self.al_echo['relief'] = SUNKEN self.message("Local Echo ON") def __log_handler__(self, *args): try: do_open = self.logFile.closed logname = self.logFile.name except: do_open = True logname = "" if do_open: if self.device.logname: logname = self.device.logname self.message("logfile from config file %s " % logname) else: fd = FileDialog(self) logname = fd.go(logname) try: if self.device.logmode not in "wa": self.device.logmode = "a" print "force _a_ppend" self.logFile = open(logname, self.device.logmode) self.al_log['relief'] = SUNKEN self.message("Logging ON: %s" % self.logFile.name) except: self.message("Error open logfile: %s" % logname) else: self.message("Logging OFF: %s" % self.logFile.name) self.logFile.flush() self.logFile.close() self.al_log['relief'] = RAISED def __connect_handler__(self, *args): if self.device.isConnected: self.device.disconnect() self.al_connect['relief'] = RAISED self.message("Disconnected") else: try: self.device.connect() self.al_connect['relief'] = SUNKEN self.message("Connected") self.al_connect['fg'] = "black" except: self.device.isConnected = False self.message( str(sys.exc_info()[1]) ) self.al_connect['fg'] = "red" def clear_screen(self, *args): self.st_trm['state'] = NORMAL self.st_trm.delete("0.0",END) self.st_trm['state'] = DISABLED def set_device(self,device): self.device = device self.rxThread = threading.Thread(target = self.__thrd_reader__) # if a thread is not a daemon, the program needs to join all self.rxThread.setDaemon(1) print "**",self.device, self.device.name self.rxThread.setName("GUI_RX_%s" % self.device.name) self.rxThread.start() self.update_title(self.device) # if self.device.echo: self.localEcho = 0 self.__echo_handler__() if self.device.log: self.__log_handler__() if self.device.auto_connect: self.__connect_handler__() def update_title(self, title): self.tVar.set(title) def show_cont(self): self.shVar.set("X") self.f_cont.pack(expand=1,fill=BOTH) def hide_cont(self): self.shVar.set("+") self.f_cont.pack_forget() def show_hide_cont(self, *args): if self.shVar.get() == "X": self.hide_cont() else: self.show_cont() def do_macro(self, *args): if self.localEcho: self.display(args[0] + "\n", "E") self.device.write(args[0]+ "\n") def write(self, data): self.textQueue.put((data, None)) def message(self, text, tag='M'): msg = "[%s:%s:%s]\n" % (time.asctime(),self.device.name, text) if self.st_trm.index(AtInsert()).find(".0") < 1: msg = "\n" + msg self.textQueue.put((msg, tag)) def display(self, text, tag = None): self.textQueue.put((text, tag)) def __display__(self, msg, tag = None): self.st_trm['state'] = NORMAL here = self.st_trm.index(AtInsert()) for d in re.split("([\r\v\t\n])", msg): if len(d): if d != '\r': self.st_trm.insert(END, d) if tag: self.st_trm.tag_add(tag, here, AtInsert()) self.st_trm.see(END) self.st_trm['state'] = DISABLED try: self.logFile.write(msg) self.logFile.flush() except: pass
class TextBox: def __init__(self): self.WIDTH = 600 self.HEIGHT = 800 self.FONT = "helvetica" self.FONT_SIZE = 12 # colours specified as RGB fractions self.bg_input = [1, 1, 1] self.fg_input = [0, 0, 0] self.bg_article = [0, 0, 0] self.fg_min_article = [0.5, 0.5, 0.5] self.fg_max_article = [0.9, 0.9, 0.9] self.fg_solution_article = [1, 1, 1] #[0.3, 0.5, 1.0] #[1, 0.7, 0.4] invert = False if invert: self.bg_input = [1. - v for v in self.bg_input] self.fg_input = [1. - v for v in self.fg_input] self.bg_article = [1. - v for v in self.bg_article] self.fg_min_article = [1. - v for v in self.fg_min_article] self.fg_max_article = [1. - v for v in self.fg_max_article] self.text = "" # what is shown in the box self.allText = "" # the text for the entire article self.sentences = [] # list of sentences in article # dictionary mapping from size to k-hot encoding indicating # which sentences are in the summary self.solutions = [] # (not used) how much weight is put on each sentence self.weights = [] self.only_summary = True self.summary_size = 1 self.summary_coherence = 0.0 self.summary_independence = 0.8 self.summarizer = Summarizer(parent=self) self.root = Tk() self.draw(init=True) #self.root.mainloop() def draw(self, init=False): if init: # show main article body self.tk_article = ScrolledText(self.root) # let user paste and enter text self.tk_user_input = ScrolledText(self.root) self.tk_summary_size_scale = Scale(self.root) self.tk_summary_size_scale_label = Label(self.root, text="Length") self.tk_summary_coherence_scale = Scale(self.root) self.tk_summary_coherence_scale_label = Label(self.root, text="Coherence") self.tk_summary_independence_scale = Scale(self.root) self.tk_summary_independence_scale_label = Label( self.root, text="Independence") self.tk_toggle_view = Button(self.root, text="more", command=self.handleToggleView) self.tk_recalculate = Button(self.root, text="Update", command=self.handleRecalculate) self.root.geometry("%dx%d" % (self.WIDTH, self.HEIGHT)) self.root.title("QuickReader V4") self.tk_article.configure(width=25, height=6, bd=0, highlightthickness=0, wrap="word", font=self.FONT) self.tk_user_input.configure(width=25, height=3, bd=0, highlightthickness=0, wrap="word", font=self.FONT) self.tk_summary_size_scale.configure( bd=0, from_=0, to=20, orient=HORIZONTAL, sliderrelief=FLAT, command=lambda event: self.handleSlider( self.tk_summary_size_scale.get())) ###### self.tk_summary_coherence_scale.configure( bd=0, from_=0, to=1, orient=HORIZONTAL, sliderrelief=FLAT, resolution=0.05, command=lambda event: self.handleCoherenceSlider( self.tk_summary_coherence_scale.get())) self.tk_summary_coherence_scale.set(self.summary_coherence) ###### self.tk_summary_independence_scale.configure( bd=0, from_=0, to=1.5, orient=HORIZONTAL, sliderrelief=FLAT, resolution=0.05, command=lambda event: self.handleIndependenceSlider( self.tk_summary_independence_scale.get())) self.tk_summary_independence_scale.set(self.summary_independence) # set colours self.root.configure(background="black") self.tk_summary_size_scale.configure(troughcolor="#444444", fg="black", background="white", activebackground="#bbbbbb") self.tk_summary_coherence_scale.configure( troughcolor="#444444", fg="black", background="white", activebackground="#bbbbbb") self.tk_summary_independence_scale.configure( troughcolor="#444444", fg="black", background="white", activebackground="#bbbbbb") self.tk_article.configure(bg=toHex(self.bg_article), fg="white", insertbackground="blue") self.tk_article.vbar.configure(bg="white", width=10, troughcolor="black") self.tk_user_input.configure(bg=toHex(self.bg_input), fg=toHex(self.fg_input), insertbackground="blue") self.tk_user_input.vbar.configure(bg="white", width=10, troughcolor="black") self.tk_user_input.focus() self.tk_user_input.bind("<KeyRelease-Return>", (lambda event: self.handleUserInput( self.tk_user_input.get("0.0", END)))) self.root.bind("<Configure>", self.resize) def setText(self, text, redraw=False): self.text = text if redraw: self.updateArticleInfo() def setSentences(self, sentences, redraw=False): self.sentences = sentences if redraw: self.updateArticleInfo() def setSolutions(self, solutions, redraw=False): self.solutions = solutions if redraw: self.updateArticleInfo() def setWeights(self, weights, redraw=False): self.weights = weights if redraw: self.updateArticleInfo() def handleToggleView(self): print("View toggle!") self.only_summary = not self.only_summary if self.only_summary: self.tk_toggle_view.configure(text="more") else: self.tk_toggle_view.configure(text="less") self.updateSummary() def handleRecalculate(self): print("Update!") self.handleUserInput(self.allText) def handleSlider(self, value): print("Slider:", value) self.summary_size = value self.updateSummary() def handleCoherenceSlider(self, value): print("Coherence Slider:", value) self.summary_coherence = value #self.updateSummary() def handleIndependenceSlider(self, value): print("Independence Slider:", value) self.summary_independence = value #self.updateSummary() def updateSummary(self): l = self.summary_size if self.only_summary and l != 0: self.setText('\n\n'.join([ self.sentences[i] for i in range(len(self.sentences)) if self.solutions[l][i] == 1 ])) else: self.setText(self.allText, redraw=False) self.updateArticleInfo() self.setWeights([0. for _ in self.sentences], redraw=True) self.tk_article.yview_moveto(0) #vbar.set(0, 0) #configure(jump=0) def handleUserInput(self, inStr): self.tk_user_input.delete("0.0", END) if inStr.strip() == "": return text = inStr text = ''.join([ch for ch in text if ord(ch) < 128]) self.setText(text, redraw=False) self.setSolutions([], redraw=False) self.setWeights([], redraw=True) text, sentences, solutions = self.summarizer.summarize( text, coherence_weight=self.summary_coherence, independence_weight=self.summary_independence, size_weight=1., beam_width=3, hard_size_limit=None) self.allText = text self.sentences = sentences self.solutions = solutions self.solutions[0] = [1. for _ in sentences] # get max length for summary max_len = max(solutions.keys()) set_len = min(max_len, 3) self.tk_summary_size_scale.configure(from_=0, to=max_len) self.tk_summary_size_scale.set(set_len) self.summary_size = set_len # text: all the text in one long string # sentences: the text split up into a list of sentences # solution: dictionary mapping summary size to a one-hot vector over the sentences, indicating # which sentences are included in the summarization # the text should be the same, but update it anyways since it needs to contain the # exact same stuff as the sentences self.updateSummary() #self.updateArticleInfo() def resize(self, event=[]): LINEH = 20.0 pixelX = self.root.winfo_width() pixelY = self.root.winfo_height() bf = 5 # buffer size in pixels # update find_icon, wiki_icon, and graph_icon # set toggle and recalculate button toggleW = 50 toggleH = 35 * 1 self.tk_toggle_view.place(x=pixelX - toggleW, y=0, width=toggleW, height=toggleH) updateW = 50 updateH = 35 * 2 self.tk_recalculate.place(x=pixelX - updateW, y=toggleH, width=updateW, height=updateH) buttonH = toggleH + updateH labelW = 90 # set position of size scale scaleW = pixelX - updateW - labelW scaleH = 35 self.tk_summary_size_scale.place(x=labelW, y=0, width=scaleW, height=scaleH) self.tk_summary_size_scale_label.place(x=0, y=0, width=labelW, height=scaleH) # set position of coherence scale coherenceW = pixelX - updateW - labelW coherenceH = 35 self.tk_summary_coherence_scale.place(x=labelW, y=scaleH, width=scaleW, height=scaleH) self.tk_summary_coherence_scale_label.place(x=0, y=scaleH, width=labelW, height=coherenceH) # set position of independence scale independenceW = pixelX - updateW - labelW independenceH = 35 self.tk_summary_independence_scale.place(x=labelW, y=scaleH + coherenceH, width=scaleW, height=scaleH) self.tk_summary_independence_scale_label.place(x=0, y=scaleH + coherenceH, width=labelW, height=independenceH) # update user input inputW = pixelX inputH = int(3.0 * LINEH) self.tk_user_input.place(x=0, y=pixelY - inputH, width=inputW, height=inputH) # update article articleW = pixelX articleH = pixelY - inputH - scaleH - coherenceH - independenceH self.tk_article.place(x=0, y=scaleH + coherenceH + independenceH, width=articleW, height=articleH) def updateArticleInfo(self): self.articleClear() self.articleCat(self.text) if self.weights != []: self.articleColour() self.root.update() def articleClear(self): self.tk_article.delete("1.0", END) self.tk_article.update() self.root.update() return def articleCat(self, inStr): self.tk_article.insert(END, inStr) self.tk_article.yview(END) def articleColour(self): ''' solution = self.solutions[self.summary_size] allText = self.text #self.tk_article.get('1.0', 'end-1c') # make sure weights are normalised maxW = max(self.weights) minW = min(self.weights) weights = self.weights if maxW != minW: weights = [(v-minW)/(maxW-minW) for v in self.weights] for i in range(len(self.sentences)): if self.only_summary and solution[i] != 1.: continue s = self.sentences[i] if len(s.strip()) == 0: continue tagNameA = ''.join([random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(10)]) L_Size = 12 # if solution[i] == 1 else 10 L_Colour = blend(self.fg_min_article, self.fg_max_article, weights[i]) L_Colour = self.fg_solution_article if solution[i] == 1 else L_Colour countVar = StringVar(self.root) pos = self.tk_article.search(s, "1.0", stopindex="end", count=countVar) self.tk_article.tag_add(tagNameA, pos, "{} + {}c".format(pos, countVar.get())) bolding = "normal" #"bold" if self.solution[i] == 1 else "normal" # font = (self.FONT, L_Size, bolding) self.tk_article.tag_config(tagNameA, foreground=toHex(L_Colour), font=font)#self.FONT+' %s'%(L_Size)) self.root.update() ''' solution = self.solutions[self.summary_size] allText = self.text #self.tk_article.get('1.0', 'end-1c') #print("=========") for i in range(len(self.sentences)): if self.only_summary and solution[i] != 1.: continue s = self.sentences[i] #if len(s.strip()) == 0: # continue #print("- ", s) tagNameA = ''.join([ random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(10) ]) L_Size = self.FONT_SIZE # if solution[i] == 1 else 10 L_Colour = self.fg_solution_article if solution[ i] == 1 else self.fg_min_article #print("\t", L_Colour) countVar = StringVar(self.root) pos = self.tk_article.search(s, "1.0", stopindex="end", count=countVar) self.tk_article.tag_add(tagNameA, pos, "{} + {}c".format(pos, countVar.get())) bolding = "normal" #"bold" if self.solution[i] == 1 else "normal" # font = (self.FONT, L_Size, bolding) self.tk_article.tag_config(tagNameA, foreground=toHex(L_Colour), font=font) #self.FONT+' %s'%(L_Size)) self.root.update()