예제 #1
0
class ScrollableFrame(Frame):
    def __init__(self, root, container, *args, **kwargs):
        super().__init__(container, *args, **kwargs)
        self.numberOfChilds = 0
        self._on_end = lambda: None
        self.canvas = Canvas(self, *args, **kwargs)
        self.scrollbar = Scrollbar(self,
                                   orient="vertical",
                                   command=self.canvas.yview)
        self.scrollable_frame = Frame(self.canvas, *args, **kwargs)
        self.scrollable_frame.bind(
            "<Configure>", lambda e: self.canvas.configure(scrollregion=self.
                                                           canvas.bbox("all")))
        self.canvas.create_window((0, 0),
                                  window=self.scrollable_frame,
                                  anchor="nw")
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.pack(side="left", fill="both", expand=True)
        self.scrollbar.pack(side="right", fill="y")
        root.bind("<MouseWheel>", lambda event: self._on_mousewheel(event))

    '''
        setOnEnd => this method sets the event OnEnd, it recives a lambda _on_end and by default is None

        @_on_end => is the lambda funcion for when the mouseWheel turn to 95%
    '''

    def setOnEnd(self, _on_end):
        self._on_end = _on_end

    '''
        _on_mousewheel => this method is an event reciver for when mouseWheel changes
    '''

    def _on_mousewheel(self, event):
        self.canvas.yview_scroll(int(-event.delta / 120), "units")
        if self.scrollbar.get()[1] > 0.95:
            self._on_end()

    def adicionarListaFrames(self, listaFrames):
        if self.scrollable_frame == len(listaFrames):
            return

        listaFramesAAdicionarados = listaFrames[self.numberOfChilds:self.
                                                numberOfChilds + 10]
        for frameAAdicionar in listaFramesAAdicionarados:
            frameAAdicionar.grid(padx=5)

        self.numberOfChilds = self.numberOfChilds + 10
예제 #2
0
class DataViewer:
    ''' DataViewer
        ======================
        David Miller, 2020
        The University of Sheffield 2020

        Wrapper GUI for a Matplotlib Figure showing the data given on creation. Called by HDF5Viewer when the user
        double clicks on a dataset.

        This GUI is designed a means of performing basic inspections of data stored in HDF5 files. If users want to
        perform something intensive or particular custom, they are advised to do so elsewhere.

        On creation, the DataViewer opens up the file specified by filename and accesses the dataset specified by the
        path dataName. It then decides how to plot the data based on the number of dimensions in the dataset. The used
        plots are as follows using their respective default options:

        |No. dims | Plots    |
        |---------|----------|
        |   1     | Line     |
        |   2     | Contourf |
        |   3     | Contourf |

        In the case of three dimensions, a scrollbar is added on top of the plot and provides a means for the user
        to select which 2D slice of the 3D dataset to show.

        The scrollbar only supports drag operations.

        Any higher dimensional data is ignored.

        The title above the Figure displays the name of the dataset and which index, if any is being displayed.

        Methods
        -------------------------
        on_key_press(event):
            Handler for key presses used on the Matploblib canvas

        scroll_data(self,*args):
            Handler for changing which slice of a 3D dataset is displayed. A new data index is chosen based on where
            the scrollbar cursor is dragged to. The index is calculated by multiplying the scrollbar positon [0,1] by
            the number of frames in the dataset and then converted to an integer.
            Updates the title and canvas on exit. Sets the scrollbar position to where the user left it.
            
            Currently this only has support for clicking and dragging the scrollbar cursor. Other operations are
            ignored.
    '''
    # using filename and name of dataset
    # create a figure and display the data
    def __init__(self,master,dataName,filename):
        self.master = master
        # save creation options
        self.dataName = dataName
        self.filename = filename
        # set title
        self.master.title("Data Viewer")
        # current colormap, default
        self.curr_cmap = getattr(matplotlib.cm,matplotlib.rcParams['image.cmap'])
        # currnt line color
        self.curr_lcol = matplotlib.rcParams['axes.prop_cycle'].by_key()['color'][0]
        ## menu bar for customisation
        # root menu
        menu = Menu(master)
        # options menu
        optsmenu = Menu(menu,tearoff=0)
        # label for graph
        self.title = StringVar()
        self.title.set(f'Displaying {dataName}')
        self.graph_title = Label(master,textvariable=self.title)
        self.graph_title.pack(side=TOP,pady=10,padx=10)
        # create figure
        self.fig = Figure(figsize=(5,5),dpi=100)
        self.axes = self.fig.add_subplot(111)
        # get data from dataset and plot data
        with h5py.File(filename,mode='r') as f:
            self.data_shape = f[dataName].shape
            # if the data is 1D, plot as line
            if len(self.data_shape)==1:
                self.axes.plot(f[dataName][()],self.curr_lcol)
                optsmenu.add_command(label="Set color",command=self.set_color)
            # if data is 2D, plot as filled contour
            elif len(self.data_shape)==2:
                self.axes.contourf(f[dataName][()],cmap=self.curr_cmap)
                optsmenu.add_command(label="Set colormap",command=self.set_colormap)
            # if data is 3D plot as contourf, but also add a scrollbar for navigation
            elif len(self.data_shape)==3:
                optsmenu.add_command(label="Set colormap",command=self.set_colormap)
                # create scroll bar for viewing different slices
                self.plot_scroll=Scrollbar(master,orient="horizontal",command=self.scroll_data)
                # add too gui
                self.plot_scroll.pack(side=TOP,fill=BOTH,expand=True)
                # plot first slice of data
                self.axes.contourf(f[dataName][:,:,0],cmap=self.curr_cmap)
                # create index for current depth index
                self.depth_index = 0
                self.title.set(f"Displaying {dataName} [{self.depth_index}]")
        # add to root menu
        menu.add_cascade(label="Options",menu=optsmenu)
        self.master.config(menu=menu)
        # create canvas to render figure
        self.fig_canvas = FigureCanvasTkAgg(self.fig,self.master)
        # update result
        self.fig_canvas.draw()
        # update canvas to set position and expansion options
        self.fig_canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=True)

        ## add matplotlib toolbar
        self.fig_toolbar = NavigationToolbar2Tk(self.fig_canvas,self.master)
        self.fig_toolbar.update()
        # add to gui. always one row below the canvas
        self.fig_canvas._tkcanvas.pack(side=TOP,fill=BOTH,expand=True)
        ## add key press handlers
        self.fig_canvas.mpl_connect("key_press_event",self.on_key_press)
        # ensure elements are expandable in grid
        num_cols,num_rows = master.grid_size()
        for c in range(num_cols):
            master.columnconfigure(c,weight=1)
        for r in range(num_rows):
            master.rowconfigure(r,weight=1)
        # finish any idle tasks and set the minimum size of the window to cur
        master.update_idletasks()
        master.after_idle(lambda: master.minsize(master.winfo_width(), master.winfo_height()))
        
    # handler for matplotlib keypress events
    def on_key_press(event):
        key_press_handler(event,self.fig_canvas,self.fig_toolbar)

    # handler for using the scrollbar to view slices of data
    def scroll_data(self,*args):
        #print(args)
        # if the user has dragged the scrollbar
        if args[0] == "moveto":
            # args is only one element in this case and is a number between 0 and 1
            # 0 is left most position and 1 is right most position
            # the data index is calculated as this number times depth and converted to integer
            self.depth_index = int(float(args[1])*self.data_shape[2])
            # if index exceeds dataset limits
            # correct it
            if self.depth_index>=self.data_shape[-1]:
                self.depth_index = self.data_shape[-1]-1
            # set the scrollbar position to where the user dragged it to
            self.plot_scroll.set(float(args[1]),(self.depth_index+1)/self.data_shape[2])
            # reopen file
            with h5py.File(self.filename,mode='r') as f:
                self.axes.contourf(f[self.dataName][:,:,self.depth_index],cmap=self.curr_cmap)
            # update canvas
            self.fig_canvas.draw()
            # update title
            self.title.set(f"Displaying {self.dataName} [{self.depth_index}]")

    def set_colormap(self):
        ch = ColormapChooser(self.master).show()
        if ch:
            self.curr_cmap = ch
            self.scroll_data("moveto",str(self.plot_scroll.get()[0]))

    def update_line(self):
        # remove first line from plot
        self.axes.lines.pop(0)
        with h5py.File(self.filename,mode='r') as f:
            self.axes.plot(f[self.dataName][()],self.curr_lcol)
        # update canvas
        self.fig_canvas.draw()

    def set_color(self):
        col = colorchooser.askcolor()
        if col[1]:
            self.curr_lcol = col[1]
            self.update_line()
예제 #3
0
                width=canvasWidth,
                xscrollcommand=xscrollbar.set,
                yscrollcommand=yscrollbar.set)
canvas.place(x=canvasxPosition, y=canvasyPosition)
im = Image.open(imagePath.get())
im_width, im_height = im.size
im = im.resize((int(im_width * imageMagnification.get()),
                int(im_height * imageMagnification.get())), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(im)
image = canvas.create_image(0, 0, anchor=NW, image=photo)
canvas.grid(row=0, column=0, sticky=N + S + E + W)
canvas.config(scrollregion=canvas.bbox(ALL))

xscrollbar.config(command=canvas.xview)
yscrollbar.config(command=canvas.yview)
print(xscrollbar.get())

# ---- zoom function ----
zoomcycle = 0
zimg_id = None

if operatingSystem == 2:

    def zoomer(event):
        global zoomcycle
        if (event.delta > 0):
            if zoomcycle != 4: zoomcycle += 1
        elif (event.delta < 0):
            if zoomcycle != 0: zoomcycle -= 1
        crop(event)
        print("zoooom")
예제 #4
0
class Gui:
    def __init__(self, root):
        global CONFIG, CURPATH
        self.root = root
        root.geometry(
            "%dx%d+0+0" %
            (round(root.winfo_screenwidth() * 0.8),
             round(root.winfo_screenheight() * 0.8)))  #default window size 80%
        root.title('Chords Autoscroll 0.9b')
        root.iconphoto(
            True, PhotoImage(file=os.path.join(CURPATH, "media", "icon.png")))
        root.option_add("*Font", "Helvetica 12")  #default font
        root.protocol("WM_DELETE_WINDOW", self.onClose)

        #general vars
        if CONFIG.get("recent"):
            self.file = FileManager(os.path.dirname(CONFIG.get("recent")[0]))
        else:
            self.file = FileManager()
        self.speed = IntVar()
        self.speed.set(30)
        self.runningScroll = False
        self.settingsPattern = re.compile(
            '\n\nChordsAutoscrollSettings:(\{.*\})')
        self.settings = {}
        #menu
        self.menubar = Menu(self.root)
        self.filemenu = Menu(self.menubar, tearoff=0)
        self.menubar.add_cascade(label="File", menu=self.filemenu)
        self.filemenu.add_command(label="Open...",
                                  command=lambda: self.openNewFile())
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Save (Ctrl+S)",
                                  command=lambda: self.saveFile(True))
        self.filemenu.add_command(label="Save as...",
                                  command=lambda: self.saveFile())
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Close",
                                  command=lambda: self.closeFile())
        #recent files (I should update this at runtime...)
        self.filemenu.add_separator()

        self.recent = Menu(self.filemenu, tearoff=0)
        self.filemenu.add_cascade(label="Recent files", menu=self.recent)

        if CONFIG.get("recent") and len(CONFIG.get("recent")) > 0:
            for n, p in enumerate(CONFIG.get("recent")):
                self.recent.add_command(
                    label=str(n + 1) + ": " + str(p),
                    command=lambda p=p: self.openNewFile(str(p)))

        self.root.config(menu=self.menubar)

        #root frame
        froot = Frame(root)
        froot.pack(side=c.TOP, pady=5, padx=5, fill=c.BOTH, expand=1)

        #main frame
        fmain = Frame(froot)
        fmain.pack(side=c.TOP, fill=c.BOTH, expand=1, anchor=c.N)

        f1 = Frame(fmain)  #text window frame
        f1.pack(side=c.LEFT, fill=c.BOTH, expand=1)

        self.txtMain = Text(
            f1, height=1, width=1, font=("Courier", 14),
            undo=True)  #maybe we can set a DARK MODE to help reading
        self.txtMain.pack(side=c.LEFT, fill=c.BOTH, expand=1)

        self.scrollbar = Scrollbar(f1, command=self.txtMain.yview)
        self.scrollbar.pack(side=c.LEFT, fill=c.Y)
        self.txtMain.config(yscrollcommand=self.scrollbar.set)

        f2 = Frame(fmain, width=100)  #right buttons panel
        f2.pack(side=c.RIGHT, anchor=c.N, padx=5, fill=c.X)
        self.btnPlay = Button(f2,
                              text="Play",
                              relief=c.RAISED,
                              font=(None, 0, "bold"))
        self.btnPlay.pack(side=c.TOP,
                          padx=5,
                          pady=5,
                          fill=c.BOTH,
                          expand=1,
                          ipady=6)
        self.btnPlay['command'] = lambda: self.autoscroll()

        f2_1 = Frame(f2)  #child frame SPEED CONTROL
        f2_1.pack(side=c.TOP, anchor=c.N, pady=(10, 0), fill=c.X)
        Label(f2_1, text="Speed:", font=("*", 8), anchor=c.E).pack(side=c.LEFT,
                                                                   padx=(2, 0))
        Label(f2_1, font=("*", 8), anchor=c.W,
              textvariable=self.speed).pack(side=c.LEFT, padx=(0, 2))
        self.btnSpeedUp = Button(f2, text="+")
        self.btnSpeedUp.pack(side=c.TOP, padx=5, pady=2, fill=c.BOTH, ipady=6)
        self.btnSpeedUp['command'] = lambda: self.speedAdd(1)
        self.btnSpeedDown = Button(f2, text="-")
        self.btnSpeedDown.pack(side=c.TOP,
                               padx=5,
                               pady=(2, 5),
                               fill=c.BOTH,
                               ipady=6)
        self.btnSpeedDown['command'] = lambda: self.speedAdd(-1)

        f2_2 = Frame(f2, width=5)  #child frame FONT SIZE
        #f2_2.pack_propagate(0)
        f2_2.pack(side=c.TOP, anchor=c.N, pady=(10, 0), fill=c.X)
        self.btnTextUp = Button(f2, text="A", font=(None, 18))
        self.btnTextUp.pack(side=c.TOP, padx=5, pady=2, fill=c.BOTH, ipady=0)
        self.btnTextUp['command'] = lambda: self.changeFontSize(1)
        self.btnTextDown = Button(f2, text="A", font=(None, 10))
        self.btnTextDown.pack(side=c.TOP,
                              padx=5,
                              pady=(2, 5),
                              fill=c.BOTH,
                              ipady=8)
        self.btnTextDown['command'] = lambda: self.changeFontSize(-1)

        #credits
        f4 = Frame(root)
        f4.pack(side=c.BOTTOM, pady=0, padx=0, fill=c.X, anchor=c.S)
        Label(
            f4,
            text=
            "© 2017 Pasquale Lafiosca. Distributed under the terms of the Apache License 2.0.",
            fg='#111111',
            bg='#BBBBBB',
            font=('', 9),
            bd=0,
            padx=10).pack(fill=c.X, ipady=2, ipadx=2)

        #shortcuts
        root.bind('<Control-s>', lambda e: self.saveFile(True))
        root.bind('<Control-S>', lambda e: self.saveFile(True))

        def startStop(e):
            if self.runningScroll:
                self.stopAutoscroll()
            else:
                self.autoscroll()

        root.bind('<Control-space>', startStop)

    def openNewFile(self, path=None):
        global CONFIG
        filename = None
        if not path:
            filename = filedialog.askopenfilename(
                initialdir=self.file.getLastUsedDir(),
                filetypes=[("Text files", "*.*")],
                title="Select a text file to open")
        else:
            if os.path.isfile(path):
                filename = path
            else:
                messagebox.showwarning("Not found",
                                       "Selected file was not found. Sorry.")
        if filename:
            self.closeFile()
            self.recent.delete(0, len(CONFIG.get("recent")) - 1)
            CONFIG.insertRecentFile(filename)
            for n, p in enumerate(CONFIG.get("recent")):
                self.recent.add_command(
                    label=str(n + 1) + ": " + str(p),
                    command=lambda p=p: self.openNewFile(str(p)))
            self.file.open(filename)
            self.txtMain.delete(1.0, c.END)
            content = self.file.getContent()
            #Settings
            m = re.search(self.settingsPattern, content)
            if m and m.group(1):
                try:
                    self.settings = json.loads(
                        m.group(1))  # Loads settings from file
                    self.speed.set(self.settings["Speed"])
                    self._setFontSize(self.settings["Size"])
                except:
                    messagebox.showwarning("Warning",
                                           "Cannot load setting data. Sorry.")
                    self._setSettingsData()
            else:
                self._setSettingsData()

            content = re.sub(
                self.settingsPattern, '',
                content)  # Remove settings string before write on screen
            self.txtMain.insert(1.0, content)

    def _setSettingsData(self):
        self.settings = {
            "Speed": self.speed.get(),
            "Size": self._getFontSize()
        }

    def _settingsChanged(self):
        if "Speed" in self.settings and "Size" in self.settings and (
                self.settings["Speed"] != self.speed.get()
                or self.settings["Size"] != self._getFontSize()):
            return True
        else:
            return False

    def saveFile(self, current=False):
        global CONFIG
        if current:
            filename = self.file.getLastFile()
        if not current or not filename:
            filename = filedialog.asksaveasfilename(
                initialdir=self.file.getLastUsedDir(),
                initialfile=self.file.getLastFile(),
                filetypes=[("Text files", "*.txt")],
                title="Select destionation",
                defaultextension=".txt")
        if filename:
            CONFIG.insertRecentFile(filename)
            self.file.open(filename)
            self._setSettingsData()
            self.file.writeContent(
                self.txtMain.get(1.0, c.END)[:-1] +
                "\n\nChordsAutoscrollSettings:" + json.dumps(self.settings))

    def closeFile(self):
        if not self.txtMain.get(1.0, c.END)[:-1]:  # Empty view
            return True
        if self.file.hasChanged(
                hashlib.md5(
                    (self.txtMain.get(1.0, c.END)[:-1] +
                     "\n\nChordsAutoscrollSettings:" +
                     json.dumps(self.settings)
                     ).encode()).hexdigest()) or self._settingsChanged():
            if messagebox.askyesno(
                    "Save changes",
                    "Current document has been modified. Do you want to save changes?"
            ):
                self.saveFile()
        self.txtMain.delete(1.0, c.END)
        self.file.close()
        return True

    def mainloop(self):
        self.root.mainloop()

    def onClose(self):
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            self.closeFile()
            self.root.destroy()

    def _getFontSize(self):
        return font.Font(font=self.txtMain["font"])["size"]

    def _setFontSize(self, newsize):
        f = font.Font(font=self.txtMain["font"])
        f.config(size=newsize)
        self.txtMain.config(font=f)
        self.txtMain.update_idletasks()

    def changeFontSize(self, a):
        f = font.Font(font=self.txtMain["font"])
        newsize = f["size"] + a
        if (newsize < 8 or newsize > 72):  #limits
            return
        f.config(size=newsize)
        self.txtMain.config(font=f)
        self.txtMain.update_idletasks()

    def autoscroll(self):
        if not self.runningScroll and threading.active_count(
        ) < 2:  # Check to avoid multiple scrolling threads
            if (float(self.scrollbar.get()[1]) == 1
                ):  #if we are at the end, let's start from beginning
                self.txtMain.see(1.0)

            self.runningScroll = True
            #INITIAL DELAY
            self.txtMain.mark_set("initialDelay", 1.0)
            self.txtMain.mark_gravity("initialDelay", c.RIGHT)
            self.txtMain.insert("initialDelay",
                                os.linesep * 20)  # SET CONSTANT HERE
            self.txtMain.config(state=c.DISABLED)
            self.txtMain.update_idletasks()
            threading.Thread(target=self.autoscroll_callback,
                             name="ScrollingThread",
                             daemon=True).start()

            self.btnPlay.config(text="Stop",
                                relief=c.SUNKEN,
                                command=lambda: self.stopAutoscroll())
            self.btnPlay.update_idletasks()

    def autoscroll_callback(self):
        while (float(self.scrollbar.get()[1]) < 1 and self.runningScroll):
            self.txtMain.yview(c.SCROLL, 1, c.UNITS)
            end = time.time() + 60 / self.speed.get()
            while (time.time() < end
                   and self.runningScroll):  # trick to stop immediately
                time.sleep(.1)

        if self.runningScroll:
            self.stopAutoscroll()

    def stopAutoscroll(self):
        self.runningScroll = False
        self.txtMain.config(state=c.NORMAL)
        self.txtMain.delete(1.0, "initialDelay")
        self.txtMain.mark_unset("initialDelay")
        self.txtMain.update_idletasks()
        self.btnPlay.config(text="Play",
                            relief=c.RAISED,
                            command=lambda: self.autoscroll())
        self.btnPlay.update_idletasks()

    def speedAdd(self, n):
        n = self.speed.get() + n
        if (n > 0 and n < 1000):
            self.speed.set(n)
예제 #5
0
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

    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._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 _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:
                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()
예제 #6
0
class ScrollableFrame(Frame):
    """
    A wrapper for the Frame class to make it scrollable.
    To make use of this fuctionality, pack anything into the .frame Frame of
    this object
    """
    def __init__(self, master, *args, **kwargs):
        self.master = master
        Frame.__init__(self, self.master, *args, **kwargs)

        # Block resizing. This is required because we bind the resizing code to
        # a <Configure> tag. This would result in one resizing to a specific
        # size to call self.configure_view again but with no arguments.
        self.block_resize = False

        # cached canvas view dimensions
        self._view_dimensions = None

        self.grid(sticky='nsew')
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.vsb = Scrollbar(self, orient='vertical')
        self.hsb = Scrollbar(self, orient='horizontal')

        self.canvas = Canvas(self,
                             bd=0,
                             bg=OSCONST.CANVAS_BG,
                             highlightthickness=0)
        self.canvas.grid(row=0, column=0, sticky='nsew')

        # configure scroll bars
        self.hsb.config(command=self.canvas.xview)
        self.canvas.config(xscrollcommand=self.hsb.set)
        self.hsb.grid(row=1, column=0, sticky='ew')
        self.vsb.config(command=self.canvas.yview)
        self.canvas.config(yscrollcommand=self.vsb.set)
        self.vsb.grid(row=0, column=1, sticky='ns')

        # everything will go in this frame
        self.frame = Frame(self.canvas)
        self.frame.grid(row=0, column=0, sticky='nsew')

        self.canvas.create_window((0, 0), window=self.frame, anchor="nw")

        self.hsb.config(command=self.canvas.xview)
        self.vsb.config(command=self.canvas.yview)

        self.bind("<Configure>", self.configure_view)

        # mouse wheel scroll bindings c/o Mikhail. T on stackexchange:
        # https://stackoverflow.com/a/37858368
        self.bind('<Enter>', self._bind_to_mousewheel)
        self.bind('<Leave>', self._unbind_to_mousewheel)

#region public methods

    def configure_view(self,
                       event=None,
                       move_to_bottom=False,
                       max_size=(None, None),
                       resize_canvas='xy',
                       needs_update=False):
        """
        Configure the size of the scrollable frame.

        Parameters
        ----------
        move_to_bottom : bool
            Whether or not to move the scroll bar all the way to the bottom.
        max_size : tuple of int's
            Maximum size that the scrollable canvas can be. This is a tuple of
            length 2. (X, Y).
        resize_canvas : str
            The directions along which the scrollable canvas should be
            stretched. One of `'x'`, `'y'`, or `'xy'` for the x-direction,
            y-direction, or both respectively.
        """
        if not needs_update:
            return
        x_size = None
        y_size = None
        bbox = self.canvas.bbox(ALL)
        if 'x' in resize_canvas:
            # find the new x size to draw
            if max_size[0] is not None:
                x_size = max(min(max_size[0], bbox[2]),
                             self._view_dimensions[0])
            else:
                x_size = bbox[2]
        if 'y' in resize_canvas:
            # find the new y size to draw
            if max_size[1] is not None:
                y_size = max(min(max_size[1], bbox[3]),
                             self._view_dimensions[1])
            else:
                y_size = bbox[3]

        self._resize_canvas(x_size, y_size)

        xview_size = int(self.canvas.config('width')[4])
        yview_size = int(self.canvas.config('height')[4])

        self.canvas.config(scrollregion=bbox)

        self._view_dimensions = (xview_size, yview_size)

        if move_to_bottom:
            self.canvas.yview_moveto(1.0)

    def reattach(self):
        self.canvas.create_window((0, 0), window=self.frame, anchor="nw")

#region private methods

    def _bind_to_mousewheel(self, event):
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)

    def _on_mousewheel(self, event):
        if self.vsb.get() != (0.0, 1.0):
            if os_name() == 'Windows':
                self.canvas.yview_scroll(int(-1 * (event.delta / 120)),
                                         "units")
            else:
                self.canvas.yview_scroll(-event.delta, "units")

    def _resize_canvas(self, width, height):
        """Resize the canvas to the specified size

        Parameters
        ----------
        width : int
            Width of the frame.
        height : int
            Height of the frame.
        """
        if self.block_resize or (width is None and height is None):
            return
        if width is not None or height is not None:
            self.block_resize = True
        else:
            self.block_resize = False
        canvas_config = dict()
        if width is not None:
            canvas_config['width'] = width
        if height is not None:
            canvas_config['height'] = height
        self.canvas.config(**canvas_config)

    def _unbind_to_mousewheel(self, event):
        self.canvas.unbind_all("<MouseWheel>")