DearPyGui icon indicating copy to clipboard operation
DearPyGui copied to clipboard

Global themes for disabled components not working

Open virgileMaon opened this issue 1 year ago • 7 comments

Version of Dear PyGui

Version: 1.9.0 Operating System: All

My Issue/Question

Global themes for disabled components not working. Enabled text color takes effect (which is also incorrect).

Expected behavior

Input float text should be red.

Related

https://github.com/hoffstadt/DearPyGui/issues/1401

Standalone, minimal, complete and verifiable example

import dearpygui.dearpygui as dpg

dpg.create_context()
dpg.create_viewport()
dpg.setup_dearpygui()

with dpg.theme() as global_theme:

    with dpg.theme_component(dpg.mvInputFloat, enabled_state=True):
        dpg.add_theme_color(dpg.mvThemeCol_Text, (255, 0, 0), category=dpg.mvThemeCat_Core)


dpg.bind_theme(global_theme)

with dpg.window(label="tutorial"):
    dpg.add_input_float(label="Input float", enabled=False)
    dpg.add_input_int(label="Input int", enabled=False)

dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

virgileMaon avatar Mar 27 '23 20:03 virgileMaon

shouldn't the enabled_state be False in order to apply the disabled theme? Anyway, it works for me with enabled_state = False as I understood from documentation

marcost83 avatar Jun 23 '23 11:06 marcost83

@marcost83 is right: enabled_state should be False. This makes theme_component(dpg.mvInputFloat) work as expected; however, theme_component(dpg.mvAll) doesn't work:

import dearpygui.dearpygui as dpg

dpg.create_context()
dpg.create_viewport(width=600, height=600)
dpg.setup_dearpygui()

with dpg.theme() as global_theme:

    with dpg.theme_component(dpg.mvAll, enabled_state=False):
        dpg.add_theme_color(dpg.mvThemeCol_Text, (0, 0, 255), category=dpg.mvThemeCat_Core)


dpg.bind_theme(global_theme)

with dpg.window(label="tutorial"):
    dpg.add_input_float(label="Input float", enabled=False)
    dpg.add_input_int(label="Input int", enabled=False)
    dpg.add_button(label="Button", enabled=False)

dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()

image

DPG 1.9.1 on Windows.

v-ein avatar Jul 04 '23 06:07 v-ein

Another issue is that disabled components do not get grayed out by default. ImGui uses TextDisabled color for that, but DPG seems to have a different approach with Text and enabled_state=False; however, with the current defaults (as of 1.9.1), disabled items retain pure white text color. Neither does dearpygui_ext.themes.create_theme_imgui_yyy set this up properly.

v-ein avatar Jul 04 '23 07:07 v-ein

A workaround for now would be to add something like this to your global theme:

# In 'for', list all the types you need
for comp_type in (dpg.mvMenuItem, dpg.mvButton, dpg.mvText):
    with dpg.theme_component(comp_type, enabled_state=False):
        dpg.add_theme_color(dpg.mvThemeCol_Text, (0.50 * 255, 0.50 * 255, 0.50 * 255, 1.00 * 255))

If you need to modify a theme created somewhere else (e.g. if you're using themes from DearPyGui_Ext), you can easily wrap it in a context manager like this:

@contextmanager
def dpg_widget(widget: Union[int, str]) -> Generator[Union[int, str], None, None]:
    try:
        dpg.push_container_stack(widget)
        yield widget
    finally:
        dpg.pop_container_stack()

<...>

my_global_theme = create_theme_imgui_light()

with dpg_widget(my_global_theme):
    with dpg.theme_component(...):
        # add the stuff you need

v-ein avatar Jul 04 '23 09:07 v-ein

Thank you for the workaround. I had a lot of components so I just listed them all from the documentation. Here it is for convenience.

# fix for disabled theme see DearPyGui/issues/2068
comps = [dpg.mvInputText, dpg.mvButton, dpg.mvRadioButton, dpg.mvTabBar, dpg.mvTab, dpg.mvImage, dpg.mvMenuBar, dpg.mvViewportMenuBar, dpg.mvMenu, dpg.mvMenuItem, dpg.mvChildWindow, dpg.mvGroup, dpg.mvDragFloatMulti, dpg.mvSliderFloat, dpg.mvSliderInt, dpg.mvFilterSet, dpg.mvDragFloat, dpg.mvDragInt, dpg.mvInputFloat, dpg.mvInputInt, dpg.mvColorEdit, dpg.mvClipper, dpg.mvColorPicker, dpg.mvTooltip, dpg.mvCollapsingHeader, dpg.mvSeparator, dpg.mvCheckbox, dpg.mvListbox, dpg.mvText, dpg.mvCombo, dpg.mvPlot, dpg.mvSimplePlot, dpg.mvDrawlist, dpg.mvWindowAppItem, dpg.mvSelectable, dpg.mvTreeNode, dpg.mvProgressBar, dpg.mvSpacer, dpg.mvImageButton, dpg.mvTimePicker, dpg.mvDatePicker, dpg.mvColorButton, dpg.mvFileDialog, dpg.mvTabButton, dpg.mvDrawNode, dpg.mvNodeEditor, dpg.mvNode, dpg.mvNodeAttribute, dpg.mvTable, dpg.mvTableColumn, dpg.mvTableRow]
for comp_type in comps:
    with dpg.theme_component(comp_type, enabled_state=False):
        dpg.add_theme_color(dpg.mvThemeCol_Text, (0.50 * 255, 0.50 * 255, 0.50 * 255, 1.00 * 255))

Disabled components also still appear clickable, such as buttons as mentioned in #1639 . You can use the same theme workaround to get most widgets to appear disabled on hover:

comps = [dpg.mvInputText, dpg.mvButton, dpg.mvRadioButton, dpg.mvTabBar, dpg.mvTab, dpg.mvImage, dpg.mvMenuBar, dpg.mvViewportMenuBar, dpg.mvMenu, dpg.mvMenuItem, dpg.mvChildWindow, dpg.mvGroup, dpg.mvDragFloatMulti, dpg.mvSliderFloat, dpg.mvSliderInt, dpg.mvFilterSet, dpg.mvDragFloat, dpg.mvDragInt, dpg.mvInputFloat, dpg.mvInputInt, dpg.mvColorEdit, dpg.mvClipper, dpg.mvColorPicker, dpg.mvTooltip, dpg.mvCollapsingHeader, dpg.mvSeparator, dpg.mvCheckbox, dpg.mvListbox, dpg.mvText, dpg.mvCombo, dpg.mvPlot, dpg.mvSimplePlot, dpg.mvDrawlist, dpg.mvWindowAppItem, dpg.mvSelectable, dpg.mvTreeNode, dpg.mvProgressBar, dpg.mvSpacer, dpg.mvImageButton, dpg.mvTimePicker, dpg.mvDatePicker, dpg.mvColorButton, dpg.mvFileDialog, dpg.mvTabButton, dpg.mvDrawNode, dpg.mvNodeEditor, dpg.mvNode, dpg.mvNodeAttribute, dpg.mvTable, dpg.mvTableColumn, dpg.mvTableRow]
for comp_type in comps:
    with dpg.theme_component(comp_type, enabled_state=False):
        dpg.add_theme_color(dpg.mvThemeCol_Text, (0.50 * 255, 0.50 * 255, 0.50 * 255, 1.00 * 255))
        dpg.add_theme_color(dpg.mvThemeCol_Button, (45, 45, 48))
        dpg.add_theme_color(dpg.mvThemeCol_ButtonHovered, (45, 45, 48))
        dpg.add_theme_color(dpg.mvThemeCol_ButtonActive, (45, 45, 48))

mef51 avatar Aug 10 '23 19:08 mef51

Just found this when having the same issue. Fwiw, this is in the documentation: https://dearpygui.readthedocs.io/en/latest/documentation/themes.html?highlight=enabled#theme-for-disabled-items

And modifying their example, this is what worked for me:

def main():
    dpg.create_context()
    gui = PackageSelectorGUI()

    dpg.create_viewport(title='Package Selector', width=600, height=300)
    dpg.setup_dearpygui()

    if gui.window_id is not None:
        dpg.set_primary_window(gui.window_id, True)

    disabled_color = (0.50 * 255, 0.50 * 255, 0.50 * 255, 1.00 * 255)
    disabled_button_color = (45, 45, 48)
    disabled_button_hover_color = (45, 45, 48)
    disabled_button_active_color = (45, 45, 48)
    with dpg.theme() as disabled_theme:
        with dpg.theme_component(dpg.mvButton, enabled_state=False):
            dpg.add_theme_color(dpg.mvThemeCol_Text, disabled_color, category=dpg.mvThemeCat_Core)
            dpg.add_theme_color(dpg.mvThemeCol_Button, disabled_button_color, category=dpg.mvThemeCat_Core)
            dpg.add_theme_color(dpg.mvThemeCol_ButtonHovered, disabled_button_hover_color, category=dpg.mvThemeCat_Core)
            dpg.add_theme_color(dpg.mvThemeCol_ButtonActive, disabled_button_active_color, category=dpg.mvThemeCat_Core)

        dpg.bind_theme(disabled_theme)

    dpg.disable_item("btn")
    
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

lifeforce-dev avatar Dec 04 '23 18:12 lifeforce-dev

I've done a bit of research on how themes are implemented, and here's what I've got.

There are several distinct issues with the themes:

  • On the global theme (the one set with bind_theme), theme components that have both item_type=dpg.mvAll and enabled_state=False are completely ignored. It's the issue described by the OP.
  • If a theme is bound to an item, and it contains item_type=dpg.mvAll components whose enabled_state is different from the state of the item itself (e.g. enabled_state=True on a disabled item), such components are discarded completely and are not inherited by children. Looks somewhat expected at first, but in some cases it leads to really weird results.
    • In particular, this means you cannot control the appearance of disabled items in a dpg.group() by binding a theme to the group with a "disabled" mvAll component (enabled_state=False + item_type=dpg.mvAll). That is, you can't say "in this group, all my disabled widgets should have red text on them" - you'll need to list all the item types one-by-one. Weird? Yes.
  • If you specify a particular item_type (not mvAll) on a component, it throws away all other components with the same item_type that were used on parent items. Unlike mvAll components, styles from different themes are often not merged in this case. The exact behavior depends on item_type and the widgets structure, but it's easy to get into situation where some of your styles are completely ignored and you have no idea why.

I might create separate tickets on problems (2) and (3) just to describe them better. However, it won't really help because all three problems can only be solved by completely redesigning the theme hierarchy. It's quite a lot of work, so little chance that it gets implemented any time soon (if it gets implemented at all).

Bottomline: bad news, this issue is rather hard to fix.

v-ein avatar Dec 14 '23 13:12 v-ein