def test_simple_legacy_add_rows_with_clear_queue(self):
        """Test plain old _legacy_add_rows after clearing the queue."""
        all_methods = self._get_unnamed_data_methods(
        ) + self._get_named_data_methods()

        for method in all_methods:
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME)

            # Make sure it has 2 rows in it.
            df_proto = _get_data_frame(self.get_delta_from_queue())
            num_rows = len(df_proto.data.cols[0].int64s.data)
            self.assertEqual(2, num_rows)

            # This is what we're testing:
            self.forward_msg_queue.clear()
            el._legacy_add_rows(NEW_ROWS)

            # Make sure there are 3 rows in the delta that got appended.
            ar = self.get_delta_from_queue().add_rows
            num_rows = len(ar.data.data.cols[0].int64s.data)
            self.assertEqual(3, num_rows)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
    def test_with_index_legacy_add_rows(self):
        """Test plain old _legacy_add_rows."""
        all_methods = self._get_unnamed_data_methods()

        for method in all_methods:
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME_WITH_INDEX)

            # Make sure it has 2 rows in it.
            df_proto = _get_data_frame(self.get_delta_from_queue())
            num_rows = len(df_proto.data.cols[0].int64s.data)
            self.assertEqual(2, num_rows)

            # This is what we're testing:
            el._legacy_add_rows(NEW_ROWS_WITH_INDEX)

            # Make sure the add_rows proto looks like we expect.
            df_proto = _get_data_frame(self.get_delta_from_queue())
            rows = df_proto.data.cols[0].int64s.data
            self.assertEqual([30, 40, 50], rows)

            index = df_proto.index.int_64_index.data.data
            self.assertEqual([3, 4, 5], index)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #3
0
    def test_legacy_add_rows_with_pyarrow_table_data(self):
        """Test that an error is raised when called with `pyarrow.Table` data."""
        all_methods = self._get_unnamed_data_methods() + self._get_named_data_methods()

        for method in all_methods:
            with self.assertRaises(StreamlitAPIException):
                # Create a new data-carrying element (e.g. st._legacy_dataframe)
                el = method(DATAFRAME)
                # This is what we're testing:
                el._legacy_add_rows(pa.Table.from_pandas(NEW_ROWS))

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #4
0
    def test_legacy_add_rows_fails_when_wrong_shape(self):
        """Test that _legacy_add_rows raises error when input has wrong shape."""
        all_methods = self._get_unnamed_data_methods() + self._get_named_data_methods()

        for method in all_methods:
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME)

            with self.assertRaises(ValueError):
                # This is what we're testing:
                el._legacy_add_rows(NEW_ROWS_WRONG_SHAPE)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #5
0
        def serialize_file_uploader(
                files: SomeUploadedFiles) -> FileUploaderStateProto:
            state_proto = FileUploaderStateProto()

            ctx = get_script_run_ctx()
            if ctx is None:
                return state_proto

            # ctx.uploaded_file_mgr._file_id_counter stores the id to use for
            # the *next* uploaded file, so the current highest file id is the
            # counter minus 1.
            state_proto.max_file_id = ctx.uploaded_file_mgr._file_id_counter - 1

            if not files:
                return state_proto
            elif not isinstance(files, list):
                files = [files]

            for f in files:
                file_info: UploadedFileInfoProto = state_proto.uploaded_file_info.add(
                )
                file_info.id = f.id
                file_info.name = f.name
                file_info.size = f.size

            return state_proto
Beispiel #6
0
    def _get_script_run_ctx(self) -> ScriptRunContext:
        """Get the ScriptRunContext for the current thread.

        Returns
        -------
        ScriptRunContext
            The ScriptRunContext for the current thread.

        Raises
        ------
        AssertionError
            If called outside of a ScriptRunner thread.
        RuntimeError
            If there is no ScriptRunContext for the current thread.

        """
        assert self._is_in_script_thread()

        ctx = get_script_run_ctx()
        if ctx is None:
            # This should never be possible on the script_runner thread.
            raise RuntimeError(
                "ScriptRunner thread has a null ScriptRunContext. Something has gone very wrong!"
            )
        return ctx
Beispiel #7
0
        def marshall_component(dg,
                               element: Element) -> Union[Any, Type[NoValue]]:
            element.component_instance.component_name = self.name
            element.component_instance.form_id = current_form_id(dg)
            if self.url is not None:
                element.component_instance.url = self.url

            # Normally, a widget's element_hash (which determines
            # its identity across multiple runs of an app) is computed
            # by hashing the entirety of its protobuf. This means that,
            # if any of the arguments to the widget are changed, Streamlit
            # considers it a new widget instance and it loses its previous
            # state.
            #
            # However! If a *component* has a `key` argument, then the
            # component's hash identity is determined by entirely by
            # `component_name + url + key`. This means that, when `key`
            # exists, the component will maintain its identity even when its
            # other arguments change, and the component's iframe won't be
            # remounted on the frontend.
            #
            # So: if `key` is None, we marshall the element's arguments
            # *before* computing its widget_ui_value (which creates its hash).
            # If `key` is not None, we marshall the arguments *after*.

            def marshall_element_args():
                element.component_instance.json_args = serialized_json_args
                element.component_instance.special_args.extend(special_args)

            if key is None:
                marshall_element_args()

            def deserialize_component(ui_value, widget_id=""):
                # ui_value is an object from json, an ArrowTable proto, or a bytearray
                return ui_value

            ctx = get_script_run_ctx()
            widget_value, _ = register_widget(
                element_type="component_instance",
                element_proto=element.component_instance,
                user_key=key,
                widget_func_name=self.name,
                deserializer=deserialize_component,
                serializer=lambda x: x,
                ctx=ctx,
            )

            if key is not None:
                marshall_element_args()

            if widget_value is None:
                widget_value = default
            elif isinstance(widget_value, ArrowTableProto):
                widget_value = component_arrow.arrow_proto_to_dataframe(
                    widget_value)

            # widget_value will be either None or whatever the component's most
            # recent setWidgetValue value is. We coerce None -> NoValue,
            # because that's what DeltaGenerator._enqueue expects.
            return widget_value if widget_value is not None else NoValue
Beispiel #8
0
    def test_remove_orphaned_files(self, get_file_recs_patch,
                                   remove_orphaned_files_patch):
        """When file_uploader is accessed, it should call
        UploadedFileManager.remove_orphaned_files.
        """
        ctx = get_script_run_ctx()
        ctx.uploaded_file_mgr._file_id_counter = 101

        file_recs = [
            UploadedFileRec(1, "file1", "type", b"123"),
            UploadedFileRec(2, "file2", "type", b"456"),
        ]
        get_file_recs_patch.return_value = file_recs

        st.file_uploader("foo", accept_multiple_files=True)

        args, kwargs = remove_orphaned_files_patch.call_args
        self.assertEqual(len(args), 0)
        self.assertEqual(kwargs["session_id"], "test session id")
        self.assertEqual(kwargs["newest_file_id"], 100)
        self.assertEqual(kwargs["active_file_ids"], [1, 2])

        # Patch _get_file_recs to return [] instead. remove_orphaned_files
        # should not be called when file_uploader is accessed.
        get_file_recs_patch.return_value = []
        remove_orphaned_files_patch.reset_mock()

        st.file_uploader("foo")
        remove_orphaned_files_patch.assert_not_called()
def _current_form(
    this_dg: "streamlit.delta_generator.DeltaGenerator",
) -> Optional[FormData]:
    """Find the FormData for the given DeltaGenerator.

    Forms are blocks, and can have other blocks nested inside them.
    To find the current form, we walk up the dg_stack until we find
    a DeltaGenerator that has FormData.
    """
    if not streamlit._is_running_with_streamlit:
        return None

    if this_dg._form_data is not None:
        return this_dg._form_data

    if this_dg == this_dg._main_dg:
        # We were created via an `st.foo` call.
        # Walk up the dg_stack to see if we're nested inside a `with st.form` statement.
        ctx = get_script_run_ctx()
        if ctx is None or len(ctx.dg_stack) == 0:
            return None

        for dg in reversed(ctx.dg_stack):
            if dg._form_data is not None:
                return dg._form_data
    else:
        # We were created via an `dg.foo` call.
        # Take a look at our parent's form data to see if we're nested inside a form.
        parent = this_dg._parent
        if parent is not None and parent._form_data is not None:
            return parent._form_data

    return None
Beispiel #10
0
    def __exit__(self, type, value, traceback):
        # with block ended
        ctx = get_script_run_ctx()
        if ctx is not None:
            ctx.dg_stack.pop()

        # Re-raise any exceptions
        return False
Beispiel #11
0
def _enqueue_message(msg):
    """Enqueues a ForwardMsg proto to send to the app."""
    ctx = get_script_run_ctx()

    if ctx is None:
        raise NoSessionContext()

    ctx.enqueue(msg)
    def test_legacy_add_rows_suceeds_when_wrong_shape(self):
        """_legacy_add_rows doesn't raise an error even if its input has the
        wrong shape. Instead, it's up to the frontend to catch and raise
        this error.
        """
        all_methods = self._get_unnamed_data_methods(
        ) + self._get_named_data_methods()

        for method in all_methods:
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME)

            # This is what we're testing:
            el._legacy_add_rows(NEW_ROWS_WRONG_SHAPE)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
    def test_legacy_add_rows_works_when_new_name(self):
        """Test _legacy_add_rows with new named datasets."""

        for method in self._get_named_data_methods():
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME)
            self.forward_msg_queue.clear()

            # This is what we're testing:
            el._legacy_add_rows(new_name=NEW_ROWS)

            # Make sure there are 3 rows in the delta that got appended.
            ar = self.get_delta_from_queue().add_rows
            num_rows = len(ar.data.data.cols[0].int64s.data)
            self.assertEqual(3, num_rows)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #14
0
def _get_session_id() -> str:
    """Semantic wrapper to retrieve current AppSession ID."""
    ctx = get_script_run_ctx()
    if ctx is None:
        # This is only None when running "python myscript.py" rather than
        # "streamlit run myscript.py". In which case the session ID doesn't
        # matter and can just be a constant, as there's only ever "session".
        return "dontcare"
    else:
        return ctx.session_id
    def test_no_index_no_data_legacy_add_rows(self):
        """Test plain old _legacy_add_rows."""
        all_methods = self._get_unnamed_data_methods()

        for method in all_methods:
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(None)
            _get_data_frame(self.get_delta_from_queue())

            # This is what we're testing:
            el._legacy_add_rows(DATAFRAME)

            # Make sure there are 2 rows in it now.
            df_proto = _get_data_frame(self.get_delta_from_queue())
            num_rows = len(df_proto.data.cols[0].int64s.data)
            self.assertEqual(2, num_rows)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #16
0
    def test_enqueue_new_session_message(self, _1, _2, patched_config):
        def get_option(name):
            if name == "server.runOnSave":
                # Just to avoid starting the watcher for no reason.
                return False

            return config.get_option(name)

        patched_config.get_option.side_effect = get_option
        patched_config.get_options_for_section.side_effect = (
            _mock_get_options_for_section()
        )

        # Create a AppSession with some mocked bits
        rs = AppSession(
            self.io_loop,
            SessionData("mock_report.py", ""),
            UploadedFileManager(),
            lambda: None,
            MagicMock(),
        )

        orig_ctx = get_script_run_ctx()
        ctx = ScriptRunContext(
            "TestSessionID", rs._session_data.enqueue, "", None, None
        )
        add_script_run_ctx(ctx=ctx)

        rs._on_scriptrunner_event(ScriptRunnerEvent.SCRIPT_STARTED)

        sent_messages = rs._session_data._browser_queue._queue
        self.assertEqual(len(sent_messages), 2)  # NewApp and SessionState messages

        # Note that we're purposefully not very thoroughly testing new_session
        # fields below to avoid getting to the point where we're just
        # duplicating code in tests.
        new_session_msg = sent_messages[0].new_session
        self.assertEqual("mock_scriptrun_id", new_session_msg.script_run_id)

        self.assertEqual(new_session_msg.HasField("config"), True)
        self.assertEqual(
            new_session_msg.config.allow_run_on_save,
            config.get_option("server.allowRunOnSave"),
        )

        self.assertEqual(new_session_msg.HasField("custom_theme"), True)
        self.assertEqual(new_session_msg.custom_theme.text_color, "black")

        init_msg = new_session_msg.initialize
        self.assertEqual(init_msg.HasField("user_info"), True)

        add_script_run_ctx(ctx=orig_ctx)
Beispiel #17
0
    def test_named_legacy_add_rows(self):
        """Test _legacy_add_rows with a named dataset."""
        for method in self._get_named_data_methods():
            # Create a new data-carrying element (e.g. st._legacy_dataframe)
            el = method(DATAFRAME)

            # Make sure it has 2 rows in it.
            df_proto = data_frame._get_data_frame(self.get_delta_from_queue())
            num_rows = len(df_proto.data.cols[0].int64s.data)
            self.assertEqual(num_rows, 2)

            # This is what we're testing:
            el._legacy_add_rows(mydata1=NEW_ROWS)

            # Make sure there are 5 rows in it now.
            df_proto = data_frame._get_data_frame(self.get_delta_from_queue())
            num_rows = len(df_proto.data.cols[0].int64s.data)
            self.assertEqual(num_rows, 5)

            # Clear the queue so the next loop is like a brand new test.
            get_script_run_ctx().reset()
            self.forward_msg_queue.clear()
Beispiel #18
0
    def test_widget_outputs_dont_alias(self):
        color = st.select_slider(
            "Select a color of the rainbow",
            options=[
                ["red", "orange"],
                ["yellow", "green"],
                ["blue", "indigo"],
                ["violet"],
            ],
            key="color",
        )

        ctx = get_script_run_ctx()
        assert ctx.session_state["color"] is not color
    def form_submit_button(
        self,
        label: str = "Submit",
        help: Optional[str] = None,
        on_click=None,
        args=None,
        kwargs=None,
    ) -> bool:
        """Display a form submit button.

        When this button is clicked, all widget values inside the form will be
        sent to Streamlit in a batch.

        Every form must have a form_submit_button. A form_submit_button
        cannot exist outside a form.

        For more information about forms, check out our
        `blog post <https://blog.streamlit.io/introducing-submit-button-and-forms/>`_.

        Parameters
        ----------
        label : str
            A short label explaining to the user what this button is for.
            Defaults to "Submit".
        help : str or None
            A tooltip that gets displayed when the button is hovered over.
            Defaults to None.
        on_click : callable
            An optional callback invoked when this button is clicked.
        args : tuple
            An optional tuple of args to pass to the callback.
        kwargs : dict
            An optional dict of kwargs to pass to the callback.

        Returns
        -------
        bool
            True if the button was clicked.
        """
        ctx = get_script_run_ctx()
        return self._form_submit_button(
            label=label,
            help=help,
            on_click=on_click,
            args=args,
            kwargs=kwargs,
            ctx=ctx,
        )
Beispiel #20
0
    def _active_dg(self) -> "DeltaGenerator":
        """Return the DeltaGenerator that's currently 'active'.
        If we are the main DeltaGenerator, and are inside a `with` block that
        creates a container, our active_dg is that container. Otherwise,
        our active_dg is self.
        """
        if self == self._main_dg:
            # We're being invoked via an `st.foo` pattern - use the current
            # `with` dg (aka the top of the stack).
            ctx = get_script_run_ctx()
            if ctx and len(ctx.dg_stack) > 0:
                return ctx.dg_stack[-1]

        # We're being invoked via an `st.sidebar.foo` pattern - ignore the
        # current `with` dg.
        return self
    def __setitem__(self, user_key: str, value: Any) -> None:
        from streamlit.script_run_context import get_script_run_ctx

        ctx = get_script_run_ctx()

        if ctx is not None:
            widget_id = self._key_id_mapping.get(user_key, None)
            widget_ids = ctx.widget_ids_this_run
            form_ids = ctx.form_ids_this_run

            if widget_id in widget_ids or user_key in form_ids:
                raise StreamlitAPIException(
                    f"`st.session_state.{user_key}` cannot be modified after the widget"
                    f" with key `{user_key}` is instantiated.")

        self._new_session_state[user_key] = value
Beispiel #22
0
    def setUp(self, override_root=True):
        self.forward_msg_queue = ForwardMsgQueue()
        self.override_root = override_root
        self.orig_report_ctx = None
        self.new_script_run_ctx = ScriptRunContext(
            session_id="test session id",
            enqueue=self.forward_msg_queue.enqueue,
            query_string="",
            session_state=SessionState(),
            uploaded_file_mgr=UploadedFileManager(),
        )

        if self.override_root:
            self.orig_report_ctx = get_script_run_ctx()
            add_script_run_ctx(threading.current_thread(),
                               self.new_script_run_ctx)

        self.app_session = FakeAppSession()
Beispiel #23
0
    def tearDown(self):
        # Some of these tests reach directly into CALL_STACK data and twiddle it.
        # Reset default values on teardown.
        MEMO_CALL_STACK._cached_func_stack = []
        MEMO_CALL_STACK._suppress_st_function_warning = 0
        SINGLETON_CALL_STACK._cached_func_stack = []
        SINGLETON_CALL_STACK._suppress_st_function_warning = 0

        # Clear caches
        st.experimental_memo.clear()
        st.experimental_singleton.clear()

        # And some tests create widgets, and can result in DuplicateWidgetID
        # errors on subsequent runs.
        ctx = script_run_context.get_script_run_ctx()
        if ctx is not None:
            ctx.widget_ids_this_run.clear()
        super().tearDown()
    def __setitem__(self, user_key: str, value: Any) -> None:
        """Set the value of the session_state entry with the given user_key.

        If the key corresponds to a widget or form that's been instantiated
        during the current script run, raise an Exception instead.
        """
        from streamlit.script_run_context import get_script_run_ctx

        ctx = get_script_run_ctx()

        if ctx is not None:
            widget_id = self._key_id_mapping.get(user_key, None)
            widget_ids = ctx.widget_ids_this_run
            form_ids = ctx.form_ids_this_run

            if widget_id in widget_ids or user_key in form_ids:
                raise StreamlitAPIException(
                    f"`st.session_state.{user_key}` cannot be modified after the widget"
                    f" with key `{user_key}` is instantiated.")

        self._new_session_state[user_key] = value
def get_container_cursor(
    root_container: Optional[int], ) -> Optional["RunningCursor"]:
    """Return the top-level RunningCursor for the given container.
    This is the cursor that is used when user code calls something like
    `st.foo` (which uses the main container) or `st.sidebar.foo` (which uses
    the sidebar container).
    """
    if root_container is None:
        return None

    ctx = get_script_run_ctx()

    if ctx is None:
        return None

    if root_container in ctx.cursors:
        return ctx.cursors[root_container]

    cursor = RunningCursor(root_container=root_container)
    ctx.cursors[root_container] = cursor
    return cursor
Beispiel #26
0
    def _get_file_recs_for_camera_input_widget(
        widget_id: str, widget_value: Optional[FileUploaderStateProto]
    ) -> List[UploadedFileRec]:
        if widget_value is None:
            return []

        ctx = get_script_run_ctx()
        if ctx is None:
            return []

        uploaded_file_info = widget_value.uploaded_file_info
        if len(uploaded_file_info) == 0:
            return []

        active_file_ids = [f.id for f in uploaded_file_info]

        # Grab the files that correspond to our active file IDs.
        return ctx.uploaded_file_mgr.get_files(
            session_id=ctx.session_id,
            widget_id=widget_id,
            file_ids=active_file_ids,
        )
Beispiel #27
0
        def serialize_camera_image_input(
            snapshot: SomeUploadedSnapshotFile, ) -> FileUploaderStateProto:
            state_proto = FileUploaderStateProto()

            ctx = get_script_run_ctx()
            if ctx is None:
                return state_proto

            # ctx.uploaded_file_mgr._file_id_counter stores the id to use for
            # the *next* uploaded file, so the current highest file id is the
            # counter minus 1.
            state_proto.max_file_id = ctx.uploaded_file_mgr._file_id_counter - 1

            if not snapshot:
                return state_proto

            file_info: UploadedFileInfoProto = state_proto.uploaded_file_info.add(
            )
            file_info.id = snapshot.id
            file_info.name = snapshot.name
            file_info.size = snapshot.size

            return state_proto
def get_session_state() -> SessionState:
    """Get the SessionState object for the current session.

    Note that in streamlit scripts, this function should not be called
    directly. Instead, SessionState objects should be accessed via
    st.session_state.
    """
    global _state_use_warning_already_displayed
    from streamlit.script_run_context import get_script_run_ctx

    ctx = get_script_run_ctx()

    # If there is no report context because the script is run bare, have
    # session state act as an always empty dictionary, and print a warning.
    if ctx is None:
        if not _state_use_warning_already_displayed:
            _state_use_warning_already_displayed = True
            if not st._is_running_with_streamlit:
                logger.warning(
                    "Session state does not function when running a script without `streamlit run`"
                )
        return SessionState()
    return ctx.session_state
Beispiel #29
0
    def multiselect(
        self,
        label: str,
        options: OptionSequence,
        default: Optional[Any] = None,
        format_func: Callable[[Any], Any] = str,
        key: Optional[Key] = None,
        help: Optional[str] = None,
        on_change: Optional[WidgetCallback] = None,
        args: Optional[WidgetArgs] = None,
        kwargs: Optional[WidgetKwargs] = None,
        *,  # keyword-only arguments:
        disabled: bool = False,
    ) -> List[Any]:
        """Display a multiselect widget.
        The multiselect widget starts as empty.

        Parameters
        ----------
        label : str
            A short label explaining to the user what this select widget is for.
        options : Sequence[V], numpy.ndarray, pandas.Series, pandas.DataFrame, or pandas.Index
            Labels for the select options. This will be cast to str internally
            by default. For pandas.DataFrame, the first column is selected.
        default: [V], V, or None
            List of default values. Can also be a single value.
        format_func : function
            Function to modify the display of selectbox options. It receives
            the raw option as an argument and should output the label to be
            shown for that option. This has no impact on the return value of
            the multiselect.
        key : str or int
            An optional string or integer to use as the unique key for the widget.
            If this is omitted, a key will be generated for the widget
            based on its content. Multiple widgets of the same type may
            not share the same key.
        help : str
            An optional tooltip that gets displayed next to the multiselect.
        on_change : callable
            An optional callback invoked when this multiselect's value changes.
        args : tuple
            An optional tuple of args to pass to the callback.
        kwargs : dict
            An optional dict of kwargs to pass to the callback.
        disabled : bool
            An optional boolean, which disables the multiselect widget if set
            to True. The default is False. This argument can only be supplied
            by keyword.

        Returns
        -------
        list
            A list with the selected options

        Example
        -------
        >>> options = st.multiselect(
        ...     'What are your favorite colors',
        ...     ['Green', 'Yellow', 'Red', 'Blue'],
        ...     ['Yellow', 'Red'])
        >>>
        >>> st.write('You selected:', options)

        .. output::
           https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/widget.multiselect.py
           height: 420px

        .. note::
           User experience can be degraded for large lists of `options` (100+), as this widget
           is not designed to handle arbitrary text search efficiently. See this
           `thread <https://discuss.streamlit.io/t/streamlit-loading-column-data-takes-too-much-time/1791>`_
           on the Streamlit community forum for more information and
           `GitHub issue #1059 <https://github.com/streamlit/streamlit/issues/1059>`_ for updates on the issue.

        """
        ctx = get_script_run_ctx()
        return self._multiselect(
            label=label,
            options=options,
            default=default,
            format_func=format_func,
            key=key,
            help=help,
            on_change=on_change,
            args=args,
            kwargs=kwargs,
            disabled=disabled,
            ctx=ctx,
        )
Beispiel #30
0
    def color_picker(
        self,
        label: str,
        value: Optional[str] = None,
        key: Optional[Key] = None,
        help: Optional[str] = None,
        on_change: Optional[WidgetCallback] = None,
        args: Optional[WidgetArgs] = None,
        kwargs: Optional[WidgetKwargs] = None,
        *,  # keyword-only arguments:
        disabled: bool = False,
    ) -> str:
        """Display a color picker widget.

        Parameters
        ----------
        label : str
            A short label explaining to the user what this input is for.
        value : str
            The hex value of this widget when it first renders. If None,
            defaults to black.
        key : str or int
            An optional string or integer to use as the unique key for the widget.
            If this is omitted, a key will be generated for the widget
            based on its content. Multiple widgets of the same type may
            not share the same key.
        help : str
            An optional tooltip that gets displayed next to the color picker.
        on_change : callable
            An optional callback invoked when this color_picker's value
            changes.
        args : tuple
            An optional tuple of args to pass to the callback.
        kwargs : dict
            An optional dict of kwargs to pass to the callback.
        disabled : bool
            An optional boolean, which disables the color picker if set to
            True. The default is False. This argument can only be supplied by
            keyword.

        Returns
        -------
        str
            The selected color as a hex string.

        Example
        -------
        >>> color = st.color_picker('Pick A Color', '#00f900')
        >>> st.write('The current color is', color)

        .. output::
           https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/widget.color_picker.py
           height: 335px

        """
        ctx = get_script_run_ctx()
        return self._color_picker(
            label=label,
            value=value,
            key=key,
            help=help,
            on_change=on_change,
            args=args,
            kwargs=kwargs,
            disabled=disabled,
            ctx=ctx,
        )