Closed DRomatzki closed 6 months ago
Hi @DRomatzki sorry I don't quite follow what the problem is. To access the value of components, you need to pass them as inputs to your event (e.g. .click()
), which passes the values of these components as parameters in your function. Are you saying that you have too many input components, and its inconvenient to write them all out explicitly as inputs to your event?
Yes. I am writing a GUI to set the configuration parameters for a server. There are about 20 parameters. So now writing a .change event for each of them clogs the module and is counter productive. In my example the slider changes value and the textbox is updated as per .change event. However, if you then read the value of the textbox from a function, it returns the original value, before the slider has changed it, and not the update value. I thought that was clear. I am not familiar with your source code but is this not part of the post-process part? I have registered another bug of a dropdown box which gives a warning when you enter a value which is actually in the choices. That was about 2 weeks ago. The solution you found was that you used the old value and not the new value in the post-process, or something like that. This problem might be the same here. You do not update the changed value of the textbox value parameter in the post process. I think a workaround would be to update statev with each change event, but that is stupid. Querying a gradio object must return the current value, not the value set in the previous event.
Hi @DRomatzki I'm sorry I'm having a hard time following what you've described. To be clear, you cannot access the latest value of a component using the .value
attribute. You must pass in the components as inputs.
Some things that may help you:
*. You can bind multiple event triggers together using gr.on
: https://www.gradio.app/guides/blocks-and-event-listeners#binding-multiple-triggers-to-a-function
set
of components, even all of them (set(iface.components)
) to an event trigger, which passes them in as a dict you can access in your function: https://www.gradio.app/guides/blocks-and-event-listeners#function-input-list-vs-dict Hi again. I think we misunderstand each other. Have you looked at the printed values in the terminal from the function def print_local():?
Looking at the code it all seems fine and simple. Running it, however, is something different. 1) Run the code 2) Change the slider value to , say, 4. 3) The textbox is updated to "4" via the .change event 4) Press the button and check the terminal for the printed output. 5) The result printed is textbox 0, slider 2. These are the initial values and not the new values.
If you look at my example code you will see an event trigger, my_slider.change(lambda x:x,my_slider,my_text). In this case the value of the slider is transferred to the textbox. That is correct as you say and it happens. The textbox GUI is updated and we can see it. Now I want to access the new updated textbox value and I do it from a function., def print_local():. That is also correct. That means I am trying to access the new and updated property from the objects. This can be done and is fully compliant with your code base. However, I found out that the supposedly updated property of the object has not been updated and are still the old values. That is my point and a bug in my opinion. Please look at the terminal print out. My code does everything you say. It uses events to update other components on user input, except the visual part is updated but not the backend values. I do not know how else to explain. The backend values of objects are not what the visual values of these objects show. I can only presume that the same code on your system behaves differently than on mine but that is highly unlikely. Please compare the printed output from "def print_local():" in the terminal with the GUI values. Personnally I believe it is in your post processing routine when object values change. You update the GUI part of the object but not the property ".value" A work around from my side would be to assign an elem_ID to each object and then query the actual value of the object as it is displayed in the browser using js or something., But that is not how it is supposed to be. Instead of printing the values I want to use the updated values that the user has changed for other purposes. I do not know what the other people did previously. Am I making a mistake? Again... run the code and look at the printout in the terminal. The printed values do NOT correspond with the GUI shown values.
Hi @DRomatzki this is actually the expected behavior. When you directly access the attributes of a component (e.g. .value
or.lines
), you will get the initial value of that attribute. This is what is defined in the backend and this does not change. We couldn't change it because these component objects are shared across all users of an app (since there's only one backend serving many different users). This is why we never suggest users accessing attributes of a component directly.
When you update a property, what happens is that we actually create a copy of a component and store it a dictionary that is keyed by your browser session ID. This component is used and updated as needed.
If you would ever need to access the updated properties of a component, you should pass it in as an input to your function using an event listener, like you do here:
my_slider.change(lambda x:x,my_slider,my_text)
This is expected behavior, not a bug so I'll go ahead and close this issue
HI again Before I start my last argument... I would like to thank you for your patience and the brilliant code, which is gradio. Thanks a lot. My argument is the scope of the gradio objects..... The way that I now understand is that a copy is made when the object is created, meaning my_text-gr.text(). All action, events, etc are now applicable to that copied object and the backend is ignored since we are now in a session. This means the scope of the object is now in the session iface.launch() and therefor its scope must be in that session. All references to that object must be on the copy and not the backend. The backend is not needed at that point anymore. Howewver, there are now 2 interface (or scopes), the interface and the backend. I still consider this as a bug because accessing the objects value must be in the scope of the interface, Sorry for arguing but this is how I see it.
I understand what you're saying @DRomatzki, but that isn't technically possible, because in Python, we can't change what a variable that a developer has defined points to. In your example, my_slider
will always point to the original gr.Slider
that you've defined. That's why we never encourage users to access attributes of components directly. Is there a particular example that you saw that does this? In the Gradio docs, afaik, we always mention passing components objects in event handlers if you want to get their (updated) value in a function
One last shot.. please. I thank you again for your patience... you certainly have a lot. :-) BTW I have upgrade gradio to the latest version .4.19.* (I cant remember the last digits. I have read the referenced pages mentioned above and noticed the list that can be passed. I have used that in my code already. however, your suggestion of set(iface.components) does not work. The error is my_set=set(iface.components) AttributeError: 'Blocks' object has no attribute 'components' So I played around and modified the code as follows.
import gradio as gr
my_list={}
def process_list(*args):
for i in range(len(args)):
print(my_list[i], args[i])
with gr.Blocks() as iface: my_button= gr.Button(value="Show values",elem_id="my_button1") my_text=gr.Textbox(label="Set Value",value=0,elem_id="my_text")
my_slider=gr.Slider(label="Slider value", step=1,minimum=0, maximum=20,value=2,elem_id="my_slider_1")
my_set=list(iface.children)
for i in range(len(my_set)):
my_list[i]=iface.children[i].elem_id
my_button.click(process_list,my_set,None)
my_slider.change(lambda x:x,my_slider,my_text)
iface.launch()
It now works! If you other ideas that work better please let me know. Thanks again!
Yes sorry .components
was incorrect. .children
is fine, or doing iface.blocks
will also work I think
Describe the bug
I used a slider and a textbox. On slider change the value in the text box changes. However, there is a button and when pressed it calls a function outside the gr.Blocks code, and prints the values of the slider and textbox. they are different from what is shown.. It seems the values of the gr objects are not updated correctly or accessing the gr components outside the gr Blocks. is not practical. Unfortunately I consider this as a bug and is critical since putting all your code in a single module is confusing and difficult to debug. I can not write functions for every gr object because there an more than 20 variables. Maybe there is a workaround but any workaround would be inefficient and possible impractical. I have no idea how to solve it except by writing multiple repeat lines of code. State variables?
If I missed something , my apologies.
Have you searched existing issues? š
Reproduction
System Info
Severity
Blocking usage of gradio