def create_widgets(self): ''' Creates all widgets. ''' self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) validate_btn = Button(self, text='Validate weight restrictions', command=self.on_validate_weights) validate_btn.grid(row=0, column=0, padx=10, pady=5, sticky=N + W) panel = LabelFrame(self, text='Weight restrictions') panel.columnconfigure(0, weight=1) panel.rowconfigure(0, weight=1) panel.grid(row=1, column=0, padx=5, pady=5, sticky=E + W + S + N) weight_tab_main = VerticalScrolledFrame(panel) weight_tab_main.grid(row=0, column=0, sticky=E + W + S + N) weights_tab = weight_tab_main.interior self.abs_weights = TextForWeights(weights_tab, 'absolute', 'Input1 <= 0.02', self.current_categories, self.params, 'ABS_WEIGHT_RESTRICTIONS') self.abs_weights.grid(row=0, column=0, padx=10, pady=5, sticky=N + W) self.virtual_weights = TextForWeights(weights_tab, 'virtual', 'Input1 >= 0.34', self.current_categories, self.params, 'VIRTUAL_WEIGHT_RESTRICTIONS') self.virtual_weights.grid(row=1, column=0, padx=10, pady=5, sticky=N + W) self.price_ratio_weights = TextForWeights(weights_tab, 'price ratio', 'Input1/Input2 <= 5', self.current_categories, self.params, 'PRICE_RATIO_RESTRICTIONS', True) self.price_ratio_weights.grid(row=2, column=0, padx=10, pady=5, sticky=N + W)
def create_widgets(self): ''' Creates all widgets. ''' self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) validate_btn = Button(self, text='Validate weight restrictions', command=self.on_validate_weights) validate_btn.grid(row=0, column=0, padx=10, pady=5, sticky=N+W) panel = LabelFrame(self, text='Weight restrictions') panel.columnconfigure(0, weight=1) panel.rowconfigure(0, weight=1) panel.grid(row=1, column=0, padx=5, pady=5, sticky=E+W+S+N) weight_tab_main = VerticalScrolledFrame(panel) weight_tab_main.grid(row=0, column=0, sticky=E+W+S+N) weights_tab = weight_tab_main.interior self.abs_weights = TextForWeights(weights_tab, 'absolute', 'Input1 <= 0.02', self.current_categories, self.params, 'ABS_WEIGHT_RESTRICTIONS') self.abs_weights.grid(row=0, column=0, padx=10, pady=5, sticky=N+W) self.virtual_weights = TextForWeights(weights_tab, 'virtual', 'Input1 >= 0.34', self.current_categories, self.params, 'VIRTUAL_WEIGHT_RESTRICTIONS') self.virtual_weights.grid(row=1, column=0, padx=10, pady=5, sticky=N+W) self.price_ratio_weights = TextForWeights(weights_tab, 'price ratio', 'Input1/Input2 <= 5', self.current_categories, self.params, 'PRICE_RATIO_RESTRICTIONS', True) self.price_ratio_weights.grid(row=2, column=0, padx=10, pady=5, sticky=N+W)
class Histogram(Frame): def __init__(self, parent, master): super(Histogram, self).__init__(parent) self.window = master #type:VisAnaWindow self.parent = parent self.param_y = None self.param_x = None self.ds = self.window.ds #type:DataSource self.columnconfigure(1, weight=1) self.rowconfigure(1, weight=1) self.tframe = LabelFrame(self, text="Tooltip") #self.tframe.grid(column=0, row=1, sticky=(S,W,N,E)) self.tframe.rowconfigure(0, weight=1) self.tframe.columnconfigure(0, weight=1) self.tooltip = StringVar(self.tframe) self.tlabel = Label(self.tframe, textvariable=self.tooltip, justify="left", anchor="nw", wraplength=200) self.tlabel.grid(column=0, row=0, sticky=(W, N)) self.select_rect = None if self.ds is None: self.settings = Label( self, text="No data, please open a file via File -> Open") self.settings.grid(column=1, row=1, sticky=(S, W, N, E)) return self.settings = HControls(self, self.ds.base().get_attr_names()) self.settings.grid(column=0, row=0, sticky=(S, W, N, E)) self.apply_settings() ################### # PLOT-EVENT HANDLER ## is called by the plot to confirm if the mouseevent was inside/on a plotted line or a marker def handle_pick(self, line, mouseevent): if mouseevent.button == 1: return self.handle_mouse_event(mouseevent) else: return False, dict() ## is called to to do something when the mouse hovers over the plot and has changed its position. ## if no mousebutton is pressed and no points were selected, a hover-tooltip is shown. ## if the left button is pressed, (re-)draw the selection indicator def handle_hover(self, mouseevent): if not mouseevent.button in [1, 3] and self.select_rect is None: isover, props = self.handle_mouse_event(mouseevent) if isover: self.draw_tooltip(mouseevent, props["ind"]) elif mouseevent.button in [1, 3]: ## handle case if mouse is outside the canvas if mouseevent.xdata == None: xmin = self.mouse_pressed[0] xmax = self.mouse_pressed[0] ymin = self.mouse_pressed[1] ymax = self.mouse_pressed[1] else: xmin = min(mouseevent.xdata, self.mouse_pressed[0]) xmax = max(mouseevent.xdata, self.mouse_pressed[0]) ymin = min(mouseevent.ydata, self.mouse_pressed[1]) ymax = max(mouseevent.ydata, self.mouse_pressed[1]) bbox = (xmin, ymin, xmax, ymax) self.clean_tooltip(True, emit=False) bbox2 = self.ax.transData.transform(bbox) c_height = self.canvas.figure.bbox.height bbox3 = (bbox2[0], c_height - bbox2[1], bbox2[2], c_height - bbox2[3]) self.select_rect = self.canvas.get_tk_widget().create_rectangle( bbox3, dash=".", outline=self.fgcol) ## is called whenever a mousebutton is clicked while the mouse is over the plot. ## if the left button is pushed, we begin to draw a selection area def handle_mouse_down(self, mouseevent): if mouseevent.button in [1, 3]: self.clean_tooltip(True) self.mouse_pressed = (mouseevent.xdata, mouseevent.ydata) ## is called whenever a mouse button is released while hovering over the plot ## if the left button was pressed and there are points within the selection area, select those points and show a ## tooltip containing information about those selected points. If not, clean up. def handle_mouse_up(self, mouseevent): if mouseevent.button in [1, 3]: ## handle case if mouse is outside the canvas if mouseevent.xdata == None: xmin = self.mouse_pressed[0] xmax = self.mouse_pressed[0] ymin = self.mouse_pressed[1] ymax = self.mouse_pressed[1] else: xmin = min(mouseevent.xdata, self.mouse_pressed[0]) xmax = max(mouseevent.xdata, self.mouse_pressed[0]) ymin = min(mouseevent.ydata, self.mouse_pressed[1]) ymax = max(mouseevent.ydata, self.mouse_pressed[1]) if xmin == xmax and ymin == ymax: self.clean_tooltip(True) else: if mouseevent.button == 1: if self.param_x == self.ds.get_time_colname(): xmin = mdates.num2date(xmin) xmax = mdates.num2date(xmax) if self.param_y == self.ds.get_time_colname(): ymin = mdates.num2date(ymin) ymax = mdates.num2date(ymax) self.ds.select("ss_selected", self.param_x, xmin, xmax, "ss_show") self.ds.select("ss_selected", self.param_y, ymin, ymax, "ss_selected") ind = self.ds.df("ss_selected").index.values if len(ind) > 0: text = "Selected area from ({:.1f}; {:.1f})\n\t to ({:.1f}; {:.1f})"\ .format(xmin,ymin,xmax,ymax) self.draw_tooltip(mouseevent, ind, True) self.window.history.add(text) else: self.clean_tooltip(True) else: self.clean_tooltip(True, emit=False) self.ax.set_xlim((xmin, xmax), emit=False) self.ax.set_ylim((ymin, ymax)) self.canvas.draw() ## handle any mouse event where it has to be clarified whether there's a marker under the mouse or not. If so, ## return all index values of the concerning markers. def handle_mouse_event(self, mouseevent, radius=5): """ find the points within a certain radius from the mouse in data coords and attach the index numbers of the found elements which are the data points that were picked """ self.clean_tooltip() # print("PICKER") # print(mouseevent, vars(mouseevent)) if self.param_x == self.ds.get_time_colname( ) or self.param_y == self.ds.get_time_colname(): return False, dict() xydata = self.ax.transData.transform( self.ds.df("ss_show")[[self.param_x, self.param_y]]).transpose() try: mxy = self.ax.transData.transform( [mouseevent.xdata, mouseevent.ydata]) except ValueError: return False, dict() xdata = xydata[0] ydata = xydata[1] mousex = mxy[0] mousey = mxy[1] if mouseevent.xdata is None: return False, dict() d = pd.np.sqrt((xdata - mousex)**2. + (ydata - mousey)**2.) ind = self.ds.df("ss_show").index.values[pd.np.nonzero( pd.np.less_equal(d, radius))[0]] if len(ind) > 0: props = dict(ind=ind) return True, props else: return False, dict() ## draws a tooltip and generates the information it contains from an event with/and a list of index values def draw_tooltip(self, event, ind=None, selected=False): if ind is None: ind = event.ind #event = event.mouseevent # Generate the Tooltip-String selstr = "" if selected: selstr = "selected " if len(ind) is 1: text = selstr + "value:" self.ds.select_ids("ss_selected", ind, "ss_show") for col, cdata in self.ds.df("ss_selected").iteritems(): text += '\n{}: {}'.format(col, cdata[ind[0]]) else: text = selstr + "%s values:" % len(ind) if not selected: self.ds.select_ids("ss_selected", ind, "ss_show") self.ds.aggregate("ss_sel_aggremin", "MIN", in_table="ss_selected") self.ds.aggregate("ss_sel_aggremax", "MAX", in_table="ss_selected") for col, cdata in self.ds.df("ss_sel_aggremin").iteritems(): #text += '\n{}:\n min:\t{}\n max:\t{}'.format(col, cdata[0], self.ds.df("ss_sel_aggremax")[col][0]) text += '\n{}: {} to {}'.format( col, cdata[0], self.ds.df("ss_sel_aggremax")[col][0]) # write the tooltip self.tooltip.set(text) # # Draw the box and write the string on it # # c_height = self.canvas.figure.bbox.height # c_width = self.canvas.figure.bbox.width # y = c_height - event.y # x = event.x # # # get bounding box of a possible tooltip # self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + 2, y, anchor=tk.NW, text=text) # bbox = self.canvas.get_tk_widget().bbox(self.plot_tooltip) # self.canvas.get_tk_widget().delete(self.plot_tooltip) # # # print("bbox:", bbox) # # # make sure the tooltip is within bounds # if bbox[2] > c_width: # adj = -2 # if bbox[3] > c_height: # anchor = tk.SE # else: # anchor = tk.NE # else: # adj = 2 # if bbox[3] > c_height: # anchor = tk.SW # else: # anchor = tk.NW # # get the new bounding box # if anchor is not tk.NW: # =^= the anchor had to be modified # self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + adj, y, anchor=anchor, text=text) # bbox = self.canvas.get_tk_widget().bbox(self.plot_tooltip) # self.canvas.get_tk_widget().delete(self.plot_tooltip) # # self.plot_tooltip_rect = self.canvas.get_tk_widget().create_rectangle(bbox, fill="yellow") # self.plot_tooltip = self.canvas.get_tk_widget().create_text(x + adj, y, anchor=anchor, text=text) ## remove the tooltip if shown def clean_tooltip(self, with_select_rect=False, emit=True): self.tooltip.set("") if with_select_rect and self.select_rect is not None: self.canvas.get_tk_widget().delete(self.select_rect) self.select_rect = None #if emit: #self.action_str = None #TO-DO #self.trigger_update(self.TIMELINE_SELECTION) #### Handle Signals from Outside def apply_settings(self, ev=None): self.lgvar = self.settings.doLog() self.redraw_plot() def redraw_plot(self): self.window.status.set("Redraw Histogram...") self.draw_plot() self.window.status.set("") # the underlying data changed, called by VisAnaWindow.openFile def ds_changed(self): olds = self.ds self.ds = self.window.ds if olds is None: self.settings.destroy() newcols = self.window.calc.get_all_columns(with_time=True, with_custom=False) self.settings = HControls(self, newcols) self.settings.grid(column=0, row=0, sticky=(S, W, N, E)) # a cluster recalculation was processed, called by VisAnaWindow.redo_plots def cluster_changed(self, in_table): self.ds.link("h_show", in_table) self.apply_settings() def draw_plot(self): self.clean_tooltip(True) self.fig = Figure(figsize=(5, 5), dpi=100) #type:Figure self.ax = self.fig.add_subplot(111) #type:Axes self.ax.clear() self.ax.grid(True) tabl = self.ds.get_data("h_show") d = tabl.df() if tabl.centroids is not None: k = len(tabl.centroids) cluster_params = self.window.calc.cluster_params print("hist ", cluster_params) # subplot_num = 0 # print(self.centroids) y_pos = pd.np.arange(len(cluster_params)) # print(y_pos) width = 0.95 / k # colors = ["#d62728", "blue", "green", "brown"] max_y_val = 0 for c in range(0, k): # subplot_num += 1 ystdev = [] one_value_cluster = d.loc[d['_cluster'] == c] # for i in range(0, len(datasource.GRAIN_COLS)): for i in range(0, len(cluster_params)): col = one_value_cluster[cluster_params[i]] stdev = pd.np.std(col) ystdev.append(stdev) cen = [ tabl.centroids[c][i] for i in range(0, len(cluster_params)) ] ## cluster label for legend c_label = "Cluster " + str(c) self.ax.bar( y_pos + width * (c - (k / 2.3)), cen, width, align="center", log=self.lgvar, #alpha=0.75, color=COLORS[c], ecolor="black", yerr=ystdev, label=c_label) for i in range(0, len(cen)): y_val = cen[i] + ystdev[i] #print("y_val:",str(y_val)) if y_val > max_y_val: max_y_val = y_val #print("new max_y_val:",str(max_y_val)) #print("cen:",str(cen)) #print("ysdtev:",str(ystdev)) #print("ypos:",str(y_pos)) self.ax.grid(True) # self.ax.set_xticklabels # self.ax.set_ylim(0, 1, emit=False) #max_y_val = max(map(max, tabl.centroids)) self.ax.set_ylim(0, max_y_val * 1.05, emit=False) self.ax.set_xticks(y_pos + width / 4) self.ax.set_xticklabels(cluster_params) # self.ax.callbacks.connect('xlim_changed', self.handle_view_change) # self.ax.callbacks.connect('ylim_changed', self.handle_view_change) ## add legend self.ax.legend(loc="upper right", shadow=True) else: cluster_params = [ col for col in self.window.calc.get_all_columns(after_calc=True) if col not in ["OutdoorTemp", "RelHumidity", "Daytime"] ] print("hist ", cluster_params) # subplot_num = 0 # print(self.centroids) y_pos = pd.np.arange(len(cluster_params)) # print(y_pos) width = 0.95 / 1 # colors = ["#d62728", "blue", "green", "brown"] # subplot_num += 1 ystdev = [] one_value_cluster = d # for i in range(0, len(datasource.GRAIN_COLS)): for i in range(0, len(cluster_params)): col = one_value_cluster[cluster_params[i]] stdev = pd.np.std(col) ystdev.append(stdev) cen = d[cluster_params].mean(axis=0) print(type(cen), type(cen.max()), cen.max()) ## cluster label for legend self.ax.bar( y_pos + width * (0 - (1 / 2.3)), cen, width, align="center", log=self.lgvar, # alpha=0.75, color="blue", ecolor="black", yerr=ystdev) self.ax.grid(True) # self.ax.set_xticklabels # self.ax.set_ylim(0, 1, emit=False) max_y_val = cen.max() self.ax.set_ylim(0, max_y_val * 1.1, emit=False) self.ax.set_xticks(y_pos + width / 4) self.ax.set_xticklabels(cluster_params) ## add legend # self.ax.legend(loc="upper right", shadow=True) self.canvas = FigureCanvasTkAgg(self.fig, self) #type:FigureCanvasTkAgg self.canvas.get_tk_widget().grid(column=1, row=0, sticky=(N, E, W, S), rowspan=2)
def crea_GUI(self): root = self.root #Menús self.menubar = Menu(root, tearoff=0) m_archivo = Menu(self.menubar, tearoff=0) m_archivo.add_command(label='Abrir', command=self.abrir_dataset, accelerator='Ctrl+O') m_archivo.add_separator() m_archivo.add_command(label='Salir', command=self.cerrar_aplicacion) self.menubar.add_cascade(label='Archivo', menu=m_archivo) m_proyecto = Menu(self.menubar, tearoff=0) m_proyecto.add_command(label='Abrir', command=self.abrir_proyecto, state="disabled") m_proyecto.add_separator() m_proyecto.add_checkbutton(label='Clase al final', onvalue=True, offvalue=False, variable=self.clase_al_final) self.menubar.add_cascade(label='Proyecto', menu=m_proyecto) self.m_configuracion = Menu(self.menubar, tearoff=0) self.m_configuracion.add_command( label='Ruta datasets', command=lambda: self.rutas('datasets')) self.m_configuracion.add_command( label='Ruta resultados', command=lambda: self.rutas('resultados')) self.m_configuracion.add_checkbutton(label='Rutas relativas', onvalue=True, offvalue=False, variable=self.rutas_relativas, command=self.cambia_rutas) self.m_configuracion.add_separator() #TODO Revisar self.v_tamanyo_muestra, no la uso # self.v_tamanyo_muestra = StringVar(root, 'Tamaño muestra ({:,})'.\ # format(self._tamanyo_muestra)) self.m_cfg_tamanyo_muestra = \ self.m_configuracion.add_command(label='Tamaño muestra ({:,})'.\ format(self._tamanyo_muestra), command=lambda: self.tamanyo_muestra(\ self._tamanyo_muestra)) self.m_configuracion.add_separator() self.m_configuracion.add_checkbutton(label='Utiliza sha1', onvalue=True, offvalue=False, variable=self.usa_sha1) self.menubar.add_cascade(label='Configuración', menu=self.m_configuracion) m_ver = Menu(self.menubar, tearoff=0) self.v_tipo_dataset = StringVar(self.root, 'Dataset original') m_ver.add_radiobutton(label='Dataset original', value='Dataset original', variable=self.v_tipo_dataset, command=self.muestra_atributos_y_clase) m_ver.add_radiobutton(label='Dataset sin evidencias incompletas', value='Dataset sin evidencias incompletas', variable=self.v_tipo_dataset, command=self.muestra_atributos_y_clase) m_ver.add_radiobutton(label='Dataset sin atributos constantes', value='Dataset sin atributos constantes', variable=self.v_tipo_dataset, command=self.muestra_atributos_y_clase) m_ver.add_radiobutton(label='Catálogo', value='Catálogo', variable=self.v_tipo_dataset, command=self.muestra_atributos_y_clase) m_ver.add_radiobutton(label='Catálogo Robusto', value='Catálogo Robusto', variable=self.v_tipo_dataset, command=self.muestra_atributos_y_clase) m_ver.add_separator() m_ver.add_checkbutton(label='Log del proceso', onvalue=True, offvalue=False, variable=self.mostrar_proceso, state='disabled') self.menubar.add_cascade(label='Ver', menu=m_ver) root.config(menu=self.menubar) #Dataset de clasificación lf_dataset = LabelFrame(root, text='Dataset de Clasificación') lf_dataset.pack(fill='both', expand=True, padx=5, pady=5) Label(lf_dataset, text='Nombre:').grid(row=0, column=0, sticky='e') self.v_nombre_dataset = StringVar(root, '-------') self.l_nombre_dataset = Label(lf_dataset, textvariable=self.v_nombre_dataset) self.l_nombre_dataset.grid(row=0, column=1, sticky='w') Label(lf_dataset, text='Tamaño:').grid(row=0, column=2, sticky='e') self.v_tamanyo_dataset = StringVar(root, '-------') Label(lf_dataset, textvariable=self.v_tamanyo_dataset).grid(row=0, column=3, sticky='w') Label(lf_dataset, text='Ubicación:').grid(row=1, column=0, sticky='e') self.v_ruta_dataset = StringVar(root, '-------------------------') #TODO Expandir en columnas 1-3, puede ser muy larga Label(lf_dataset, textvariable=self.v_ruta_dataset).grid(row=1, column=1, sticky='w', columnspan=3) #Dataset de clasificación / Muestra lf_dataset_muestra = LabelFrame(lf_dataset, text='Muestra') lf_dataset_muestra.grid(row=2, column=0, sticky='nsew', columnspan=4, padx=5, pady=5) self.sb_v_t_muestra = Scrollbar(lf_dataset_muestra) self.sb_v_t_muestra.grid(row=0, column=1, sticky='sn') self.sb_h_t_muestra = Scrollbar(lf_dataset_muestra, orient='horizontal') self.sb_h_t_muestra.grid(row=1, column=0, sticky='ew') self.t_muestra = Text(lf_dataset_muestra, yscrollcommand=self.sb_v_t_muestra.set, xscrollcommand=self.sb_h_t_muestra.set, bd=0, wrap='none', state='disabled', height=8) self.t_muestra.grid(row=0, column=0, sticky='nswe') self.sb_v_t_muestra.config(command=self.t_muestra.yview) self.sb_h_t_muestra.config(command=self.t_muestra.xview) lf_dataset_muestra.rowconfigure(0, weight=1) lf_dataset_muestra.columnconfigure(0, weight=1) lf_dataset.rowconfigure(2, weight=3) lf_dataset.columnconfigure(1, weight=1) lf_dataset.columnconfigure(3, weight=1) #Dataset de clasificación / Evidencias lf_dataset_evidencias = LabelFrame(lf_dataset, text='Evidencias') lf_dataset_evidencias.grid(row=3, column=0, sticky='nsew', padx=5, pady=5) Label(lf_dataset_evidencias, text='Total:').grid(row=0, column=0, sticky='e') self.v_evidencias_total = StringVar(root, '-------') Label(lf_dataset_evidencias, textvariable=self.v_evidencias_total).\ grid(row=0, column=1, sticky='w') Label(lf_dataset_evidencias, text='Completas:').grid(row=1, column=0, sticky='e') self.v_evidencias_completas = StringVar(root, '-------') Label(lf_dataset_evidencias, textvariable=self.v_evidencias_completas).\ grid(row=1, column=1, sticky='w') Label(lf_dataset_evidencias, text='Únicas:').grid(row=2, column=0, sticky='e') self.v_evidencias_catalogo = StringVar(root, '-------') Label(lf_dataset_evidencias, textvariable=self.v_evidencias_catalogo).\ grid(row=2, column=1, sticky='w') Label(lf_dataset_evidencias, text='Robustas:').grid(row=3, column=0, sticky='e') self.v_evidencias_robustas = StringVar(root, '-------') Label(lf_dataset_evidencias, textvariable=self.v_evidencias_robustas).\ grid(row=3, column=1, sticky='w') #Dataset de clasificación / Atributos lf_dataset_clase_y_atributos = LabelFrame(lf_dataset, text='Clase y atributos') lf_dataset_clase_y_atributos.grid(row=3, column=1, sticky='nsew', columnspan=3, padx=5, pady=5) PROPIEDADES_ATRIBUTOS = ('Nombre', 'count', 'unique', 'top', 'freq', 'mean', 'std', 'min', '25%', '50%', '75%', 'max') self.sb_h_tv_clase = Scrollbar(lf_dataset_clase_y_atributos, orient='horizontal') self.sb_h_tv_clase.grid(row=1, column=0, sticky='ew') self.tv_clase = Treeview(lf_dataset_clase_y_atributos, columns=PROPIEDADES_ATRIBUTOS, height=1, xscrollcommand=self.sb_h_tv_clase.set) self.tv_clase.grid(row=0, column=0, sticky='ew') self.sb_h_tv_clase.config(command=self.tv_clase.xview) self.tv_clase.heading("#0", text="#") self.tv_clase.column("#0", minwidth=30, width=40, stretch=False) self.sb_v_tv_atributos = Scrollbar(lf_dataset_clase_y_atributos) self.sb_v_tv_atributos.grid(row=2, column=1, sticky='sn') self.sb_h_tv_atributos = Scrollbar(lf_dataset_clase_y_atributos, orient='horizontal') self.sb_h_tv_atributos.grid(row=3, column=0, sticky='ew') self.tv_atributos = Treeview(lf_dataset_clase_y_atributos, columns=PROPIEDADES_ATRIBUTOS, yscrollcommand=self.sb_v_tv_atributos.set, xscrollcommand=self.sb_h_tv_atributos.set) self.tv_atributos.grid(row=2, column=0, sticky='nsew') self.tv_atributos.bind('<ButtonRelease-1>', self.selectItem) self.sb_v_tv_atributos.config(command=self.tv_atributos.yview) self.sb_h_tv_atributos.config(command=self.tv_atributos.xview) self.tv_atributos.heading("#0", text="#") self.tv_atributos.column("#0", minwidth=30, width=40, stretch=False) for i in PROPIEDADES_ATRIBUTOS: self.tv_clase.heading(i, text=i) self.tv_clase.column(i, minwidth=50, width=50, stretch=False) self.tv_atributos.heading(i, text=i) self.tv_atributos.column(i, minwidth=50, width=50, stretch=False) lf_dataset_clase_y_atributos.rowconfigure(2, weight=1) lf_dataset_clase_y_atributos.columnconfigure(0, weight=1) lf_dataset.rowconfigure(3, weight=1)