Get global mouse position and use in python program? - python

As part of a larger project, I am trying to create a snapshot tool that works similar to the Mac OS X snapshot. It should take a first click, a second click, and return an image of the area created by the square.
I have some python functions that take an first point (x, y) and a second point (x, y) and create a snapshot of the square that those points create on the screenshot. The missing piece is getting the mouse locations of the initial click and second click, then passing that data to the python program to create the snapshot.
In other words, the flow of the program should be:
first click (save x, y)
second click (save x2, y2)
run snapshot.py using the saved clicked data to return the screenshot
I've only found solutions that can return the position of the pointer within a frame. If it helps, I'm using "import gtk" and "from Xlib import display"
edit: I have tried to use Tkinter to make an invisible frame that covers the whole screen. The idea was to use that invisible frame to get the exact coordinates of two mouse clicks, and then the invisible frame would disappear, pass the coordinates on to the screenshot function, and it would be done. However, the code I've been writing doesn't keep the frame transparent.
edit 2: This code can create a window, make it transparent, size it to the screen, then return the mouse coordinates on that window. I can use this to simply return the mouse coordinates on two clicks, then remove the window and send those coordinates to the snapshot code. When I run the below code line-by-line in the python shell, it works perfectly. However, whenever I run the code as a whole, it seems to skip the part where it makes the window transparent. Even if I copy and paste a block of code that includes the 'attributes("-alpha", 0.1)' into the python shell, it ignores that line.
from Tkinter import *
root = Tk()
root.attributes('-alpha', 0.1)
maxW = root.winfo_screenwidth()
maxH = root.winfo_screenheight()
root.geometry("{0}x{1}+0+0".format(maxW, maxH))
def callback(event):
print "clicked at: ", event.x, "and: ", event.y
root.bind("<Button-1>", callback)
def Exit(event):
root.destroy()
root.bind("<Escape>", Exit)
# root.overrideredirect(True)
root.mainloop()
I am open to using any c or c++ code, or any language's code, to return the coordinates of the mouse on a click. This guy wrote some code to actually make the computer click at given points, which may be on the same track as my problem.

It's just a indentation problem - you bound the callback in the callback by mistake - try this instead:
root.geometry("{0}x{1}+0+0".format(maxW, maxH))
def callback(event):
print "clicked at: ", event.x, "and: ", event.y
root.bind("<Button-1>", callback)
EDIT
Ok, here's a theory - maybe when you run it from the command line, it takes longer for the root window to appear, for some reason, so you set the alpha before it exists, and the alpha option gets ignored. Give this a try:
root.wait_visibility(root)
root.attributes('-alpha', 0.1)

Related

How to make window look like a win10 widget. Python

I'm trying to create a windows app that shows weather like windows 10 widgets using Tkinter. The issue I ran into is that I cannot remove the window's border to make it look like a real widget.
Here's what it looks like and what I'd like to remove:
self.root.overrideredirect(1) kinda works but I still can minimize the window, which is what I do not want to be available.
That's what I want it to look and behave like:
To sum up:
How to disable window title and all of that on top properly?
How to move the window after, set the position of it on the screen?
I'm new to Python, any help is appreciated. Thank you all. Peace
To clarify: The app is supposed to be not affected by tab+alt (and other ways to minimize it) and be always on desktop, not on top of any other app, right on the desktop.
UPD
The thing I missed working around with win32gui is that I cannot get the handle the way I was trying to due to the fact that the code of getting the handle before the window itself was created (before mainloop) so root.wait_visibility() was the solution for me. After that I got the handle using EnumWindows and positioned my window the way I needed, finally disabling the border with root.overrideredirect. The suggested solutions will be tried as well. Thank you for help!
top-right corner of the desktop:
You can try this:
root.overrideredirect(1)
root.geometry('250x150+900+600')
#900+600 is x+y axis position of window
And for making close button like of widget you can make transparent frame in side, put button there and in command .destroy().
Edit
From TheLizzard's comment, visit here for further information. From there:
To make the window draggable, put bindings for (mouse clicks) and (mouse movements) on the window.
import tkinter
class Win(tkinter.Tk):
def __init__(self,master=None):
tkinter.Tk.__init__(self,master)
self.overrideredirect(True)
self._offsetx = 0
self._offsety = 0
self.bind('<Button-1>',self.clickwin)
self.bind('<B1-Motion>',self.dragwin)
def dragwin(self,event):
x = self.winfo_pointerx() - self._offsetx
y = self.winfo_pointery() - self._offsety
self.geometry('+{x}+{y}'.format(x=x,y=y))
def clickwin(self,event):
self._offsetx = event.x
self._offsety = event.y
win = Win()
win.mainloop()
You can use this for some part in top of the window rather then in whole window. By making new frame there implement it, to make your widget moveable.
And from second comment of TheLizzard, visit here for more information. From there:
If you want the window to stay above all other windows.
root.attributes("-topmost", True)

Display continuously change of an image in python

I am writing a python program that gradually changes an image step by step, adjusting each pixel by a small amount in each step. To get a visualization of what the program is doing during runtime, I want it to display the image at each step, always overwriting the currently shown image so that it doesen't open bunch of display windows.
I already tried matplotlib, opencv and skimage, with their according possibilities to display an image and update the frame content in the course of the program:
# using scimage
viewer = ImageViewer(image)
viewer.show(main_window=False) # set the parameter to false so that the code doesn't block here but continues computation
..other code..
viewer.update_image(new_image)
# using matplotlib
myplot = plt.imshow(image)
plt.show(block=False)
.. other code..
myplot.set_data(new_image)
plt.show()
# using opencv
cv2.imshow('image',image)
.. other code ..
cv2.imshow('image', new_image)
I always ran into the problem that when it was supposed to open a frame with an image, it did not display the image but only a black screen. Weirdly enough, when I ran the code in IntelliJ in debug-mode and hit a breakpoint after the display-function, it worked.
What can I do so that it is displayed correctly when running the program normally and not with a breakpoint?
Here's the thing, I think your program does work, except it does and finishes unless you tell it to pause, which is why your breakpoint strategy is working.
Try pausing after showing image -
You can ask for user input. It'll pause until you enter some input to the program.
Put the program thread to sleep for some specified amount of time. This'll freeze your program for some given specified time, but you'll be able to see the image if it's already rendered.
Edit -
Since opencv's waitKey method is working for you now, you can use this method again to prevent the program from closing image window. Use waitKey(0) as your last program statement. It waits for a key press indefinitely, and returns the pressed key's code. Press any key to continue (but remember to have your image window in focus or it won't work), and your program should close if it's used in the end.
Also, I've striked earlier suggested options for pausing a program, because I'm unsure if it would've helped. I think waitKey method is more complex, and helps pause the program without freezing it.
Well, I am still not sure what exactly your goal is but here is a code piece that modifies an image inside of a window whenever the upper button is pressed.
from tkinter import Tk, Canvas, Button, Label, PhotoImage, mainloop
import random
WIDTH, HEIGHT = 800, 600
def modify_image():
print ("modifiying image...")
for x in range(1000):
img.put ( '#%06x' % random.randint(0, 16777215), # 6 char hex color
( random.randint(0, WIDTH), random.randint(0, HEIGHT) ) # (x, y)
)
canvas.update_idletasks()
print ("done")
canvas = Canvas(Tk(), width=WIDTH, height=HEIGHT, bg="#000000")
canvas.pack()
Button(canvas,text="modifiying image",command=modify_image).pack()
img = PhotoImage(width=WIDTH, height=HEIGHT)
Label(canvas,image=img).pack()
mainloop()
The function modify_image() adds 1000 random pixels to the image within the main window. Note the tkinter module is a default python module.

Tkinter does not update while withdrawn

I have a program that will show an image on screen when a hotkey is pressed on my keyboard. Depending on which key was pressed a different image will be shown.
After 3 seconds of no input my root Tk gets withdraw()n so there is nothing on screen.
Now, whenever I want to show an image I call this function:
def drawOverlay(self, index):
self.canvas.itemconfig(self.overlayWidget, image=self.overlayImages[index])
self.deiconify()
The problem is that I see the old image for a few milliseconds before it is replaced.
I tried to find out why there is the delay eventhough I deiconify() only after i switched the image and came across this answer which suggested to call deiconify() using the root's after(). I tried that and when it still didn't work I played around with the call and found out that calling
self.after(1000, self.deiconify())
causes the old image to appear for exactly 1 second after which it is replaced with the new one.
This leads me to believe that the Tk or the Canvas is unable to update while it is withdraw()n. Is there any way around this? I'd rather have a short delay before the image is displayed than the old image flashing on the screen for a few frames but I have no idea how to accomplish this.
Edit: So I'm an idiot for actually calling deiconify instead of just passing it to after because of the parantheses.
That does fix the old image appearing for the duration but does not fix the flashing.
Edit2: I managed to reproduce this problem with the following code.
Pressing any key on the keyboard will make a green rectagle appear. Waiting for it to vanish and then pressing another key will make a red rectangle appear.
Only sometimes can you see the flashes happening so try for a couple times. I did not manage to reproduce when -transparentcolor isnt set but I can't tell if this is due to the option being the problem or due to the reduced rendering times making the problem almost imperceptable. It is also a bit easier to see then overrideredirect is set.
Edit3: I have worked the code down further by using actual images. Even without the keypress or additional event callbacks this code produces a black flash before displaing the image. -transparentcolor and overrideredirect(True) seem to be vital to reproducing this. Manually calling update() before reduces the frequency of this ocurring but still causes flashing consistently on larger images. This points to rendering time being one of the factors.
overlay.png
overlayRaw.png
from tkinter import Tk, Canvas
from PIL import ImageTk, Image
TRANSCOLOR = "blue"
IMAGE_SMALL = "overlay.png"
IMAGE_LARGE = "overlayRaw.png"
class Overlay(Tk):
def __init__(self):
Tk.__init__(self)
self.rawImage = Image.open(IMAGE_SMALL)
self.image = ImageTk.PhotoImage(self.rawImage)
self.canvas = Canvas(self, width = self.rawImage.size[0], height = self.rawImage.size[1])
self.canvas.pack()
self.wm_attributes('-transparentcolor', TRANSCOLOR) # If disabled stops the flashes from ocurring even on large images.
self.overrideredirect(True) # If disabled the Windows animation for opening windows plays. Stops the flashing from ocurring
self.withdraw()
self.overlayWidget = self.canvas.create_image(0, 0, image = self.image, anchor = "nw")
self.deiconify() # Flashes Clearly Everytime
## self.update_idletasks()
## self.deiconify() # Only Flashes Sometimes. Always flashes on large images
## self.update()
## self.deiconify() # Only Flashes Sometimes. Always flashes on large images
## self.after(0, self.deiconify) # Flashes Clearly everytime
## self.after(200, self.deiconify) # Only Flashes Sometimes. Always flashes on large images
## self.update()
## self.after(200, self.deiconify) # Flashes Clearly Everytime
o = Overlay()
o.mainloop()
I don't see much that could cause a flicker. My recommendation would be to add an explicit call to self.update before the call to deiconify happens. That should instruct tkinter to redraw everything on the canvas. However, that won't help if tkinter on your platform defers drawing until the window is mapped.
Try replacing this:
self.after(0,self.deiconify)
self.after(2000, self.hideOverlay)
with this:
self.update()
self.deiconify()

Tkinter Canvas invalidation issue (object clipped while moved)

I'm pretty new to Python and I'm currently playing a bit with Tkinter which looks amazingly simple. I've tried to implement a simple drag and drop effect using the following code (right mouse button creates a circle, left mouse button allows dragging) :
from tkinter import *
class Point:
def __init__(self, ref, x, y):
self.ref = ref
self.x = x
self.y = y
points = []
selected = None
def OnSelect(event):
global selected
for p in points:
if event.x>=(p.x-10) and event.y>=(p.y-10) and event.x<(p.x+10) and event.y<(p.y+10):
selected = p
break
def OnMMove(event):
if selected is not None:
selected.x = event.x
selected.y = event.y
canvas.coords(selected.ref, event.x-10, event.y-10, event.x+10, event.y+10)
def OnStopDrag(event):
global selected
selected = None
def OnCreate(event):
point = canvas.create_oval(event.x-10, event.y-10, event.x+10, event.y+10, fill="black")
points.append(Point(point, event.x, event.y))
window = Tk()
window.wm_title("Python")
canvas = Canvas(window, width=800, height=600, background='white')
canvas.bind("<Button-1>", OnSelect)
canvas.bind("<B1-Motion>", OnMMove)
canvas.bind("<ButtonRelease-1>", OnStopDrag)
canvas.bind("<Button-3>", OnCreate)
canvas.pack(fill=BOTH, expand=YES)
window.mainloop()
As can be seen in this code, I'm using canvas.cords to move the dragged object around. Everything works fine when the mouse cursor is slowly moved while dragging, however when the mouse cursor is moved rapidly, the dragged circle seems to be partialy clipped in a rectangle while moved as seen on this picture (the whole circle is correctly drawn entirely again when dragging stops or slows down) :
I've already encountered a similar issue whend using GDI in a Win32 C application, when calling screen invalidation to repaint the window client area on the sole area covered by the initial position of the circle being currently dragged.
And indeed, when the window created in my example code is placed above a window which is constantly and entirely being redrawn, like a video game window, the cropping effect while dragging elements is not seen and the whole circle is correctly being redrawn as it is dragged.
Is there a way to solve this issue, like a canvas setting making window invalidation being called on a wider or the whole client area ? I would like to stick with Tkinter so I'm not really interested in switching to another GUI API/framework. This code has been tested on Windows 10.
This answer may confuse you (it does me). But a solution is to configure the cursor within OnMMove. Here is the excerpt that worked on Windows for me.
def OnMMove(event):
if selected is not None:
canvas.configure(cursor='arrow')
selected.x = event.x
selected.y = event.y
canvas.coords(selected.ref, event.x-10, event.y-10, event.x+10, event.y+10)

Unclosable window using tkinter

Hey I am making a program that take a picture using my webcam when I type the wrong password. The program will be open and I want it unclosable.
I need to know how to make a window unclosable using tkinter.
You can try all of the many things #abarnert suggested, but I think the easiest way would be to just ignore the close event.
From this question:
Here you have a concrete example:
import Tkinter as tk
import tkMessageBox as messagebox
root = tk.Tk()
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
(edited code for Windows)
So change on_closing() to
def on_closing():
pass
and that makes it unclosable. I tried Alt+F4, the close button, closing it from the Windows Taskbar, all to no avail. The only way I was able to kill it was to use Task Manager.
Tkinter doesn't have any way to do this directly. But it does have something that may be good enough, or it may be too much overkill: the overrideredirect flag:
If non-zero, this prevents the window manager from decorating the window. In other words, the window will not have a title or a border, and it cannot be moved or closed via ordinary means.
That's not quite up-to-date; it may actually have a title or border on some platforms… but it won't be closable.
This is easy to use: just do root.overrideredirect(True) (or, if you want to do it to a different Toplevel window instead of your root, window.overrideredirect(True)).
But notice that it can't be moved or closed, not just that it can't be closed. (It also can't be resized, if you want that.)
So, the only thing you can do is set the flag, but then bind the mouse-button events to handle moving manually. For example, in your window's __init__ method:
self.overrideredirect(True) # if this is a Toplevel
#self.parent.overrideredirect(True) # if this is a Frame on root
self.bind('<ButtonPress-1>', self.move_start)
self.bind('<ButtonRelease-1>', self.move_end)
self.bind('<B1-Motion>', self.move_move)
And then:
def move_start(self, event):
self.startx, self.starty = event.x, event.y
def move_stop(self, event):
self.move_move(event)
def move_move(self, event):
x = self.winfo_x() + event.x - self.startx
y = self.winfo_y() + event.y - self.starty
self.geometry("+%s+%s" % (x, y))
Obviously, if you want any widgets within the window to accept clicks, you probably don't want to make the whole window a drag area. In fact, you may not want to make the whole window a drag area even if it doesn't have anything to click, because that's not really following Mac or Windows human interface guidelines. You could fake a grip area—a title bar, a border around the window, etc.—just by, e.g., adding a Label pinned to the side(s) you want to grip from and only binding there, or by creating a "child" window inset from the main window that steals the bindings. But this is never going to look like a "native" window.
If you really need a native window, but with the close box (the X in the top-right corner on Windows, the red dot in the top-left on Mac, etc.) disabled or missing (and, on Windows, with the "close" item on the window menu disabled, and Alt+F4, and so on, and similarly for X11…)… As far as I know, there's no cross-platform way to do that in Tkinter. You will have to write code for each platform that gets at the underlying native window objects and does native window things to them. At that point, you probably want to look at using a more powerful windowing library than Tkinter—e.g., I believe Qt, Gtk+, and wx all have much simpler ways of creating a normal window but with the close box disabled.

Categories