-
Notifications
You must be signed in to change notification settings - Fork 406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ideas for new widgets #77
Comments
A tooltip widget that can be attached to any other widget https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/tooltip.htm#BABBIJBJ |
Hi! I don't know if it helps with anything, but I've been using this piece of code from stackoverflow to display tooltips:
|
@fredcardoso, I've created a branch to implement this feature. You can see the class here |
@israel-dryer, I'm working on a scrollable Frame widget with some improvements over most I've seen on github (option to automatically hide scrollbars, cross-platform, mousewheel works even when hovering over contained widgets, less jittery scrolling by limiting frame rate) which I'll be happy to contribute to ttkbootstrap when it's finished. I also hope to port the InteractiveNotebook to 1.0 soon after I've migrated my project from 0.5 . |
@daniilS, that would be great. |
@daniilS, here's an imitation Windows 10 toast notification that would work cross-platform. You can set a delay which will result in a fade-out after delay=x milliseconds. Or, you can click to close. I'm also going to add the option of having 'action' buttons. I'm using the Microsoft toast as the basis of the design. The components are the icon, title, and text. The icon is just an emoji, which is accessible with the The only issue I might have is that the taskbar covers the (-0, -0) coordinates, so I have to add some padding to the bottom so it doesn't get covered by the taskbar. Not sure how to get around that piece. |
@daniilS, I've been thinking about this scrolledtext & scrolledframe for a while as well. I've finally worked out a solution that I like with the autohide scrollbar. I decided to experiment with using place instead of pack for the scrollbar, and it's a much smoother effect as it doesn't' cause the window to resize. I've added optional horizontal scrollbars as well. You can see the scrolled widgets here in a dedicated branch. Finally, I added a ScrolledFrame widget. This is one I have not been able to find a perfect solution for, but I've decided to go against the grain on what is typically implemented here. I'm exposing the internal frame in the object instead of the container. This has the benefit of being able to be used like a normal frame, instead of having to use a reference to an internal container. There are just a few situations where a reference to the container is needed (Notebook, Panedwindow), but otherwise, it can be used as normal. Here is an example of import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledText
app = ttk.Window()
st = ScrolledText(app, padding=20, width=30, height=10, hbar=True)
st.pack(fill=BOTH, expand=YES)
for x in range(25):
st.insert(END, f'This is a really long line of text... it is {x+1} of 25!\n')
app.mainloop() Here is an example of import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledFrame
app = ttk.Window()
sf = ScrolledFrame(app)
sf.pack(fill=BOTH, expand=YES, padx=10, pady=10)
for x in range(20):
ttk.Button(sf, text=f"button {x}").pack(anchor=W)
app.mainloop() Here is an example of import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledFrame
app = ttk.Window()
nb = ttk.Notebook()
nb.pack(padx=10, pady=10)
sf = ScrolledFrame(app)
nb.add(sf.container, text="ScrolledFrame", padding=10)
for x in range(20):
ttk.Button(sf, text=f"button {x}").pack(anchor=W)
app.mainloop() |
A suggestion for a widget would be a treeview like KivyMD's datatables a 'Rows per page' combobox, pagination buttons (first, previous, next, last) and autoscroll (Scrolled treeview) vertical and horizontal and a button to refresh the data |
@israel-dryer I should have time to take a look at the scrolled frame later this week. I've got a good way to add scrolling to all future child widgets (differing somewhat from the scrollutil implementation, I'll explain the differences later), and have a solution for the scrolling becoming jittery when moving the scrollbar quickly, so can add that functionality straight away. I'm exploring how feasible it would be to add smooth scrolling with the mousewheel. Re: the suggestion by @antrrax, the tablelist widget looks similar to what you're interested in. |
@antrrax, @daniilS here's a very rough prototype. The example below is paginated with a million records. The pagination can be turned off however.
I set this up so that the pages are only created in the table when the page is requested. I presume this would improve performance as I do not have to wait for hundreds of thousands of records to get inserted into the table before showing it on the screen. ⭐ The only other feature I can think of that I would like to implement is an optional search bar that would filter the contents of the table. ❓Can you think of other features that would be helpful? |
Very useful column sorting option. It would be nice if you could sort columns with the following types of information Some Text, 5, 12.1, 2021-08-31, 2.5.2.1, U$5.30, 15% codes may have several and mixed separators (. - /) = (8980.046.688.998.890) (156.799.179-39) (00776574/0001-56) see an example of sort (incomplete) Another useful option would be to color the lines in the treeview. And differentiate the even lines from the odd ones creating a zebra effect. A button to update table data and Another more advanced option would be a submenu in the header to show or hide the columns in the treeview. (I don't know if this would be useful for all people in all cases) Here is an image of a Crud with this feature. Clicking on the header sorts the data. With the mouse over in the header, it displays a sub-menu option with the following data: |
@israel-dryer haven't gotten to the scrolled frame yet, but took a look at what you asked about the toast notification. On Windows, here's a good way to display the toast in the corner of the screen. It accounts for the taskbar location/size, multiple monitors (which may have different DPIs), and works at any DPI awareness context. I'm not sure if equivalent API calls exist for Linux and macOS, but if not then maybe creating an invisible Toplevel in the zoomed state with and without overrideredirect set, and querying wm_geometry and winfo_geometry could help. import ctypes
import tkinter as tk
user32 = ctypes.windll.user32
# See the links in https://stackoverflow.com/a/4631379/6660529
SPI_GETWORKAREA = 0x30
SM_CXSCREEN = 0
SM_CYSCREEN = 1
# Adapted from
# https://github.com/saveenr/saveenr/blob/master/Demos/python/ui_get_desktop_workarea.py
class RECT(ctypes.Structure):
_fields_ = [
("x0", ctypes.c_ulong),
("y0", ctypes.c_ulong),
("x1", ctypes.c_ulong),
("y1", ctypes.c_ulong),
]
# Gets the usable area (excluding things like the taskbar) of the primary monitor. See
# the link at the top for API calls for other monitors.
workarea = RECT()
user32.SystemParametersInfoA(SPI_GETWORKAREA, 0, ctypes.byref(workarea), 0)
# Not used in this example, but useful in other cases, e.g. if you want to display a
# window in the middle of the screen.
primary_monitor_width = user32.GetSystemMetrics(SM_CXSCREEN)
primary_monitor_height = user32.GetSystemMetrics(SM_CYSCREEN)
popup_width = 200
popup_height = 100
# Distance from the corner of the work area
margin = 10
# Position of the top left corner of the window
position_x = workarea.x1 - popup_width - margin
position_y = workarea.y1 - popup_height - margin
popup = tk.Tk()
popup.overrideredirect(True)
popup.geometry(f"{popup_width}x{popup_height}+{position_x}+{position_y}")
popup.mainloop() |
A suggestion of 2 widgets Entry with placeholder and menu: The other entry widget that inherits the above options and adds a hide and show password button. It also shows a hint when the password is hidden and something is typed with caps lock enabled. The menu and shortcuts are simple functions, but they are very much needed in tkinter Here I use this code, it's far from being professional, the undo and redo part is confusing but it works. Feel free to change the code, redo it from scratch or get inspired by it. You would also need to add these styles to the theme layout.
|
A Widgets suggestion for Api would be to redo the filedialogs and the color dialog for a cross-platform standardization. https://pythonbasics.org/tkinter-filedialog/
These rederized dialogs in Linux have a very ugly and outdated look. See screenshot, it uses menubuttom instead of combobox and with ttk.bootstrap it visually gets even weirder with these components in the primary com of the theme. When you have free time, it would be interesting to create components to include in the Api. Get inspired by Windows dialogs or Qt Dialogs that have a beautiful visual appearance. |
This could be too complex because of tkinter's limitations, but could there be a VideoPlayer widget similar to the one in KivyMD? |
The video component is not difficult. But it's the sound that is problematic. |
I do know of this one tutorial of how to make a tkinter music player, which includes a couple parts about scrubbing between song positions, but this only works with MP3 and OGG files so the video would have to be split with ffmpeg or a similar utility. (the tutorial by the way): https://www.youtube.com/playlist?list=PL2P1yZHTR7QtY2flZuHWjz8Z46W4QJRsO |
A suggestion would be to add a vertical and horizontal ScrolledFrame like this one: DoubleScrolledFrame: try:
import tkinter as tk
from tkinter import ttk
except ImportError:
import Tkinter as tk
import ttk
class DoubleScrolledFrame:
"""
A vertically scrolled Frame that can be treated like any other Frame
ie it needs a master and layout and it can be a master.
keyword arguments are passed to the underlying Frame
except the keyword arguments 'width' and 'height', which
are passed to the underlying Canvas
note that a widget layed out in this frame will have Canvas as self.master,
if you subclass this there is no built in way for the children to access it.
You need to provide the controller separately.
"""
def __init__(self, master, **kwargs):
width = kwargs.pop('width', None)
height = kwargs.pop('height', None)
self.outer = tk.Frame(master, **kwargs)
self.vsb = ttk.Scrollbar(self.outer, orient=tk.VERTICAL)
self.vsb.grid(row=0, column=1, sticky='ns')
self.hsb = ttk.Scrollbar(self.outer, orient=tk.HORIZONTAL)
self.hsb.grid(row=1, column=0, sticky='ew')
self.canvas = tk.Canvas(self.outer, highlightthickness=0, width=width, height=height)
self.canvas.grid(row=0, column=0, sticky='nsew')
self.outer.rowconfigure(0, weight=1)
self.outer.columnconfigure(0, weight=1)
self.canvas['yscrollcommand'] = self.vsb.set
self.canvas['xscrollcommand'] = self.hsb.set
# mouse scroll does not seem to work with just "bind"; You have
# to use "bind_all". Therefore to use multiple windows you have
# to bind_all in the current widget
self.canvas.bind("<Enter>", self._bind_mouse)
self.canvas.bind("<Leave>", self._unbind_mouse)
self.vsb['command'] = self.canvas.yview
self.hsb['command'] = self.canvas.xview
self.inner = tk.Frame(self.canvas)
# pack the inner Frame into the Canvas with the topleft corner 4 pixels offset
self.canvas.create_window(4, 4, window=self.inner, anchor='nw')
self.inner.bind("<Configure>", self._on_frame_configure)
self.outer_attr = set(dir(tk.Widget))
def __getattr__(self, item):
if item in self.outer_attr:
# geometry attributes etc (eg pack, destroy, tkraise) are passed on to self.outer
return getattr(self.outer, item)
else:
# all other attributes (_w, children, etc) are passed to self.inner
return getattr(self.inner, item)
def _on_frame_configure(self, event=None):
x1, y1, x2, y2 = self.canvas.bbox("all")
height = self.canvas.winfo_height()
width = self.canvas.winfo_width()
self.canvas.config(scrollregion = (0,0, max(x2, width), max(y2, height)))
def _bind_mouse(self, event=None):
self.canvas.bind_all("<4>", self._on_mousewheel)
self.canvas.bind_all("<5>", self._on_mousewheel)
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbind_mouse(self, event=None):
self.canvas.unbind_all("<4>")
self.canvas.unbind_all("<5>")
self.canvas.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
"""Linux uses event.num; Windows / Mac uses event.delta"""
func = self.canvas.xview_scroll if event.state & 1 else self.canvas.yview_scroll
if event.num == 4 or event.delta > 0:
func(-1, "units" )
elif event.num == 5 or event.delta < 0:
func(1, "units" )
def __str__(self):
return str(self.outer)
# **** SCROLL BAR TEST *****
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
if __name__ == "__main__":
root = tk.Tk()
root.title("Scrollbar Test")
root.geometry('400x500')
lbl = tk.Label(root, text="Hold shift while using the scroll wheel to scroll horizontally")
lbl.pack()
# use the Scrolled Frame just like any other Frame
frame = DoubleScrolledFrame(root, width=300, borderwidth=2, relief=tk.SUNKEN, background="light gray")
#frame.grid(column=0, row=0, sticky='nsew') # fixed size
frame.pack(fill=tk.BOTH, expand=True) # fill window
for i in range(30):
for j in range(20):
label = tk.Label(frame, text="{}{}".format(alphabet[j], i), relief='ridge')
label.grid(column=j, row=i, sticky='ew', padx=2, pady=2)
root.mainloop() |
Hello sir, How to make scrolltext not editable. |
I've had a problem with using themes. The tkinter menubar stays white no matter what theme is used which looks quite ugly alongside alongside all the other dark widgets. Maybe a method to customize the tkinter window menubar's color? |
An issue for collecting ideas on new widget styles to add into ttk bootstrap.
The text was updated successfully, but these errors were encountered: