3

I have an application that gets the css3 colour of the pixel your cursor is on, and I would like to use tkinter to display the text in a little window. I following is the tkinter part of my code:

import pyautogui, PIL
import tkinter as tk

def cursorpixel():
    x,y = pyautogui.position()
    pixel = (x,y,x+1,y+1)
    return pixel

def grabColor(square, max_colors=256):
    img=PIL.ImageGrab.grab(square)
    color = img.getcolors(max_colors)
    return color

def main():
    root=tk.Tk()
    root.minsize(150, 50)
    color = tk.Label(root,
                     text= grabColor(cursorpixel()),
                     fg = "black",
                     font = "Arial").pack()
    root.mainloop()

while __name__ == "__main__":
    main()

This works as I want, without the function of updating the label text whenever my cursor moves across the screen. It works once when launching the application and the label text stays the same. How would I make it so the label text updates whenever my cursor moves? I am using python 3.7

Thank you

Jeff
  • 75
  • 1
  • 9

3 Answers3

3

Assigning a variable to the text argument doesn't help, because even if the value of the variable changes, it will not be reflected in the label. Here is my approach to this (this is just one out of many possible ways)

import pyautogui, PIL
import tkinter as tk
from threading import Thread

def cursorpixel():
    x,y = pyautogui.position()
    pixel = (x,y,x+1,y+1)
    grabColor(pixel)

def grabColor(square, max_colors=256):
    global color_label,root
    img=PIL.ImageGrab.grab(square)
    color = img.getcolors(max_colors)
    color_label.config(text=color)

def refresh():
    while True:
        cursorpixel()

def main():
    global color_label,root
    root=tk.Tk()
    root.minsize(150, 50)
    color_label = tk.Label(root,
                     fg = "black",
                     font = "Arial")
    color_label.pack()
    Thread(target=refresh).start()
    root.mainloop()

if __name__ == "__main__":
    main()

NOTES

  • I have used multi threading instead and created a function refresh() which triggers the cursorpixel() in an infinite loop.
  • I have called the grabColor() function from cursorpixel() having pixel as parameter.
  • I have used the color_label.config() method to change the text in the label, you could also use color_label['text'] or maybe assign a textvariable var = StringVar() to the label and then use var.set() on it.
  • I am not sure if it is a good choice to put the __name__='__main__' in a while loop as you will not be able to close the window without terminating the task, new one will pop up every time you try to do so.
astqx
  • 2,058
  • 1
  • 10
  • 21
  • Will multi threading still work if this code gets extended more? – Delrius Euphoria Dec 29 '20 at 05:26
  • @CoolCloud with the knowledge I have, I don't see why it shouldn't, if you have a case in mind, please let me know, I'd be glad to learn – astqx Dec 29 '20 at 05:37
  • @CoolCloud not necessarily since tkinter is single threaded. But a `tk.StingVar()` and tracing it seems staible to me. – Thingamabobs Dec 29 '20 at 05:42
  • I've seen at some places about this, like @martineau said "_Multiple threads cannot interact with Tkinter objects. A good way to safely multi-thread in a Tkinter application is to use Queue object(s) to communicate (pass data) from auxiliary threads to the main one. Typically the self.after() is used to schedule periodic retrieval of the data the other thread(s) are generating._" – Delrius Euphoria Dec 29 '20 at 05:44
  • 1
    @CoolCloud they might have to make some changes to optimize it then, in case they have multiple auxiliary threads to handle, but if this is the only one, I don't think there should be a problem. – astqx Dec 29 '20 at 05:53
3

Answer

I added the .after command into the grabColor() function and combined the cursorpixel and grabColor() functions. I used .config to update the color. Here is the code:

import pyautogui, PIL
from tkinter import *

root=Tk()
root.minsize(150, 50)
colortext = Label(root)
colortext.pack()

def grabColor():
    x,y = pyautogui.position()
    pixel = (x,y,x+1,y+1)
    img=PIL.ImageGrab.grab(bbox=pixel)
    color = img.getcolors()
    colortext.config(text=str(color))
    root.after(100, grabColor)

grabColor()

root.mainloop()

Sources / Additional Resources
How do I create an automatically updating GUI using Tkinter?

DapperDuck
  • 2,728
  • 1
  • 9
  • 21
0

You can use after() to grab the color periodically:

import tkinter as tk
from PIL import ImageGrab

def grab_color(label):
    x, y = label.winfo_pointerxy()
    color = ImageGrab.grab((x, y, x+1, y+1)).getpixel((0, 0))
    label.config(text=str(color))
    label.after(100, grab_color, label)

def main():
    root = tk.Tk()
    color_label = tk.Label(root, width=20)
    color_label.pack(padx=10, pady=10)
    grab_color(color_label)
    root.mainloop()

if __name__ == "__main__":
    main()

Note that winfo_pointerxy() is used instead of pyautogui.position() to reduce dependency on external module.

acw1668
  • 40,144
  • 5
  • 22
  • 34