Con cada clic izquierdo del mouse, self.LEFT_MB_Counter se incrementa para que el valor siempre cambie. Quiero que el valor en self.LEFT_MB_Counter se muestre en el campo de entrada self.left_MB_entry pero no puedo lograrlo.

¿Cómo puedo hacer que el campo de entrada se actualice y muestre siempre el valor actual en self.LEFT_MB_Counter?

from win32api import GetKeyState
import tkinter.ttk
import tkinter


class MainApplication:
    """Class that creates the widgets and window."""
    def __init__(self, master):
        """Method that creates the class constructor."""
        self.master = master
        self.var = tkinter.IntVar(value=0)   
        self.left_MB_entry = self.Entry(self.var)
        self.left_MB_entry.grid()

    def Entry(self, text_var, justify="center"):
        """Method that defines a default entry field."""
        entry = tkinter.ttk.Entry(self.master, textvariable=text_var, justify=justify)
        return entry

class MouseCounter:
    """Class that counts mouse button clicks."""
    def __init__(self):
        """Method that creates the class constructor."""
        self.LEFT_MB = 0x01  # Virtual-key code from Microsoft for LEFT MButton
        self.Old_State_LEFT_MB = GetKeyState(self.LEFT_MB)  # LEFT MButton Down = -127 or -128, MButton Up = 0 or 1
        self.LEFT_MB_Counter = 0  # Initialize to 0

    def count(self):
        # Following block of code monitors LEFT MButton
        New_State_LEFT_MB = GetKeyState(self.LEFT_MB)
        if New_State_LEFT_MB != self.Old_State_LEFT_MB:  # Button state changed
            self.Old_State_LEFT_MB = New_State_LEFT_MB
            print(New_State_LEFT_MB)
            if New_State_LEFT_MB < 0:
                self.LEFT_MB_Counter += 1
                print("Count:", self.LEFT_MB_Counter)
                print('Left Button Pressed')
            else:
                print('Left Button Released')
        root.after(1, self.count)


root = tkinter.Tk()
root.style = tkinter.ttk.Style()
#  ('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
root.style.theme_use("clam")
APP = MainApplication(root)  # Create object instance of the user interface
root.after(0, MouseCounter().count())
root.mainloop()  # Display the user interface
0
probat 25 feb. 2018 a las 22:06

3 respuestas

La mejor respuesta

Resumen de la solución

La raíz del problema es simplemente que la instancia de MouseCounter no tiene referencia a la aplicación y, por lo tanto, no puede afectar nada en la aplicación. Esto no es algo exclusivo de tkinter, es solo un principio fundamental de Python. Para cambiar un objeto, necesita una referencia a un objeto.

Una vez que se asegura de que la instancia de MouseCounter tiene una referencia a la instancia de MainApplication, el problema se vuelve bastante trivial de resolver.

Detalles de la solución

Lo primero que debe hacer es asociar correctamente var con el widget de entrada. Su código lo pasa como un argumento posicional que no es la forma correcta de hacerlo. Debe asignar la variable al atributo textvariable:

    self.var = tkinter.IntVar(value=0)   
    self.left_MB_entry = self.Entry(textvariable=self.var)

A continuación, debe asegurarse de que MouseCounter pueda pasar una instancia de la aplicación principal:

class MouseCounter:
    """Class that counts mouse button clicks."""
    def __init__(self, master):
        self.master = master
        ...

Cuando cree la instancia, pase APP como la instancia de MainApplication:

APP = MainApplication(root)
mc = MouseCounter(APP) 
root.after(0, mc.count)

A continuación, simplemente necesita actualizar var desde su contador:

def count(self):
    ...
        if New_State_LEFT_MB < 0:
            ...
            self.master.var.set(self.LEFT_MB_Counter)
            ...

Nota: tiene un ligero malentendido sobre cómo funciona after. No afecta el código, pero si lo va a usar, debe usarlo correctamente.

Considera este código:

root.after(0, MouseCounter().count())

Es funcionalmente idéntico a lo siguiente:

result = MouseCounter().count()
root.after(0, None)

after requiere una referencia para una función, no una llamada de función real. Debe eliminar el paréntesis final:

root.after(0, MouseCounter().count)

Aún mejor sería crear la instancia de MouseCounter y guardar una referencia para que el recolector de basura no la coseche:

counter = MouseCounter()
root.after(0, counter.count)
1
Bryan Oakley 25 feb. 2018 a las 22:54

Puede hacerlo fácilmente actualizando el atributo IntVar llamado APP.var de la instancia MainApplication en el método MouseCounter.count().

Aquí hay una versión modificada de su código que muestra cómo hacer esto:

from win32api import GetKeyState
import tkinter.ttk
import tkinter


class MainApplication:
    """Class that creates the widgets and window."""
    def __init__(self, master):
        """Method that creates the class constructor."""
        self.master = master
        self.var = tkinter.IntVar(value=0)
        self.left_MB_entry = self.Entry(self.var)
        self.left_MB_entry.grid()

    def Entry(self, text_var, justify="center"):
        """Method that defines a default entry field."""
        entry = tkinter.ttk.Entry(self.master, textvariable=text_var, justify=justify)
        return entry

class MouseCounter:
    """Class that counts mouse button clicks."""
    def __init__(self, variable):
        """Method that creates the class constructor."""
        self.LEFT_MB = 0x01  # Virtual-key code from Microsoft for LEFT MButton
        self.Old_State_LEFT_MB = GetKeyState(self.LEFT_MB)  # LEFT MButton Down = -127 or -128, MButton Up = 0 or 1
        self.LEFT_MB_Counter = 0  # Initialize to 0
        self.variable = variable
        self.variable.set(self.LEFT_MB_Counter)

    def count(self):
        # Following block of code monitors LEFT MButton
        New_State_LEFT_MB = GetKeyState(self.LEFT_MB)
        if New_State_LEFT_MB != self.Old_State_LEFT_MB:  # Button state changed
            self.Old_State_LEFT_MB = New_State_LEFT_MB
            print(New_State_LEFT_MB)
            if New_State_LEFT_MB < 0:
                self.LEFT_MB_Counter += 1
                self.variable.set(self.LEFT_MB_Counter)
                print("Count:", self.LEFT_MB_Counter)
                print('Left Button Pressed')
            else:
                print('Left Button Released')
        root.after(1, self.count)


root = tkinter.Tk()
root.style = tkinter.ttk.Style()
#  ('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
root.style.theme_use("clam")
APP = MainApplication(root)  # Create object instance of the user interface
mouse_counter = MouseCounter(APP.var)  # Create an instance outside of mainloop.
root.after(0, mouse_counter.count)
root.mainloop()  # Display the user interface
2
martineau 25 feb. 2018 a las 19:59

Una opción es hacer LEFT_MB_Counter un IntVar que pueda ser utilizado directamente por el Entry:

from multiprocessing import Process, Pipe
import time
from win32api import GetKeyState
import tkinter.ttk
import tkinter


class MainApplication(object):
    """Class that creates the widgets and window."""
    def __init__(self, master):
        """Method that creates the class constructor."""
        self.master = master
        self.mc = MouseCounter(master)
        root.after(0, self.mc.count)
        self.left_MB_entry = self.Entry(self.mc.LEFT_MB_Counter)
        self.left_MB_entry.grid()


    def Entry(self, text_var, justify="center"):
        """Method that defines a default entry field."""
        entry = tkinter.ttk.Entry(self.master, textvariable=text_var, justify=justify)
        return entry

class MouseCounter:
    """Class that counts mouse button clicks."""
    def __init__(self, master):
        """Method that creates the class constructor."""
        self.master = master
        self.LEFT_MB = 0x01  # Virtual-key code from Microsoft for LEFT MButton
        self.Old_State_LEFT_MB = GetKeyState(self.LEFT_MB)  # LEFT MButton Down = -127 or -128, MButton Up = 0 or 1
        self.LEFT_MB_Counter = tkinter.IntVar(0)  # Initialize to 0

    def count(self):
        # Following block of code monitors LEFT MButton
        New_State_LEFT_MB = GetKeyState(self.LEFT_MB)
        if New_State_LEFT_MB != self.Old_State_LEFT_MB:  # Button state changed
            self.Old_State_LEFT_MB = New_State_LEFT_MB
            print(New_State_LEFT_MB)
            if New_State_LEFT_MB < 0:
                self.LEFT_MB_Counter.set(self.LEFT_MB_Counter.get() + 1)
                print("Count:", self.LEFT_MB_Counter.get())
                print('Left Button Pressed')
            else:
                print('Left Button Released')
        self.master.after(1, self.count)


root = tkinter.Tk()
root.style = tkinter.ttk.Style()
root.style.theme_use("clam")
APP = MainApplication(root) 

root.mainloop()  # Display the user interface
2
FJSevilla 25 feb. 2018 a las 21:30