I am currently struggling trying to use the panel library in Python, in order to build an interactive dashboard to analyze and display CSV data. My current goal is to let the user enter an initial and a final date, which will be used to filter a DataFrame once a button is pressed. However, whenever I press the button, the on_click function is not completely executed before the script stops running. The code snippet is the following:
import panel as pn
pn.extension()
def acquire_data(dateBeginning, dateEnd):
eventDF = pd.read_csv('multi.csv')
eventDF['Date']= pd.to_datetime(eventDF['Date'])
dateDF = eventDF[eventDF.upvotes > 8]
print(eventDF)
def register_dates(event, save=True):
dateBeginning = date1Picker.value
dateEnd = date2Picker.value
if dateBeginning < dateEnd:
text = pn.widgets.StaticText(name='Static Text', value='A string')
spinner = pn.indicators.LoadingSpinner(width=50, height=50, value=True, color='info', bgcolor='light')
layout = pn.Column(text, spinner, align='center')
layout.app()
print('getting in')
acquire_data(dateBeginning, dateEnd)
print('getting out')
spinner.value = False
else:
print('Not working')
#pn.pane.Alert('## Alert\nThis is a warning!')
return save
date1Picker = pn.widgets.DatePicker(name='Date Initiale', margin=25)
date2Picker = pn.widgets.DatePicker(name='Date Finale', margin=25)
button = pn.widgets.Button(name="Analyse", button_type='primary', margin=(25, 0, 20, 200), width=200)
button.on_click(register_dates)
dateLayout = pn.Row(date1Picker, date2Picker)
layout = pn.Column(dateLayout, button, width=200, align='center')
layout.app()
I was also aiming at having the first layout be replaced by the one with the spinner and the text once the button is pressed, but I haven't found anything in the doc mentioning how to do so. If anyone could give me a hint regarding these issues, that would really help me!
In def acquire_data(dateBeginning, dateEnd):
pd.read_csv('multi.csv'), pd.to_datetime(eventDF['Date'])
For start, in this function I think you forgot to import panda and your app just crash.
add: import pandas as pd
Ex:
import panel as pn
import pandas as pd
Related
I have this very easy program which I want to display one random line from a file each time I click on the Button.
Problem is a new line is display at startup of the program, but nothing happens when I click the button, can someone explain me why ?
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
fen = Tk()
fen.title("lexique anglais politique")
defi = StringVar()
defi.set(entree())
lab = Label(fen, textvariable=defi).pack()
Button(fen, text='Beste Bat', command=entree).pack()
fen.mainloop()
As stated in one of the comments (by #matszwecja), your entree() function doesn't really do anything appart from returning a value.
Nothing in your code updates the actual label. Try something like this :
from random import randrange
from tkinter import *
def entree():
n=randrange(251)
fs = open('lexique','r')
liste = fs.readlines()
return liste[n]
def update_label():
lab.config(text=entree())
fen = Tk()
fen.title("lexique anglais politique")
lab = Label(fen, text=entree())
lab.pack()
Button(fen, text='Beste Bat', command=update_label).pack()
fen.mainloop()
In this example, the entree() function is used to go get a line from your file, and the update_label() function is used to actually update the label.
Also, if you want to be able to update a label, you'll have to pack it after assigning it to a variable.
On a side note, it could be worth noting that hardcoding values that could change in the future is generally considered bad practice. In that regard, I think coding the entree() function this way might be a better idea :
def entree():
fs = open('lexique','r')
liste = fs.readlines()
n=randrange(len(liste))
return liste[n]
This way, if you ever add or remove lines to your "lexique" file, you will not have to change the code.
I am just studying widget interaction in Python and Jupyter. My task is:
t=pd.DataFrame({'string':['i live here','you live in eltham','machine learning','learning english','go home','go back'],
'number':[1,3,2,3,1,2],
'word':['a','haha','runing over there','abcdefg','aaa','bye']})
import ipywidgets as widgets
from IPython.display import display
widgets.Text(
value='Hello World',
placeholder='Type something',
description='keyword:',
disabled=False
)
I need to type in some word, for example 'live', then the code will automatically search the data frame t and display all the rows with live in it.
I am seeking some hints, because I do not know where to start.
finally figure out a simple example. just put it here for someone who might need it.
t=pd.DataFrame({'string':['i live here','you live in eltham','machine learning','learning english','go home','go back','live home'],
'number':[1,3,2,3,1,2,4],
'word':['a','haha','runing over there','abcdefg','aaa','bye','hou']})
def myFUN_searchString(value,string):
s=string.split(' ')
return value in s
def myFUN_search(value):
t.loc[:,'Flag']=''
t.loc[:,'Flag']=[myFUN_searchString(value,x) for x in t.loc[:,'string']]
return t.loc[:,'Flag']
import ipywidgets as widgets
from IPython.display import display
keyword=widgets.Text(
value='electricity',
placeholder='Type something',
description='keyword:',
disabled=False
)
display(keyword)
button = widgets.Button(description="search")
display(button)
output = widgets.Output()
#output.capture()
def on_button_clicked(b):
t.loc[:,'Flag']=myFUN_search(keyword.value)
t1=t.loc[(t['Flag'])]
t1.drop(['Flag'],axis=1,inplace=True)
t1.reset_index(drop=True,inplace=True)
if t1.shape[0]>30:
t1=t1.loc[0:30]
display(t1)
button.on_click(on_button_clicked)
display(output)
I'm doing my first big project which is a quiz. I am stuck on trying to limit the time the user has to answer a question. I've searched and the only option that seems to work is using a timer thread. I'm not familiar with threading or any slightly advanced tkInter at all so I'm all ears.
def revisionMode(question):
inputAnswer = StringVar()
#-----Creation & placement of buttons and labels
qLabel = Label(screen1, text = question.prompt[0]
qLabel.grid(row = 6, column = 2)
answerBox = Entry(screen1, textvariable = inputAnswer)
answerBox.grid(column = 2, row = 10)
t = Timer(7.0, nextQuestion, args=(False, question.difficulty), kwargs=None)
t.start()
#-----The button that will trigger the validation of the answer
Button(screen1, text = "Submit", command = lambda: checkAnswer(question)).grid(column = 3, row = 9)
The error I get from that is that is:
RuntimeError: main thread is not in main loop.
From my understanding and googling, tkinter and threading don't work together very well and I've seen solutions using Queues.
You don't need a timer thread for something this simple. Tkinter widgets have a method named after which can be used to run commands in the future.
For example, to call nextQuestion after 7 seconds, you would do this:
screen1.after(7000, nextQuestion, False, question.difficulty)
If you want to cancel the timer, save the return value and use it in a call to after_cancel:
after_id = screen1.after(7000, nextQuestion, False, question.difficulty)
...
screen1.after_cancel(after_id)
I want that when I go to secondpage and back to the mainpage it removes the buttons created in the mainloop. So when I open the second page again, it needs to make the buttons again. with the updated list
Now it shows the buttons 2 times. I know to not add it into a function it will render just 1 time but the point is it will be removed when it is in a function and it need to remaked every time I open the mainpage. So that I can edit for example the list when the application is open and it will be rendered again.
Here is my code:
from tkinter import *
items = ['ijs', 'water', 'lolly', 'laptop']
root = Tk()
def buttonmaker():
for x in items:
button = Button(master=secondpage, text=x ).pack()
def mainpagetosecondpage():
mainpage.pack_forget()
buttonmaker()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage).pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage).pack()
mainpage.pack()
root.mainloop()
If something is unclear, please ask
You need to call buttonmaker only once, during the setup of the second frame:
from tkinter import *
root = Tk()
def buttonmaker():
items = ['ijs', 'water', 'lolly', 'laptop']
for x in items:
button = Button(master=secondpage, text=x )
button.pack()
def mainpagetosecondpage():
mainpage.pack_forget()
secondpage.pack()
def secondpagetomainpage():
secondpage.pack_forget()
mainpage.pack()
#mainpage
mainpage = Frame(master=root)
main_to_second_button = Button(master=mainpage, text='secondpage', command=mainpagetosecondpage)
main_to_second_button.pack()
#scondpage
secondpage = Frame(master=root)
Second_to_main_button = Button(master=secondpage, text='mainpage', command=secondpagetomainpage)
Second_to_main_button.pack()
buttonmaker()
mainpage.pack()
root.mainloop()
Also, you need to avoid putting the layout on the same line as the initialization. Code like variable = Widget(args).pack() will lead to bugs. Use 2 lines like I did above.
So I'm grabbing links of events off a website and putting them into a drop down menu to be selected. My code for the menu:
import Tkinter as tk
from Tkinter import StringVar
selectMenu = tk.Tk()
# #-> this is what I have
# Followed by what you can use
#var = Vars()
#events = var.GetVars('Event')
events = " "
options = []
links = []
#forms = (driver.find_elements_by_class_name("with-cats")) #This is what I have
forms = ["Yolo ","Dad? Closed","Anotha One","Normies! Closed"] #This is so you can try it for yourself
for x in forms:
#info = x.text
info = x #Again, this is so you can try it for yourself
if events in info.lower():
links.append(x)
for link in range(0,len(links)):
#options.append(links[link].text)
options.append(links[link])
list(set(options))
selection = []
for link in range(0,len(options)):
selection.append(options[link])
select = StringVar(selectMenu)
select.set("--None Selected--")
menu = tk.OptionMenu(selectMenu, select, *(selection))
msg = "Which one would you like to attend?"
label = tk.Label(selectMenu, text=msg, font="Helvedica 14")
label.pack(side='top', pady=10)
menu.pack(side="top", pady=10)
selectMenu.attributes('-topmost', True)
selectMenu.mainloop()
So this works fine and dandy, but I would like to improve the look to make it more obvious which events are open. To clarify, an event found that is open and put into the menu may look like "This is a cool event", but one that is closed would be read as "This is a cool event Closed". My aim is to be able to make the foreground red of either just the word Closed or the string containing Closed, whichever is possible if any (And I'm not sure if it's possible because menus and buttons on osx are usually defaulted to system settings, maybe there is a way around this?).
Current: Desired:
According to the documentation for OptionMenu here and here I don't think there is a way to set the color of text.
You might be able to get something close to what you want by using a listBox instead. See post here for the listBox example.
Found a solution! Using a Menu inside of a MenuButton the same way Tkinter creates MenuOptions, I was able to create a custom MenuOption. If you want to add more options, you can use the menbutton.configure() option to edit the button, and menbutton.menu to edit the menu items.
import Tkinter as tk
from Tkinter import Menu, Menubutton
class Vars():
global vari
vari = {}
def GetVars(self, var):
return vari.get(str(var))
def SendVars(self, var, val):
vari[str(var)] = val
class App():
def buttselect(self, link, menbutton, selectMenu):
var = Vars()
var.SendVars("Selection", link) # Store selected event
menbutton.configure(text=link) # Set menu text to the selected event
def prnt(self, link):
var = Vars()
print var.GetVars("Selection") # Print event
def __init__(self, selectMenu):
events = " "
options = []
links = []
forms = ["Yolo ","Dad? Closed","Anotha One","Normies! Closed"] #This is so you can try it for yourself
menbutton = Menubutton (selectMenu, text="--None Selected--", relief="raised")
menbutton.grid()
menbutton.menu = Menu (menbutton, tearoff=0)
menbutton["menu"] = menbutton.menu
#Get a list of event names
for x in forms:
info = x #Again, this is so you can try it for yourself
#If desired event keyword is in an event name, add it to the correct links
if events in info.lower():
links.append(x)
#Remove duplicates
for link in range(0,len(links)):
options.append(links[link])
list(set(options))
#Final list of event names turned into menu commands
for link in options:
if "Closed" in link:
menbutton.menu.add_command( label= link, command= lambda link=link: self.buttselect(link, menbutton, selectMenu), foreground='red')
else:
menbutton.menu.add_command( label= link, command= lambda link=link: self.buttselect(link, menbutton, selectMenu))
b = tk.Button(selectMenu, text="Selection", command= lambda link=link: self.prnt(link)) #Print selected event
b.pack()
msg = "Which one would you like to attend?"
label = tk.Label(selectMenu, text=msg, font="Helvedica 14")
label.pack(side='top', pady=10)
menbutton.pack(side="top", pady=10)
selectMenu = tk.Tk()
selectMenu.attributes('-topmost', True)
app = App(selectMenu)
selectMenu.mainloop()
This results in exactly the result desired:
I found a way!
Let's say x is an optionmenu with options:
options=['Red','Blue','Green']
defopt=tk.StringVar(options[0]) #StringVariable to hold the selected option.
x=tk.OptionMenu(self.optmenuframe,defopt,*options)
Now, get the menu object from the optionmenu and use entryconfig method. That's it!
x.children['menu'].entryconfig(0,foreground='red')
x.children['menu'].entryconfig(1,foreground='blue')
x.children['menu'].entryconfig(2,foreground='green')
#0 is the index of the option you want to apply the configurations to.