Python tkinter/ttkboostrap dateentry 在只读状态下不起作用

问题描述 投票:0回答:3

我目前正在使用 ttkbootstrap 库,而不是过去的 ttk 库。我曾经在“只读”状态下使用 ttk DateEntry 小部件,效果非常好。然而,由于某些原因,我需要停止使用 ttk 库,转而使用 ttkbootstrap 库。 所以我创建了一个 ttkbootstrap DateEntry 就像文档中所述:https://ttkbootstrap.readthedocs.io/en/latest/styleguide/dateentry/

import ttkbootstrap as tb
from ttkbootstrap import constants

root = tb.Window()
date_entry = tb.DateEntry(root)
date_entry.pack(padx = 10, pady = 10)
date_entry["state"] = "readonly"

root.mainloop()

“只读”状态有效,因为我无法使用条目中的键盘进行写入。然而,当我尝试选择一个新日期时,日期似乎没有改变,因此该条目将永远只在今天的日期。

我知道这种交互与 ttkbootstrap 库特别相关,因为 dateentry 小部件在 ttk 库中工作得很好。

我尝试搜索 DateEntry 类的源代码,但没有发现任何可以解释这种行为的内容。我仍然确信创建一个只读 DateEntry 并非不可能,因为它是这个库中您需要的最重要的东西之一。

python tkinter ttk ttkbootstrap
3个回答
1
投票

您不应该编辑 ttkbootstrap 的代码。它会在下次更新时被覆盖。

您可以简单地创建一个继承自

DateEntry
的新类,在初始化时定义条目的状态并仅在按下按钮时启用该字段:

import ttkbootstrap as tb

class CustomDateEntry(tb.DateEntry):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.entry["state"] = "readonly"

    def _on_date_ask(self):
        self.entry["state"] = "normal"
        super()._on_date_ask()
        self.entry["state"] = "readonly"

root = tb.Window()
date_entry = CustomDateEntry(root)
date_entry.pack(padx = 10, pady = 10)

root.mainloop()

编辑:按照@acw1668的建议,调用

super()._on_date_ask()
而不是编写新函数。


0
投票

只读状态使得日期只能读取而不能更改/编辑,因此它将其设置为当前日期,我认为这是不言自明的,我的问题是为什么不直接删除只读行因为用户仍然可以通过日历弹出窗口选择日期?


0
投票

好吧,我刚刚进入 ttkbootstrap DateEntry 的源代码并复制该类以及 Querybox 类。为了避免这个问题,我解释说我发现了一件事,你必须将日期条目与全局文本变量链接起来。然后您可以用

entry.insert(...)
切换每个
textvariable.set(...)
。 您还必须转换复制的 Querybox 类中
textvariable.get(...)
中的所有插入内容。

这里有 DateEntry 类:

class DateEntry_test(ttk.Frame):
    """A date entry widget combines the `Combobox` and a `Button`
    with a callback attached to the `get_date` function.

    When pressed, a date chooser popup is displayed. The returned
    value is inserted into the combobox.

    The date chooser popup will use the date in the combobox as the
    date of focus if it is in the format specified by the
    `dateformat` parameter. By default, this format is "%Y-%m-%d".

    The bootstyle api may be used to change the style of the widget.
    The available colors include -> primary, secondary, success,
    info, warning, danger, light, dark.

    The starting weekday on the date chooser popup can be changed
    with the `firstweekday` parameter. By default this value is
    `6`, which represents "Sunday".

    The `Entry` and `Button` widgets are accessible from the
    `DateEntry.Entry` and `DateEntry.Button` properties.

    ![](../../assets/widgets/date-entry.png)
    """

    def __init__(
        self,
        master=None,
        dateformat=r"%x",
        firstweekday=6,
        startdate=None,
        bootstyle="",
        **kwargs,
    ):
        """
        Parameters:

            master (Widget, optional):
                The parent widget.

            dateformat (str, optional):
                The format string used to render the text in the entry
                widget. For more information on acceptable formats, see https://strftime.org/

            firstweekday (int, optional):
                Specifies the first day of the week. 0=Monday, 1=Tuesday,
                etc...

            startdate (datetime, optional):
                The date that is in focus when the widget is displayed. Default is
                current date.

            bootstyle (str, optional):
                A style keyword used to set the focus color of the entry
                and the background color of the date button. Available
                options include -> primary, secondary, success, info,
                warning, danger, dark, light.

            **kwargs (Dict[str, Any], optional):
                Other keyword arguments passed to the frame containing the
                entry and date button.
        """
        self._dateformat = dateformat
        self._firstweekday = firstweekday

        self._startdate = startdate or datetime.today()
        self._bootstyle = bootstyle
        super().__init__(master, **kwargs)

        # add visual components
        entry_kwargs = {"bootstyle": self._bootstyle}
        if "width" in kwargs:
            entry_kwargs["width"] = kwargs.pop("width")
        self.textvariable = tk.StringVar()
        self.entry = ttk.Entry(self, textvariable=self.textvariable, **entry_kwargs)
        
        self.entry.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES)

        self.button = ttk.Button(
            master=self,
            command=self._on_date_ask,
            bootstyle=f"{self._bootstyle}-date",
        )
        self.button.pack(side=tk.LEFT)

        # starting value
        self.textvariable.set(self._startdate.strftime(self._dateformat))

    def __getitem__(self, key: str):
        return self.configure(cnf=key)

    def __setitem__(self, key: str, value):
        self.configure(cnf=None, **{key: value})

    def _configure_set(self, **kwargs):
        """Override configure method to allow for setting custom
        DateEntry parameters"""

        if "state" in kwargs:
            state = kwargs.pop("state")
            if state in ["readonly", "invalid"]:
                self.entry.configure(state=state)
            elif state in ("disabled", "normal"):
                self.entry.configure(state=state)
                self.button.configure(state=state)
            else:
                kwargs[state] = state
        if "dateformat" in kwargs:
            self._dateformat = kwargs.pop("dateformat")
        if "firstweekday" in kwargs:
            self._firstweekday = kwargs.pop("firstweekday")
        if "startdate" in kwargs:
            self._startdate = kwargs.pop("startdate")
        if "bootstyle" in kwargs:
            self._bootstyle = kwargs.pop("bootstyle")
            self.entry.configure(bootstyle=self._bootstyle)
            self.button.configure(bootstyle=[self._bootstyle, "date"])
        if "width" in kwargs:
            width = kwargs.pop("width")
            self.entry.configure(width=width)

        super(ttk.Frame, self).configure(**kwargs)

    def _configure_get(self, cnf):
        """Override the configure get method"""
        if cnf == "state":
            entrystate = self.entry.cget("state")
            buttonstate = self.button.cget("state")
            return {"Entry": entrystate, "Button": buttonstate}
        if cnf == "dateformat":
            return self._dateformat
        if cnf == "firstweekday":
            return self._firstweekday
        if cnf == "startdate":
            return self._startdate
        if cnf == "bootstyle":
            return self._bootstyle
        else:
            return super(ttk.Frame, self).configure(cnf=cnf)

    def configure(self, cnf=None, **kwargs):
        """Configure the options for this widget.

        Parameters:

            cnf (Dict[str, Any], optional):
                A dictionary of configuration options.

            **kwargs:
                Optional keyword arguments.
        """
        if cnf is not None:
            return self._configure_get(cnf)
        else:
            return self._configure_set(**kwargs)

    def _on_date_ask(self):
        """Callback for pushing the date button"""
        _val = self.entry.get() or datetime.today().strftime(self._dateformat)
        try:
            self._startdate = datetime.strptime(_val, self._dateformat)
        except Exception as e:
            print("Date entry text does not match", self._dateformat)
            self._startdate = datetime.today()
            self.entry.delete(first=0, last=tk.END)
            self.textvariable.set(self._startdate.strftime(self._dateformat))

        old_date = datetime.strptime(_val, self._dateformat)

        # get the new date and insert into the entry
        new_date = Querybox_test.get_date(
            parent=self.entry,
            startdate=old_date,
            firstweekday=self._firstweekday,
            bootstyle=self._bootstyle,
        )
        self.entry.delete(first=0, last=tk.END)
        self.textvariable.set(new_date.strftime(self._dateformat))
        self.entry.focus_force()

和 Querybox 类:

class Querybox_test:
    """This class contains various static methods that request data
    from the end user."""

    @staticmethod
    def get_color(
        parent=None, title="Color Chooser", initialcolor=None, **kwargs
    ):
        """Show a color picker and return the select color when the
        user pressed OK.

        ![](../../assets/dialogs/querybox-get-color.png)

        Parameters:

            parent (Widget):
                The parent widget.

            title (str):
                Optional text that appears on the titlebar.

            initialcolor (str):
                The initial color to display in the 'Current' color
                frame.

        Returns:

            Tuple[rgb, hsl, hex]:
                The selected color in various colors models.
        """
        from ttkbootstrap.dialogs.colorchooser import ColorChooserDialog

        dialog = ColorChooserDialog(parent, title, initialcolor)
        if "position" in kwargs:
            position = kwargs.pop("position")
        else:
            position = None
        dialog.show(position)
        return dialog.result

    @staticmethod
    def get_date(
        parent=None,
        title=" ",
        firstweekday=6,
        startdate=None,
        bootstyle="primary",
    ):
        """Shows a calendar popup and returns the selection.

        ![](../../assets/dialogs/querybox-get-date.png)

        Parameters:

            parent (Widget):
                The parent widget; the popup will appear to the
                bottom-right of the parent widget. If no parent is
                provided, the widget is centered on the screen.

            title (str):
                The text that appears on the popup titlebar.

            firstweekday (int):
                Specifies the first day of the week. `0` is Monday, `6` is
                Sunday (the default).

            startdate (datetime):
                The date to be in focus when the widget is displayed;

            bootstyle (str):
                The following colors can be used to change the color of the
                title and hover / pressed color -> primary, secondary, info,
                warning, success, danger, light, dark.

        Returns:

            datetime:
                The date selected; the current date if no date is selected.
        """
        chooser = DatePickerDialog(
            parent=parent,
            title=title,
            firstweekday=firstweekday,
            startdate=startdate,
            bootstyle=bootstyle,
        )
        return chooser.date_selected

    @staticmethod
    def get_string(
        prompt="", title=" ", initialvalue=None, parent=None, **kwargs
    ):
        """Request a string type input from the user.

        ![](../../assets/dialogs/querybox-get-string.png)

        Parameters:

            prompt (str):
                A message to display in the message box above the entry
                widget.

            title (str):
                The string displayed as the title of the message box. This
                option is ignored on Mac OS X, where platform guidelines
                forbid the use of a title on this kind of dialog.

            initialvalue (Any):
                The initial value in the entry widget.

            parent (Widget):
                Makes the window the logical parent of the message box. The
                messagebox is displayed on top of its parent window.

            **kwargs (Dict):
                Other optional keyword arguments.

        Returns:

            str:
                The string value of the entry widget.
        """
        initialvalue = initialvalue or ""
        if "position" in kwargs:
            position = kwargs.pop("position")
        else:
            position = None
        dialog = QueryDialog(
            prompt, title, initialvalue, parent=parent, **kwargs
        )
        dialog.show(position)
        return dialog._result

    @staticmethod
    def get_integer(
        prompt="",
        title=" ",
        initialvalue=None,
        minvalue=None,
        maxvalue=None,
        parent=None,
        **kwargs,
    ):
        """Request an integer type input from the user.

        ![](../../assets/dialogs/querybox-get-integer.png)

        Parameters:

            prompt (str):
                A message to display in the message box above the entry
                widget.

            title (str):
                The string displayed as the title of the message box. This
                option is ignored on Mac OS X, where platform guidelines
                forbid the use of a title on this kind of dialog.

            initialvalue (int):
                The initial value in the entry widget.

            minvalue (int):
                The minimum allowed value.

            maxvalue (int):
                The maximum allowed value.

            parent (Widget):
                Makes the window the logical parent of the message box. The
                messagebox is displayed on top of its parent window.

            **kwargs (Dict):
                Other optional keyword arguments.

        Returns:

            int:
                The integer value of the entry widget.
        """
        initialvalue = initialvalue or ""
        if "position" in kwargs:
            position = kwargs.pop("position")
        else:
            position = None
        dialog = QueryDialog(
            prompt,
            title,
            initialvalue,
            minvalue,
            maxvalue,
            datatype=int,
            parent=parent,
            **kwargs,
        )
        dialog.show(position)
        return dialog._result

    @staticmethod
    def get_float(
        prompt="",
        title=" ",
        initialvalue=None,
        minvalue=None,
        maxvalue=None,
        parent=None,
        **kwargs,
    ):
        """Request a float type input from the user.

        ![](../../assets/dialogs/querybox-get-float.png)

        Parameters:

            prompt (str):
                A message to display in the message box above the entry
                widget.

            title (str):
                The string displayed as the title of the message box. This
                option is ignored on Mac OS X, where platform guidelines
                forbid the use of a title on this kind of dialog.

            initialvalue (float):
                The initial value in the entry widget.

            minvalue (float):
                The minimum allowed value.

            maxvalue (float):
                The maximum allowed value.

            parent (Widget):
                Makes the window the logical parent of the message box. The
                messagebox is displayed on top of its parent window.

            **kwargs (Dict):
                Other optional keyword arguments.

        Returns:

            float:
                The float value of the entry widget.
        """
        initialvalue = initialvalue or ""
        if "position" in kwargs:
            position = kwargs.pop("position")
        else:
            position = None
        dialog = QueryDialog(
            prompt,
            title,
            initialvalue,
            minvalue,
            maxvalue,
            datatype=float,
            parent=parent,
            **kwargs,
        )
        dialog.show(position)
        return dialog._result

    @staticmethod
    def get_font(parent=None, **kwargs):
        """Request a customized font

        ![](../../assets/dialogs/querybox-get-font.png)

        Parameters:

            parent (Widget):
                Makes the window the logical parent of the dialog box. The
                dialog is displayed on top of its parent window.

            **kwargs (Dict):
                Other keyword arguments.

        Returns:

            Font:
                A font object.
        """
        if "position" in kwargs:
            position = kwargs.pop("position")
        else:
            position = None
        dialog = FontDialog(parent=parent, **kwargs)
        dialog.show(position)
        return dialog.result
© www.soinside.com 2019 - 2024. All rights reserved.