PySimpleGUI / PySimpleGUI

Python GUIs for Humans! PySimpleGUI is the top-rated Python application development environment. Launched in 2018 and actively developed, maintained, and supported in 2024. Transforms tkinter, Qt, WxPython, and Remi into a simple, intuitive, and fun experience for both hobbyists and expert users.
https://www.PySimpleGUI.com
Other
13.34k stars 1.84k forks source link

[Bug] Do not always detect event.endswith("+UP) #5714

Open ZexiDilling opened 2 years ago

ZexiDilling commented 2 years ago

Type of Issue (Enhancement, Error, Bug, Question)

BUG


Operating System

Windows 10

PySimpleGUI Port (tkinter, Qt, Wx, Web)

tkinter


Version information

Python version: 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)] port: tkinter tkinter version: 8.6.12 PySimpleGUI version: 4.60.1 Python version (sg.sys.version)

3.10.2 PySimpleGUI Version (sg.version)

4.60.1 GUI Version (tkinter (sg.tclversion_detailed), PySide2, WxPython, Remi)

8.6.12

Your Experience In Months or Years (optional)

Years Python programming experience

Years Programming experience overall

Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)

Anything else you think would be helpful?


Troubleshooting

These items may solve your problem. Please check those you've done by changing - [ ] to - [X]

Detailed Description

I have a canvas with between alot of figures, and are using the event.endswith("+UP") to detect if mousebutton have been released. This event works most of the time, but not every time. It seems to be worse, the more figures that are on the canvas at the same time.

Code To Duplicate

A short program that isolates and demonstrates the problem (Do not paste your massive program, but instead 10-20 lines that clearly show the problem)

This pre-formatted code block is all set for you to paste in your bit of code:


# Paste your code here

import PySimpleGUI as sg

def setup():
    col = [[sg.Text('Choose what clicking a figure does', enable_events=True)],
           [sg.Radio('Select samples', 1, key='-RECT_SAMPLES-', enable_events=True)],
           ]

    layout = [
        [sg.Graph(canvas_size=(400, 400), graph_bottom_left=(0, 0), graph_top_right=(400, 400),
                  background_color='grey', key="-CANVAS-", enable_events=True, drag_submits=True, motion_events=True),
         sg.Col(col, key="-COL-")],
    ]
    return sg.Window("Plate test", layout, finalize=True)

def plate_layout():
    window = setup()
    graph = window["-CANVAS-"]
    dragging = False
    start_point = end_point = prior_rect = None

    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break
        if event == "-CANVAS-":
            x, y = values["-CANVAS-"]
            if not dragging:
                start_point = (x, y)
                dragging = True
            else:
                end_point = (x, y)
            if prior_rect:
                graph.delete_figure(prior_rect)
            if None not in (start_point, end_point):
                if values["-RECT_SAMPLES-"]:
                    prior_rect = graph.draw_rectangle(start_point, end_point, fill_color="green", line_color="green")

        elif event.endswith("+UP"):

            # deletes the rectangle used for selection - stays around if the event is not detected
            if prior_rect:
                graph.delete_figure(prior_rect)
            start_point, end_point = None, None
            dragging = False
            prior_rect = None

if __name__ == "__main__":
    plate_layout()

Screenshot, Sketch, or Drawing


Watcha Makin?

I am trying to build a plate selection tool for working with SBS format plates. I want to be able to select wells for preparing different plate lateouts and for data handling.

plate_test_png

jason990420 commented 2 years ago

Issue found and not yet confirmed what situation it will happen.

PySimpleGUI commented 2 years ago

In general, for events being missed, can we confirm if only on 8.6.12?

jason990420 commented 2 years ago

I did the test on 8.6.9.

PySimpleGUI commented 2 years ago

I did the test on 8.6.9.

Thank you very much

huangxin168 commented 2 years ago

I also encounter this problem, and solve it with:

     graph.bind("<Motion>", "+MOVE+")
     graph.bind("<B1-Motion>", "+DRAG+")
     graph.bind("<ButtonPress-1>", "+PRESS+")
     graph.bind("<ButtonRelease-1>", "+RELEASE+")  # not used infact

and set these False when sg.Graph():

    enable_events=False,
    drag_submits=False,
    motion_events=False,

see this: https://stackoverflow.com/questions/24135170/drawing-rectangle-using-mouse-events-in-tkinter

jason990420 commented 2 years ago

With user binded events, the same, sometime button released event missed, and also some time button pressed event also missed.

from random import randint
import PySimpleGUI as sg

sg.theme("DarkBlue")
sg.set_options(font=('Courier New', 12))

w, h = size = (640, 480)

layout = [
    [sg.Graph(size, (0, 0), size, pad=(0, 0), key='GRAPH',
        # enable_events=True, drag_submits=True, motion_events=True
        )],
    [sg.Text('', pad=(0, 0), relief=sg.RELIEF_SUNKEN, expand_x=True,
        justification='center', key='STATUS')],
]

window = sg.Window('Title', layout, margins=(0, 0), finalize=True)
graph, status = window['GRAPH'], window['STATUS']

dx = dy = 10
for i in range(h//dy):
    for j in range(w//dx):
        x, y = j*dx, i*dy
        graph.draw_rectangle((x, y), (x+dx, y+dy), fill_color=f"#{randint(0, 16777215):06x}")

graph.bind("<Motion>", " MOTION")
graph.bind("<B1-Motion>", " DRAG")
graph.bind("<ButtonPress-1>", " PRESS")
graph.bind("<ButtonRelease-1>", " RELEASE")

count, pressed, figure, start = 0, False, None, None

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        break
    print(event)
    count += 1

    # User binded events
    if event in ("GRAPH MOTION", "GRAPH DRAG", "GRAPH PRESS", "GRAPH RELEASE"):
        e = graph.user_bind_event
        x, y = graph._convert_canvas_xy_to_xy(e.x, e.y)
        status.update(f'{event} #{count}:({x}, {y})')
    if event == "GRAPH PRESS":
        start, figure = (x, y), None
    elif event == "GRAPH DRAG":
        if figure:
            graph.delete_figure(figure)
        if start == None:
            start = (x, y)
        stop = (x, y)
        figure = graph.draw_rectangle(start, stop, fill_color=f"#{randint(0, 16777215):06x}")
    elif event == "GRAPH RELEASE":
        if figure:
            graph.delete_figure(figure)
        figure, start = None, None

    """
    # binded events from Graph options
    if event in ("GRAPH", "GRAPH+MOVE", "GRAPH+UP"):
        x, y = values['GRAPH']
        status.update(f'{event} #{count}:({x}, {y})')
    if event == "GRAPH":
        if pressed:
            if figure:
                graph.delete_figure(figure)
            stop = values['GRAPH']
            figure = graph.draw_rectangle(start, stop, fill_color=f"#{randint(0, 16777215):06x}")
        else:
            start, figure, pressed = values['GRAPH'], None, True
    elif event == "GRAPH+UP":
        pressed = False
        if figure:
            graph.delete_figure(figure)
        figure = None
    """

window.close()

If button released event missed, rectangle(s) will stay there.

image

Platform

PySimpleGUI commented 2 years ago

How intermittent are the dropped events? Or how frequent are they? Sounds like I've got a race condition somewhere maybe. Am I recalling correctly that only 1 bind per widget is allowed per event? If the Graph element has an event binding, creating a user one will override the Graph element's event, is that right?

PySimpleGUI commented 2 years ago

Using Jason's code, I have not been able to get a failure after maybe 50 to 100 clicks and drags using:

PySimpleGUI commented 2 years ago

I added 2 lines of code and a parm to the Window creation:

window = sg.Window('Title', layout, margins=(0, 0), finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)

And in the event loop:

    elif event == 'Version':
        sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)

This gives me a popup to show my environment. I'm unable to get a failure with this setup either....

image