mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
finished textbox, combined CTkTextbox and CTkScrolledTextbox into one class, enhanced test_text.py
This commit is contained in:
parent
14b39aa7b4
commit
0cbec6fea1
@ -64,7 +64,6 @@ from .widgets.ctk_optionmenu import CTkOptionMenu
|
|||||||
from .widgets.ctk_combobox import CTkComboBox
|
from .widgets.ctk_combobox import CTkComboBox
|
||||||
from .widgets.ctk_scrollbar import CTkScrollbar
|
from .widgets.ctk_scrollbar import CTkScrollbar
|
||||||
from .widgets.ctk_textbox import CTkTextbox
|
from .widgets.ctk_textbox import CTkTextbox
|
||||||
from .widgets.ctk_scrollbar_textbox import CTkScrolledTextbox
|
|
||||||
|
|
||||||
# import windows
|
# import windows
|
||||||
from .windows.ctk_tk import CTk
|
from .windows.ctk_tk import CTk
|
||||||
|
@ -1,452 +0,0 @@
|
|||||||
import time
|
|
||||||
import tkinter
|
|
||||||
from typing import Union, Tuple
|
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
|
||||||
from .ctk_scrollbar import CTkScrollbar
|
|
||||||
from ..theme_manager import ThemeManager
|
|
||||||
from ..draw_engine import DrawEngine
|
|
||||||
from .widget_base_class import CTkBaseClass
|
|
||||||
|
|
||||||
from .widget_helper_functions import pop_from_dict_by_set
|
|
||||||
|
|
||||||
|
|
||||||
class CTkScrolledTextbox(CTkBaseClass):
|
|
||||||
"""
|
|
||||||
Textbox with x and y scrollbars, rounded corners, and all text features of tkinter.Text widget.
|
|
||||||
Scrollbars only appear when they are needed. Text is wrapped on line end by default,
|
|
||||||
set wrap='none' to disable automatic line wrapping.
|
|
||||||
For detailed information check out the documentation.
|
|
||||||
|
|
||||||
Detailed methods and parameters of the underlaying tkinter.Text widget can be found here:
|
|
||||||
https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/text.html
|
|
||||||
(most of them are implemented here too)
|
|
||||||
"""
|
|
||||||
|
|
||||||
_scrollbar_update_time = 200 # interval in ms, to check if scrollbars are needed
|
|
||||||
_scrollbars_activated = True
|
|
||||||
|
|
||||||
# attributes that are passed to and managed by the tkinter textbox only:
|
|
||||||
_valid_tk_text_attributes = {"autoseparators", "cursor", "exportselection",
|
|
||||||
"insertborderwidth", "insertofftime", "insertontime", "insertwidth",
|
|
||||||
"maxundo", "padx", "pady", "selectborderwidth", "spacing1",
|
|
||||||
"spacing2", "spacing3", "state", "tabs", "takefocus", "undo", "wrap",
|
|
||||||
"xscrollcommand", "yscrollcommand"}
|
|
||||||
|
|
||||||
def __init__(self, *args,
|
|
||||||
width: int = 200,
|
|
||||||
height: int = 200,
|
|
||||||
corner_radius: Union[int, str] = "default_theme",
|
|
||||||
border_width: Union[int, str] = "default_theme",
|
|
||||||
|
|
||||||
bg_color: Union[str, Tuple[str, str], None] = None,
|
|
||||||
fg_color: Union[str, Tuple[str, str], None] = "default_theme",
|
|
||||||
border_color: Union[str, Tuple[str, str]] = "default_theme",
|
|
||||||
text_color: Union[str, str] = "default_theme",
|
|
||||||
scrollbar_color: Union[str, Tuple[str, str]] = "default_theme",
|
|
||||||
scrollbar_hover_color: Union[str, Tuple[str, str]] = "default_theme",
|
|
||||||
|
|
||||||
font: any = "default_theme",
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
|
||||||
if "master" in kwargs:
|
|
||||||
super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs.pop("master"))
|
|
||||||
else:
|
|
||||||
super().__init__(*args, bg_color=bg_color, width=width, height=height)
|
|
||||||
|
|
||||||
# color
|
|
||||||
self._fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
|
|
||||||
self._border_color = ThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color
|
|
||||||
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
|
|
||||||
self._scrollbar_color = ThemeManager.theme["color"]["scrollbar_button"] if scrollbar_color == "default_theme" else scrollbar_color
|
|
||||||
self._scrollbar_hover_color = ThemeManager.theme["color"]["scrollbar_button_hover"] if scrollbar_hover_color == "default_theme" else scrollbar_hover_color
|
|
||||||
|
|
||||||
# shape
|
|
||||||
self._corner_radius = ThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius
|
|
||||||
self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width
|
|
||||||
|
|
||||||
# text
|
|
||||||
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
|
|
||||||
|
|
||||||
self._canvas = CTkCanvas(master=self,
|
|
||||||
highlightthickness=0,
|
|
||||||
width=self._apply_widget_scaling(self._current_width),
|
|
||||||
height=self._apply_widget_scaling(self._current_height))
|
|
||||||
self._canvas.grid(row=0, column=0, padx=0, pady=0, rowspan=2, columnspan=2, sticky="nsew")
|
|
||||||
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
|
||||||
self._draw_engine = DrawEngine(self._canvas)
|
|
||||||
|
|
||||||
self._textbox = tkinter.Text(self,
|
|
||||||
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
|
||||||
width=0,
|
|
||||||
height=0,
|
|
||||||
font=self._apply_font_scaling(self._font),
|
|
||||||
highlightthickness=0,
|
|
||||||
relief="flat",
|
|
||||||
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
|
||||||
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
|
||||||
**pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
|
|
||||||
|
|
||||||
self._check_kwargs_empty(kwargs, raise_error=True)
|
|
||||||
|
|
||||||
self._y_scrollbar = CTkScrollbar(self,
|
|
||||||
width=8,
|
|
||||||
border_spacing=0,
|
|
||||||
fg_color=self._fg_color,
|
|
||||||
scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color,
|
|
||||||
orientation="vertical",
|
|
||||||
command=self._textbox.yview)
|
|
||||||
self._textbox.configure(yscrollcommand=self._y_scrollbar.set)
|
|
||||||
self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="ns",
|
|
||||||
padx=(self._apply_widget_scaling(3), self._apply_widget_scaling(3 + self._border_width)),
|
|
||||||
pady=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0))
|
|
||||||
|
|
||||||
self._x_scrollbar = CTkScrollbar(self,
|
|
||||||
height=8,
|
|
||||||
border_spacing=0,
|
|
||||||
fg_color=self._fg_color,
|
|
||||||
scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color,
|
|
||||||
orientation="horizontal",
|
|
||||||
command=self._textbox.xview)
|
|
||||||
self._textbox.configure(xscrollcommand=self._x_scrollbar.set)
|
|
||||||
self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ew",
|
|
||||||
pady=(self._apply_widget_scaling(3), self._apply_widget_scaling(3 + self._border_width)),
|
|
||||||
padx=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0))
|
|
||||||
|
|
||||||
self._hide_x_scrollbar = True
|
|
||||||
self._hide_y_scrollbar = True
|
|
||||||
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
|
||||||
|
|
||||||
self.after(500, self._check_if_scrollbars_needed)
|
|
||||||
|
|
||||||
super().bind('<Configure>', self._update_dimensions_event)
|
|
||||||
self._draw()
|
|
||||||
|
|
||||||
def _manage_grid_commands(self, re_grid_textbox=False, re_grid_x_scrollbar=False, re_grid_y_scrollbar=False):
|
|
||||||
|
|
||||||
# configure 2x2 grid
|
|
||||||
self.grid_rowconfigure(0, weight=1)
|
|
||||||
self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(max(self._corner_radius, self._border_width)))
|
|
||||||
self.grid_columnconfigure(0, weight=1)
|
|
||||||
self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(max(self._corner_radius, self._border_width)))
|
|
||||||
|
|
||||||
if re_grid_textbox:
|
|
||||||
self._textbox.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew",
|
|
||||||
padx=(self._apply_widget_scaling(max(self._corner_radius, self._border_width)), 0),
|
|
||||||
pady=(self._apply_widget_scaling(max(self._corner_radius, self._border_width)), 0))
|
|
||||||
|
|
||||||
if re_grid_x_scrollbar:
|
|
||||||
if not self._hide_x_scrollbar:
|
|
||||||
self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ew",
|
|
||||||
pady=(3, 3 + self._border_width),
|
|
||||||
padx=(max(self._corner_radius, self._border_width), 0)) # scrollbar grid method without scaling
|
|
||||||
else:
|
|
||||||
self._x_scrollbar.grid_forget()
|
|
||||||
|
|
||||||
if re_grid_y_scrollbar:
|
|
||||||
if not self._hide_y_scrollbar:
|
|
||||||
self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="ns",
|
|
||||||
padx=(3, 3 + self._border_width),
|
|
||||||
pady=(max(self._corner_radius, self._border_width), 0)) # scrollbar grid method without scaling
|
|
||||||
else:
|
|
||||||
self._y_scrollbar.grid_forget()
|
|
||||||
|
|
||||||
def _check_if_scrollbars_needed(self, event=None):
|
|
||||||
""" Method hides or places the scrollbars if they are needed on key release event of tkinter.text widget """
|
|
||||||
|
|
||||||
if self._textbox.xview() != (0.0, 1.0) and not self._x_scrollbar.winfo_ismapped(): # x scrollbar needed
|
|
||||||
self._hide_x_scrollbar = False
|
|
||||||
self._manage_grid_commands(re_grid_x_scrollbar=True)
|
|
||||||
elif self._textbox.xview() == (0.0, 1.0) and self._x_scrollbar.winfo_ismapped(): # x scrollbar not needed
|
|
||||||
self._hide_x_scrollbar = True
|
|
||||||
self._manage_grid_commands(re_grid_x_scrollbar=True)
|
|
||||||
|
|
||||||
if self._textbox.yview() != (0.0, 1.0) and not self._y_scrollbar.winfo_ismapped(): # y scrollbar needed
|
|
||||||
self._hide_y_scrollbar = False
|
|
||||||
self._manage_grid_commands(re_grid_y_scrollbar=True)
|
|
||||||
elif self._textbox.yview() == (0.0, 1.0) and self._y_scrollbar.winfo_ismapped(): # y scrollbar not needed
|
|
||||||
self._hide_y_scrollbar = True
|
|
||||||
self._manage_grid_commands(re_grid_y_scrollbar=True)
|
|
||||||
|
|
||||||
if self._textbox.winfo_exists():
|
|
||||||
self.after(200, self._check_if_scrollbars_needed)
|
|
||||||
|
|
||||||
def _set_scaling(self, *args, **kwargs):
|
|
||||||
super()._set_scaling(*args, **kwargs)
|
|
||||||
|
|
||||||
self._textbox.configure(font=self._apply_font_scaling(self._font))
|
|
||||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
|
||||||
height=self._apply_widget_scaling(self._desired_height))
|
|
||||||
self._manage_grid_commands(re_grid_textbox=False, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
|
||||||
self._draw()
|
|
||||||
|
|
||||||
def _set_dimensions(self, width=None, height=None):
|
|
||||||
super()._set_dimensions(width, height)
|
|
||||||
|
|
||||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
|
||||||
height=self._apply_widget_scaling(self._desired_height))
|
|
||||||
self._draw()
|
|
||||||
|
|
||||||
def _draw(self, no_color_updates=False):
|
|
||||||
|
|
||||||
requires_recoloring = self._draw_engine.draw_rounded_rect_with_border(self._apply_widget_scaling(self._current_width),
|
|
||||||
self._apply_widget_scaling(self._current_height),
|
|
||||||
self._apply_widget_scaling(self._corner_radius),
|
|
||||||
self._apply_widget_scaling(self._border_width))
|
|
||||||
|
|
||||||
if no_color_updates is False or requires_recoloring:
|
|
||||||
if self._fg_color is None:
|
|
||||||
self._canvas.itemconfig("inner_parts",
|
|
||||||
fill=ThemeManager.single_color(self._bg_color, self._appearance_mode),
|
|
||||||
outline=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
|
||||||
self._textbox.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
|
||||||
bg=ThemeManager.single_color(self._bg_color, self._appearance_mode),
|
|
||||||
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
|
|
||||||
self._x_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color)
|
|
||||||
self._y_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color)
|
|
||||||
else:
|
|
||||||
self._canvas.itemconfig("inner_parts",
|
|
||||||
fill=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
|
||||||
outline=ThemeManager.single_color(self._fg_color, self._appearance_mode))
|
|
||||||
self._textbox.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
|
||||||
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
|
||||||
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
|
|
||||||
self._x_scrollbar.configure(fg_color=self._fg_color, scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color)
|
|
||||||
self._y_scrollbar.configure(fg_color=self._fg_color, scrollbar_color=self._scrollbar_color,
|
|
||||||
scrollbar_hover_color=self._scrollbar_hover_color)
|
|
||||||
|
|
||||||
self._canvas.itemconfig("border_parts",
|
|
||||||
fill=ThemeManager.single_color(self._border_color, self._appearance_mode),
|
|
||||||
outline=ThemeManager.single_color(self._border_color, self._appearance_mode))
|
|
||||||
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
|
||||||
|
|
||||||
self._canvas.tag_lower("inner_parts")
|
|
||||||
self._canvas.tag_lower("border_parts")
|
|
||||||
|
|
||||||
def configure(self, require_redraw=False, **kwargs):
|
|
||||||
if "fg_color" in kwargs:
|
|
||||||
self._fg_color = kwargs.pop("fg_color")
|
|
||||||
require_redraw = True
|
|
||||||
|
|
||||||
# check if CTk widgets are children of the frame and change their _bg_color to new frame fg_color
|
|
||||||
for child in self.winfo_children():
|
|
||||||
if isinstance(child, CTkBaseClass) and hasattr(child, "_fg_color"):
|
|
||||||
child.configure(bg_color=self._fg_color)
|
|
||||||
|
|
||||||
if "border_color" in kwargs:
|
|
||||||
self._border_color = kwargs.pop("border_color")
|
|
||||||
require_redraw = True
|
|
||||||
|
|
||||||
if "text_color" in kwargs:
|
|
||||||
self._text_color = kwargs.pop("text_color")
|
|
||||||
require_redraw = True
|
|
||||||
|
|
||||||
if "corner_radius" in kwargs:
|
|
||||||
self._corner_radius = kwargs.pop("corner_radius")
|
|
||||||
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
|
||||||
require_redraw = True
|
|
||||||
|
|
||||||
if "border_width" in kwargs:
|
|
||||||
self._border_width = kwargs.pop("border_width")
|
|
||||||
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
|
||||||
require_redraw = True
|
|
||||||
|
|
||||||
if "width" in kwargs:
|
|
||||||
self._set_dimensions(width=kwargs.pop("width"))
|
|
||||||
|
|
||||||
if "height" in kwargs:
|
|
||||||
self._set_dimensions(height=kwargs.pop("height"))
|
|
||||||
|
|
||||||
if "font" in kwargs:
|
|
||||||
self._font = kwargs.pop("font")
|
|
||||||
self._textbox.configure(font=self._apply_font_scaling(self._font))
|
|
||||||
|
|
||||||
self._textbox.configure(**pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
|
|
||||||
super().configure(require_redraw=require_redraw, **kwargs)
|
|
||||||
|
|
||||||
def cget(self, attribute_name: str) -> any:
|
|
||||||
if attribute_name == "corner_radius":
|
|
||||||
return self._corner_radius
|
|
||||||
elif attribute_name == "border_width":
|
|
||||||
return self._border_width
|
|
||||||
|
|
||||||
elif attribute_name == "fg_color":
|
|
||||||
return self._fg_color
|
|
||||||
elif attribute_name == "border_color":
|
|
||||||
return self._border_color
|
|
||||||
elif attribute_name == "text_color":
|
|
||||||
return self._text_color
|
|
||||||
|
|
||||||
elif attribute_name == "font":
|
|
||||||
return self._font
|
|
||||||
|
|
||||||
else:
|
|
||||||
return super().cget(attribute_name)
|
|
||||||
|
|
||||||
def bind(self, sequence=None, command=None, add=None):
|
|
||||||
""" called on the tkinter.Text """
|
|
||||||
|
|
||||||
# if sequence is <KeyRelease>, allow only to add the binding to keep the _textbox_modified_event() being called
|
|
||||||
if sequence == "<KeyRelease>":
|
|
||||||
return self._textbox.bind(sequence, command, add="+")
|
|
||||||
else:
|
|
||||||
return self._textbox.bind(sequence, command, add)
|
|
||||||
|
|
||||||
def unbind(self, sequence, funcid=None):
|
|
||||||
""" called on the tkinter.Text """
|
|
||||||
return self._textbox.unbind(sequence, funcid)
|
|
||||||
|
|
||||||
def insert(self, index, text, tags=None):
|
|
||||||
self._check_if_scrollbars_needed()
|
|
||||||
return self._textbox.insert(index, text, tags)
|
|
||||||
|
|
||||||
def get(self, index1, index2=None):
|
|
||||||
return self._textbox.get(index1, index2)
|
|
||||||
|
|
||||||
def bbox(self, index):
|
|
||||||
return self._textbox.bbox(index)
|
|
||||||
|
|
||||||
def compare(self, index, op, index2):
|
|
||||||
return self._textbox.compare(index, op, index2)
|
|
||||||
|
|
||||||
def dlineinfo(self, index):
|
|
||||||
return self._textbox.dlineinfo(index)
|
|
||||||
|
|
||||||
def edit_modified(self, arg=None):
|
|
||||||
return self._textbox.edit_modified(arg)
|
|
||||||
|
|
||||||
def edit_redo(self):
|
|
||||||
self._check_if_scrollbars_needed()
|
|
||||||
return self._textbox.edit_redo()
|
|
||||||
|
|
||||||
def edit_reset(self):
|
|
||||||
return self._textbox.edit_reset()
|
|
||||||
|
|
||||||
def edit_separator(self):
|
|
||||||
return self._textbox.edit_separator()
|
|
||||||
|
|
||||||
def edit_undo(self):
|
|
||||||
self._check_if_scrollbars_needed()
|
|
||||||
return self._textbox.edit_undo()
|
|
||||||
|
|
||||||
def image_create(self, index, **kwargs):
|
|
||||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
|
||||||
|
|
||||||
def image_cget(self, index, option):
|
|
||||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
|
||||||
|
|
||||||
def image_configure(self, index):
|
|
||||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
|
||||||
|
|
||||||
def image_names(self):
|
|
||||||
raise AttributeError("embedding images is forbidden, because would be incompatible with scaling")
|
|
||||||
|
|
||||||
def index(self, i):
|
|
||||||
return self._textbox.index(i)
|
|
||||||
|
|
||||||
def mark_gravity(self, mark, gravity=None):
|
|
||||||
return self._textbox.mark_gravity(mark, gravity)
|
|
||||||
|
|
||||||
def mark_names(self):
|
|
||||||
return self._textbox.mark_names()
|
|
||||||
|
|
||||||
def mark_next(self, index):
|
|
||||||
return self._textbox.mark_next(index)
|
|
||||||
|
|
||||||
def mark_previous(self, index):
|
|
||||||
return self._textbox.mark_previous(index)
|
|
||||||
|
|
||||||
def mark_set(self, mark, index):
|
|
||||||
return self._textbox.mark_set(mark, index)
|
|
||||||
|
|
||||||
def mark_unset(self, mark):
|
|
||||||
return self._textbox.mark_unset(mark)
|
|
||||||
|
|
||||||
def scan_dragto(self, x, y):
|
|
||||||
return self._textbox.scan_dragto(x, y)
|
|
||||||
|
|
||||||
def scan_mark(self, x, y):
|
|
||||||
return self._textbox.scan_mark(x, y)
|
|
||||||
|
|
||||||
def search(self, pattern, index, *args, **kwargs):
|
|
||||||
return self._textbox.search(pattern, index, *args, **kwargs)
|
|
||||||
|
|
||||||
def see(self, index):
|
|
||||||
return self._textbox.see(index)
|
|
||||||
|
|
||||||
def tag_add(self, tagName, index1, index2=None):
|
|
||||||
return self._textbox.tag_add(tagName, index1, index2)
|
|
||||||
|
|
||||||
def tag_bind(self, tagName, sequence, func, add=None):
|
|
||||||
return self._textbox.tag_bind(tagName, sequence, func, add)
|
|
||||||
|
|
||||||
def tag_cget(self, tagName, option):
|
|
||||||
return self._textbox.tag_cget(tagName, option)
|
|
||||||
|
|
||||||
def tag_config(self, tagName, **kwargs):
|
|
||||||
if "font" in kwargs:
|
|
||||||
raise AttributeError("'font' option forbidden, because would be incompatible with scaling")
|
|
||||||
return self._textbox.tag_config(tagName, **kwargs)
|
|
||||||
|
|
||||||
def tag_delete(self, *tagName):
|
|
||||||
return self._textbox.tag_delete(*tagName)
|
|
||||||
|
|
||||||
def tag_lower(self, tagName, belowThis=None):
|
|
||||||
return self._textbox.tag_lower(tagName, belowThis)
|
|
||||||
|
|
||||||
def tag_names(self, index=None):
|
|
||||||
return self._textbox.tag_names(index)
|
|
||||||
|
|
||||||
def tag_nextrange(self, tagName, index1, index2=None):
|
|
||||||
return self._textbox.tag_nextrange(tagName, index1, index2)
|
|
||||||
|
|
||||||
def tag_prevrange(self, tagName, index1, index2=None):
|
|
||||||
return self._textbox.tag_prevrange(tagName, index1, index2)
|
|
||||||
|
|
||||||
def tag_raise(self, tagName, aboveThis=None):
|
|
||||||
return self._textbox.tag_raise(tagName, aboveThis)
|
|
||||||
|
|
||||||
def tag_ranges(self, tagName):
|
|
||||||
return self._textbox.tag_ranges(tagName)
|
|
||||||
|
|
||||||
def tag_remove(self, tagName, index1, index2=None):
|
|
||||||
return self._textbox.tag_remove(tagName, index1, index2)
|
|
||||||
|
|
||||||
def tag_unbind(self, tagName, sequence, funcid=None):
|
|
||||||
return self._textbox.tag_unbind(tagName, sequence, funcid)
|
|
||||||
|
|
||||||
def window_cget(self, index, option):
|
|
||||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
|
||||||
|
|
||||||
def window_configure(self, index, option):
|
|
||||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
|
||||||
|
|
||||||
def window_create(self, index, **kwargs):
|
|
||||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
|
||||||
|
|
||||||
def window_names(self):
|
|
||||||
raise AttributeError("embedding widgets is forbidden, would probably cause all kinds of problems ;)")
|
|
||||||
|
|
||||||
def xview(self, *args):
|
|
||||||
return self._textbox.xview(*args)
|
|
||||||
|
|
||||||
def xview_moveto(self, fraction):
|
|
||||||
return self._textbox.xview_moveto(fraction)
|
|
||||||
|
|
||||||
def xview_scroll(self, n, what):
|
|
||||||
return self._textbox.xview_scroll(n, what)
|
|
||||||
|
|
||||||
def yview(self, *args):
|
|
||||||
return self._textbox.yview(*args)
|
|
||||||
|
|
||||||
def yview_moveto(self, fraction):
|
|
||||||
return self._textbox.yview_moveto(fraction)
|
|
||||||
|
|
||||||
def yview_scroll(self, n, what):
|
|
||||||
return self._textbox.yview_scroll(n, what)
|
|
@ -2,6 +2,7 @@ import tkinter
|
|||||||
from typing import Union, Tuple
|
from typing import Union, Tuple
|
||||||
|
|
||||||
from .ctk_canvas import CTkCanvas
|
from .ctk_canvas import CTkCanvas
|
||||||
|
from .ctk_scrollbar import CTkScrollbar
|
||||||
from ..theme_manager import ThemeManager
|
from ..theme_manager import ThemeManager
|
||||||
from ..draw_engine import DrawEngine
|
from ..draw_engine import DrawEngine
|
||||||
from .widget_base_class import CTkBaseClass
|
from .widget_base_class import CTkBaseClass
|
||||||
@ -11,7 +12,9 @@ from .widget_helper_functions import pop_from_dict_by_set
|
|||||||
|
|
||||||
class CTkTextbox(CTkBaseClass):
|
class CTkTextbox(CTkBaseClass):
|
||||||
"""
|
"""
|
||||||
Textbox with rounded corners, and all text features of tkinter.Text widget.
|
Textbox with x and y scrollbars, rounded corners, and all text features of tkinter.Text widget.
|
||||||
|
Scrollbars only appear when they are needed. Text is wrapped on line end by default,
|
||||||
|
set wrap='none' to disable automatic line wrapping.
|
||||||
For detailed information check out the documentation.
|
For detailed information check out the documentation.
|
||||||
|
|
||||||
Detailed methods and parameters of the underlaying tkinter.Text widget can be found here:
|
Detailed methods and parameters of the underlaying tkinter.Text widget can be found here:
|
||||||
@ -19,6 +22,8 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
(most of them are implemented here too)
|
(most of them are implemented here too)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_scrollbar_update_time = 200 # interval in ms, to check if scrollbars are needed
|
||||||
|
|
||||||
# attributes that are passed to and managed by the tkinter textbox only:
|
# attributes that are passed to and managed by the tkinter textbox only:
|
||||||
_valid_tk_text_attributes = {"autoseparators", "cursor", "exportselection",
|
_valid_tk_text_attributes = {"autoseparators", "cursor", "exportselection",
|
||||||
"insertborderwidth", "insertofftime", "insertontime", "insertwidth",
|
"insertborderwidth", "insertofftime", "insertontime", "insertwidth",
|
||||||
@ -31,13 +36,17 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
height: int = 200,
|
height: int = 200,
|
||||||
corner_radius: Union[int, str] = "default_theme",
|
corner_radius: Union[int, str] = "default_theme",
|
||||||
border_width: Union[int, str] = "default_theme",
|
border_width: Union[int, str] = "default_theme",
|
||||||
|
border_spacing: int = 3,
|
||||||
|
|
||||||
bg_color: Union[str, Tuple[str, str], None] = None,
|
bg_color: Union[str, Tuple[str, str], None] = None,
|
||||||
fg_color: Union[str, Tuple[str, str], None] = "default_theme",
|
fg_color: Union[str, Tuple[str, str], None] = "default_theme",
|
||||||
border_color: Union[str, Tuple[str, str]] = "default_theme",
|
border_color: Union[str, Tuple[str, str]] = "default_theme",
|
||||||
text_color: Union[str, str] = "default_theme",
|
text_color: Union[str, str] = "default_theme",
|
||||||
|
scrollbar_color: Union[str, Tuple[str, str]] = "default_theme",
|
||||||
|
scrollbar_hover_color: Union[str, Tuple[str, str]] = "default_theme",
|
||||||
|
|
||||||
font: any = "default_theme",
|
font: any = "default_theme",
|
||||||
|
activate_scrollbars: bool = True,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
# transfer basic functionality (_bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
||||||
@ -50,23 +59,22 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
self._fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
|
self._fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color
|
||||||
self._border_color = ThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color
|
self._border_color = ThemeManager.theme["color"]["frame_border"] if border_color == "default_theme" else border_color
|
||||||
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
|
self._text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color
|
||||||
|
self._scrollbar_color = ThemeManager.theme["color"]["scrollbar_button"] if scrollbar_color == "default_theme" else scrollbar_color
|
||||||
|
self._scrollbar_hover_color = ThemeManager.theme["color"]["scrollbar_button_hover"] if scrollbar_hover_color == "default_theme" else scrollbar_hover_color
|
||||||
|
|
||||||
# shape
|
# shape
|
||||||
self._corner_radius = ThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius
|
self._corner_radius = ThemeManager.theme["shape"]["frame_corner_radius"] if corner_radius == "default_theme" else corner_radius
|
||||||
self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width
|
self._border_width = ThemeManager.theme["shape"]["frame_border_width"] if border_width == "default_theme" else border_width
|
||||||
|
self._border_spacing = border_spacing
|
||||||
|
|
||||||
# text
|
# text
|
||||||
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
|
self._font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if font == "default_theme" else font
|
||||||
|
|
||||||
# configure 1x1 grid
|
|
||||||
self.grid_rowconfigure(0, weight=1)
|
|
||||||
self.grid_columnconfigure(0, weight=1)
|
|
||||||
|
|
||||||
self._canvas = CTkCanvas(master=self,
|
self._canvas = CTkCanvas(master=self,
|
||||||
highlightthickness=0,
|
highlightthickness=0,
|
||||||
width=self._apply_widget_scaling(self._current_width),
|
width=self._apply_widget_scaling(self._current_width),
|
||||||
height=self._apply_widget_scaling(self._current_height))
|
height=self._apply_widget_scaling(self._current_height))
|
||||||
self._canvas.grid(row=0, column=0, padx=0, pady=0, rowspan=1, columnspan=1, sticky="nsew")
|
self._canvas.grid(row=0, column=0, padx=0, pady=0, rowspan=2, columnspan=2, sticky="nsew")
|
||||||
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
||||||
self._draw_engine = DrawEngine(self._canvas)
|
self._draw_engine = DrawEngine(self._canvas)
|
||||||
|
|
||||||
@ -74,31 +82,116 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
||||||
width=0,
|
width=0,
|
||||||
height=0,
|
height=0,
|
||||||
font=self._font,
|
font=self._apply_font_scaling(self._font),
|
||||||
highlightthickness=0,
|
highlightthickness=0,
|
||||||
relief="flat",
|
relief="flat",
|
||||||
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
||||||
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
||||||
**pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
|
**pop_from_dict_by_set(kwargs, self._valid_tk_text_attributes))
|
||||||
self._textbox.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew",
|
|
||||||
padx=self._apply_widget_scaling(self._corner_radius),
|
|
||||||
pady=self._apply_widget_scaling(self._corner_radius))
|
|
||||||
|
|
||||||
self._check_kwargs_empty(kwargs, raise_error=True)
|
self._check_kwargs_empty(kwargs, raise_error=True)
|
||||||
|
|
||||||
|
# scrollbars
|
||||||
|
self._scrollbars_activated = activate_scrollbars
|
||||||
|
self._hide_x_scrollbar = True
|
||||||
|
self._hide_y_scrollbar = True
|
||||||
|
|
||||||
|
self._y_scrollbar = CTkScrollbar(self,
|
||||||
|
width=8,
|
||||||
|
height=0,
|
||||||
|
border_spacing=0,
|
||||||
|
fg_color=self._fg_color,
|
||||||
|
scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color,
|
||||||
|
orientation="vertical",
|
||||||
|
command=self._textbox.yview)
|
||||||
|
self._textbox.configure(yscrollcommand=self._y_scrollbar.set)
|
||||||
|
self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="ns",
|
||||||
|
padx=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)),
|
||||||
|
pady=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0))
|
||||||
|
|
||||||
|
self._x_scrollbar = CTkScrollbar(self,
|
||||||
|
height=8,
|
||||||
|
width=0,
|
||||||
|
border_spacing=0,
|
||||||
|
fg_color=self._fg_color,
|
||||||
|
scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color,
|
||||||
|
orientation="horizontal",
|
||||||
|
command=self._textbox.xview)
|
||||||
|
self._textbox.configure(xscrollcommand=self._x_scrollbar.set)
|
||||||
|
self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ew",
|
||||||
|
pady=(self._apply_widget_scaling(3), self._apply_widget_scaling(self._border_spacing + self._border_width)),
|
||||||
|
padx=(self._apply_widget_scaling(self._corner_radius + self._border_width), 0))
|
||||||
|
|
||||||
|
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
||||||
|
|
||||||
|
self.after(500, self._check_if_scrollbars_needed)
|
||||||
|
|
||||||
super().bind('<Configure>', self._update_dimensions_event)
|
super().bind('<Configure>', self._update_dimensions_event)
|
||||||
self._draw()
|
self._draw()
|
||||||
|
|
||||||
|
def _manage_grid_commands(self, re_grid_textbox=False, re_grid_x_scrollbar=False, re_grid_y_scrollbar=False):
|
||||||
|
|
||||||
|
# configure 2x2 grid
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_rowconfigure(1, weight=0, minsize=self._apply_widget_scaling(max(self._corner_radius, self._border_width + self._border_spacing)))
|
||||||
|
self.grid_columnconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=0, minsize=self._apply_widget_scaling(max(self._corner_radius, self._border_width + self._border_spacing)))
|
||||||
|
|
||||||
|
if re_grid_textbox:
|
||||||
|
self._textbox.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew",
|
||||||
|
padx=(self._apply_widget_scaling(max(self._corner_radius, self._border_width + self._border_spacing)), 0),
|
||||||
|
pady=(self._apply_widget_scaling(max(self._corner_radius, self._border_width + self._border_spacing)), 0))
|
||||||
|
|
||||||
|
if re_grid_x_scrollbar:
|
||||||
|
if not self._hide_x_scrollbar and self._scrollbars_activated:
|
||||||
|
self._x_scrollbar.grid(row=1, column=0, rowspan=1, columnspan=1, sticky="ewn",
|
||||||
|
pady=(3, self._border_spacing + self._border_width),
|
||||||
|
padx=(max(self._corner_radius, self._border_width + self._border_spacing), self._border_spacing)) # scrollbar grid method without scaling
|
||||||
|
else:
|
||||||
|
self._x_scrollbar.grid_forget()
|
||||||
|
|
||||||
|
if re_grid_y_scrollbar:
|
||||||
|
if not self._hide_y_scrollbar and self._scrollbars_activated:
|
||||||
|
self._y_scrollbar.grid(row=0, column=1, rowspan=1, columnspan=1, sticky="nsw",
|
||||||
|
padx=(3, self._border_spacing + self._border_width),
|
||||||
|
pady=(max(self._corner_radius, self._border_width + self._border_spacing), self._border_spacing)) # scrollbar grid method without scaling
|
||||||
|
else:
|
||||||
|
self._y_scrollbar.grid_forget()
|
||||||
|
|
||||||
|
def _check_if_scrollbars_needed(self, event=None):
|
||||||
|
""" Method hides or places the scrollbars if they are needed on key release event of tkinter.text widget """
|
||||||
|
|
||||||
|
if self._scrollbars_activated:
|
||||||
|
if self._textbox.xview() != (0.0, 1.0) and not self._x_scrollbar.winfo_ismapped(): # x scrollbar needed
|
||||||
|
self._hide_x_scrollbar = False
|
||||||
|
self._manage_grid_commands(re_grid_x_scrollbar=True)
|
||||||
|
elif self._textbox.xview() == (0.0, 1.0) and self._x_scrollbar.winfo_ismapped(): # x scrollbar not needed
|
||||||
|
self._hide_x_scrollbar = True
|
||||||
|
self._manage_grid_commands(re_grid_x_scrollbar=True)
|
||||||
|
|
||||||
|
if self._textbox.yview() != (0.0, 1.0) and not self._y_scrollbar.winfo_ismapped(): # y scrollbar needed
|
||||||
|
self._hide_y_scrollbar = False
|
||||||
|
self._manage_grid_commands(re_grid_y_scrollbar=True)
|
||||||
|
elif self._textbox.yview() == (0.0, 1.0) and self._y_scrollbar.winfo_ismapped(): # y scrollbar not needed
|
||||||
|
self._hide_y_scrollbar = True
|
||||||
|
self._manage_grid_commands(re_grid_y_scrollbar=True)
|
||||||
|
else:
|
||||||
|
self._hide_x_scrollbar = False
|
||||||
|
self._hide_x_scrollbar = False
|
||||||
|
self._manage_grid_commands(re_grid_y_scrollbar=True)
|
||||||
|
|
||||||
|
if self._textbox.winfo_exists():
|
||||||
|
self.after(200, self._check_if_scrollbars_needed)
|
||||||
|
|
||||||
def _set_scaling(self, *args, **kwargs):
|
def _set_scaling(self, *args, **kwargs):
|
||||||
super()._set_scaling(*args, **kwargs)
|
super()._set_scaling(*args, **kwargs)
|
||||||
|
|
||||||
self._textbox.configure(font=self._apply_font_scaling(self._font))
|
self._textbox.configure(font=self._apply_font_scaling(self._font))
|
||||||
self._textbox.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew",
|
|
||||||
padx=self._apply_widget_scaling(self._corner_radius),
|
|
||||||
pady=self._apply_widget_scaling(self._corner_radius))
|
|
||||||
|
|
||||||
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
self._canvas.configure(width=self._apply_widget_scaling(self._desired_width),
|
||||||
height=self._apply_widget_scaling(self._desired_height))
|
height=self._apply_widget_scaling(self._desired_height))
|
||||||
|
self._manage_grid_commands(re_grid_textbox=False, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
||||||
self._draw()
|
self._draw()
|
||||||
|
|
||||||
def _set_dimensions(self, width=None, height=None):
|
def _set_dimensions(self, width=None, height=None):
|
||||||
@ -120,20 +213,30 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
self._canvas.itemconfig("inner_parts",
|
self._canvas.itemconfig("inner_parts",
|
||||||
fill=ThemeManager.single_color(self._bg_color, self._appearance_mode),
|
fill=ThemeManager.single_color(self._bg_color, self._appearance_mode),
|
||||||
outline=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
outline=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
||||||
|
self._textbox.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
||||||
|
bg=ThemeManager.single_color(self._bg_color, self._appearance_mode),
|
||||||
|
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
|
||||||
|
self._x_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color)
|
||||||
|
self._y_scrollbar.configure(fg_color=self._bg_color, scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color)
|
||||||
else:
|
else:
|
||||||
self._canvas.itemconfig("inner_parts",
|
self._canvas.itemconfig("inner_parts",
|
||||||
fill=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
fill=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
||||||
outline=ThemeManager.single_color(self._fg_color, self._appearance_mode))
|
outline=ThemeManager.single_color(self._fg_color, self._appearance_mode))
|
||||||
|
self._textbox.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
||||||
|
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
||||||
|
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
|
||||||
|
self._x_scrollbar.configure(fg_color=self._fg_color, scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color)
|
||||||
|
self._y_scrollbar.configure(fg_color=self._fg_color, scrollbar_color=self._scrollbar_color,
|
||||||
|
scrollbar_hover_color=self._scrollbar_hover_color)
|
||||||
|
|
||||||
self._canvas.itemconfig("border_parts",
|
self._canvas.itemconfig("border_parts",
|
||||||
fill=ThemeManager.single_color(self._border_color, self._appearance_mode),
|
fill=ThemeManager.single_color(self._border_color, self._appearance_mode),
|
||||||
outline=ThemeManager.single_color(self._border_color, self._appearance_mode))
|
outline=ThemeManager.single_color(self._border_color, self._appearance_mode))
|
||||||
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
self._canvas.configure(bg=ThemeManager.single_color(self._bg_color, self._appearance_mode))
|
||||||
|
|
||||||
self._textbox.configure(fg=ThemeManager.single_color(self._text_color, self._appearance_mode),
|
|
||||||
bg=ThemeManager.single_color(self._fg_color, self._appearance_mode),
|
|
||||||
insertbackground=ThemeManager.single_color(self._text_color, self._appearance_mode))
|
|
||||||
|
|
||||||
self._canvas.tag_lower("inner_parts")
|
self._canvas.tag_lower("inner_parts")
|
||||||
self._canvas.tag_lower("border_parts")
|
self._canvas.tag_lower("border_parts")
|
||||||
|
|
||||||
@ -157,13 +260,12 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
|
|
||||||
if "corner_radius" in kwargs:
|
if "corner_radius" in kwargs:
|
||||||
self._corner_radius = kwargs.pop("corner_radius")
|
self._corner_radius = kwargs.pop("corner_radius")
|
||||||
self._textbox.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew",
|
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
||||||
padx=self._apply_widget_scaling(self._corner_radius),
|
|
||||||
pady=self._apply_widget_scaling(self._corner_radius))
|
|
||||||
require_redraw = True
|
require_redraw = True
|
||||||
|
|
||||||
if "border_width" in kwargs:
|
if "border_width" in kwargs:
|
||||||
self._border_width = kwargs.pop("border_width")
|
self._border_width = kwargs.pop("border_width")
|
||||||
|
self._manage_grid_commands(re_grid_textbox=True, re_grid_x_scrollbar=True, re_grid_y_scrollbar=True)
|
||||||
require_redraw = True
|
require_redraw = True
|
||||||
|
|
||||||
if "width" in kwargs:
|
if "width" in kwargs:
|
||||||
@ -200,6 +302,11 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
|
|
||||||
def bind(self, sequence=None, command=None, add=None):
|
def bind(self, sequence=None, command=None, add=None):
|
||||||
""" called on the tkinter.Text """
|
""" called on the tkinter.Text """
|
||||||
|
|
||||||
|
# if sequence is <KeyRelease>, allow only to add the binding to keep the _textbox_modified_event() being called
|
||||||
|
if sequence == "<KeyRelease>":
|
||||||
|
return self._textbox.bind(sequence, command, add="+")
|
||||||
|
else:
|
||||||
return self._textbox.bind(sequence, command, add)
|
return self._textbox.bind(sequence, command, add)
|
||||||
|
|
||||||
def unbind(self, sequence, funcid=None):
|
def unbind(self, sequence, funcid=None):
|
||||||
@ -207,6 +314,7 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
return self._textbox.unbind(sequence, funcid)
|
return self._textbox.unbind(sequence, funcid)
|
||||||
|
|
||||||
def insert(self, index, text, tags=None):
|
def insert(self, index, text, tags=None):
|
||||||
|
self._check_if_scrollbars_needed()
|
||||||
return self._textbox.insert(index, text, tags)
|
return self._textbox.insert(index, text, tags)
|
||||||
|
|
||||||
def get(self, index1, index2=None):
|
def get(self, index1, index2=None):
|
||||||
@ -225,6 +333,7 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
return self._textbox.edit_modified(arg)
|
return self._textbox.edit_modified(arg)
|
||||||
|
|
||||||
def edit_redo(self):
|
def edit_redo(self):
|
||||||
|
self._check_if_scrollbars_needed()
|
||||||
return self._textbox.edit_redo()
|
return self._textbox.edit_redo()
|
||||||
|
|
||||||
def edit_reset(self):
|
def edit_reset(self):
|
||||||
@ -234,6 +343,7 @@ class CTkTextbox(CTkBaseClass):
|
|||||||
return self._textbox.edit_separator()
|
return self._textbox.edit_separator()
|
||||||
|
|
||||||
def edit_undo(self):
|
def edit_undo(self):
|
||||||
|
self._check_if_scrollbars_needed()
|
||||||
return self._textbox.edit_undo()
|
return self._textbox.edit_undo()
|
||||||
|
|
||||||
def image_create(self, index, **kwargs):
|
def image_create(self, index, **kwargs):
|
||||||
|
@ -52,7 +52,7 @@ class App(customtkinter.CTk):
|
|||||||
self.main_button_1 = customtkinter.CTkButton(self, fg_color=None, border_width=2)
|
self.main_button_1 = customtkinter.CTkButton(self, fg_color=None, border_width=2)
|
||||||
self.main_button_1.grid(row=3, column=3, padx=(10, 20), pady=(10, 20), sticky="nsew")
|
self.main_button_1.grid(row=3, column=3, padx=(10, 20), pady=(10, 20), sticky="nsew")
|
||||||
|
|
||||||
self.textbox = customtkinter.CTkScrolledTextbox(self)
|
self.textbox = customtkinter.CTkTextbox(self)
|
||||||
self.textbox.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew")
|
self.textbox.grid(row=0, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew")
|
||||||
|
|
||||||
# create radiobutton frame
|
# create radiobutton frame
|
||||||
@ -127,7 +127,7 @@ class App(customtkinter.CTk):
|
|||||||
self.progressbar_1.start()
|
self.progressbar_1.start()
|
||||||
|
|
||||||
self.textbox.insert("1.0", "CTkTextbox\n\n" + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
self.textbox.insert("1.0", "CTkTextbox\n\n" + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
self.textbox.configure(border_width=5, corner_radius=20, wrap="none")
|
self.textbox.configure(border_width=5, corner_radius=5, wrap="none")
|
||||||
self.radiobutton_frame.configure(border_width=3)
|
self.radiobutton_frame.configure(border_width=3)
|
||||||
|
|
||||||
def open_input_dialog(self):
|
def open_input_dialog(self):
|
||||||
|
@ -1,28 +1,95 @@
|
|||||||
import customtkinter
|
import customtkinter
|
||||||
|
|
||||||
#customtkinter.set_widget_scaling(2)
|
customtkinter.set_widget_scaling(0.9)
|
||||||
#customtkinter.set_window_scaling(2)
|
customtkinter.set_window_scaling(0.9)
|
||||||
#customtkinter.set_spacing_scaling(2)
|
customtkinter.set_spacing_scaling(0.9)
|
||||||
|
|
||||||
customtkinter.set_appearance_mode("dark")
|
customtkinter.set_appearance_mode("dark")
|
||||||
|
|
||||||
app = customtkinter.CTk()
|
app = customtkinter.CTk()
|
||||||
app.title("test_scrollbar.py")
|
app.title("test_scrollbar.py")
|
||||||
|
app.geometry("800x1200")
|
||||||
app.grid_rowconfigure(0, weight=1)
|
app.grid_rowconfigure(0, weight=1)
|
||||||
app.grid_columnconfigure((0, 1), weight=1)
|
app.grid_columnconfigure((0, 1, 2, 3), weight=1)
|
||||||
|
|
||||||
textbox_1 = customtkinter.CTkScrolledTextbox(app, fg_color=None, corner_radius=0)
|
textbox_1 = customtkinter.CTkTextbox(app, fg_color=None, corner_radius=0, border_spacing=0)
|
||||||
textbox_1.grid(row=0, column=0, sticky="nsew")
|
textbox_1.grid(row=0, column=0, sticky="nsew")
|
||||||
textbox_1.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
textbox_1.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
frame_1 = customtkinter.CTkFrame(app, corner_radius=0)
|
frame_1 = customtkinter.CTkFrame(app, corner_radius=0)
|
||||||
frame_1.grid(row=0, column=1, sticky="nsew")
|
frame_1.grid(row=0, column=1, sticky="nsew")
|
||||||
frame_1.grid_rowconfigure((0, 1), weight=1)
|
frame_1.grid_rowconfigure((0, 1, 2, 3, 4), weight=1)
|
||||||
frame_1.grid_columnconfigure(0, weight=1)
|
frame_1.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
textbox_2 = customtkinter.CTkScrolledTextbox(frame_1, wrap="none")
|
textbox_2 = customtkinter.CTkTextbox(frame_1, wrap="none")
|
||||||
textbox_2.grid(row=0, column=0, sticky="nsew", padx=20, pady=20)
|
textbox_2.grid(row=0, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_2 = customtkinter.CTkTextbox(frame_1, wrap="none", corner_radius=30)
|
||||||
|
textbox_2.grid(row=1, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_2 = customtkinter.CTkTextbox(frame_1, wrap="none", corner_radius=0, border_width=30)
|
||||||
|
textbox_2.grid(row=2, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_2 = customtkinter.CTkTextbox(frame_1, wrap="none", corner_radius=60, border_width=15)
|
||||||
|
textbox_2.grid(row=3, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_2 = customtkinter.CTkTextbox(frame_1, wrap="none", corner_radius=0, border_width=0)
|
||||||
|
textbox_2.grid(row=4, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_2.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
frame_2 = customtkinter.CTkFrame(app, corner_radius=0, fg_color=None)
|
||||||
|
frame_2.grid(row=0, column=2, sticky="nsew")
|
||||||
|
frame_2.grid_rowconfigure((0, 1, 2, 3, 4), weight=1)
|
||||||
|
frame_2.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_2)
|
||||||
|
textbox_3.grid(row=0, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_2, corner_radius=30)
|
||||||
|
textbox_3.grid(row=1, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_2, corner_radius=0, border_width=30)
|
||||||
|
textbox_3.grid(row=2, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_2, corner_radius=60, border_width=15)
|
||||||
|
textbox_3.grid(row=3, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_2, corner_radius=0, border_width=0, border_spacing=20)
|
||||||
|
textbox_3.grid(row=4, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
frame_3 = customtkinter.CTkFrame(app, corner_radius=0, fg_color=None)
|
||||||
|
frame_3.grid(row=0, column=3, sticky="nsew")
|
||||||
|
frame_3.grid_rowconfigure((0, 1, 2, 3, 4), weight=1)
|
||||||
|
frame_3.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_3, activate_scrollbars=False)
|
||||||
|
textbox_3.grid(row=0, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_3, corner_radius=10, border_width=2, activate_scrollbars=False)
|
||||||
|
textbox_3.grid(row=1, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_3, corner_radius=0, border_width=2, activate_scrollbars=False)
|
||||||
|
textbox_3.grid(row=2, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_3, corner_radius=0, border_width=2, activate_scrollbars=False)
|
||||||
|
textbox_3.grid(row=3, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
textbox_3 = customtkinter.CTkTextbox(frame_3, corner_radius=0, border_width=0, activate_scrollbars=False, border_spacing=10)
|
||||||
|
textbox_3.grid(row=4, column=0, sticky="nsew", padx=20, pady=20)
|
||||||
|
textbox_3.insert("0.0", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n\n" * 20)
|
||||||
|
|
||||||
|
app.after(3000, lambda: customtkinter.set_appearance_mode("light"))
|
||||||
app.mainloop()
|
app.mainloop()
|
||||||
|
Loading…
Reference in New Issue
Block a user