I am building a FLET app, but sometimes I have a datatable which is too large for the frame it is in. For the row I have a scrollbar appearing, but for the column I just don't seem to get it working.
In this code a scrollbar simply does not appear.
import pandas as pd
pd.options.display.max_columns = 100
from services.bag import PCHN
from utils.convertors import dataframe_to_datatable
import flet as ft
def main(page: ft.page):
def bag_service(e):
pc = '9351BP' if postal_code_field.value == '' else postal_code_field.value
hn = '1' if house_number_field.value == '' else house_number_field.value
address = PCHN(pc,
hn).result
bag_container[0] = dataframe_to_datatable(address)
page.update() # This is not updating my bag_table in place though. It stays static as it is.
# define form fields
postal_code_field = ft.TextField(label='Postal code')
house_number_field = ft.TextField(label='House number')
submit_button = ft.ElevatedButton(text='Submit', on_click=bag_service)
# fields for the right column
address = PCHN('9351BP', '1').result
bag_table = dataframe_to_datatable(address)
bag_container = [bag_table]
# design layout
# 1 column to the left as a frame and one to the right with two rows
horizontal_divider = ft.Row
left_column = ft.Column
right_column = ft.Column
# fill the design
page.add(
horizontal_divider(
[left_column(
[postal_code_field,
house_number_field,
submit_button
]
),
right_column(
[
ft.Container(
ft.Row(
bag_container,
scroll='always'
),
bgcolor=ft.colors.BLACK,
width=800,)
],scroll='always'
)
]
)
)
if __name__ == '__main__':
ft.app(target=main,
view=ft.WEB_BROWSER,
port=666
)
I am lost as to what could be the case here. Any help would be much appreciated.
Related
I am looking for a clever/interactive way to modify the wrong values in a database by clicking on the plotly graph showing them. In other words, I want to add a sort of data modification zone, preferably on top of my plotly graphs (maybe a form with submit button or so ... ? )
For now, I am able to access the data point informations when clicking on it using the clickData property of the graph.
Here is a simplified version of the callback function I used.
#app.callback(
Output('output', 'children'),
[Input('graph_interaction', 'clickData')])
def update_output(clickData):
if clickData:
point_index = clickData['points'][0]['pointIndex']
point_value = clickData['points'][0]['y']
print(clickData)
# update the point value in the database using SQLAlchemy
# ...
return 'You clicked on data point {} with value {}'.format(point_index, point_value)
return ''
Any insights on how to add a modification area (form ?) to interact with the database and modify wrong values ?
Thank you
I've written a dash app that allows the user to interactively select and deselect data points to remove from a sample dataset. The main considerations are the following:
we should use dcc.Store to store data because global variables will break your app (see this example on storing data in the documentation). we can store both the dataframe (in the form of a dictionary with the index as keys, which will guarantee uniqueness), and also store the index of the points we click
clicking points on the figure will update the clicked points we store, and also populate a textbox so the user can see which points they are removing. clicking the same point again will remove that point from storage (and the textbox)
there are two buttons: the update button will remove the clicked points from the stored points and update the figure. there is also a button to clear all points we want to remove from storage and the textbox (this is because it appears dash cannot process selecting and then immediately deselecting a point, so we'll use this button instead)
import numpy as np
import pandas as pd
import plotly.express as px
import dash
from dash import Input, Output, dcc, html, ctx
from typing import List
app = dash.Dash(__name__)
df = pd.DataFrame({
'x': list(range(5,10)),
'y': list(range(1,6))
})
fig = px.scatter(df, x='x', y='y')
app.layout = html.Div([
html.Div(
[
dcc.Textarea(id='selected-points-textbox'),
html.Br(),
html.Button('Clear Selection', id='clear-textbox', n_clicks=0),
html.Button('Update Data', id='update-data', n_clicks=0),
],
style={"padding-left": "80px"},
),
html.Div([
dcc.Graph(figure=fig, id='graph-interaction'),
dcc.Store(id='store-selected-points'),
dcc.Store(id='store-data')
])
])
#app.callback(
Output('store-selected-points','data'),
Output('store-data','data'),
Output('selected-points-textbox','value'),
Output('graph-interaction','figure'),
[Input('graph-interaction', 'clickData'),
Input('clear-textbox', 'n_clicks'),
Input('update-data', 'n_clicks'),
Input('store-selected-points','data'),
Input('store-data','data'),
Input('graph-interaction','figure'),
])
def show_selection(clickData, clearButton, updateButton, storedSelectedPoints, storedData, fig):
## initialize storedSelectedPoints and storedData
## we will store the pointIndex in the storedSelectedPoints
if storedSelectedPoints is None:
storedSelectedPoints = []
if storedData is None:
storedData = df.to_dict('index')
## storedData is in the following format:
# {
# 0: {'x': 5, 'y': 1},
# 1: {'x': 6, 'y': 2},
# 2...
# }
if ctx.triggered_id == "clear-textbox":
storedSelectedPoints = []
storedSelectedPointsText = '[]'
elif ctx.triggered_id == "update-data":
for p in storedSelectedPoints:
del storedData[p]
storedSelectedPoints = []
storedSelectedPointsText = '[]'
print(f"storedData with points removed: {storedData}")
df_new = pd.DataFrame.from_dict(storedData, orient='index')
fig = px.scatter(df_new, x='x', y='y')
## update the point value in the database using SQLAlchemy
elif clickData is not None:
## note that these index values will need to be strings
point_index = str(clickData['points'][0]['pointIndex'])
if point_index not in storedSelectedPoints:
storedSelectedPoints.append(point_index)
else:
storedSelectedPoints.remove(point_index)
storedSelectedPointsText = str(
[[storedData[p]['x'], storedData[p]['y']] for p in storedSelectedPoints]
)
return storedSelectedPoints, storedData, storedSelectedPointsText, fig
storedSelectedPointsText = str(storedSelectedPoints)
return storedSelectedPoints, storedData, storedSelectedPointsText, fig
if __name__ == "__main__":
app.run_server(debug=True)
I created a form with two drop down menu's:
My goal is to make one dropdown dependent on the other dropdown.
This picture illustrates my goal and the current situation.
The sample code below can be run in Google Colab or Jupyter notebook to replicate the current situation.
##title
import ipywidgets as widgets
from ipywidgets import HBox, Label
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
import time
import pandas as pd
#Create DF
df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
df
# Layout
form_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between',
)
button_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='center',
padding = '5%'
)
# Independent dropdown item
drop_down_input = 'Dropdown_input_1'
drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
drop_down.observe(dropdown_handler, names='value')
# Dependent drop down
# Dependent drop down elements
dependent_drop_down_elements = {}
dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E']
# Define dependent drop down
dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
drop_down.observe(dropdown_handler, names='value')
# Button
button = widgets.Button(description='Add row to dataframe')
out = widgets.Output()
def on_button_clicked(b):
global df
button.description = 'Row added'
time.sleep(1)
with out:
new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
df = df.append(new_row, ignore_index=True)
button.description = 'Add row to dataframe'
out.clear_output()
display(df)
button.on_click(on_button_clicked)
# Form items
form_items = [
Box([Label(value='Independent dropdown'),
drop_down], layout=form_item_layout),
Box([Label(value='Dependent dropdown'),
dependent_drop_down], layout=form_item_layout)
]
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 1px',
align_items='stretch',
width='30%',
padding = '1%'
))
display(form)
display(out)
Is this possible? If so, can you explain how?
You can do this by setting a if statement in your dropdown_handler function that checks if the value chosen in your independent dropdown is Dropdown_input_1 or Dropdown_input2. Still in the dropdown_handler, you can then modify accordingly the options arguments to either dependent_drop_down_elements['Dropdown_input_1'] or dependent_drop_down_elements['Dropdown_input_2'].
See code below:
import ipywidgets as widgets
from ipywidgets import HBox, Label
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
import time
import pandas as pd
#Create DF
df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
df
# Layout
form_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='space-between',
)
button_item_layout = Layout(
display='flex',
flex_flow='row',
justify_content='center',
padding = '5%'
)
# Independent dropdown item
drop_down_input = 'Dropdown_input_1'
drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))
# Dependent drop down
# Dependent drop down elements
dependent_drop_down_elements = {}
dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E']
# Define dependent drop down
dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))
def dropdown_handler(change):
global drop_down_input
print('\r','Dropdown: ' + str(change.new),end='')
drop_down_input = change.new
#If statement checking on dropdown value and changing options of the dependent dropdown accordingly
if change.new=='Dropdown_input_2':
dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_2']
elif change.new=='Dropdown_input_1':
dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_1']
drop_down.observe(dropdown_handler, names='value')
# Button
button = widgets.Button(description='Add row to dataframe')
out = widgets.Output()
def on_button_clicked(b):
global df
button.description = 'Row added'
time.sleep(1)
with out:
new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
df = df.append(new_row, ignore_index=True)
button.description = 'Add row to dataframe'
out.clear_output()
display(df)
button.on_click(on_button_clicked)
# Form items
form_items = [
Box([Label(value='Independent dropdown'),
drop_down], layout=form_item_layout),
Box([Label(value='Dependent dropdown'),
dependent_drop_down], layout=form_item_layout)
]
form = Box(form_items, layout=Layout(
display='flex',
flex_flow='column',
border='solid 1px',
align_items='stretch',
width='30%',
padding = '1%'
))
display(form)
display(out)
As an example, this is what the output looks like when you pick Dropdown_input_2 on the first widget:
Code:
fields = ['Label1','Label2','Label3','Label4','Label5','Label6']
selected_item = ['Entry1','Entry2','Entry3','Entry4','Entry5','Label6']
stringvar_list = [tk.StringVar() for s in range(len(selected_item))]
for var in range(len(stringvar_list)):
stringvar_list[var].set(selected_item[var])
for i in range(len(fields)):
lbl = ttk.Label(root,text=fields[i])
ent = ttk.Entry(root,textvariable=stringvar_list[i])
lbl.grid(row=row,column=col,padx=2,pady=2)
ent.grid(row=row,column=col+1,padx=2,pady=2)
row+=1
When I run this code, I get this(the entries are not filled):
But when I run the same code in debugging mode,no changes - I just skip through the breakpoint, I get this(Entries are filled):
I'm using VS Code, if it matters.
Extended Code:
def modify(fields):
# fields = fields[1:] #Original
fields = ['Label1','Label2','Label3','Label4','Label5','Label6'] # For Simplicity and Underestanding
# selected_item = tree.item(tree.selection()[0])['values'][1:] #Original
selected_item = ['Entry1','Entry2','Entry3','Entry4','Entry5','Label6'] # For Simplicity and Underestanding
modify_top = tk.Toplevel(root)
stringvar_list = [tk.StringVar() for s in range(len(selected_item))]
for var in range(len(stringvar_list)):
stringvar_list[var].set(selected_item[var])
row = 0
col = 0
frm = ttk.Frame(modify_top)
for i in range(len(fields)):
lbl = ttk.Label(frm,text=fields[i])
ent = ttk.Entry(frm,textvariable=stringvar_list[i])
lbl.grid(row=row,column=col,padx=2,pady=2)
ent.grid(row=row,column=col+1,padx=2,pady=2)
row+=1
frm.grid(sticky='nsew',row=0,column=0,padx=30,pady=20)
make_changes_btn = ttk.Button(modify_top,text='Make Changes',command = lambda : make_changes())
make_changes_btn.grid(row=1,column=0)
Issues:
As #acw1668 commented, making stringvar_list global did the job, but I'm referencing it's values in the same function, so that shouldn't be a problem.
Even if the issue is with the scope, then why does the program work fine in debugging mode?
import requests
import bs4
from tkinter import *
def statno():
covidTk = Tk()
covidTk.title('Data statistics of COVID-19')
web = requests.get('https://www.worldometers.info/coronavirus')
objSoup = bs4.BeautifulSoup(web.text,'lxml')
lb1 = Label(covidTk,text='Country: ')
lb1.grid(row=1,column=0,padx=10,pady=10)
lb2 = Label(covidTk,text=entcon.get())
lb2.grid(row=1,column=1,padx=10,pady=10)
table = objSoup.find('table',attrs={'id':'main_table_countries_today'})
headings = [th.get_text() for th in table.find('tr').find_all('th')]
set_of_datas = []
for row in table.find_all('tr')[1:]:
data = dict(zip(headings,(td.get_text() for td in row.find_all('td'))))
set_of_datas.append(data)
print(set_of_datas)
win = Tk()
win.title('COVID-19 tracker')
web = requests.get('https://www.worldometers.info/coronavirus')
objSoup = bs4.BeautifulSoup(web.text,'lxml')
lbtitle = Label(win,text='Covid-19 Statistics')
lbtitle.grid(row=0,columnspan=2)
lbcon = Label(win,text='Country: ')
lbcon.grid(row=1,column=0,padx=10,pady=20)
conname = StringVar()
entcon = Entry(win,textvariable=conname)
entcon.grid(row=1,column=1,padx=10,pady=20)
btncheck = Button(win,text='Check data',command=statno)
btncheck.grid(row=2,column=1,padx=10,pady=10)
lbcase = Label(win,text='Coronavirus Cases: ')
lbcase.grid(row=3,column=0)
total = objSoup.find_all('div',{'class':'maincounter-number'})
total_cases = total[0]
lbdat1 = Label(win,text=total_cases.text)
lbdat1.grid(row=3,column=1)
lbdeaths = Label(win,text='Deaths: ')
lbdeaths.grid(row=4,column=0)
total_deaths = total[1]
lbdat2 = Label(win,text=total_deaths.text)
lbdat2.grid(row=4,column=1)
lbcase = Label(win,text='Recovered: ')
lbcase.grid(row=5,column=0)
total_recover = total[2]
lbdat3 = Label(win,text=total_recover.text)
lbdat3.grid(row=5,column=1)
When I print(set_of_datas) the values come out are not encoded well. Any methods to make the value appear without garbled strings? I may want to ask do I need to use intersecting of rows and columns? How to refer the data by using the table of the country with the number statistics together? Because I do not know how to get the corresponding datas if I type the country name and clicked the button. Can any people teach me the method to refer data in the table?
How would I add another event to the pane created in our GUI manager class (below). I want to detect when the x button closes the pane. I've tried using the same format as wx.EVT_MENU for wx.EVT_CLOSE but it didn't work.
def popup_panel(self, p):
"""
Add a panel object to the AUI manager
:param p: panel object to add to the AUI manager
:return: ID of the event associated with the new panel [int]
"""
ID = wx.NewId()
self.panels[str(ID)] = p
self.graph_num += 1
if p.window_caption.split()[0] in NOT_SO_GRAPH_LIST:
windowcaption = p.window_caption
else:
windowcaption = 'Graph'#p.window_caption
windowname = p.window_name
# Append nummber
captions = self._get_plotpanel_captions()
while (1):
caption = windowcaption + '%s'% str(self.graph_num)
if caption not in captions:
break
self.graph_num += 1
# protection from forever-loop: max num = 1000
if self.graph_num > 1000:
break
if p.window_caption.split()[0] not in NOT_SO_GRAPH_LIST:
p.window_caption = caption
#p.window_caption = windowcaption+ str(self.graph_num)
p.window_name = windowname + str(self.graph_num)
style1 = self.__gui_style & GUIFRAME.FIXED_PANEL
style2 = self.__gui_style & GUIFRAME.FLOATING_PANEL
if style1 == GUIFRAME.FIXED_PANEL:
self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
Name(p.window_name).
Caption(p.window_caption).
Position(10).
Floatable().
Right().
Dock().
MinimizeButton().
Resizable(True).
# Use a large best size to make sure the AUI
# manager takes all the available space
BestSize(wx.Size(PLOPANEL_WIDTH,
PLOPANEL_HEIGTH)))
self._popup_fixed_panel(p)
elif style2 == GUIFRAME.FLOATING_PANEL:
self._mgr.AddPane(p, wx.aui.AuiPaneInfo().
Name(p.window_name).Caption(p.window_caption).
MinimizeButton().
Resizable(True).
# Use a large best size to make sure the AUI
# manager takes all the available space
BestSize(wx.Size(PLOPANEL_WIDTH,
PLOPANEL_HEIGTH)))
self._popup_floating_panel(p)
# Register for showing/hiding the panel
wx.EVT_MENU(self, ID, self.on_view)
if p not in self.plot_panels.values() and p.group_id != None:
self.plot_panels[ID] = p
if len(self.plot_panels) == 1:
self.panel_on_focus = p
self.set_panel_on_focus(None)
if self._data_panel is not None and \
self._plotting_plugin is not None:
ind = self._data_panel.cb_plotpanel.FindString('None')
if ind != wx.NOT_FOUND:
self._data_panel.cb_plotpanel.Delete(ind)
if caption not in self._data_panel.cb_plotpanel.GetItems():
self._data_panel.cb_plotpanel.Append(str(caption), p)
return ID
I want to be able to pick up the event in the plotting child class.
def create_panel_helper(self, new_panel, data, group_id, title=None):
"""
"""
## Set group ID if available
## Assign data properties to the new create panel
new_panel.set_manager(self)
new_panel.group_id = group_id
if group_id not in data.list_group_id:
data.list_group_id.append(group_id)
if title is None:
title = data.title
new_panel.window_caption = title
new_panel.window_name = data.title
event_id = self.parent.popup_panel(new_panel)
#remove the default item in the menu
if len(self.plot_panels) == 0:
pos = self.menu.FindItem(DEFAULT_MENU_ITEM_LABEL)
if pos != -1:
self.menu.Delete(DEFAULT_MENU_ITEM_ID)
# Set UID to allow us to reference the panel later
new_panel.uid = event_id
# Ship the plottable to its panel
wx.CallAfter(new_panel.plot_data, data)
self.plot_panels[new_panel.group_id] = new_panel
# Set Graph menu and help string
helpString = 'Show/Hide Graph: '
for plot in new_panel.plots.itervalues():
helpString += (' ' + plot.label + ';')
self.menu.AppendCheckItem(event_id, new_panel.window_caption,
helpString)
self.menu.Check(event_id, IS_WIN)
wx.EVT_MENU(self.parent, event_id, self._on_check_menu)
wx.EVT_CLOSE(self.parent, event_id, self._on_close_panel)
wx.EVT_SHOW(new_panel, self._on_show_panel)
Did you try catching wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE or wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED? I would think that would do what you want. I am assuming you're using wx.aui rather than wx.agw.aui. I suspect the latter is similar though.
EDIT: Oops. I read this wrong. I thought the OP wanted to know about AUINotebook. The event you're probably looking for is wx.aui.EVT_AUI_PANE_CLOSE