param.rx seems not to work with Qt widgets

Hi, rx seems very cool, but my studio is heavily built up using Qt through PySide, and there’s no appetite to migrate to another framework (like Panel) or split support.

As far as I can tell, I’m recreating the pandas dataframe example from the rx docs here Reactive Functions and Expressions — param v2.1.1 ,
only using a QLineEdit widget instance Synopsis - Qt for Python
instead of a pandas frame. If I try to bind the rx setText with an rx-wrapped string, or even a normal string, nothing happens.

I’ve tried introspecting the rx stuff as it executes to see what the issue is, but the accessor system’s interaction with getattribute is a bit beyond me.

I wondered if this is a known issue, or you have any advice. Thanks.

from param.reactive import rx
from PySide2 import QtWidgets # or PySide6, I get the same result

text = "initial_value" # raw str text value
rxtext = rx(text)

# create the Qt application, required to start building widgets
# event loop doesn't run yet
app = QtWidgets.QApplication()


line = QtWidgets.QLineEdit() # create a QLineEdit widget instance
rxline = rx(line) # wrap widget instance in rx - which should also wrap all its methods
# by default QLineEdit has no displayed text

rxline.setText(rxtext) # link rx wrappers together
# see that the widget still has no text ._.
rxtext.rx.value = "second_value"
# still nothing ._.

# if we just call the normal method on the normal instance, obviously it works
# uncomment the line below to see desired result 
# line.setText(text)

# display the widget
line.show()
# run the Qt application
app.exec_()

Thanks for your help

Here is another approach that seems to work

image

from param.reactive import rx
from PySide6 import QtWidgets # or PySide6, I get the same result

rxtext = rx("")

# create the Qt application, required to start building widgets
# event loop doesn't run yet
app = QtWidgets.QApplication()
line = QtWidgets.QLineEdit() # create a QLineEdit widget instance

rxtext.rx.watch(line.setText)
rxtext.rx.value="some-value"


# display the widget
line.show()
# run the Qt application
app.exec()

I see, so you always need to make the connection explicitly, you can’t just wrap the objects and put them together. Thanks!

1 Like

This is the part that doesn’t work in the original code:

The problem being that the setText call is lazy here and doesn’t actually evaluate unless you explicitly ask it to. rx is designed for building reactive pipelines, which means imperative method calls, e.g. to call something that has a side-effect rather than simply chaining method calls, have to be invoked through watch. The approach @Marc provided is therefore the correct way to do this.

1 Like

I think I understand, but then what’s the difference between this case and the pandas example in the documentation here ?

Why does df.head(nrows) properly affect the dataframe and evaluates, while rxline.setText(rxtext) doesn’t?

df.head(nrows)

  • df.head(nrows) is a method that actually returns something.
  • The reactive expression df.head(nrows) is displayed in a notebook. Param has setup the notebook such that it knows how to run/ render df.head(nrows) including how to re-run and re-render when something specific changes.

rxline.setText(rxtext)

  • rxline.setText(rxtext) is a method that does not return anything. It has sideeffects.
  • The reactive expression rxline.setText(rxtext) is not being rendered and not being run by anything. As Philipp tries to explain its really just a lazy expression that waits for something to evaluate it.
    • You can think of it as a lazy expression just as there are lazy expressions in Polars and Dask. Expressions that are waiting to be evaluated, run, computed or similar.
    • With the .watch method we specify that a method should be run when something specific changes.
1 Like