def set(self, lo, hi): if float(lo) <= 0.0 and float(hi) >= 1.0: # grid_remove is currently missing from Tkinter! # self.tk.call("grid", "remove", self) # print('removed') self.grid_remove() else: # print('added') self.grid() Scrollbar.set(self, lo, hi)
class Scrolling_Area(Frame, object): def __init__(self, master, width=None, height=None, mousewheel_speed=2, scroll_horizontally=True, xscrollbar=None, scroll_vertically=True, yscrollbar=None, outer_background=None, inner_frame=Frame, **kw): super(Scrolling_Area, self).__init__(master, **kw) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._clipper = Frame(self, background=outer_background, width=width, height=height) self._clipper.grid(row=0, column=0, sticky=N + E + W + S) self._width = width self._height = height self.innerframe = inner_frame(self._clipper, padx=0, pady=0, highlightthickness=0) self.innerframe.place(in_=self._clipper, x=0, y=0) if scroll_vertically: if yscrollbar is not None: self.yscrollbar = yscrollbar else: self.yscrollbar = Scrollbar(self, orient=VERTICAL) self.yscrollbar.grid(row=0, column=1, sticky=N + S) self.yscrollbar.set(0.0, 1.0) self.yscrollbar.config(command=self.yview) else: self.yscrollbar = None self._scroll_vertically = scroll_vertically if scroll_horizontally: if xscrollbar is not None: self.xscrollbar = xscrollbar else: self.xscrollbar = Scrollbar(self, orient=HORIZONTAL) self.xscrollbar.grid(row=1, column=0, sticky=E + W) self.xscrollbar.set(0.0, 1.0) self.xscrollbar.config(command=self.xview) else: self.xscrollbar = None self._scroll_horizontally = scroll_horizontally self._jfraction = 0.05 self._startX = 0 self._startY = 0 # Whenever the clipping window or scrolled frame change size, # update the scrollbars. self.innerframe.bind('<Configure>', self._on_configure) self._clipper.bind('<Configure>', self._on_configure) self.innerframe.xview = self.xview self.innerframe.yview = self.yview Mousewheel_Support(self).add_support_to(self.innerframe, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar) def update_viewport(self): # compute new height and width self.update() frameHeight = float(self.innerframe.winfo_reqheight()) frameWidth = float(self.innerframe.winfo_reqwidth()) if self._width is not None: width = min(self._width, frameWidth) else: width = self._frameWidth if self._height is not None: height = min(self._height, frameHeight) else: height = self._frameHeight self._clipper.configure(width=width, height=height) def _on_configure(self, event): self._frameHeight = float(self.innerframe.winfo_reqheight()) self._frameWidth = float(self.innerframe.winfo_reqwidth()) # resize the visible part if self._scroll_horizontally: self.xview("scroll", 0, "unit") if self._scroll_vertically: self.yview("scroll", 0, "unit") def xview(self, mode=None, value=None, units=None): value = float(value) clipperWidth = self._clipper.winfo_width() frameWidth = self._frameWidth _startX = self._startX if mode is None: return self.xscrollbar.get() elif mode == 'moveto': # absolute movement self._startX = int(value * frameWidth) else: # mode == 'scroll' # relative movement if units == 'units': jump = int(clipperWidth * self._jfraction) else: jump = clipperWidth self._startX = self._startX + value * jump if frameWidth <= clipperWidth: # The scrolled frame is smaller than the clipping window. self._startX = 0 hi = 1.0 #use expand by default relwidth = 1 else: # The scrolled frame is larger than the clipping window. #use expand by default if self._startX + clipperWidth > frameWidth: self._startX = frameWidth - clipperWidth hi = 1.0 else: if self._startX < 0: self._startX = 0 hi = (self._startX + clipperWidth) / frameWidth relwidth = '' if self._startX != _startX: # Position frame relative to clipper. self.innerframe.place(x=-self._startX, relwidth=relwidth) lo = self._startX / frameWidth self.xscrollbar.set(lo, hi) def yview(self, mode=None, value=None, units=None): value = float(value) clipperHeight = self._clipper.winfo_height() frameHeight = self._frameHeight _startY = self._startY if mode is None: return self.yscrollbar.get() elif mode == 'moveto': self._startY = value * frameHeight else: # mode == 'scroll' if units == 'units': jump = int(clipperHeight * self._jfraction) else: jump = clipperHeight self._startY = self._startY + value * jump if frameHeight <= clipperHeight: # The scrolled frame is smaller than the clipping window. self._startY = 0 hi = 1.0 # use expand by default relheight = 1 else: # The scrolled frame is larger than the clipping window. # use expand by default if self._startY + clipperHeight > frameHeight: self._startY = frameHeight - clipperHeight hi = 1.0 else: if self._startY < 0: self._startY = 0 hi = (self._startY + clipperHeight) / frameHeight relheight = '' if self._startY != _startY: # Position frame relative to clipper. self.innerframe.place(y=-self._startY, relheight=relheight) lo = self._startY / frameHeight self.yscrollbar.set(lo, hi)
class Tk_Table(Frame, object): def __init__(self, master, columns, data=None, command=None, editable=True, sort=True, select_mode=None, autoscroll=True, vscrollbar=True, hscrollbar=False, heading_anchor = CENTER, cell_anchor=W, style=None, scrollbar_background=None, scrollbar_troughcolor=None, height=None, padding=None, adjust_heading_to_content=False, stripped_rows=None, selection_background=None, selection_foreground=None, cell_background=None, cell_foreground=None, cell_font=None, field_background=None, heading_font= None, heading_background=None, heading_foreground=None, cell_pady=2, column_header=True, row_numbers=True, entry_background="#d6d6d6", entry_foreground=None, entry_validatecommand=None, entry_selectbackground="#1BA1E2", entry_selectborderwidth=None, entry_selectforeground=None, entry_font = "TkDefaultFont", rowlabel_anchor=E, rowlabel_minwidth=0, rowlabel_hoverbackground="#FFFFFF",frame_relief=None, frame_borderwidth=None, frame_background=None): frame_kwargs = {} if frame_relief is not None: frame_kwargs["relief"] = frame_relief if frame_borderwidth is not None: frame_kwargs["borderwidth"] = frame_borderwidth if frame_background is not None: frame_kwargs["background"] = frame_background Frame.__init__(self, master, class_="Multicolumn_Listbox", **frame_kwargs) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) self._multicolumn_listbox = Multicolumn_Listbox(self, columns, data=data, command=command, sort=sort, select_mode=select_mode, heading_anchor = heading_anchor, cell_anchor=cell_anchor, style=style, height=height, padding=padding, adjust_heading_to_content=adjust_heading_to_content, stripped_rows=stripped_rows, selection_background=selection_background, selection_foreground=selection_foreground, cell_background=cell_background, cell_foreground=cell_foreground, cell_font=cell_font, field_background=field_background, heading_font=heading_font, heading_background=heading_background, heading_foreground=heading_foreground, cell_pady=cell_pady, headers=column_header) self._multicolumn_listbox.interior.grid(row=0, column=1, sticky= N+E+W+S) self._mousewheel_detection = True if row_numbers: self._row_numbers = Row_Header(self, font=self._multicolumn_listbox.font, row_height=self._multicolumn_listbox.row_height, row_minwidth=rowlabel_minwidth, hover_background = rowlabel_hoverbackground, anchor=rowlabel_anchor, onclick=self._on_click_row_label) self._row_numbers.grid(row=0, column=0, sticky= N+S+E) self._multicolumn_listbox.interior.bind("<Map>", self._place_vertically_row_numbers) else: self._row_numbers = None if editable: self._selected_cell = None self._entry_popup = None self._multicolumn_listbox.interior.bind("<1>", self._edit_cell) def configure(event): """ if self._entry_popup: self._entry_popup.destroy() return """ self._multicolumn_listbox.interior.update_idletasks() self._update_position_of_entry() self._multicolumn_listbox.interior.bind("<Configure>", configure) self._entry_kwargs = entry_kwargs = {} if entry_background is not None: entry_kwargs["background"] = entry_background if entry_foreground is not None: entry_kwargs["foreground"] = entry_foreground if entry_validatecommand is not None: entry_kwargs["validatecommand"] = entry_validatecommand if entry_selectbackground is not None: entry_kwargs["selectbackground"] = entry_selectbackground if entry_selectforeground is not None: entry_kwargs["selectforeground"] = entry_selectforeground if entry_font is not None: entry_kwargs["font"] = entry_font if command is not None: self._command = command self._multicolumn_listbox.interior.bind("<<TreeviewSelect>>", self._on_select) scrollbar_kwargs = {} if scrollbar_background is not None: scrollbar_kwargs["background"] = scrollbar_background if scrollbar_troughcolor is not None: scrollbar_kwargs["throughcolor"] = scrollbar_troughcolor if vscrollbar: if editable: if row_numbers: def yview_command(*args): self._multicolumn_listbox.interior.yview(*args) self._row_numbers.yview(*args) self._update_position_of_entry() else: def yview_command(*args): self._multicolumn_listbox.interior.yview(*args) self._update_position_of_entry() else: if row_numbers: def yview_command(*args): self._multicolumn_listbox.interior.yview(*args) self._row_numbers.yview(*args) else: yview_command = self._multicolumn_listbox.interior.yview self._vbar=Scrollbar(self,takefocus=0, command=yview_command, **scrollbar_kwargs) self._vbar.grid(row=0, column=2, sticky= N+S) def yscrollcommand(first,last): first, last = float(first), float(last) if first <= 0 and last >= 1: if self._mousewheel_detection: if autoscroll: self._vbar.grid_remove() if row_numbers: unbind_function_onMouseWheel(self._multicolumn_listbox.interior) self._mousewheel_detection = False else: if not self._mousewheel_detection: if autoscroll: self._vbar.grid() if row_numbers: bind_function_onMouseWheel(self._row_numbers, "y", binding_widget=self._multicolumn_listbox.interior, unit="pages") self._mousewheel_detection = True self._vbar.set(first, last) if editable: self._update_position_of_entry() self._multicolumn_listbox.interior.config(yscrollcommand=yscrollcommand) if hscrollbar: if editable: def xview_command(*args): self._multicolumn_listbox.interior.xview(*args) self._update_position_of_entry() else: xview_command = self._multicolumn_listbox.interior.xview self._hbar=Scrollbar(self,takefocus=0, command=xview_command, **scrollbar_kwargs) self._hbar.grid(row=1, column=1, sticky= E+W) if autoscroll: if editable: def xscrollcommand(f,l, self=self): make_autoscroll(self._hbar, f, l) self._update_position_of_entry() else: def xscrollcommand(f,l, hbar=self._hbar): make_autoscroll(hbar, f, l) self._multicolumn_listbox.interior.config(xscrollcommand=xscrollcommand) else: self._multicolumn_listbox.interior.config(xscrollcommand=self._hbar.set) def _place_vertically_row_numbers(self, event): self._multicolumn_listbox.interior.unbind("<Map>") item_ID = self._multicolumn_listbox.interior.insert('', 0, values=[""]*self._multicolumn_listbox.number_of_columns) self._multicolumn_listbox.interior.update() x,y,w,h = self._multicolumn_listbox.interior.bbox(item_ID) self._multicolumn_listbox.interior.delete(item_ID) self._row_numbers.grid_configure(pady=(y,0)) def _edit_cell(self, event): '''Executed, when a row is clicked. Opens an entry popup above the cell, so it is possible to select text ''' # close previous popups if self._entry_popup: self._destroy_entry() # what row and column was clicked on item_ID = self._multicolumn_listbox.interior.identify_row(event.y) if not item_ID: return column = self._multicolumn_listbox.interior.identify_column(event.x) if column == "": return # get column position info x,y,width,height = self._multicolumn_listbox.interior.bbox(item_ID, column) # place Entry popup properly column_number = int(column[1:])-1 cell_data = self._multicolumn_listbox.item_ID_to_row_data(item_ID)[column_number] self._entry_popup = Entry(self._multicolumn_listbox.interior, exportselection=True, borderwidth=0, **self._entry_kwargs) self._entry_popup.place(x=x, y=y, width=width, height=height) self._entry_popup.insert(0, cell_data) self._entry_popup.focus_force() self._entry_popup.bind("<Control-a>", lambda event: self._select_all_entry_data) self._entry_popup.bind("<Escape>", lambda event: self._destroy_entry()) self._entry_popup.bind("<FocusOut>", lambda event: self._destroy_entry()) bind_function_onMouseWheel(self._multicolumn_listbox.interior, "y", binding_widget=self._entry_popup, callback=self._update_position_of_entry, unit="pages") if self._row_numbers: bind_function_onMouseWheel(self._row_numbers, "y", binding_widget=self._entry_popup, unit="pages") self._entry_popup.bind("<Return>", self._on_update_cell) self._selected_cell = item_ID, column, column_number def _on_click_row_label(self, index): if self._selected_cell and self._multicolumn_listbox.item_ID(index) == self._selected_cell[0]: self._destroy_entry() self._multicolumn_listbox.toogle_selection(index) def _select_all_entry_data(self): ''' Set selection on the whole text ''' self._entry_popup.selection_range(0, 'end') # returns 'break' to interrupt default key-bindings return 'break' def _destroy_entry(self): self._entry_popup.destroy() self._entry_popup = None self._selected_cell = None def _on_update_cell(self, event): item_ID, column, column_number = self._selected_cell data = self._entry_popup.get() row_data = self._multicolumn_listbox.item_ID_to_row_data(item_ID) row_data[column_number] = data self._multicolumn_listbox.interior.item(item_ID, values=row_data) self._destroy_entry() def _update_position_of_entry(self): if self._selected_cell: bbox = self._multicolumn_listbox.interior.bbox(self._selected_cell[0], self._selected_cell[1]) if bbox == "": self._entry_popup.place_forget() else: x,y,width,height = bbox self._entry_popup.place(x=x, y=y, width=width, height=height) def configure_column(self, index, width=None, minwidth=None, anchor=None, stretch=None): self._multicolumn_listbox.configure_column(self, index, width=None, minwidth=None, anchor=None, stretch=None) def row_data(self, index): return self._multicolumn_listbox.row_data(index) def update_row(self, index, data): self._multicolumn_listbox.update_row(index,data) def delete_row(self, index): self._multicolumn_listbox.delete_row(index) if self._row_numbers: self._row_numbers.pop() def insert_row(self, data, index=None): self._multicolumn_listbox.insert_row(data, index) if self._row_numbers: self._row_numbers.new_label() def column_data(self, index): return self._multicolumn_listbox.column_data(index) def update_column(self, index, data): self._multicolumn_listbox.update_column(index, data) def clear(self): self._multicolumn_listbox.clear() if self._row_numbers: self._row_numbers.delete_labels() def update(self, data): current_number_of_rows = self._multicolumn_listbox.number_of_rows self._multicolumn_listbox.update(data) if self._row_numbers: number_of_rows = len(data) if current_number_of_rows < number_of_rows: for i in range(number_of_rows - current_number_of_rows): self._row_numbers.new_label() else: n_labels = current_number_of_rows - number_of_rows self._row_numbers.pop(n_labels =n_labels) def focus(self, index=None): self._multicolumn_listbox.focus(index) def state(self, state=None): self._multicolumn_listbox.state(state) @property def number_of_rows(self): return self._multicolumn_listbox.number_of_rows @property def number_of_columns(self): return self._multicolumn_listbox.number_of_columns def toogle_selection(self, index): self._multicolumn_listbox.toogle_selection(index) def select_row(self, index): self._multicolumn_listbox.select_row(index) def deselect_row(self, index): self._multicolumn_listbox.deselect_row(index) def deselect_all(self): self._multicolumn_listbox.deselect_all() def set_selection(self, indices): self._multicolumn_listbox.set_selection(indices) @property def selected_rows(self): return self._multicolumn_listbox.selected_rows @property def indices_of_selected_rows(self): return self._multicolumn_listbox.indices_of_selected_rows def delete_all_selected_rows(self): number_of_deleted_rows = self._multicolumn_listbox.delete_all_selected_rows() if self._row_numbers: self._row_numbers.pop(n_labels=number_of_deleted_rows) @property def table_data(self): return self._multicolumn_listbox.table_data @table_data.setter def table_data(self, data): self.update(data) def cell_data(self, row, column): return self._multicolumn_listbox.cell_data(row, column) def update_cell(self, row, column, value): self._multicolumn_listbox.update_cell(row, column, value) def __getitem__(self, index): return self._multicolumn_listbox[index] def __setitem__(self, index, value): self._multicolumn_listbox[index] = value def bind(self, event, handler): self._multicolumn_listbox.bind(event, handler) def sort_by(self, col, descending): self._multicolumn_listbox.sort_by(col, descending)
def set(self, lo, hi): self.tk.call("grid", "remove", self) Scrollbar.set(self, lo, hi)
class Scrolling_Area(Frame, object): def __init__(self, master, width=None, height=None, mousewheel_speed = 2, scroll_horizontally=True, xscrollbar=None, scroll_vertically=True, yscrollbar=None, outer_background=None, inner_frame=Frame, **kw): super(Scrolling_Area, self).__init__(master, **kw) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self._clipper = Frame(self, background=outer_background, width=width, height=height) self._clipper.grid(row=0, column=0, sticky=N+E+W+S) self._width = width self._height = height self.innerframe = inner_frame(self._clipper, padx=0, pady=0, highlightthickness=0) self.innerframe.place(in_=self._clipper, x=0, y=0) if scroll_vertically: if yscrollbar is not None: self.yscrollbar = yscrollbar else: self.yscrollbar = Scrollbar(self, orient=VERTICAL) self.yscrollbar.grid(row=0, column=1,sticky=N+S) self.yscrollbar.set(0.0, 1.0) self.yscrollbar.config(command=self.yview) else: self.yscrollbar = None self._scroll_vertically = scroll_vertically if scroll_horizontally: if xscrollbar is not None: self.xscrollbar = xscrollbar else: self.xscrollbar = Scrollbar(self, orient=HORIZONTAL) self.xscrollbar.grid(row=1, column=0, sticky=E+W) self.xscrollbar.set(0.0, 1.0) self.xscrollbar.config(command=self.xview) else: self.xscrollbar = None self._scroll_horizontally = scroll_horizontally self._jfraction=0.05 self._startX = 0 self._startY = 0 # Whenever the clipping window or scrolled frame change size, # update the scrollbars. self.innerframe.bind('<Configure>', self._on_configure) self._clipper.bind('<Configure>', self._on_configure) self.innerframe.xview = self.xview self.innerframe.yview = self.yview Mousewheel_Support(self).add_support_to(self.innerframe, xscrollbar=self.xscrollbar, yscrollbar=self.yscrollbar) def update_viewport(self): # compute new height and width self.update() frameHeight = float(self.innerframe.winfo_reqheight()) frameWidth = float(self.innerframe.winfo_reqwidth()) if self._width is not None: width = min(self._width, frameWidth) else: width = self._frameWidth if self._height is not None: height = min(self._height, frameHeight) else: height = self._frameHeight self._clipper.configure(width=width, height=height) def _on_configure(self, event): self._frameHeight = float(self.innerframe.winfo_reqheight()) self._frameWidth = float(self.innerframe.winfo_reqwidth()) # resize the visible part if self._scroll_horizontally: self.xview("scroll", 0, "unit") if self._scroll_vertically: self.yview("scroll", 0, "unit") def xview(self, mode = None, value = None, units = None): value = float(value) clipperWidth = self._clipper.winfo_width() frameWidth = self._frameWidth _startX = self._startX if mode is None: return self.xscrollbar.get() elif mode == 'moveto': # absolute movement self._startX = int(value * frameWidth) else: # mode == 'scroll' # relative movement if units == 'units': jump = int(clipperWidth * self._jfraction) else: jump = clipperWidth self._startX = self._startX + value * jump if frameWidth <= clipperWidth: # The scrolled frame is smaller than the clipping window. self._startX = 0 hi = 1.0 #use expand by default relwidth = 1 else: # The scrolled frame is larger than the clipping window. #use expand by default if self._startX + clipperWidth > frameWidth: self._startX = frameWidth - clipperWidth hi = 1.0 else: if self._startX < 0: self._startX = 0 hi = (self._startX + clipperWidth) / frameWidth relwidth = '' if self._startX != _startX: # Position frame relative to clipper. self.innerframe.place(x = -self._startX, relwidth = relwidth) lo = self._startX / frameWidth self.xscrollbar.set(lo, hi) def yview(self, mode = None, value = None, units = None): value = float(value) clipperHeight = self._clipper.winfo_height() frameHeight = self._frameHeight _startY = self._startY if mode is None: return self.yscrollbar.get() elif mode == 'moveto': self._startY = value * frameHeight else: # mode == 'scroll' if units == 'units': jump = int(clipperHeight * self._jfraction) else: jump = clipperHeight self._startY = self._startY + value * jump if frameHeight <= clipperHeight: # The scrolled frame is smaller than the clipping window. self._startY = 0 hi = 1.0 # use expand by default relheight = 1 else: # The scrolled frame is larger than the clipping window. # use expand by default if self._startY + clipperHeight > frameHeight: self._startY = frameHeight - clipperHeight hi = 1.0 else: if self._startY < 0: self._startY = 0 hi = (self._startY + clipperHeight) / frameHeight relheight = '' if self._startY != _startY: # Position frame relative to clipper. self.innerframe.place(y = -self._startY, relheight = relheight) lo = self._startY / frameHeight self.yscrollbar.set(lo, hi)