How to dynamically create callbacks for multiple file uploaded using dcc.Upload using Dash Plotly? - callback

When i was doing an interface which dynamically create buttons after an upload of multiple pictures using dcc.Upload, i noticed only the last file treated have the buttons active. The other files have the interface layout correctly adjoined, but none of the callback works.
So i would like to know how to properly handle having multiple pictures uploaded using dcc.Upload so i can interact with all pictures.
Here is my code (simplified) to reproduce the problem (simply upload 2 pictures and try to click on the buttons) :
from dash import Dash, html, dcc
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
#CSS
external_stylesheets=[dbc.themes.BOOTSTRAP]
#app
app = Dash(__name__,external_stylesheets=external_stylesheets,suppress_callback_exceptions=True)
#layout
app.layout = html.Div([
dcc.Upload(
id='upload-image',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '70%',
'height': '60px',
'justify':'center',
'text-align':'center',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '500px',
'textAlign': 'center',
'margin': '10px'
},
# Allow multiple files to be uploaded
multiple=True
),
html.Div(id='output-image-upload'),
])
#callback upload button
#app.callback(Output('output-image-upload', 'children'),
Input('upload-image', 'contents'),
State('upload-image', 'filename'),
State('upload-image', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [
parse_contents(c, n, d) for c, n, d in
zip(list_of_contents, list_of_names, list_of_dates)]
return children
def parse_contents(contents,unused,unused2):
import random
result = random.randrange(1000)
return html.Div([
dcc.Store(id='store-picture',data=result),
dbc.Row([
dbc.Col(
html.Img(
src=contents,
style={
'margin-left':'auto',
'margin-right':'0px',
'width':'40%',
'padding-left':'20px'}),
),
dbc.Col(
html.Div(result,
style={
'font-size':'40px',
'padding-top':'10px',
'font-weight':'bold',
'font-family':'courier',
}
)
),
dbc.Col([
dbc.Row(html.Div('Is the prediction correct?')),
dbc.Row([
dbc.Col(
html.Button('Yes',id='button-pred-yes',n_clicks=0)
),
dbc.Col(
html.Button('No',id='button-pred-no',n_clicks=0)
)
]),
dbc.Row(
html.Div(id='div-prediction-result')
)
])
])
])
#app.callback(
Output('div-prediction-result','children'),
[Input('button-pred-yes','n_clicks'),Input('button-pred-no','n_clicks')],
[State('store-picture','data')]
)
def confirm_prediction(bpy,bpn,data):
prediction = data
if bpy:
return prediction
elif bpn:
return prediction
#--------- LAUNCHER ------------
if __name__ == '__main__':
app.run_server(debug=True)
What confuses me is that there's multiple buttons appearing, but only the last one works.

Related

Plotly Dash - Python - Issue with callback function

Please bare with my limited experience in programming. At least, I will elaborate.
My task is to create a Dropdown for a number of launch sites, and then plot the success rate in a pie chart. The user can also select a range of payloads with a RangeSlider, that will be used to create a scatter plot.
This is how the result should look like:
enter image description here
Here is the code:
from dash import Dash, dcc, html, Input, Output
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
URL = 'https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBM-DS0321EN-SkillsNetwork/datasets/spacex_launch_geo.csv'
df_spacex = pd.read_csv(URL)
df_spacex.head()
# dropdown menu to choose from the various sites:
app = Dash(__name__)
app.layout = html.Div(children = [
html.Div([
html.H1('SpaceX Launch Records Dashboard',
style = {'textAlign': 'center',
'color': 'black',
'font-size': 24}
),
html.Label('Dropdown:',
style = {'color': 'black',
'font-size': 18}
),
dcc.Dropdown(id = 'site-dropdown',
options = [{'label': 'All Sites', 'value': 'ALL'},
{'label': 'CCAFS SCL-40', 'value': 'CCAFS SLC-40'},
{'label': 'CCAFS LC-40', 'value': 'CCAFS LC-40'},
{'label': 'VAFB SLC-4E', 'value': 'VAFB SLC-4E'},
{'label': 'KSC LC-39A', 'value': 'KSC LC-39A'}],
value = 'ALL',
placeholder = 'Select a site',
searchable = True,
style = {'width': '80%', 'height': '3px',
'textAlign': 'left', 'font-size': '16px'}
),
]),
html.Div([
html.Br(),
html.Br(),
dcc.Graph(id = 'success-pie-chart'),
html.Br(),
html.Label('Payload range slider (kg):',
style = {'color': 'black',
'font-size': 18}
),
dcc.RangeSlider(id = 'payload-slider',
min = df_spacex['Payload Mass (kg)'].min(),
max = df_spacex['Payload Mass (kg)'].max(),
step = 1000,
marks = {0: '0',
100: '100'},
value = [2000, 8000]
),
html.Br(),
dcc.Graph(id = 'success-payload-scatter-chart')
])
])
# function decorator to specify function input and output
#app.callback(Output('success-pie-chart', 'figure'),
Output('success-payload-scatter-chart', 'figure'),
Input('site-dropdown', 'value'),
Input('payload-slider', 'value'))
def get_pie_chart(entered_site, payload):
if entered_site == 'ALL':
fig = px.pie(df_spacex, values = 'class', names = 'Launch Site',
title = 'Successful Launch Distribution by Site')
else:
# choose the rows that have the same launch site as the one entered
df_filtered = df_spacex.loc[df_spacex['Launch Site'] == entered_site]
fig = px.pie(df_filtered, names = 'class',
title = 'Launch Distribution for ' + entered_site)
return fig
def get_scatter_chart(entered_site, payload):
# expand equals True to return a data frame
# with seperated strings in different columns
df_split = df_spacex['Booster Version'].str.split(' ', expand = True)
# save the modified data frame
df_mod = df_spacex.drop(columns = ['Booster Version'], inplace = False)
df_mod['Booster Version'] = df_split[1]
print(payload)
# based on the input payload range choose the corresponding rows
df_mod = df_mod.loc[(df_mod['Payload Mass (kg)'] >= payload[0]) & (df_mod['Payload Mass (kg)'] <= payload[1])]
if entered_site == 'ALL':
fig = px.scatter(df_mod,
x = 'Payload Mass (kg)',
y = 'class',
color = 'Booster Version',
hover_data = ['Booster Version'])
else:
df_filtered = df_mod.loc[df_mod['Launch Site'] == entered_site]
fig = px.scatter(df_filtered,
x = 'Payload Mass (kg)',
y = 'class',
color = 'Booster Version',
hover_data = ['Booster Version'])
return fig
if __name__ == '__main__':
app.run_server(debug = True, use_reloader = False)
Finally, the error that I get is the following:
dash._grouping.SchemaTypeValidationError: Schema: [<Output `success-pie-chart.figure`>, <Output `success-payload-scatter-chart.figure`>]
Path: ()
Expected type: (<class 'tuple'>, <class 'list'>)
Received value of type <class 'plotly.graph_objs._figure.Figure'>:
I really appreciate your time guys.
Please let me know if I can provide any more info or help clarify.
I've tested the functions that take the user's input and produce the pie and scatter charts outside Dash, and they worked fine. So, the error is arising from Dash.

How to pass DOM elements for libraries (eg. ChartJS, Hightcharts) in Virtual DOMs (such as Qwik)?

Background
I have personally used React, Vue and Angular extensively in the past. And a lot of times I need to create applications with charts generated within them from selective data. I'm recently trying out Qwik due to its promise of speed and attempted to create charts within it using ChartJs. But while ChartJs has separate libraries available for React, Vue, Angular, Svelte, etc. it does not have one for Qwik understandably.
Issue
Many plugins such as Highcharts and ChartJs often require a DOM element to be sent to its functions to identify where to render their output. But when we are dealing with virtual DOMs, I can't run JS selector scripts to fetch DOM elements and pass them into a function within a component. Therefore, as of now, I have not been able to use ChartJs in my Qwik project.
Attempts
I have only looked for solutions for this issue and not found any workable approaches. From ChartJs docs the following code is their raw JS way of implementing charts:
new Chart(
document.getElementById('acquisitions'),
{
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Acquisitions by year',
data: data.map(row => row.count)
}
]
}
}
);
As expected document.getElementById does not work inside a component and that is where I'm stuck. I've only created the useMount$() function where I expect to place the logic for generating my chart and also looked around for React solutions by perhaps using references and what not. But, other than that, I have been unable to find anything more.
I understand that looking at the source code of the React library for ChartJs would provide me clues but while I investigate a library (which I find difficult at my current level) I was hoping for a pointer to the solution from the Stack Overflow community.
Searching "ref" on the Qwik docs does not return any search results but I had found the git project from another developer online and tried to replicate the use of references from his approach:
Child component code:
import { component$, useMount$, Ref, useStylesScoped$ } from "#builder.io/qwik";
import { Chart } from 'chart.js/auto';
interface GraphProps {
data: object[];
reference: Ref<Element>;
}
export default component$((props: GraphProps) => {
useStylesScoped$(styles);
useMount$(() => {
new Chart(
props.reference.value,
{
<... options here ...>
}
);
});
return (
<div id="chartContent">
</div>
);
});
Parent component code:
import { component$, useRef } from "#builder.io/qwik";
import ContentCard from "../components/contentCard/contentCard";
import ChartJSGraph from "../components/chartJSGraph/chartJSGraph";
...
export default component$(() => {
const leftChartContainer = useRef();
return (
<div>
<div className="row">
<ContentCard>
<div className="graph-container">
<ChartJSGraph
data={[
{ year: 2010, count: 10 },
...
]}
reference={leftChartContainer}
/>
</div>
</ContentCard>
</div>
</div>
)
});
As these are just findings from a YouTuber's code it could be outdated so is certainly not necessarily a reliable source. But so far searching the official docs have not led me to any official approach for references.
The DOM element that is passed to the charting library can only be accessed once it has been mounted to the page. Qwik/Vue/React all provide component mounted hooks.
https://qwik.builder.io/docs/components/lifecycle/#usemount
https://vuejs.org/api/composition-api-lifecycle.html#onmounted
https://reactjs.org/docs/react-component.html#componentdidmount
Inside these mounted hooks you can reference your DOM element via id or querySelector or using the internal DOM reference feature of Qwuik/Vue/React and then use that when initialising the chart. The latter is the cleaner approach.
For example, in Vue:
<template>
<div id="acquisitions" ref="chartEl"></div>
</template>
<script setup>
import Chart from 'chart.js/auto';
import { ref, onMounted } from 'vue';
const chartEl = ref(null)
onMounted(() => {
const chartOpts = {
type: 'bar',
data: {
labels: data.map(row => row.year),
datasets: [
{
label: 'Acquisitions by year',
data: data.map(row => row.count)
}
]
}
}
new Chart(
chartEl.value,
chartOpts
);
})
</script>
Solution
Sadly this was a silly issue of perhaps on my network side or god knows what why the search engine on the Qwik doc never suggested anything for me when I looked up "Ref" in their docs. But my problem has been solved after finding the following link:
https://qwik.builder.io/tutorial/hooks/use-signal/#example
For future reference for myself or any beginners facing the similar issue, I'm writing down my implementation below:
// Root component
import { component$, useSignal } from "#builder.io/qwik";
...
import ChartJSGraph from "../components/chartJSGraph/chartJSGraph";
export default component$(() => {
const chartData1 = useSignal({
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: 'Inventory Value per Outlet',
data: [65, 59, 80, 81, 56, 55, 40],
fill: false,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
});
return (
<div class="w-100 h-100">
...
<ChartJSGraph
width={'100%'}
height={'25px'}
chartData={chartData1.value}
/>
</div>
);
});
And here's the code for my ChartJSGraph component that uses the data supplied to generate the chart while using the reference of the canvas element to point to ChartJS where to create the chart.
// ChartJSGraph component
import { component$, useClientEffect$, useSignal } from "#builder.io/qwik";
import { Chart } from 'chart.js/auto';
...
interface GraphProps {
height: string;
width: string;
chartData: object;
}
export default component$((props: GraphProps) => {
const outputRef = useSignal<Element>();
useClientEffect$(() => {
new Chart(
outputRef.value,
{
type: 'line',
data: props.chartData
}
);
});
return (
<>
<canvas ref={outputRef} width={props.width} height={props.height}>
</canvas>
</>
);
});

react-mapbox-gl markers are not displayed correctly

I have a site with mapbox, the map itself works fine, but I can't add markers to it.
I copied the code from one source on which everything works, but when I added it to my project, the markers shifted from the correct coordinates on the map and got even more shifted when approaching.
here's my code
import React, { useState } from "react";
import ReactDOM from "react-dom";
import ReactMapboxGl, { Layer, Marker } from "react-mapbox-gl";
import { observer } from "mobx-react-lite";
import state from "../../state/state";
const Map = ReactMapboxGl({
accessToken:
"pk.eyJ1IjoibmFnaHQiLCJhIjoiY2wyYTJrazZxMDFlbzNpbnp0eTNnOG44aCJ9.i3nyiAJBTDyWviIWhsX-Zg",
});
const IndexMap = observer(({ coordinats }) => {
return (
<div style={{ height: "100vh", width: "100%", overflow: "hidden" }}>
<Map
style="mapbox://styles/mapbox/streets-v9" // eslint-disable-line
containerStyle={{
height: "100%",
width: "100%",
}}
center={{
lat: 51.5285582,
lng: -0.2416815,
}}
zoom={[12]}
>
<Marker coordinates={[-0.2416815, 51.5285582]} anchor="bottom">
<h1>marker</h1>
</Marker>
</Map>
</div>
);
});
export default IndexMap;
I think there are not enough styles for the map to set them in the right location.
I don't know what the problem was. I just moved the project to the folder with the working file. The link to the folder with the working file -https://codesandbox.io/embed/pwly8?codemirror=1

Sunmi T2 - Flutter

I am using the sunmi_printer_t1mini for my sunmi T2 printer to print tokens, the printing seems to be going fine, but the token paper in not being cut, I checked if there is any function for cutting the paper but I couldn't find any, can anyone please help me with this.
Below I have attached my code which I run to print.
_testPrint() {
Log.print('Test Print');
try {
Printer.text("Printer OK!",
styles: PrintStyle(
bold: true, align: PrintAlign.center, size: PrintSize.mdd));
Printer.text("Powered by Grapes IDMR",
styles: PrintStyle(
bold: true, align: PrintAlign.center, size: PrintSize.mdd));
Printer.cutPaper();
} catch (e, s) {
Completer().completeError(e, s);
}
}
simply use
SunmiFlutterPrint.autoOutPaper();
not that this also should be imported:
import 'package:sunmi_flutter_print/sunmi_flutter_print.dart';

trying to generate a PDF and view or email it with React Native

I spent the last few days playing with react-native-html-to-pdf (https://github.com/christopherdro/react-native-html-to-pdf ), react-native-mail (by chirag04) and react-native-view-pdf (by cnjon)
There is another version of react-native-mail by parkerdan that I have yet to try, but the chrirag04's version basically corrupted all my projects and was a pain to uninstall.
react-native-html-to-pdf doesn't seem to generate any error, and I can't seem have access to the pdf generated. here a snippet of the code I am running:
import RNHTMLtoPDF from 'react-native-html-to-pdf';
import PDFView from 'react-native-pdf-view';
...
createPDF() {
var options = {
html: '<h1>PDF TEST</h1>', // HTML String
// ****************** OPTIONS BELOW WILL NOT WORK ON ANDROID **************
fileName: 'test', /* Optional: Custom Filename excluded extention
Default: Randomly generated
*/
directory: 'docs', /* Optional: 'docs' will save the file in the `Documents`
Default: Temp directory
*/
height: 800, /* Optional: 800 sets the height of the DOCUMENT that will be produced
Default: 612
*/
width: 1056, /* Optional: 1056 sets the width of the DOCUMENT that will produced
Default: 792
*/
padding: 24, /* Optional: 24 is the # of pixels between the outer paper edge and
corresponding content edge. Example: width of 1056 - 2*padding
=> content width of 1008
Default: 10
*/
};
RNHTMLtoPDF.convert(options).then((filePath) => {
AlertIOS.alert(
'creat pdf',
'filePath=' + filePath
);
return (
<PDFView ref={(pdf)=>{this.pdfView = pdf;}}
src={filePath}
onLoadComplete = {(pageCount)=>{
this.pdfView.setNativeProps({
zoom: 1.5
});
}}
/>
)
});
};
and later in the code I call it with:
<TouchableHighlight onPress={this.createPDF} style={styles.button}>
<Text>create pdf </Text>
</TouchableHighlight>
I get the AlertIOS, with something that looks like a valid filepath (any hint to check the path is correct, let me know)
But that's it, I don't seem to find the test.pdf document anywhere.
Can anyone tell what I am doing wrong?
Many Thanks,
Cheufte
I think the file path is document directory you can go through the file path by first clicking the windows option in xcode after that find devices option upon clicking device option all the information of your device will appear then select the application and see it's container and you will find your pdf file.
var localpath= RNFS.DocumentDirectoryPath + filePath
<PDFView ref={(pdf)=>{this.pdfView = pdf;}}
path={localpath}
onLoadComplete = {(pageCount)=>{
this.pdfView.setNativeProps({
zoom: 1.5
});
}}
/>
write path in place of src because it is deprecated.
import React, { Component } from 'react';
import {
AlertIOS,
AppRegistry,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import RNHTMLtoPDF from 'react-native-html-to-pdf';
export default class testApp extends Component {
createPDF() {
var options2 = {
html: '<h1>PDF TEST</h1>', // HTML String
// ****************** OPTIONS BELOW WILL NOT WORK ON ANDROID **************
fileName: 'test2', /* Optional: Custom Filename excluded extension
Default: Randomly generated
*/
directory: 'docs', /* Optional: 'docs' will save the file in the `Documents`
Default: Temp directory */
base64: true ,
height: 800,
width: 1056, /* Optional: 1056 sets the width of the DOCUMENT that will produced
Default: 792
*/
padding: 24, /* Optional: 24 is the # of pixels between the outer paper edge and
corresponding content edge. Example: width of 1056 - 2*padding
=> content width of 1008
Default: 10
*/
};
RNHTMLtoPDF.convert(options2).then((data2) => {
console.log(data2.filePath);
console.log(data2.base64);
AlertIOS.alert(
'options2 filename' + options2.fileName,
'data2 filePath=' + data2.filePath
);
});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to testApp
</Text>
<TouchableHighlight onPress={this.createPDF}>
<Text>Create PDF</Text>
</TouchableHighlight>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
AppRegistry.registerComponent('testApp', () => testApp);