PySimpleGUI / PySimpleGUI/PySimpleGUI
[Enhancement] Modification for option 'grab_anywhere' of sg.Window
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 13.8k
- Forks
- 1.8k
- PR merge metrics
- No merged PRs in 30d
Description
Type of Issues (Enhancement, Error, Bug, Question)
Enhancement
Operating System
WIN 10
Python version
Python 3.8.7
PySimpleGUI Port and Version
Ports = tkinter
PySimpleGUI Version: 4.33.0.2
tkinter version: 8.6.9
Your Experience Levels In Months or Years
2 yrs - Python programming experience
10+ yrs - Programming experience overall
Yes, tkinter - Have used another Python GUI Framework (tkinter, Qt, etc) previously (yes/no is fine)?
You have completed these steps:
- Read instructions on how to file an Issue
- Searched through main docs http://www.PySimpleGUI.org for your problem
- Searched through the readme for your specific port if not PySimpleGUI (Qt, WX, Remi)
- Looked for Demo Programs that are similar to your goal http://www.PySimpleGUI.com
- Note that there are also Demo Programs under each port on GitHub
- Run your program outside of your debugger (from a command line)
- Searched through Issues (open and closed) to see if already reported
- Try again by upgrading your PySimpleGUI.py file to use the current one on GitHub. Your problem may have already been fixed but is not yet on PyPI.
Description of Problem / Question / Details
Option grab_anywhere in sg.Window may not work well with some elements, like element with scrollbar, input element, ...
Here, provide idea to handle such issue by set those elements not working to grab anywhere in window.
Event binding
Unless you specify otherwise, the bindings happen in the following order:
if there is a binding directly on the widget it will be fired before any other bindings.
if there is a binding on the widget's class, it is fired next
if there is a binding on the toplevel widget that contains the widget, it is fired next (note: the root window is considered a toplevel window in this context)
if there is a binding on "all" it will fire next.
There should be internal binding for 'grab' on some widgets, like scrollbar,.....
They generate events from '<B1-Motion>', '<ButtonRelease-1>', then '<ButtonPress-1>'. So add one variable to confirm '<ButtonPress-1>' should go first, else variable will be None and no grab action.
Code To Duplicate
import tkinter as tk
from tkinter import ttk
import PySimpleGUI as sg
class Window(sg.Window):
NO_DRAG_WIDGETS = (tk.Button, tk.Entry, tk.Scrollbar, tk.Listbox, tk.Message,
tk.PanedWindow, tk.Scale, tk.Text, ttk.Entry, ttk.PanedWindow, ttk.Scale,
ttk.Scrollbar, ttk.Sizegrip, ttk.Treeview, type(None))
drag_widget = None
def _StartMove(self, event):
"""
Used by "Grab Anywhere" style windows. This function is bound to mouse-down. It marks the beginning of a drag.
:param event: event information passed in by tkinter. Contains x,y position of mouse
:type event: (event)
"""
try:
self.TKroot.x = event.x
self.TKroot.y = event.y
self.drag_widget = event.widget
except:
pass
def _StopMove(self, event):
"""
Used by "Grab Anywhere" style windows. This function is bound to mouse-up. It marks the ending of a drag.
Sets the position of the window to this final x,y coordinates
:param event: event information passed in by tkinter. Contains x,y position of mouse
:type event: (event)
"""
try:
self.TKroot.x = event.x
self.TKroot.y = event.y
self.drag_widget = None
except Exception as e:
print('stop move error', e, event)
def _OnMotion(self, event):
"""
Used by "Grab Anywhere" style windows. This function is bound to mouse motion. It actually moves the window
:param event: event information passed in by tkinter. Contains x,y position of mouse
:type event: (event)
"""
try:
if isinstance(self.drag_widget, Window.NO_DRAG_WIDGETS):
return
deltax = event.x - self.TKroot.x
deltay = event.y - self.TKroot.y
x = self.TKroot.winfo_x() + deltax
y = self.TKroot.winfo_y() + deltay
self.TKroot.geometry("+%s+%s" % (x, y)) # this is what really moves the window
# print('{},{}'.format(x,y))
if Window._move_all_windows:
for window in Window._active_windows:
x = window.TKroot.winfo_x() + deltax
y = window.TKroot.winfo_y() + deltay
window.TKroot.geometry("+%s+%s" % (x, y)) # this is what really moves the window
except Exception as e:
pass
headings = ['President', 'Date of Birth']
data = [
['Ronald Reagan', 'February 6'],
['Abraham Lincoln', 'February 12'],
['George Washington', 'February 22'],
['Andrew Jackson', 'March 15'],
['Thomas Jefferson', 'April 13'],
['Harry Truman', 'May 8'],
['John F. Kennedy', 'May 29'],
['George H. W. Bush', 'June 12'],
['George W. Bush', 'July 6'],
['John Quincy Adams', 'July 11'],
['Garrett Walker', 'July 18'],
['Bill Clinton', 'August 19'],
['Jimmy Carter', 'October 1'],
['John Adams', 'October 30'],
['Theodore Roosevelt', 'October 27'],
['Frank Underwood', 'November 5'],
['Woodrow Wilson', 'December 28'],
]
layout = [
[sg.Text("President to search:")],
[sg.Input(size=(33, 1), key='-INPUT-'), sg.Button('Search')],
[sg.Table(data, headings=headings, justification='left', key='-TABLE-')],
]
window = Window("Title", layout, grab_anywhere=True, finalize=True)
table = window['-TABLE-']
entry = window['-INPUT-']
entry.bind('<Return>', 'RETURN-')
widget = table.Widget
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
# print(event, values)
if event in ('Search', '-INPUT-RETURN-'):
text = values['-INPUT-'].lower()
if text == '':
continue
row_colors = []
for row, row_data in enumerate(data):
if text in row_data[0].lower():
row_colors.append((row, 'green'))
else:
row_colors.append((row, sg.theme_background_color()))
table.update(row_colors=row_colors)
for iid in widget.get_children():
print(iid, widget.item(iid))
window.close()
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing how sg.Window implements grab_anywhere and its mouse event bindings, then compare that behavior with the provided Window subclass and Tkinter widget event-order notes. The change is done when widgets such as entries, scrollbars, and tables retain their normal mouse behavior while other window areas can still move the window.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- desktop
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100