How to create a copy-to-clipboard button in streamlit? - python

This discussion provides a workaround:
import streamlit as st
from bokeh.models.widgets import Button
from bokeh.models import CustomJS
from streamlit_bokeh_events import streamlit_bokeh_events
import pandas as pd
text_to_be_copied = "Some text"
copy_dict = {"content": text_to_be_copied}
copy_button = Button(label="Copy Text")
copy_button.js_on_event("button_click", CustomJS(args=copy_dict, code="""
navigator.clipboard.writeText(content);
"""))
no_event = streamlit_bokeh_events(
copy_button,
events="GET_TEXT",
key="get_text",
refresh_on_update=True,
override_height=75,
debounce_time=0)
However, the button looks different than the usual streamlit buttons and cannot be used like those for example like this:
if st.button("Some button"):
st.write("Some reaction")
Is there a way to modify the streamlit buttons such that they copy a text to the clipboard?

You must use pyperclip library to achieve what you want.
import pyperclip
text_to_be_copied = 'The text to be copied to the clipboard.'
pyperclip.copy(text_to_be_copied)

Related

Python exec(script, globals()) not showing GUI (iPyWidgets)

Hello my fellow programmers,
I want to include a script from another file that creates a UI with iPyWidgets.
The problem is that the code will execute but nothing is shown. I am using Jupyterhub to display the button.
When I run the code on itself, the button is showing.
button_code.py
from ipywidgets import widgets
from IPython.display import display
from IPython.display import clear_output
from ipywidgets import Button, HBox, VBox, Layout, Button, Text, Textarea
widgets.Button(description = 'clear',
layout=Layout(width='20%', height='100%'))
call_button_script.py
import os
def call_script():
script_dir = os.path.dirname(os.path.abspath(__file__))
script_fqn = os.path.join(script_dir, 'button_code.py')
script = open(script_fqn).read()
exec(script, globals())
The code is executed via the following import:
from call_button_script import call_script
call_script()
Am I missing something crucial or is there another possible mistake?
Thank you for your time.
Is your from call_button_script import call_script call_script() in the same directory ? did you've seen the output on the console
The answer was to just import the whole file.
I´ve put it in a class and initialized it all in the init
The GUI wasn´t showing because it was missing display() in the code

curdoc().add_root() causes bokeh plot rendering to fail silently

The bokeh application below is intended to generate a random dataset when the button is pushed. I am trying to serve the app using the bokeh.client style, where there is one session that may be shared between simultaneous viewers.
If I include the line: curdoc().add_root(column(p,button)) the plot will not be in the browser. I get a blank page with happy messages in JS console. If I remove it, I get a static plot, with no button. Can anyone explain what's wrong with my approach here?
I should add that the app works in the other server style with multiple distinct sessions. There I call bokeh serve myapp.py and don't make calls to the session object.
import numpy as np
from bokeh.plotting import figure, curdoc
from bokeh.layouts import column
from bokeh.models import Button
from bokeh.client import push_session, pull_session
points = 100*np.random.rand(3,100)
points_x = points[0].tolist()
points_y = points[1].tolist()
p = figure(x_range=(0,100), y_range=(0,100))
circle_p = p.circle(x = points_x,
y = points_y,
size = 20,
color = "navy",
alpha = 0.5)
ds = circle_p.data_source
#callback function to update circles
def button_callback():
new_data = dict()
new_points = 100*np.random.rand(3,100)
new_data['x'] = new_points[0].tolist()
new_data['y'] = new_points[1].tolist()
new_data['z'] = new_points[2].tolist()
ds.data = new_data
#Add the button widget thingie to trigger the update
button = Button(label="Update")
button.on_click(button_callback)
# Put the button and plot in a layout in document
curdoc().add_root(column(p,button))
#create a session
session = push_session(curdoc())
session.show(p)
session.loop_until_closed()
You just want
session.show()
not
session.show(p)
Because you want to show the whole document, not just the plot. The first version works for me with Bokeh 0.12.6 (the latter also kind of works, but the plot is duplicated twice. My guess is you are using an older version that also had some layout bugs)

Adding objects dynamically in bokeh server application

I would like to add objects dynamically on the bokeh server. The example I am trying to run is the following bokeh server app:
from bokeh.layouts import column
from bokeh.plotting import curdoc
from bokeh.models import Button
def add_button():
print("adding button")
curdoc().add_root(column(button, button2))
button = Button(label="Start", button_type="success")
button.on_click(add_button)
button2 = Button(label="Next", button_type="success")
curdoc().add_root(column(button))
Many thanks for any help.
Did you want to keep adding a new button each time?
if so try this :
from bokeh.layouts import column, layout
from bokeh.plotting import curdoc
from bokeh.models import Button
from bokeh.models.widgets import Div
def add_button():
print("adding button")
layout.children.append(Button(label="Hi I am another button", button_type="success"))
button = Button(label="Click to add a button", button_type="success")
button.on_click(add_button)
layout = layout([[button]])
curdoc().add_root(layout)
If you only wanted to add a new button once, then just append Button2.

Python Bokeh send additional parameters to widget event handler

I want to send additional data to a bokeh event handler (e.g. an on_change or on_click method). A minimal example that increments or decrements an integer is below (I run this app with 'bokeh serve --show app.py). I had to write separate event handlers that do almost identical things in this example. To write this app with just one event handler function, I need to pass additional data or the event handler must know the calling object. How do I do that?
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import widgetbox
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
def minus_callback():
text.text = str(int(text.text) - 1)
def plus_callback():
text.text = str(int(text.text) + 1)
minus.on_click(minus_callback)
plus.on_click(plus_callback)
# I would prefer to just use one callback and pass additional data to it:
# minus.on_click(callback, action='decrement')
# plus.on_click(callback, action='increment')
layout = widgetbox(minus, plus, text)
curdoc().add_root(layout)
The standard functools.partial facility that is built into python works fine with Bokeh callbacks.
from functools import partial
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import widgetbox
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
def callback(foo):
print(foo)
minus.on_click(partial(callback, foo="minus"))
plus.on_click(partial(callback, foo="plus"))
layout = widgetbox(minus, plus, text)
curdoc().add_root(layout)
Extending on #bigreddot's answer, this code outputs the updated value to the text attribute of the Paragraph object.
from functools import partial
from bokeh.plotting import curdoc
from bokeh.models.widgets import Button, Paragraph
from bokeh.layouts import column
def callback(update):
text.text = str(int(text.text) + update)
minus = Button(label='-')
plus = Button(label='+')
text = Paragraph(text='0')
minus.on_click(partial(callback, update=-1))
plus.on_click(partial(callback, update=+1))
layout = column(minus, plus, text)
curdoc().add_root(layout)

Python QtGui Calendar Widget Call button

I am using a calendar widget for Python. And I need to call the widget when a button is clicked.
The situation is that I cannot find what is the method in the calendar class that displays the widget itself.
The calendar class was taken from here:
http://www.eurion.net/python-snippets/snippet/Calendar_Date%20picker.html
Here are my imports:
from tkinter import *
from tkinter import ttk
import tkinter.messagebox
import time
import requests #needs to be installed
import pymysql #needs to be installed
import csv
import win32com.client #needs to be installed
from calendar import Calendar
import datetime
Here is the button creation:
# Calendar Buttons
calBut=ttk.Button(f2, width=4, text="Cal", command=Calendar.what_method?).grid(column=3,row=1, sticky=W)
As far as I know, I can just set the command of the button to call the widget display method located in the calendar class.
How to get the method that displays the calendar widget each time my button is clicked? None of the ones showing are displaying the widget.
Using Python 3.3.5
Spider
WinPython 3.3.5
**EDIT**
The program has tabs and the f2 indicates the tab where the button will be.
from tkinter import *
from tkinter import ttk
import tkinter.messagebox
import time
import requests #needs to be installed
import pymysql #needs to be installed
import csv
import win32com.client #needs to be installed
import datetime
from calendar import Calendar
import calendar
#################################
# Create Button Click Calendar
def callback():
root2=Toplevel(f2)
ttkcal = Calendar(root2,firstweekday=calendar.SUNDAY)
ttkcal.pack(expand=1, fill='both')
root2.update()
root2.minsize(root2.winfo_reqwidth(), root2.winfo_reqheight())
# Calendar Buttons
b=ttk.Button(f2, width=4, text="Cal", command=callback).grid(column=3,row=1, sticky=W)
When I press the button, it opens the calendar window, but it is empty. And the console gives me error:
TypeError: __init__() got multiple values for argument 'firstweekday
Thank you
Not so easy. The problem is that you mix the two GUI libraries. Therefore it is necessary two main event loops (at least): one for Tkinter code and one for PyQt code.
One way to do what you want - using subprocess and threading modules to run calendar.py in different thread. Example:
from tkinter import *
from tkinter import ttk
import subprocess
import threading
master = Tk()
def callback():
subprocess.call('python calendar.py')
b=ttk.Button(master, width=4, text="Cal", command=lambda:threading.Thread(target=callback).start()).grid(column=3,row=1, sticky=W)
mainloop()
Another way - creating Qt main event loop inside callback function (dirty solution):
from tkinter import *
from tkinter import ttk
from calendar import Calendar
import sys
from PyQt4 import QtGui
master = Tk()
def callback():
app = QtGui.QApplication(sys.argv)
gui = Calendar()
gui.show()
app.exec_()
b=ttk.Button(master, width=4, text="Cal", command=callback).grid(column=3,row=1, sticky=W)
mainloop()
EDIT: How to call widget.
First of all, look at this answer, and modify your ttkcalendar.py as kalgasnik suggested. Then try this:
from tkinter import *
from tkinter import ttk
from ttkcalendar import Calendar
import calendar
master = Tk()
def callback():
root2=Toplevel(master)
ttkcal = Calendar(root2,firstweekday=calendar.SUNDAY)
ttkcal.pack(expand=1, fill='both')
root2.update()
root2.minsize(root2.winfo_reqwidth(), root2.winfo_reqheight())
b=ttk.Button(master, width=4, text="Cal", command=callback).grid(column=3,row=1, sticky=W)
mainloop()
EDIT 2. Solving the problems
Ok, it seems I found all problems.
Actually, you import twice the same module - standard calendar module:
from calendar import Calendar
import calendar
But you do not import the class Calendar from ttkcalendar module (Do not forget to change it as described
here).
So, import should look like this:
import ttkcalendar
import calendar
Creating calendar (I changed the code a bit for clarity):
ttkcal = ttkcalendar.Calendar(root2,firstweekday=calendar.SUNDAY)
In your code, the main window is initialized twice:
line 15: master = Tk()
line 960: root = Tk()
You need to remove the first initialization.
You mix pack() and grid() in the same master window. According the docs, it is a bad idea:
Warning: Never mix grid and pack in the same master window. Tkinter
will happily spend the rest of your lifetime trying to negotiate a
solution that both managers are happy with. Instead of waiting, kill
the application, and take another look at your code. A common mistake
is to use the wrong parent for some of the widgets.
So, instead nb.pack(fill='both', expand='yes') you have to write something like this
nb.grid(column=0, row=0, sticky=(W, E))
Finally, here are links to the fixed code:
ttkcalendar.py (already modified, ready to use): https://gist.github.com/anonymous/5e0d973f57e185572df2
Your script with described modifications:
https://gist.github.com/anonymous/65cb808dc64e414c0c12

Categories

Resources