I am working with the DASH library, and my app now has multiple tabs, but I need to make each tab have a different URL, has anyone accomplish that?
If you want to change the URL shown in the address bar when a new tab is selected, you can add a dcc.Location component and use dcc.Link components to select your tabs. Your dcc.Location component is the input for your tab change.
The example in the official docs does this:
https://dash.plot.ly/urls
As suggested by #papalagi here, the only method that worked for me is to use two dcc.Location components (one that works as "input" url and one as "output" url) and write two callbacks:
One that is fired when the url (dcc.Location) is changed and updates the selected tab (dcc.Tabs)
Another one that is fired when the selected tab changes (dcc.Tabs) and updates the displayed url (dcc.Location)
Since this scenario implies a circular dependency, it explains the need two dcc.Location objects.
Looking at code, I implemented a solution where each tab has its own hash within the same page but this approach could be changed to update whole paths instead of hashes.
Firstly, in the layout of the app I include two dcc.Location components.
app.layout = dhc.Div([
dcc.Location(id='url', refresh=False),
dcc.Location(id='url-output', refresh=False),
dhc.Div(id='page-content')
])
Then, I write the callbacks:
#app.callback(
inputs=[Input('url', 'hash')],
output=Output('main-tabs', 'value'))
def update_tab(hashh):
print('>>> UPDATE TAB', hashh)
return hash_tabs_value_dict.get(hashh.lstrip('#'), 'tab-1')
#app.callback(
inputs=[Input('main-tabs', 'value')],
output=Output('url-output', 'hash'))
def update_tab_hash(tab_value):
print('>>> UPDATE HASH', tab_value)
return '#' + tabs_value_hash_dict.get(tab_value, '')
P.S. In hash_tabs_value_dict and tabs_value_hash_dict I have a couple of dictionaries that store the mapping between the tabs values ('tab-1', 'tab-2', ...) and the desired values that I want to show.
Related
stackoverflow!
It's my first question, I was just a reader before
I'm trying to make an app in python using PyQt5: I need to get data from DB into a table, every row has an EDIT button, when you push it, fields of the row become editable and you can change the data and the EDIT button changes to SAVE button. When you push it , data should be saved and be sent to database(I didn't made a "commit to DB" function yet) but its not saving and comes to previous amounts every time I click SAVE.
When I click EDIT button, it takes a cell text and than replace the widget in a cell by the same , but editable version(just change an option to EDITABLE = TRUE is not working), and it makes it for the whole row.
The save button should make the same, but it makes cells UNEDITABLE again...
tell me why it is so?
my function on SAVE button is
def ButtonSaveClicked(self):
s = self.sender()
roww = int((s.text().split())[1]) - 1
print('SAVE')
# replacing the existing active widgets by inactive with a same text
q = self.ui.tableWidget.item(roww, 0).text()
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 0, self.ui.btn_sell)
q = self.ui.tableWidget.item(roww, 1).text()
print(q)
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 1, self.ui.btn_sell)
q = self.ui.tableWidget.item(roww, 2).text()
print(q)
self.ui.btn_sell = QtWidgets.QLineEdit(q)
self.ui.btn_sell.setEnabled(False)
self.ui.tableWidget.setCellWidget(roww, 2, self.ui.btn_sell)
self.ui.btn_sell = QtWidgets.QPushButton("Edit " +str(roww+1))
self.ui.btn_sell.setEnabled(True)
self.ui.tableWidget.setCellWidget(roww, 3, self.ui.btn_sell)
self.ui.btn_sell.clicked.connect(self.ButtonEditClicked)
enter image description here
I will attempt to answer your question, but I think you may also want to consider the following alternative:
You can set your table to have Edit Triggers instead of you needing to keep track of the state of a QPushButton and keep replacing items and copying data back and forth between them.
My suggestion would be to use the QAbstractItemView::DoubleClicked edit trigger and you can apply that to your QTableWidget like this:
self.tableWidget.setSelectionBehavior(QAbstractItemView::SelectItems)
self.tableWidget.setSelectionMode(QAbstractItemView::SingleSelection)
self.tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked)
If the user double clicks on a cell, that cell becomes editable.
setSelectionBehavior() lets you choose whether you want each cell, row or column to be selected when clicked.
setSelectionMode() determines whether one (cell,row,column) or many (cells,rows,columns) can be selected at once. I think in your case you want to select one cell at a time.
This way the user can double click the cells they wish to edit and then you can have a "Save" button at the bottom of the widget which saves all the changes.
I will try to reproduce your problem above, but I wanted to bring this functionality to your attention as it may save you a lot of headaches.
(My first question and post here)
Here's my situation:
I have to program a data displaying software on Python dash/plotly. And I am stuck at the part where I have to transfer data from one page of my app.py to the other one (for now offline). Both two Dash layout pages are defined in app.py. The structure is basically the same like the one the third example code in that dash/plotly docu page (https://dash.plotly.com/urls), so I have url_bar_and_content_div defined and assigned to app.layout, and I have layout_page1 and layout_page2, as well as app.validation_layout in which all these layouts are listed in the *html.Div()... *
(I can't show concrete code because it's for my internship)
I am able to redirect from page 1 to page 2, right now I'm coding the Divs and callbacks of page 2. On page 2, I want to submit and store data of any file I work with, preferably in a pd.DataFrame or in a dict. And I'm picturing it more or less, that I use an html.Button() to "Submit", which after being clicked finally saves the data, "transfers" it to page 1 and redirects the user to page 1 too. And so here's where I am stuck now: How do I transfer that saved/submitted data to layout_page_1? How do I even make page 2 store and submit data in a df or smth using callbacks to begin with?? I know that using global variables in callbacks is not possible, and I have also ofc looked at the dash docu page for this matter (https://dash.plotly.com/sharing-data-between-callbacks). It hasn't really helped me though, like I didn't know where I should approach my issue with this information, and I also didn't really understand many things. I have never used the dcc.Store()-component before, I am not a pro/expert at Python programming, and this is my first internship in which I am working with Dash for the first time...
So how do I approach this problem? Do I have to add dcc.Store() to both layout-Divs? Do they have to have the same id, must they have to be named "memory","local" or "session", or is it irrelevant? How would I "connect" those two Store-components then, how can I make these two layouts "communicate" with each other and enable smooth data transition from page 2 to page 1 and vice versa??
I'd be so much thankful if someone here would help me out on this, I hope I'll get a response soon, because I have to finish that task in a few weeks. Thanks in advance and cheers!
Edit: #PeterWood Alright, so again, I'll explain what I have to do and I'll use some code to illustrate my problem better!
I have my main app program which I always execute, and it contains the following layouts:
app = dash.Dash(__name__)
url_bar_and_content_div = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
]) # just like in https://dash.plotly.com/urls
layout_page1 = html.Div([ ...
,html.Div(id="data-display",
children=[],...),
"""this is basically the place where the data from page 2 should be loaded (no plot, just saved text and value information)
"""
])
layout_page2 = html.Div([
...
dcc.Textarea(id,...),
dash_table.DataTable( id=...,... ), # contains values and data which I want to work with/save
html.Button(id,...children='Submit', ....) # the Submit Button
...
])
# index layout
app.layout = url_bar_and_content_div
# complete layout
app.validation_layout = html.Div([
url_bar_and_content_div,
layout_page1,
layout_page2
])
# **Again** , the layout-structure is the **same** as that one in the 3rd code example in https://dash.plotly.com/urls
# callbacks etc.
So page 2 consists of a DataTable which mainly consists of text descriptions and values (of many types, str, float, int, NaN etc.). Whenever I click on a line/row on the DataTable, its text gets displayed on the Textarea-field above. And when I click on the "Submit"-button, all the displayed text and values should then be finally "saved" and somehow redirected to page 1 and displayed on the 'data-display'-component (see code again).
My question: How do I do that? How do I program a temporary df which saves text from layout_page2 and displays it on layout_page1?
Here the callback which does the data display on dcc.Textarea when a not-empty DataTable-cell gets clicked:
#app.callback([Output('textarea-p2','value'), # the Text-block where the text from DataTable gets shown
Output('datatable-p2','selected_cells'),
Output('datatable-p2','active_cell')]
[Input('datatable-p2','active_cell')],
[State('datatable-p2','data'),
State('textarea-p2','value')])
def update_data_display(active_cell, data_cells, displayed_text):
if dash.callback_context.triggered[0]['prop_id'].split('.')[0] == 'datatable-p2':
...
if not pd.isnull(data_cells[row][col]):
content = text on that 'row' of DataTable + '\n' # work with active_cells and a df = pd.DataFrame(data_cells)
...
displayed_text += content
return displayed_text, [], None
#"[], None" so that every time I click on a cell it gets unmarked again, just for clarification
What should I change on that callback update_data_display for the next step of sending the Text-block-data to 'data-display' on page 1?
And also how should my callback on the "Submit"-button look like then?
Should both of my layout_pagex have a dcc.Store()-component? So how do I proceed with it now?
I am trying to make my dash datatable interactive with my Mapbox. So when I click on “A” highlighted as the image shown below, it should show me the latitude/longitude of the point on mapbox. Correct me if I am wrong but I need to use the callback function - clickData. But I tried a few times using clickData but it did not work. Was wondering if there is any code I can refer to or any website out there that talks about dash datatable interactive with clickData . Thank you!
This is my table:
This is my coding for mapbox:
fig = px.scatter_mapbox(df4_1.to_dict('records'), lat="Latitude", lon="Longitude", hover_name="Name", hover_data=["Id"],color_discrete_sequence=["purple"], zoom=9, height=450)
fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
clickData is not a property of a dash datatable, what you have, is an active_cell property. There are examples on how to use the active cell to update plots. The caveat is that you don't get the value inside of the selected cell, but the row, and column id. This active_cell is not reliable if you have sorting enabled, or editing.
They give an example on how to get the value if you have a static datasource(df), but If you don't have a static datasource then you need to use a sort of data sharing solution between callbacks like a dcc.Store. https://dash.plotly.com/datatable/interactivity
Anyway at this point the problem just increased in complexity so I would suggest a different simpler model:
In that case I would say to separate the mapbox and the datatable entirely and just have a text input that generates the plot and have a simple callback:
#put this before your mapbox
dcc.Input(
id="text_input_id",
type="text,
placeholder="input name of mapbox")
#app.callback(
Output("out-all-types", "children"),
Input("text_input_id", "value"),
)
def render_mapbox(val):
data=get_data(val) #do your stuff, get the data, make the plot
fig= ....
return fig
Given the following table
class ProductsTable(Table):
allow_sort=True
id=Col('ID', show=False)
price=Col('Price')
available_online=Col('Available online?')
available_num=Col('In stock')
edit=ButtonCol('Edit', url_kwargs=dict(id='id'), endpoint='/products')
def sort_url(self, col_id, reverse=False):
if reverse:
order = 'desc'
else:
order = 'asc'
return '?sort={}&order={}'.format(col_id, order)
From this I get the following example paths:
http://localhost:5000/products?sort=price&order=asc
http://localhost:5000/products?sort=available_num&order=asc
I use the parameters to generate an SQL query, which I execute on my SQLite DB and render the respective sorted table.
Now my issue comes from the reverse argument in my sort_url. I am unable to find any example that doesn't have it as optional argument and hence I'm unable to find anything that tells me how this argument can be set.
Of course I can always alter the URL. For the two examples above this would mean
http://localhost:5000/products?sort=price&order=desc
http://localhost:5000/products?sort=available_num&order=desc
However I want the order to change whenever the user clicks on the head of the specific table column.
How do I do that? Do I have to employ the actual HTML (and also add JavaScript) or is it possible to do it through Flask. Tables I've seen online normally have the arrows up/down symbol
that, whenever clicked, toggle the sorting order.
This involves JS and HTML modifications. I'd like to stick to Flask and Python if possible. The way Flask tables currently seem to work is a click cannot toggle the value of reverse.
Even if I expose the reverse parameter to the constructor of my own table like this
def __init__(self, items, reverse):
super().__init__(items, sort_reverse=reverse)
so that I can set it depending on the value extracted from the URL, my question how to actually set the value remains unanswered.
I'm building a small PyGTK application and I have an text input field (currently a ComboBoxEntry) which is populated with a few values that the user should be able to choose from.
I think what I want to do is to filter out the matching fields and only show those ones so the user using the keyboard arrows can choose one of the matching ones.
To give some background the predefined values are a bunch of urls and the user should be able to choose from theese or fill in a new one.
Example:
the predefined urls:
http://www.google.com
http://www.google.com/android
http://www.greatstuff.com
http://www.facebook.com
When a user types 'http://www.g'
The three URLs starting with that string is to be shown (in some way) and when typeing 'http://www.goog' the two starting with that is to be shown
Any Ideas?
An Entry with an EntryCompletion seems more appropriate than a ComboBoxEntry. As always, the tutorial is a good start.
It's very easy to set up when the predefined URLs list is small and fixed.
You just need to populate a ListStore:
# simplified example from the tutorial
import gtk
urls = [
'http://www.google.com',
'http://www.google.com/android',
'http://www.greatstuff.com',
'http://www.facebook.com',
]
liststore = gtk.ListStore(str)
for s in urls:
liststore.append([s])
completion = gtk.EntryCompletion()
completion.set_model(liststore)
completion.set_text_column(0)
entry = gtk.Entry()
entry.set_completion(completion)
# boilerplate
window = gtk.Window()
window.add(entry)
window.connect('destroy', lambda w: gtk.main_quit())
window.show_all()
gtk.main()
Users are not likely to bother typing "http://" or even "www.", so you probably want to match any part of the URL (e.g. just "og" works!):
def match_anywhere(completion, entrystr, iter, data):
modelstr = completion.get_model()[iter][0]
return entrystr in modelstr
completion.set_match_func(match_anywhere, None)
This will test every value in the ListStore for a match, so it's not scalable to huge lists (I mean huge; a 1000 works fine).
Be sure to play with the various options of EntryCompletion, to configure the most pleasant behavior.
You may want to look at how Deskbar Applet's Cuemiac does it.
Well, you obviously want to deal with prefixes so you'll probably want to use some sort of trie. Of course, there are issues to deal with. For instance, after a person has typed in a few letters ( or maybe even just one) you will want to either traverse the rest of the branches of the trie to find suggestions, or have suggestions stored in each node. A lot of these sorts of decisions depend on how many possible suggestions you plan on having.