0

I'm building an app for personal use in Python using the Custom Tkinter module. I have been trying different ways to implement a function that allows a user to rename a single tab in the tabview widget, but I can't figure it out. I've read the documentation multiple times, but either it's not possible, or more likely I'm not getting something.

The first snippet below shows me trying to implement the desired functionality, but I've substituted the name change for a color change in the code, since that code throws no errors.

    self.button = customtkinter.CTkButton(self,text="Rename", command=self.rename_tab)
    self.button.grid(row=1, column=0)


def rename_tab(self):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Rename tab")
    new_name=dialog.get_input()
    self.tabview.configure(text_color=new_name)

This doesn't work properly, because it changes the color of the entire tabview widget, not just a single tab. Also, it's not the name change I'm after.

I then tried passing the desired tab name to the function, but when I ran the program, it opened only the dialog box and not the entire program. Faulty code for that below:

   category_1='Placeholder'

    self.button = customtkinter.CTkButton(self,text="Rename", command=self.rename_tab(category_1)
    self.button.grid(row=1, column=0)


def rename_tab(self, tabname):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Rename tab")
    new_name=dialog.get_input()
    self.tabview.configure(tabname, text_color=new_name)

Finally in any of these situations and more that I've tried, if I pass in text or name as an attribute to configure I get an error: ValueError: ['text'] are not supported arguments, which doesn't quite make sense because the text_color argument is valid, and the documentation for TTk (which admittedly is not the same module, but similar) shows that "text" is an argument that is supported by "button like widgets"

There definitely seems to be an error in my logic, but I can't grok it.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Fuzzy
  • 1
  • 2

2 Answers2

1

Take a look at this GitHub issue, you actually can rename tabs in customtkinter's Tabview.

This is the solution mentioned:

tabview._segmented_button._buttons_dict["Old tab name"].configure(text="New tab name")

Combine with your code:

Without function:

self.tabview._segmented_button._buttons_dict[tabname].configure(text=new_name)

With function:

def rename_tab(self, tabname):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Rename tab")
    new_name = dialog.get_input()
    self.tabview._segmented_button._buttons_dict[tabname].configure(text=new_name)

Note: If a KeyError is raised, that's because this method only works if you only change the tab's name once throughout the whole code. If you want to rename multiple times like this:

tabview._segmented_button._buttons_dict["Old tab name"].configure(text="New tab name")
tabview._segmented_button._buttons_dict["New tab name"].configure(text="Even newer tab name")

you can create a new variable that will hold the active (current) tab's name by using the .get() method and use it to access the _buttons_dict variable and change the tab's name:

active_tab = tabview.get()
tabview._segmented_button._buttons_dict[active_tab].configure(text="New tab name")
# [Imagine there were a lot of codes here...]
tabview._segmented_button._buttons_dict[active_tab].configure(text="Even newer tab name")

- This is the perfect code if you just want to rename a tab and don't want to touch anything else: (Thank @Fuzzy a lot for contributing to this answer)

def rename_tab(self):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Add a tab")
    active_tab=self.tabview.get()
    new_name=dialog.get_input()
    self.tabview._segmented_button._buttons_dict[active_tab].configure(text=new_name)

Or, if you want to get the tab's name later in your code, you will need to update the _buttons_dict (dictionary) with the new tab name (method used):

tabview._segmented_button._buttons_dict["Old tab name"].configure(text="New tab name")
tabview._segmented_button._buttons_dict["New tab name"] = tabview._segmented_button._buttons_dict["Old tab name"]
del tabview._segmented_button._buttons_dict["Old tab name"]
tabview._segmented_button._buttons_dict["New tab name"].configure(text="Even newer tab name")

- This is the perfect code if you want to rename a tab but also want to get that tab's name later in your code:

def rename_tab(self, tabname):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Rename tab")
    new_name = dialog.get_input()
    self.tabview._segmented_button._buttons_dict[tabname].configure(text=new_name)
    self.tabview._segmented_button._buttons_dict[new_name] = self.tabview._segmented_button._buttons_dict[tabname]
    del self.tabview._segmented_button._buttons_dict[tabname]

The code above is quite complicated. But I will try my best to explain to you.

First, let's divide this piece of code into 3 parts:

Part 1:

tabview._segmented_button

Every time you create a CTkTabview, it will create a variable named _segmented_button, which is a CTkSegmentedButton with some CTkFrame which will be shown when you click on the tab and hidden every time you click on an another tab. Basically, that CTkSegmentedButton is the buttons on the CTkTabview that you can click on to move to an another tab.

Part 2:

._buttons_dict["Old tab name"]

Every time you create a CTkSegmentedButton, a new variable named _buttons_dict, which is a Python dictionary (dict) is created. When you use tabview.add(...) to add a tab into the tabview, the name of that tab will be added into _buttons_dict as a key and the button that holds that name will be added as a value. So _buttons_dict would look like this: {"tab name": <CTkButton object>}. So, if you want to rename a tab, you just need to call that button from _buttons_dict by using _buttons_dict["tab name"]

Part 3:

.configure(text="New tab name")

After accessing into the button object, you simply just need to change the text argument into your custom name and that's it, you've just renamed your tab!


NOTE(s):
- This is not an official way to rename your tabs since this method can stop working with new updates anytime (Last checked customtkinter version: 5.2.0).

Hung
  • 155
  • 10
  • Wow, such a detailed answer... – Tom Jul 02 '23 at 18:27
  • What a Chad answer. I'd upvote you, but apparently I don't have enough reputation points. Thank you for taking the time to explain how the solution works. – Fuzzy Jul 04 '23 at 12:34
0

Hung answered this question thoroughly, but I'd like to help lower the ladder for the people coming behind me. The final solution I came up with was this:

def rename_tab(self):
    dialog = customtkinter.CTkInputDialog(text="What is the tab name?", title="Add a tab")
    active_tab=self.tabview.get()
    new_name=dialog.get_input()
    self.tabview._segmented_button._buttons_dict[active_tab].configure(text=new_name)

The active_tab variable uses .get() to reference the currently active tab, which makes this function reusable for others.

Fuzzy
  • 1
  • 2
  • Thank you for the improvement of my answer! I really appreciate it. I actually forgot the `.get()` method. That is such a better and cleaner way to access the `CTkTabview`'s name. Also, I have updated my answer to your solution. – Hung Jul 05 '23 at 13:00
  • But I have a little reminder. When you call the currently active tab like that, you did not change the name of the tab that was stored in the `_buttons_dict` variable. Instead it just changed the `text` argument of the CTkSegmentedButton that displays the tab's name. If you try to get the name of that tab, it will always return the original (default) name of the tab that you have given when you first created it. So be careful. – Hung Jul 05 '23 at 13:13
  • I'm confused on this as it doesn't seem to be the case for me. In my method you can see that using ',get' to reference the tab name inside the dictionary. If I rename the already renamed tab it still works, which means the dictionary is being updated. – Fuzzy Jul 06 '23 at 01:23
  • Yes, it always works if you just want to rename a tab. But if you want to get the name of that tab later, it's a problem. For example, the default tab's name that you gave when you first created it is `tab1`, and later, you renamed it to `tab2`. You'll see it displays `tab2` because you only made change to the CTkSegmentedButton and forced it to display `tab2`. When you try to get that tab's name (for example, using `.get()`), it will return `tab1` (the default name) because the CTkTabview is still thinking the tab that you renamed is still `tab1` like nothing happened. – Hung Jul 06 '23 at 05:44
  • You can check it by adding this line: `print(self.tabview.get())` at the end of the `rename_tab()` function. No matter how many times you renamed the tab and what it is displaying, you will always get the default name that you have given when you first created it. In your case, which you just want to rename the tab and don't want to touch anything else, it is not necessary to think about this. It's just a little reminder. – Hung Jul 06 '23 at 05:49