Streamlit all buttons are the same - python

import streamlit as st
import base64
def custom_button(text='button', bgpng=None, height='height: 225px;', width='width: 225px'):
with open (bgpng, 'rb') as img:
convert_img = base64.b64encode(img.read()).decode('utf-8')
background_img = f"background-image: url('data:image/png;base64, {convert_img}');"
st.markdown(f"""
<style>
div.stButton > button:first-child {{
{background_img}
{height}
{width}
}}
</style>""", unsafe_allow_html=True)
button = st.button(text)
return button
button1 = custom_button(text='button', bgpng=r'data\pic1.png')
button2 = custom_button(text='button2', bgpng=r'data\pic2.png')
The problem is the following, the last button always determines the properties of the other buttons, in this case the background image of button2 determines the background image of button1. So all buttons have the same background image. How can i solve it?

You create x styles for x buttons so the last style is applyed since it's the same priority.
I don't use python but I guess you could set an id for your buttons and apply your styles to #{button_id} (or something like that), or apply inline style like below :
<button style="background-color:orange;">button</button>
<button style="background-color:grey;">button2</button>

I found a solution.
If the streamlit code is ready, in the browser, right click, inspect , copy the css selector, then we get the unique button css selector. We need to replace this piece of code:
"div.stButton > button:first-child"
Should be replaced with: "copied css selector"
We have to do it for each button.

Related

Is it possible to get the current position of an ipywidget?

I'm using the ipyvuetfy lib that create nice dashboarding element based ont he vuetify.js lib and ipyvwidgets.
I need to place a dialog in a specific position (on top of another element) like this menu placed on top of a select folder btn.
How can I access the the current position of a widget relative to the window ?
It is possible with Javascript code, by identifying the widget with a custom class and using the jQuery offset() method, and then setting the DOM Style top and left properties of the child (here a Card) of the ipyvuetify Dialog, with the style position set as fixed. I haven't found how to execute the JS code via the Dialog activator slot, so the Dialog widget is triggered via the click.stop event:
import ipyvuetify as v
import ipywidgets as w
from IPython.display import Javascript
out = w.Output(layout={'display': 'none'})
js = '''
var x = $(".myparentwidget").offset();
var d = document.getElementsByClassName("mydialog")[0];
d.style.top = x.top+"px";
d.style.left= x.left+"px";'''
def on_click(widget, event, data):
dialog.v_model=True
with out:
display(Javascript(js, lib="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"))
btn = v.Btn(
children=["Open dialog"],
class_='myparentwidget'
)
btn.on_event("click.stop", on_click)
dialog = v.Dialog(
v_model=False,
children=[
v.Card(
class_='mydialog',
style_="position: fixed; width: 300px",
children=[
v.CardTitle(children=["Dialog window"]),
v.CardText(children=['Dialog text']),
]
)
],
)
v.Layout(children=[btn,dialog,out])

Dash modal with multiple buttons that open it

Following this part of the docs: https://dash-bootstrap-components.opensource.faculty.ai/l/components/modal I've created a modal in my Dash app. The trigger for the modal will be dynamically rendered thumbnails. When any of them is clicked, the modal should open and display the image from the thumbnail as it's body.
Is is possible, inside Dash, to have multiple buttons (I don't know how many will there be, depending on how many thumbnails in the database) that will all open the same modal dialog and pass some of their data to the modal (such as img src in my case)?
The input in the example above is simple:
[
Input("open", "n_clicks"), Input("close", "n_clicks")
],
but in reality I don't know how many will there be and can't hardcode an ID.
Any suggestions?
Yes, you can have multiple buttons open a modal. Just as you showed, the callback would have an Input for each one. No, you cannot create them dynamically. Dash does not play well with any ID that is not in the layout at the start of running the app.
Create a set of buttons dynamically using the below list comprehension:
[dcc.Button(x, id={'type': 'thumbnail_button', 'index': x}) for x in thumbnail_list]
Use the pattern-matching callback to open modal when any of these buttons are clicked:
#app.callback(
Output('your-modal', 'is_open'),
[Input({'type': 'thumbnail_button', 'index': ALL}, 'n_clicks')]
)
def handle_button_click(n_clicks):
invoker = [p['prop id'] for p in dash.callback_context.triggered][0]
invoker_type = json.loads(invoker.split('.')[0])['type']
invoker_index = json.loads(invoker.split('.')[0])['index']
if invoker_type == "thumbnail_button":
return not is_open
else:
return is_open
Lastly the imports:
from dash.dependencies import Input, Output, ALL

IPython nbwidgets: Toggle Visiblity by button click

I'm using the ToggleButton and want to link it's value to the visilbiity of another widget.
I've come accross the Widget Events, but it is unclear to me how to bind the style property of the other widget to the value of the ToggleButton.
Has anyone done something similar?
I would use an observe call on the ToggleButton to change the visibility of the other widget. A simple example below.
toggle = ipyw.ToggleButton(description='Toggle visible')
to_hide = ipyw.IntRangeSlider(description = 'hide me')
display(to_hide)
display(toggle)
def hide_slider(widg):
if widg['new']:
to_hide.layout.display = 'none'
else:
to_hide.layout.display = ''
toggle.observe(hide_slider, names=['value'])

PyWinAuto trouble clicking the Next Button

This code gets the first window from InstallShield.
from pywinauto import application
from pywinauto import findwindows
app = application.Application()
app.start("MyInstallShieldApp.exe")
time.sleep(15)
hwnd = findwindows.find_windows(title=u"InstallShield Wizard", class_name="MsiDialogCloseClass")
print ("|", str(hwnd), "|")
dlg = app.Window_(handle=hwnd).Wait("enabled", timeout=25, retry_interval=0.5)
Now I want to click the Next button. Swapy says that the Next button has the text '&Next >' and the Button number is 1. But none of these click statements have any effect.
dlg.Click("Next")
dlg.Click(coords=(977, 711))
dlg.Click(button="left")
You misapply Click method. It has the next signatire - Click(button=u'left', pressed=u'', coords=(0, 0), double=False, absolute=False)
To click a button, click should be performed on the button object. So you shoud navigate to the button at first.
In your case the code may look something like:
dlg['&Next >'].Click()
Again, please do not guess, read the docs and see the examples

Toolbar chevron doesn't expand when QPushbuttons added

I'm attempting to add a set of push buttons to a toolbar that can be toggled. When there are more buttons than can be displayed, the chevron appears, but it is greyed out and doesn't show the remaining contents.
Initially I had:
toolbar = QtGui.QToolbar()
newButton = QtGui.QPushButton('name')
newButton.toggled.connect(myAction)
toolbar.addWidget(newButton)
I read that I need to create a customWidgetAction, so I have tried the following:
toolbar = QtGui.QToolbar()
newButton = QtGui.QPushButton()
widgetAction = QtGui.QWidgetAction(newButton)
widgetAction.toggled.connect(myAction)
newWidget = widgetAction.createWidget(newButton)
toolbar.addWidget(newButton)
However using this code, the button doesn't appear in the toolbar. Any pointers on what I'm doing wrong?
I see the same behavior. If widgets are added to a toolbar and the toolbar cannot display them all, it will display a chevron but the chevron will be greyed out. The chevron will not be greyed out for actions though and they will be displayed in a drop down fashion.
My example just using standard QActions:
from PySide import QtGui
app = QtGui.QApplication([])
tb = QtGui.QToolBar()
#tb.addWidget(QtGui.QPushButton('AAA'))
#tb.addWidget(QtGui.QPushButton('BBB'))
#tb.addWidget(QtGui.QPushButton('CCC')) # will not be shown in case the toolbar is too short, chevron will be greyed
tb.addAction(QtGui.QAction('AAA', tb))
tb.addAction(QtGui.QAction('BBB', tb))
tb.addAction(QtGui.QAction('CCC', tb))
tb.addAction(QtGui.QAction('DDD', tb))
tb.addAction(QtGui.QAction('EEE', tb))
tb.resize(50, 40)
tb.show()
app.exec_()
and if you want to connect the action to something useful the pattern is:
toolbar = QtGui.QToolBar()
action = QtGui.QAction(icon, 'Create new scenario', toolbar)
action.triggered.connect(..)
toolbar.addAction(action)
QWidgetAction seems to be slightly more complex than just QAction although it should work as well. If you do not need the added functionality, rather use QAction and a simple icon.

Categories