TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
11.58k stars 1.08k forks source link

tkinter.TclError: bad screen distance "200.0" #571

Open snoo-snoo opened 2 years ago

snoo-snoo commented 2 years ago

Python 3.10 Windows 11

When i init a CtkFrame like this:

 def __init__(self, parent, data):
        super().__init__(parent)

i always get

_tkinter.TclError: bad screen distance "200.0"

The error is located at ctk_frame.py line 41:

self.canvas = CTkCanvas(master=self, highlightthickness=0, width=self.apply_widget_scaling(self._current_width), height=self.apply_widget_scaling(self._current_height))

when changing to:


        self.canvas = CTkCanvas(master=self,
                                highlightthickness=0,
                                width=int(self.apply_widget_scaling(self._current_width)),
                                height=int(self.apply_widget_scaling(self._current_height)))

it works, but then next "TclError: bad screen distance" is located in ctk_label.py, line 47.

Tried it on several computers, but error is always the same.

Any idea what this might cause? looks it somehow receives a float instead of an int, but width and height are default values.

abhaymakes commented 2 years ago

Can you send a minimal reproducible example?

P.S. Looking at the widgets, might it be a problem in the base_widget_class.py with the return value of the apply_widget_scaling function?

snoo-snoo commented 2 years ago

Yeah, i got the problem, here is a minimal reproducible example:

import tkinter
import customtkinter
import locale

locale.setlocale(locale.LC_ALL, '')

customtkinter.set_appearance_mode("dark")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  # Themes: "blue" (standard), "green", "dark-blue"

app = customtkinter.CTk()
app.geometry("400x580")
app.title("CustomTkinter simple_example.py")

frame_1 = customtkinter.CTkFrame(master=app)
frame_1.pack(pady=20, padx=60, fill="both", expand=True)

label_1 = customtkinter.CTkLabel(master=frame_1, justify=tkinter.LEFT)
label_1.pack(pady=12, padx=10)

app.mainloop()

The problem is, when using the locale package, then the integer values get converted to floats. But have no idea how to fix it

abhaymakes commented 2 years ago

@snoo-snoo I tried your code, and it works without any problem on my machine. image

snoo-snoo commented 2 years ago

@TheFallen-Cat try changing your OS language to German. Then you should be able to replicate the error. sorry, forgot about that.

abhaymakes commented 2 years ago

Ah okay, lemme try that in a bit.

abhaymakes commented 2 years ago

@snoo-snoo I did. And if i leave the locale.setlocale(locale.LC_ALL, ' ') like this it works, but if i put locale.setlocale(locale.LC_ALL, 'de_du') it throws the error. So, maybe try running your code with empty quotation marks as locale will auto-detect the language of the system, no need to put the langugae code(de_du) specifically.

jicart commented 1 year ago

I'm having the same problem. I've turned off the locale change as a workaround, but I'm wondering if there might be a fix for it. This is the code I've used to reproduce the issue:

import locale import customtkinter as ctk

root = ctk.CTk() label = ctk.CTkLabel(root, text="some text")

locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8') label2 = ctk.CTkLabel(root, text="some text") # throws _tkinter.TclError: bad screen distance "0.0"

jicart commented 1 year ago

It would be nice to have a better error message for this bug. I'm kind of ashamed to admit it, but I've spent about 20 min trying to debug this same error again. Yeah, I know. Pretty stupid. The error message is way too cryptic and I did not remember it. I only realized it was this issue again when I looked into one particular import that was changing locale.

Wolf-SO commented 1 year ago

After reducing the example given by @snoo-snoo even more to

import customtkinter
import locale

locale.setlocale(locale.LC_ALL, '')
app = customtkinter.CTk()
customtkinter.CTkFrame(master=app)

I can still reproduce the issue on a German Win10 (customtkinter-5.1.2, Python 3.11.1):

C:\Test>python ctk-issue-571.py
Traceback (most recent call last):
  File "C:\Test\ctk-issue-571.py", line 7, in <module>
    customtkinter.CTkFrame(master=app)
  File "C:\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_frame.py", line 56, in __init__
    self._canvas = CTkCanvas(master=self,
                   ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\site-packages\customtkinter\windows\widgets\core_rendering\ctk_canvas.py", line 31, in __init__
    super().__init__(*args, **kwargs)
  File "C:\Python311\Lib\tkinter\__init__.py", line 2744, in __init__
    Widget.__init__(self, master, 'canvas', cnf, kw)
  File "C:\Python311\Lib\tkinter\__init__.py", line 2628, in __init__
    self.tk.call(
_tkinter.TclError: bad screen distance "200.0"
TomSchimansky commented 1 year ago

I will have a look.

Heribert17 commented 1 year ago

I have the same problem. It seems to be a bug in tkinter when converting the float to string for tk. You can test it with this piece of code. When commenting the call to locale it works, with the locale call you get the errormessage. When you change the width and height to integers it also works with the locale call.

from tkinter import
import locale

locale.setlocale(locale.LC_ALL, "")
# create a GUI window widget
window=Tk()
# create an inner window
cv =Canvas(window,width=500.0,height=500.0)
cv.pack()
# create a rectangle
cv.create_rectangle(50,20,200,200,fill="red")

window.mainloop()

I found this discussion in the Python forum about float in tkinter and German locale [https://github.com/python/cpython/issues/56767]

Heribert17 commented 1 year ago

During migration of one of my projects to customertkinter i steped into the same problem again. I'm on Winbdows 10, Python 3.11.6 and newest custometkinter. The problem occurs when using a locale (for eample locale.setlocale(locale.LC_ALL, "DE-de")). A solution for that problem is to modify the _apply_widget_scaling and _reverse_widget_scaling in scaling_base_class.py to return integer and not float. After that change the programm excutes without error. I also tested it with the complex_example.py by adding the locale setting. This ist the change in scaling_base_class.py

    # changed float -> int in return (and Union)
    def _apply_widget_scaling(self, value: Union[int, float]) -> Union[int]:
        assert self.__scaling_type == "widget"
        return int(value * self.__widget_scaling)
    # # changed float -> int in return (and Union)
    def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[int]:
        assert self.__scaling_type == "widget"
        return int(value / self.__widget_scaling)
Heribert17 commented 12 months ago

I found that you need the float value, otherwise scrollbars arn't working. I changed the code above to:

    def _apply_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value * self.__widget_scaling
        else:
            return int(value * self.__widget_scaling)

    def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value / self.__widget_scaling
        else:
            return int(value / self.__widget_scaling)
OddUnicorn commented 10 months ago

I found that you need the float value, otherwise scrollbars arn't working. I changed the code above to:

    def _apply_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value * self.__widget_scaling
        else:
            return int(value * self.__widget_scaling)

    def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value / self.__widget_scaling
        else:
            return int(value / self.__widget_scaling)

Had no problems since writing my program over half a year ago. But today the error "bad screen distance" suddenly appeared, even after disabling setlocale. Your solution fixed it, thanks.

Jan-Weigang commented 10 months ago

Can confirm this. Just ran into the same issue on german locale. Changed the above things in scaling_base_class.py. No errors afterwards.

Bug38 commented 9 months ago

I found that you need the float value, otherwise scrollbars arn't working. I changed the code above to:

    def _apply_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value * self.__widget_scaling
        else:
            return int(value * self.__widget_scaling)

    def _reverse_widget_scaling(self, value: Union[int, float]) -> Union[float, int]:
        assert self.__scaling_type == "widget"
        if isinstance(value, float):
            return value / self.__widget_scaling
        else:
            return int(value / self.__widget_scaling)

I just ran into the same issue, your solution fixed it thanks a lot!