inclusion-international / speech-jokey

A speech synthesis software with integration of several TTS APIs, SSML support and optimizations for users with motor impairment. (course ASSIST HEIDI WS2023 and SS2024)
MIT License
1 stars 0 forks source link

Issues with kivyMD: TypeError: Properties ['text'] passed to init may not be existing property names #4

Open HackXIt opened 1 month ago

HackXIt commented 1 month ago

Also i tried to do some UI stuff the last few days and test some stuff with kivy in the playground. Unfortunately I had some problems there as well. Espacially with kivyMD. I think in our version are a few things missing maybe. E.g. there is no MDRectangleFlatButton and MDFlatButton. Also the MDDialog does not quite work how it is supposed to. I tried to implement a PopUp dialog that tells you which voice you have selected, but I cant put anything in the dialog because I always get the error: 'TypeError: Properties ['text'] passed to init may not be existing property names.' But in the documentation 'text' is a valid property for an MDDialog. I tried it in the branch 'develop_ui_fixes' its in the main_screen.py in the method select_voice().

In regard to commit: a2902de

HackXIt commented 1 month ago

In regards to the other problems you mentioned about MDDialog, it works perfectly fine for the ExitDialog: grafik

Have a look at modules/dialog/exitdialog.kv.

PLEASE NOTE loaddialog.kv and savedialog.kv are DEPRECATED.

I am using Filemanager of KivyMD directly in the application main_screen.py to load/save from/to a file.

    def __init__(self, title: str, **kwargs):
        super(MainScreen, self).__init__(**kwargs)
        # self.file_load_popup = loaddialog.LoadDialog(callback=self.load_textfile, title="Load file", size_hint=(0.9, 0.9))
        # # self.file_load_popup.size = (400, 400)
        # self.file_save_popup = savedialog.SaveDialog(callback=self.save_textfile, title="Save file", size_hint=(0.9, 0.9))
        # # self.file_save_popup.size = (400, 400)
        # self.settings_popup = app_settings.AppSettingsPopup()
        self.title = title
        self.last_path = None
        self.opened_file = None
        # FIXME This is used to keep track of the file manager state (open or closed) but is not currently used
        self.manager_open = False
        self.file_manager = MDFileManager(
            exit_manager=self.exit_manager,
            select_path=self.select_path,
            icon_selection_button="folder-marker"
        )
    def on_load_file(self):
        if self.last_path is not None:
            path = self.last_path
        else:
            path = os.path.expanduser("~")
        self.file_manager.show(path)
        self.manager_open = True
HackXIt commented 1 month ago

Text is not a valid property for the MDDialog directly. MDDialog is just a class for handling everything around it.

Title is valid for MDDialog.

If you want to use text it must be on one of the children of the created MDDialog, as highlighted in the documentation:

from kivy.lang import Builder
from kivy.uix.widget import Widget

from kivymd.app import MDApp
from kivymd.uix.button import MDButton, MDButtonText
from kivymd.uix.dialog import (
    MDDialog,
    MDDialogIcon,
    MDDialogHeadlineText,
    MDDialogSupportingText,
    MDDialogButtonContainer,
    MDDialogContentContainer,
)
from kivymd.uix.divider import MDDivider
from kivymd.uix.list import (
    MDListItem,
    MDListItemLeadingIcon,
    MDListItemSupportingText,
)

KV = '''
MDScreen:
    md_bg_color: self.theme_cls.backgroundColor

    MDButton:
        pos_hint: {'center_x': .5, 'center_y': .5}
        on_release: app.show_alert_dialog()

        MDButtonText:
            text: "Show dialog"  # <--- text is VALID here
'''

class Example(MDApp):
    def build(self):
        return Builder.load_string(KV)

    def show_alert_dialog(self):
        MDDialog(  # <--- text is NOT VALID here
            # ----------------------------Icon-----------------------------
            MDDialogIcon(
                icon="refresh",
            ),
            # -----------------------Headline text-------------------------
            MDDialogHeadlineText(
                text="Reset settings?",  # <--- text is VALID here
            ),
            # -----------------------Supporting text-----------------------
            MDDialogSupportingText(
                text="This will reset your app preferences back to their "
                "default settings. The following accounts will also "
                "be signed out:",
            ),
            # -----------------------Custom content------------------------
            MDDialogContentContainer(
                MDDivider(),
                MDListItem(
                    MDListItemLeadingIcon(
                        icon="gmail",
                    ),
                    MDListItemSupportingText(
                        text="KivyMD-library@yandex.com",  # <--- text is VALID here
                    ),
                    theme_bg_color="Custom",
                    md_bg_color=self.theme_cls.transparentColor,
                ),
                MDListItem(
                    MDListItemLeadingIcon(
                        icon="gmail",
                    ),
                    MDListItemSupportingText(
                        text="kivydevelopment@gmail.com",  # <--- text is VALID here
                    ),
                    theme_bg_color="Custom",
                    md_bg_color=self.theme_cls.transparentColor,
                ),
                MDDivider(),
                orientation="vertical",
            ),
            # ---------------------Button container------------------------
            MDDialogButtonContainer(
                Widget(),
                MDButton(
                    MDButtonText(text="Cancel"), # <--- text is VALID here
                    style="text",
                ),
                MDButton(
                    MDButtonText(text="Accept"), # <--- text is VALID here
                    style="text",  
                ),
                spacing="8dp",
            ),
            # -------------------------------------------------------------
        ).open()

Example().run()

You need to think about the KivyMD components in terms of Lego pieces.

MDDialog is just one part of the building, if you want text, you need to put that into a button, label or something, and then put that widget INTO the dialog, which then displays the text.

The Dialog component only handles the "temporary window/screen" that is opened to display a seperate portion of UI elevated from the actual application. It's functionality comes from all the handlers it provides and the mechanisms such as open and dismiss. To use 'text' or any other property that correspond to specific widgets, you need to place those widgets into the dialog in a hierarchical manner.

HackXIt commented 1 month ago

To highlight it more clearly based on your example:

    def select_voice(self, voice_name):
        # Verwerk de geselecteerde stemnaam
        log.info("%s: Selected voice: %s", self.__class__.__name__, voice_name)
        # Je kunt hier verdere acties ondernemen, zoals het instellen van de geselecteerde stem in de API
        # TODO: Popup: you have selected this voice
        if not self.voice_dialog:
            self.voice_dialog = MDDialog(
                MDLabel(text="Hello World")
            )
        self.voice_dialog.open()
        ...
Additional Copy-pasta from the discord messages: BTW... ``` def select_voice(self, voice_name): # Verwerk de geselecteerde stemnaam log.info("%s: Selected voice: %s", self.__class__.__name__, voice_name) # Je kunt hier verdere acties ondernemen, zoals het instellen van de geselecteerde stem in de API # TODO: Popup: you have selected this voice if not self.voice_dialog: self.voice_dialog = MDDialog( # TODO: should work like below, but don't know why it doesn't... # text="Selected voice: " + voice_name ) self.voice_dialog.open() time.sleep(3) # does also not work as expexted... (window is opened after 3s and then closed immediately after) self.voice_dialog.dismiss() ``` This change will not affect synthesis at all, you must use the settings or indirectly change the settings from the result, so that synthesis will be affected. The dialog worked for me however, which is really weird.. The above code should actually not work for me, weird that it did. The way you're opening the dialog is wrong. You can't open a dialog and then dismiss it in the same function, it will never have a chance to be viewed in normal circumstances. You must use a callback from whatever button you have in the dialog which then calls the dismiss function. Kivy is generally somewhat asynchronous, there's background tasks that are regularly scheduled for things like rendering. As an example: ```python if text_item == "Exit": ExitDialog().open() This opens the ExitDialog and in the corresponding component file exitdialog.py I have: def on_cancel(self, *args): self.dismiss() def on_exit(self, *args): App.get_running_app().stop() exit(0) ``` The dismiss should be called from something else AFTER you're done dealing with whatever the dialog was doing, usually by user input. HackXIt — Today at 4:23 AM If you don't have a button, you can call the dismiss from wherever you want, for example once a selection was made and a corresponding change occured Ah I see, that's why it worked: ```python def on_select_voice(self): api = App.get_running_app().api # print(f"This is the api used: {api}") print("clicked on voice selection") if api: voice_names = api.get_available_voices() if voice_names: # Maak een lijst van menu-items voor elke stem menu_items = [ { "text": voice_name, "on_release": lambda x=voice_name: self.select_voice(x), } for voice_name in voice_names ] # Maak een dropdown-menu met de stemopties dropdown_menu = MDDropdownMenu( # Dit moet overeenkomen met de ID van de knop waarmee de stemmen worden geselecteerd caller=self.ids.btn_select_voice, items=menu_items, width_mult=4, ) dropdown_menu.open() else: log.error("%s: No voices available from API.", self.__class__.__name__) else: log.error("%s: API not available.", self.__class__.__name__) ``` I was using the dropdown thing, not the dialog thing. For that dropdown you're missing a corresponding dismiss, which potentially can result in the dropdown staying on the screen forever. I hope you can continue with all the remarks I've made and also the recent commits which hopefully improved some things. If there's any more questions, feel free to ask.