Foros Python

Versión completa: Problema con TREEVIEW en proyecto python MVC
Actualmente estas viendo una versión simplificada de nuestro contenido. Ver la versión completa con el formato correcto.
Hola, soy nuevo en Python pero me defiendo.  [Imagen: tongue.png] He relizado un proyecto, pero quiero hacerlo con una estructura MVC (Modelo, Vista, Controlador). El tema es que quiero sacar un TreeView donde mostrar datos y cuando pulsen en una fila(row) realizar tareas, pero me casca el programa cuando hago click en una cualquier fila y da el siguiente error:



AttributeError: 'Controlador' object has no attribute 'master'



He quitado del proyecto todo y he dejado solo lo que nos interesa, los 3 archivos(clases) Modelo.py, Vista.py y Controlador.py y el TreeView en Vista.



También os dejo un archivo test con un TreeView donde sí que medio funciona.



El tema es recuperar la fila, o el indice de la fila. En cualquier caso un dato de la fila que permita saber cual ha sido seleccionada.



Os paso también todo en .zip



Saludos y Gracias de antemano.

Enrique  [Imagen: smile.png] [Imagen: smile.png] [Imagen: smile.png]






Código:
# MODELO (modelo.py)
class Modelo:

    datos = [1, 'ES', 'España', 34,
            2, 'FR', 'Francia', 33,
            3, 'GR', 'Alemania', 49,
            4, 'IT', 'Italia', 39,
            5, 'GB', 'Reino Unido', 44]

    def __init__(self):
        self.datos.append(5)
        self.datos.append('PT')
        self.datos.append('Portugal')
        self.datos.append(351)

    def set_datos(self, dato, i):
        self.datos[i] = dato[i]

    def get_datos(self, i):
        return self.datos[i]

Código:
# CONTROLADOR (controlador.py)

##############################################################
# APLICACION PYTHON 3                                        #
# Pograma principal                                          #
##############################################################
import sys

from modelo import Modelo
from vista import Vista


class Controlador:

    def __init__(self):
        self.modelo = Modelo()
        self.vista = Vista(self)

    def update_treeview_paises(self):
        for i in range(6):
            print(i)
            self.vista.treeview_paises.insert('', 'end', text=str(self.modelo.get_datos((i*4)+0)),
                                                  values=(self.modelo.get_datos((i*4)+1),
                                                          self.modelo.get_datos((i*4)+2),
                                                          self.modelo.get_datos((i*4)+3)),
                                                          tags=("item",))

    def main(self):
        self.update_treeview_paises()
        self.vista.main()

    def salir(self):
        sys.exit(0)


if __name__ == '__main__':
    controlador = Controlador()
    controlador.main()


Código:
# VISTA (vista.py)

import os
import sys
import tkinter as tk
import tkinter.ttk as ttk

from tkinter import scrolledtext, NSEW, W
from typing import re


class Vista(tk.Tk):
    PAD = 5

    def __init__(self, master):
        super().__init__()
        self.master = master
        self.title("Menú Principal")
        self.config(bg='lightgrey', borderwidth=1)


        # Frame Principal
        self.frm_main = ttk.Frame(self, relief='ridge')
        self.frm_main.pack(padx=self.PAD, pady=self.PAD, fill="both", expand='yes')

        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        w = sw * 0.7
        h = sh * 0.7
        x = (sw - w) / 2
        y = (sh - h) / 2
        self.geometry("%dx%d+%d+%d" % (w, h, x, y))
        self.state('zoomed')  # Maximizada

        # Dibujamos la Barra de Menú
        self.menubar = tk.Menu(self)
        self.config(menu=self.menubar, bg="lightgrey", highlightcolor="white", highlightthickness="1")

        self.filemenu = tk.Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(label="Nuevo")
        self.filemenu.add_command(label="Abrir")
        self.filemenu.add_command(label="Bandeja")
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Salir", command=self.salir)

        self.editmenu = tk.Menu(self.menubar, tearoff=0)
        self.editmenu.add_command(label="Cortar")
        self.editmenu.add_command(label="Copiar")
        self.editmenu.add_command(label="Pegar")

        self.datosmenu = tk.Menu(self.menubar, tearoff=0)

        self.subdatos = tk.Menu(self.datosmenu, tearoff=0)
        self.subdatos.add_command(label="Datos")
        self.datosmenu.add_cascade(label='Recoger Datos',underline=0)

        self.helpmenu = tk.Menu(self.menubar, tearoff=0)
        self.helpmenu.add_command(label="Ayuda")
        self.helpmenu.add_separator()
        self.helpmenu.add_command(label="Acerca de...")

        self.menubar.add_cascade(label="Archivo", menu=self.filemenu)
        self.menubar.add_cascade(label="Editar", menu=self.editmenu)
        self.menubar.add_cascade(label="Datos", menu=self.datosmenu)
        self.menubar.add_cascade(label="Ayuda", menu=self.helpmenu)

        # Titulo
        self.lbl_tit = tk.Label(self.frm_main, fg='black', bg='lightgrey', text="Main Menu")
        self.lbl_tit.pack()

        # Frame de arriba
        self.frm_up = ttk.Frame(self.frm_main)
        self.frm_up.pack(fill='x', padx=self.PAD, pady=self.PAD)

        # Database
        self.frm_dat = tk.LabelFrame(self.frm_up, text="Database")
        self.frm_dat.pack(side='right')
        self.lbl_dat = tk.Label(self.frm_dat, text='')

        self.lbl_dat.pack()

        # Frame de arriba y central
        self.frm_upc = ttk.Frame(self.frm_up)
        self.frm_upc.pack(anchor='center', padx=self.PAD, pady=self.PAD)

        # IA
        self.frm_ia = tk.LabelFrame(self.frm_upc, text="Inteligencia
Artificial")
        self.frm_ia.pack(side='left')
        self.lbl_ia = tk.Label(self.frm_ia, fg='red', text="OFF")
        self.lbl_ia.pack()

        # Frame Cuadro Datos
        self.frm_cpa = ttk.Frame(self.frm_main)
        self.frm_cpa.pack(fill='x', padx=self.PAD, pady=self.PAD)

        # TREEVIEW PAISES
        self.frm_cpf = tk.LabelFrame(self.frm_cpa, text="Paises")
        self.frm_cpf.pack(fill='x', expand='yes', side='left')

        estilo = ttk.Style()
        estilo.configure("Treeview", highlightthickness=0, bd=0, font=('Calibri', 10))
        estilo.configure("Treeview.Heading", font=('Calibri', 10, 'bold'), foreground='#3790D5')

        self.treeview_paises = ttk.Treeview(self.frm_cpf, style="Treeview", columns=('Nº', 'ISO', 'País', 'Codigo'))
        self.treeview_paises.heading('#0', text='Nº')
        self.treeview_paises.heading('#1', text='ISO')
        self.treeview_paises.heading('#2', text='País')
        self.treeview_paises.heading('#3', text='Código')
        self.treeview_paises.column('#0', minwidth=80, width=80, stretch=True, anchor="c")
        self.treeview_paises.column('#1', minwidth=80, width=80, stretch=True, anchor="c")
        self.treeview_paises.column('#2', minwidth=80, width=80, stretch=True, anchor="c")
        self.treeview_paises.column('#3', minwidth=100, width=100, stretch=True, anchor="c")

        self.treeview_paises.tag_bind("item", '<<TreeviewSelect>>', self.selecciono_carrera)
        self.treeview_paises.tag_bind('item', "<Double-Button-1>", self.selecciono_carrera)

        self.treeview_paises.grid(row=0, column=0, sticky=NSEW)

        vsb_a = ttk.Scrollbar(self.frm_cpf, orient="vertical", command=self.treeview_paises.yview)
        vsb_a.grid(row=0, column=1, sticky=NSEW)
        hsb_a = ttk.Scrollbar(self.frm_cpf, orient="horizontal", command=self.treeview_paises.xview)
        hsb_a.grid(row=1, column=0, sticky=NSEW)
        self.treeview_paises.configure(xscrollcommand=hsb_a.set, yscrollcommand=vsb_a.set)

        # Boton x de cerrar ventana la oculta
        self.protocol("WM_DELETE_WINDOW", self.on_closing)


    def main(self):
        self.mainloop()


    def salir(self):
        self.master.salir()
        self.destroy()
        os._exit(0)
        sys.exit(0)

    def on_closing(self):
        self.withdraw()

    def selecciono_carrera(self, event):
        print('Seleccionada Carrera')
        #item = self.treeview_paises.identify('item', event.x, event.y)
        #print("you clicked on", self.treeview_paises.item(item, "text"))

    def filter(self, tree, col, descending):
        print ('Row: {} & Column: {} '.format(
            re.sub('I00', '', str(tree.identify_row(tree.winfo_pointerxy()[1] - tree.winfo_rooty()))),
            re.sub(r'#', '', str(tree.identify_column(tree.winfo_pointerxy()[0] - tree.winfo_rootx())))))

Código:
# TEST (test.py)


import tkinter as tk
from tkinter import ttk


class Application(ttk.Frame):

    def __init__(self, main_window):
        super().__init__(main_window)
        main_window.title("Vista de árbol en Tkinter")

        self.treeview = ttk.Treeview(self)
        # Crear una nueva etiqueta.
        self.treeview.tag_bind("mytag", "<<TreeviewSelect>>", self.item_selected)
        self.treeview.tag_bind("mytag", "<<TreeviewOpen>>",
                              self.item_opened)
        self.treeview.tag_bind("mytag", "<<TreeviewClose>>",
                              self.item_closed)

        # Añadir dos elementos indicando la etiqueta anterior para
        # que respondan a los eventos.
        item = self.treeview.insert("", tk.END, text="Elemento 1", tags=("mytag",))
        item = self.treeview.insert("", tk.END, text="Elemento 2", tags=("mytag",))
        self.treeview.insert(item, tk.END, text="Subelemento 1", tags=("mytag",))

        self.treeview.pack()

        self.pack()

    def item_selected(self, event):
        """Item seleccionado."""
        print("Seleccionado.")
        #item = self.treeview.identify('mytag', event.x, event.y)
        #print("you clicked on", self.treeview.item(item, "mytag"))

    def item_opened(self, event):
        """Item abierto."""
        print("Abierto.")

    def item_closed(self, event):
        """Item cerrado."""
        print("Cerrado.")

main_window = tk.Tk()
app = Application(main_window)
app.mainloop()
Hola Enrique. Creo que el problema está en la línea 17 de tu archivo vista.py; no deberías crear un atributo llamado master, ya que probablemente entre en colisión con un atributo homónimo de la clase tk.Tk. Usando otro nombre debería solucionar el problema. De todas formas, no creo que sea una buena idea que tu vista herede de tk.Tk, mejor podrías hacerla heredar de ttk.Frame.

Saludos!
Efectivamente, hay que quitar un parámetro que se le pasa a la Vista y dejarlo todo así:

Código:
# En el fichero controlador.py

class Controlador:

    def __init__(self):
        self.modelo = Modelo()
        self.vista = Vista()      # Sin parámetros

Código:
# En el fichero vista.py

class Vista(tk.Tk):
    PAD = 5

    def __init__(self):             # Constructor ahora sin parámetros
        super().__init__()
        #  self.master = master     # <-- Esta línea eliminada

Lo de pasarle un Frame directamente lo probaré.

Muchas gracias ¡¡¡

Salkudos