Display an image using PySimpleGUI and get coordinates of mouse click - python-3.x

Suppose I have an image named 'image.jpg'. I want to be able to display this image using PySimpleGUI and get the pixel coordinate values of a mouse click on the displayed image.
I have looked in many places on the internet but couldn't find a solution. Any ideas are appreciated.
Thanks in advance!

With Graph element, you can set option enable_events=True to generate event when click, also method draw_image to place an image onto the canvas.
Refer https://www.pysimplegui.org/en/latest/call%20reference/#graph-element
import PySimpleGUI as sg
size = (320, 240)
image = sg.EMOJI_BASE64_HAPPY_BIG_SMILE
layout = [
[sg.Graph(size, (0, 0), size, enable_events=True, key='-GRAPH-')],
]
window = sg.Window("Title", layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == '-GRAPH-':
x, y = values[event]
location = (x-28, y+28) # figure size is (56, 56)
# Using option `filename` if the image source is a file
window['-GRAPH-'].draw_image(data=image, location=location)
window.close()

Related

How to display TEXT ONLY (transparent background, with no menu bar or buttons) on screen using Python?

I have been looking everywhere for this kind of simple solution and I'm still coming up short.
All I want to do is simply splash some text only onto the screen, without a background or using a "message box" that has a menu bar/buttons with it. Just text, that's all. Maybe with a way to set a color for the font as well as I want the text to be a light gray. Using Windows 10 and Python 3.8+.
I've looked at easygui (message box, uses buttons), PySimpleGUI (best option so far, shown below, but defaults to a white background), Tkinter and pygame (but I have no clue how to set up what I'm looking for with those packages.
This is what I have using PySimpleGui, would love for this to be a transparent background if possible!
def popup_message(message):
sg.popup(str(message),
title = None,
button_color = 'white',
background_color = None,
text_color = 'grey85',#'light gray',
button_type = 5,
auto_close = True,
auto_close_duration = 20,
custom_text = (None, None),
non_blocking = False,
icon = None,
line_width = None,
font = None,
no_titlebar = True,
grab_anywhere = True,
keep_on_top = True,
location = (None, None),
relative_location = (250, 250),
any_key_closes = True,
image = None,
modal = True)
UPDATE
This is the closest thing I've got with using Tkinter. Problem now is the menu bar is still visible, and using the Label class results in a solid background. HOW CAN I ONLY SHOW TEXT ON SCREEN IN PYTHON!? I'm not even sure I'm going about this the right way, but seems like a pretty basic idea?
# Import the Tkinter Library
from tkinter import *
# Create an instance of Tkinter Frame
root = Tk()
# Create a Label to print some text
label = Label(root, text="This is a New Line Text", font= ('Helvetica 14 bold'), foreground= "red3")
label.pack()
# Create a transparent window
root.wm_attributes('-transparentcolor','#add123')
# Set the geometry of window
root.geometry("700x350")
# Add a background color to the Main Window
root.config(bg = '#add123')
root.mainloop()
Transparent background of Text in popupis not supported.
New popup function required and defined by yourself in PySimpleGUI.
Set option background_color of your Text element to one specified color for transparency, like '#add123'.
Set option transparent_color in Window to the specified color for transparency.
Set option no_titlebar in Window to hide the titlebar.
Following code show the way
import PySimpleGUI as sg
def popup(message):
global win
if win:
win.close()
layout = [[sg.Text(message, background_color=bg, pad=(0, 0))]]
win = sg.Window('title', layout, no_titlebar=True, keep_on_top=True,
location=(1000, 200), auto_close=True, auto_close_duration=3,
transparent_color=bg, margins=(0, 0))
event, values = win.read(timeout=0)
return win
bg = '#add123'
sg.set_options(font=("Courier New", 24))
layout = [[sg.Button('POPUP')]]
window = sg.Window('title', layout)
win = None
while True:
event, value = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'POPUP':
win = popup('Here is the message.')
window.force_focus()
if win:
win.close()
window.close()
You're trying to make a sort of screen without a title-menu. In, that case I can help you by telling you to use root.overrideredirect(True) which is a crucial part of your code.
You have two options for drawing text without a background.
The first is to use the canvas to draw the text as seen below. With this approach, you'd have to center the text yourself everytime the window is resized if you care about resizability.
canvas.create_text(x, y, "Text with no bg!")
Second approach is to use a disabled button with an empty img inside.
img = PhotoImage(file="file.png")
b = tk.Button(root, text="My Text", image=img, relief="FLAT", state="DISABLED")
b.image = img
b.pack()
See this answer for more info.
Let me know if you have any other questions.

update tkinter without any button moved

Before Update:
After update:
My code for the update:
def plot(data,the_main):
fig = Figure(figsize=(5,5),dpi=100)
plot1 = fig.add_subplot()
plot1.plot(data)
canvas = FigureCanvasTkAgg(fig,master = the_main)
canvas.draw()
# placing the canvas on the Tkinter window
canvas.get_tk_widget().grid(row=5,column=6)
# creating the Matplotlib toolbar
# toolbar = NavigationToolbar2Tk(canvas,the_main)
# toolbar.update()
My code before accessing the update(it's related cause the window):
def buttons_for_month(country):
the_button = tkinter.Tk()
the_button.title(country)
the_button.geometry('967x590')
month_list = [1,2,3,4,5,6,7,8,9,10,11,12]
columns = 0
for i in range(0,len(month_list)):
name = "button",i
name = tkinter.Button(master = the_button,
command = lambda: plot([1,2,3,4,5],the_button),
height = 2,
width=10,
text=month_list[i])
name.grid(row=0,column=columns)
columns += 1
I'm trying to embedding matplotlib into tkinter
It's working , but not as i expected.
I've tried just make a new window , special for the graph ..However , My desktop screen were a messed.
so my question: How to manipulate my code to make matplotlib embedded into tkinter while
keep the button(s) at their places?
I can't run your code, but I'm guessing this will do it:
canvas.get_tk_widget().grid(row=5,column=6, columnspan=7)
The reason this works is because you are putting the plot inside a single column. Every column has a uniform width so it forces column 6 to be as wide as the plot. By having the plot span multiple columns, it prevents the single column from having to be resized to fit the entire plot.
Arguably, an even better solution is to put your buttons in a frame, pack that frame at the top, and then pack the plot below it. grid is very useful when creating a matrix of widgets, but in your case you're not doing that.

PySimpleGUI how to make transparent click-through window?

I want to draw a circle on the screen but it should be click-through and I should be able to click somethings behind in this circle. So far, I achieved to draw circle and made it transparent but i couldn't make it click-through. I know that it is possible with pywin32 win32con.WS_EX_LAYERED flag but there is no information about this on PySimpleGUI documentation. How can I solve this problem?
My current window configuration:
window = sg.Window('Sample window', layout,
keep_on_top=True,
auto_size_buttons=False,
grab_anywhere=False,
no_titlebar=True,
return_keyboard_events=False,
alpha_channel=0.8,
use_default_focus=False,
transparent_color='red',
finalize=True)
If it is not possible with PySimpleGUI, is it possible to do with pywin32 module combination?
The transparent_color property can be used to create "click-through" windows. All you need to do is make whatever you want to be click through the same color as the transparent_color. Here is an example where you can toggle the transparency of a shape in a canvas to the transparent color and make it click through.
import PySimpleGUI as sg
layout = [
[sg.Canvas(size=(100, 100), key= 'canvas',)],
[sg.T('Change circle color to:'), sg.Button('Red'), sg.Button('Blue')]
]
window = sg.Window('Sample window', layout,
keep_on_top=True,
auto_size_buttons=False,
grab_anywhere=False,
no_titlebar=True,
return_keyboard_events=False,
alpha_channel=0.8,
use_default_focus=False,
transparent_color='red',
finalize=True)
window.Finalize()
canvas = window['canvas']
cir = canvas.TKCanvas.create_oval(50, 50, 100, 100)
while True:
event, values = window.read()
if event is None:
break
if event == 'Blue':
canvas.TKCanvas.itemconfig(cir, fill="Blue")
elif event == 'Red':
#Here is where the cirle changes to a recolor making it transparent and click through
canvas.TKCanvas.itemconfig(cir, fill="Red")
layout = [[sg.Button("Button) ],
[sg.Graph(canvas_size =(400,100),
graph_bottom_left=(-400,-400),
graph_top_right=(400,400),
background_color='red') ]]
window = sg.Window('Click through transparent',
layout,background_color='red',keep_on_top=True,
transparent_color='red', alpha_channel=.5,
grab_anywhere=True, resizable=True).Finalize()
while True:
event, values = window.Read()
if event == sg.WIN_CLOSED:
break
if event == 'Click':
print(" Button Clicked")
Remember to put keep_on_top = True
This works well on windows, don't know if other os

Updating image in tkinter window with newly made images

I have written a program that simulate orbits of planets around a star, I would like a visualiser to display current planet positions around the star. Since all the planets have their x and y co-ords stored, plotting them on an image is straightforward, but I don't know a good way to update the image and re-display after each simulation step.
Currently I draw the image like so (use the planet position to turn a pixel red)
def createwindow():
img = Image.new( 'RGB', (750,730), "black")
pixels = img.load()
for thing in celestials:
pixels[thing.xpos+375+sun.xpos,thing.ypos+375+sun.xpos]=250,20,20
So I have an image which displays the planets quite well, which can be remade after each time the planets move. But how do I display this in a tkinter window?
Is there a way of doing this without using a Photoimage which must be saved and loaded? Or a better way of doing this all together? Maybe assigning each planet a label and drawing them directly on a tkinter black window, updating the position of the label with each simulation step?
All help appreciated
You should probably draw the animation on a canvas, instead of on a static image; You can then move the elements of the animation that are modified at each step of the simulation.
Something along those lines: (press start at the bottom to start)
import tkinter as tk
def animate():
canvas.move(1, 5, 0) # 1 refers to the object to be moved ID, dx=5, dy=0
root.update()
root.after(50, animate)
if __name__ == '__main__':
root = tk.Tk()
frame = tk.Frame(root)
canvas = tk.Canvas(root, width=800, height=400)
canvas.pack()
canvas.create_polygon(10, 10, 50, 10, 50, 50, 10, 50) # canvas object ID created here by tkinter
btn = tk.Button(root, text='start', command=animate)
btn.pack()
root.mainloop()
There're many ways to display dynamic content, like moving planets. It depends on whether you prefer working with widgets, pixels, images or graphical primitives.
Widgets
If you prefer to move widgets, as you said, you can assign each planet to a Label. Then you can place it on a window using Place Geometry Manager:
yourLabel.place(anchor=CENTER, relx=0.5, rely=0.5, x=-100, y=50)
That'd put a center of the label to coords (-100,50) relative to its parent center (relx=0.5, rely=0.5). To change the position call .place() again with different (x,y) coords.
Pixels
To change image pixels at runtime you can use .blank(), .get() and .put() methods of PhotoImage. They're not in TkInter book, but you can see them in help(PhotoImage) in python console. To set a single pixel (x,y) to color #fa1414 use:
yourPhotoImage.put("{#fa1414}", to=(x,y))
The "{#fa1414}" argument here is actually a pixels list. Tk can put a rectangular block of pixels in one call. Colors are specified row-by-row, separated by spaces, each row enclosed in curly braces. Block of NxM colors would look like "{C0,0 C1,0 ... CN-1,0} {C0,1 C1,1 ... CN-1,1} ... {C0,M-1 C1,M-1 ... CN-1,M-1}". For example, to put 2x2 pixels to coords (4,6) use:
yourPhotoImage.put("{red green} {#7f007f blue}", to=(4,6))
Tk recognizes many symbolic color names in addition to #rrggbb syntax.
Note that all the changes are displayed right away. You don't need to manually "re-display" images. E.g. if you have a Label(image=yourPhotoImage) defined somewhere, it'd display the changes after you modify yourPhotoImage.
Images
Also PhotoImage can load images from base64-encoded strings. So, technically, to skip saving/loading a file, you can load your image from a string, containing base64-encoded .gif or .png (formats supported natively in Tk):
newimg = PhotoImage(data="R0lGODlhHgAUAPAAAP//AAAAACH5BAAAAAAALAAAAAAeABQAAAIXhI+py+0Po5y02ouz3rz7D4biSJZmUgAAOw==")
.put() also supports base64-strings. For example, if you have planets as images, loaded into base64-strings, you can put them into your image with:
yourPhotoImage.put(base64_mars_image_string, to=(100,50))
Finally, if you're using PIL anyway, you can create PIL.ImageTk.PhotoImage from your PIL.Image img and use it everywhere Tkinter accepts an image object[ref]
from PIL import ImageTk
photoimg = ImageTk.PhotoImage(img)
Graphical primitives
If you want lines or arcs you can also use Canvas. It provides a way to put together different widgets, images, texts, graphical primitives, and move them around, or configure interaction with them. A canvas-based planetary system could look like this (python2 syntax):
from Tkinter import *
import math, time
color = ["#fffc00", "#a09991", "#b6b697", "#2c81ca", "#a36447", "#b16811", "#e5ca9d", "#bcdaf2", "#7e96bc", "#d3ac8b"]
size = [20, 4, 7, 7, 5, 15, 13, 9, 9, 4]
start_time = time.time()
def update_positions():
dt = (time.time() - start_time)*5
for i in range(len(planets)):
px, py = 350+35*i*math.cos(dt/(i+1)/size[i]+4*i), 350+35*i*math.sin(dt/(i+1)/size[i]+4*i)
canvas.coords(planets[i], (px-size[i],py-size[i],px+size[i],py+size[i]))
root.after(100, update_positions)
root = Tk()
canvas = Canvas(root, width=700, height=700, bg="black")
arcs = [canvas.create_oval(350-35*i, 350-35*i, 350+35*i, 350+35*i, outline=color[i]) for i in range(len(color))]
planets = [canvas.create_oval(350, 350, 350, 350, fill=color[i]) for i in range(len(color))]
update_positions()
canvas.pack()
root.mainloop()
Note that items created on the canvas are kept until you remove them. If you want to change the drawing, you can either use methods like .coords(), .itemconfig(), and .move() to modify the items, or use .delete() to remove them.

Pygame repaint_rect with layereddirty [duplicate]

I am trying do end credits like animation the code above for Title crawl, I am trying to make the following changes to it:- 1) The text should begin at the bottom of screen at certain location, such that no other text from the string should be displayed below that location on the screen. 2) The text should stop at certain location on top of screen such that the line at the top should be deleted as soon as it reaches that location making room for other lines in the string.
I am a python newbie, I am just experimenting with things, the following code doesn't belong to me either.
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Title Crawl')
screen = pygame.display.set_mode((1000, 800))
screen_r = screen.get_rect()
font = pygame.font.SysFont("franklingothicdemibold", 40)
clock = pygame.time.Clock()
def main():
crawl = ["Star Wars - The Wilds"," ","It is a dark time for the Galaxy. The evil Dark","Lord, Vitiate is rising to power. Alone, a single", "spec is on a trip, a trip that will ultimately", "rectify the wrongs of the galaxy. The keepers ", "of peace are dying out and the DARK SIDE is", "lurking, a conniving force determined to", "become the omniarch."]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(crawl):
s = font.render(line, 1, (229, 177, 58))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((0, 0, 0))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
Use set_clip() to set the clipping region of the display surface.
e.g. Clip 100 rows at the top and the bottom:
# set clipping rectangle
clip_rect = (0, 100, screen.get_width(), screen.get_height()-200)
screen.set_clip(clip_rect)
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# cancel clipping for further drawing
screen.set_clip(None)

Resources