Understanding layout rules in PySimpleGUI - user-input

I am trying to build a GUI with PySimpleGUI for the first time and am struggling with the layout reuse rules.
The overall aim of the script will be to make entering data into a CSV table easier for users. For this purpose, the users will be able to enter data into a form, submit the data, and decide if they want to add another data set or exit.
I have tried to work with nested functions but am constantly breaking the script because I am supposed to use a new layout for each window. I have defined 3 different windows with their own layouts so far:
window1 = main window where data can be added
window2 = window with error notification
window3 = window opening after SUBMIT, asking users if they want to continue
In addition, I am trying to call the function for window1 again if users decide to add more data (repeatGUI()), but this is not permitted.
I am aware that there are other issues in the script, too, but I would mainly appreciate some input on the layout-reuse issue. What is the proper way of opening an input window multiple times?
DRAFT SCRIPT:
import csv
import PySimpleGUI as sg
sg.main_get_debug_data()
myfile="C:\\######\\File.csv"
counter=0
def buildGUI(): # defining GUI frame, title, fields and buttons
sg.ChangeLookAndFeel('GreenTan')
window1 = sg.FlexForm('MY DB', size = (900,700), resizable=True)
layout1 = [
[sg.Text('Please enter a new factoid:')],
[sg.Text('Factoid ID', size=(15, 1), key="-FACTOID-")],
[sg.Text('Person ID', size=(15, 1), key="-PERSON-")],
[sg.Text('Exact Date', size=(10, 1)), sg.InputText()],
[sg.Text('Event Before', size=(10, 1)), sg.InputText()],
[sg.Text('Event After', size=(10, 1)), sg.InputText()],
[sg.Text('Event Start', size=(10, 1)), sg.InputText()],
[sg.Text('Event End', size=(10, 1)), sg.InputText()],
[sg.Text('Event Type', size=(15, 1)), sg.InputText()],
[sg.Text('Person Name', size=(15, 1)), sg.InputText()],
[sg.Text('Person Title', size=(15, 1)), sg.InputText()],
[sg.Text('Person Function', size=(15, 1)), sg.InputText()],
[sg.Text('Place Name', size=(15, 1)), sg.InputText()],
[sg.Text('Institution Name', size=(15, 1)), sg.InputText()],
[sg.Text('Related Persons', size=(15, 1)), sg.InputText()],
[sg.Text('Alternative Names', size=(15, 1)), sg.InputText()],
[sg.Text('Source Quote', size=(10, 1)), sg.InputText()],
[sg.Text('Additional Info', size=(10, 1)), sg.InputText()],
[sg.Text('Comment', size=(10, 1)), sg.InputText()],
[sg.Text('Source', size=(10, 1)), sg.InputText()],
[sg.Text('Source URL', size=(10, 1)), sg.InputText()],
[sg.Text('Info Dump', size=(10, 1)), sg.InputText()],
[sg.Text(size=(70,1), key='-MESSAGE1-')],
[sg.Submit(), sg.Button('Clear Input')]
]
while True: # The Event Loop
event1, values1 = window1.Layout(layout1).Read()
print(layout1)
def startGUI(event1, values1): # start GUI and get data
# interact with user to get input
if event1 == 'Submit': # user clicked "Submit"
def submitGUI(values1): # submitting new data via GUI
fact_id=("#") # place holder: to be calculated later
pers_id=("#") # place holder: to be calculated later
entries=[fact_id, pers_id]
for v in values1.values():
entries.append(v)
try:
with open(myfile, 'a', encoding="utf-8") as f:
w=csv.writer(f)
w.writerow(entries) # write list items to CSV file
f.close()
try:
window3 = sg.FlexForm('NEW DATA?', size = (500,100))
layout3 = [
[sg.Text("Your input has been saved! Do you want to add a new factoid?")],
[sg.Text(size=(70,1), key='-MESSAGE2-')],
[sg.Button("YES"), sg.Button("NO"), sg.Exit()]
]
while True:
event3, values3 = window3.Layout(layout3).Read()
if event3 == 'YES': # user clicked YES
window1.close()
try:
repeatGUI() # this breaks layout rules!
except:
print("Not allowed!")
pass
elif event3 == 'NO':
window3['-MESSAGE2-'].update("See you again soon!")
window1.close()
elif event3 in (sg.WINDOW_CLOSE_ATTEMPTED_EVENT, 'Exit'):
window3.close()
except:
pass
except PermissionError:
window2 = sg.FlexForm('DB ERROR', size = (500,100))
layout2 = [
[sg.Text("Someone else is using the file! Please try again later.")],
[sg.Exit()]
]
event2, values2 = window2.Layout(layout2).Read()
if event2 in (sg.WINDOW_CLOSE_ATTEMPTED_EVENT, 'Exit'):
window2.close() # user clicked "Exit"
submitGUI(values1)
elif event1 == 'Clear Input': # user clicked "Cancel"
window1.Refresh()
elif event1 == sg.WINDOW_CLOSE_ATTEMPTED_EVENT: # user closed window
window1.close() # AttributeError: module 'PySimpleGUI' has no attribute 'WIN_CLOSED'
startGUI(event1, values1)
buildGUI()
def repeatGUI():
counter+=1
print(counter)
buildGUI()

Following way show that you use the variable layout1 as layout of sg.Window again and again, none of element in layout1 initialized after 1st time using.
while True: # The Event Loop
event1, values1 = window1.Layout(layout1).Read()
...
Following code preferred,
window1 = sg.Window('Title1', layout1, finalize=True)
while True:
event1, values1 = window1.read()
...
or
def make_window1():
layout = [
[sg.Text('Please enter a new factoid:')],
....
]
return sg.Window('Title1', layout, finalized=True)
window1 = make_window1()
while True:
event1, values1 = window1.read()
...

Based on #jason-yang 's answer, I was able to fix my layout issue and revised the whole script to
a) define layouts outside the function that starts the GUI
and
b) get rid of the attempt to open the same input window several times.
Having a persistent window is much more suitable in my case, so I am using a "Clear" button to allow new data input:
elif event1 == 'Clear to add new data': # user clicked "Clear"
window1['-MESSAGE1-'].update("Ready for your next data set!")
# update all input fields
window1['-DATE-'].update('')
window1['-BEFORE-'].update('')
window1['-AFTER-'].update('')
window1['-START-'].update('')
window1['-END-'].update('')
window1['-EVENT-'].update('')
window1['-PERSNAME-'].update('')

Related

Adding and triggering a transition within on_enter state callback

I want to store a previous state A when entering state B using a state history. Later 'on_enter' of another state X, add a transition X->A, trigger it and return to state A.
A -> B (store A) -> C -> X (add transition from X to A) -> A
from transitions.extensions import HierarchicalGraphMachine
class Matter(HierarchicalGraphMachine):
def __init__(self, states):
# self.separator = '/'
self.history = []
self.stored = []
HierarchicalGraphMachine.__init__(
self,
states=states,
initial='root',
show_state_attributes=True
)
#property
def state(self):
return self.history[-1]
#state.setter
def state(self, value):
self.history.append(value)
def store_last(self):
self.stored.append(self.history[-2])
def to_stored(self):
stored = self.stored.pop()
temp_transition = {
'trigger': 'jump',
'source': self.state,
'dest': stored
}
self.add_transition(**temp_transition)
self.trigger('jump')
self.remove_transition(**temp_transition)
TRANSITIONS = [
['walk', 'standing', 'walking'],
['stop', 'walking', 'standing'],
['drink', '*', 'caffeinated'],
['walk',
['caffeinated', 'caffeinated_dithering'],
'caffeinated_running'],
['relax', 'caffeinated', 'standing']
]
STATES = [{
'name': 'root',
'initial': 'standing',
'transitions': TRANSITIONS,
'children': [
{
'name': 'standing',
},
{
'name': 'walking',
'on_enter': 'store_last',
},
{
'name': 'caffeinated',
'children': [
'dithering',
{
'name': 'running',
'on_enter': 'to_stored'
}
]
}
]
}]
If I run the code skipping on_enter the transition is added, triggered and removed as expected.
# this works
lump = Matter(states=STATES)
lump.trigger('walk')
assert lump.history == ['root', 'root_standing', 'root_walking']
assert lump.stored == ['root_standing']
lump.trigger('drink')
# set state manually to skip on_enter
lump.state = 'root_caffeinated_running'
# run on_enter method manually works
lump.to_stored()
assert lump.state == 'root_standing'
assert lump.stored == []
assert lump.get_nested_transitions(trigger='jump') == []
lump.get_graph().draw('my_state_diagram.pdf', prog='dot')
If I run it within on_enter I get an error "MachineError: "Can't trigger event 'jump' from state(s) root_caffeinated_running!"
# cannot do the jump using on_enter
lump = Matter(states=STATES)
lump.trigger('walk')
lump.trigger('drink')
lump.trigger('walk')
# Can't trigger event 'jump' from state(s) root_caffeinated_running!
lump.get_graph().draw('my_state_diagram.pdf', prog='dot')
By default transitions will add auto transitions which can be used to reach every state directly without the need to add transitions temporarily. In combination with Machine.trigger that can be used to call events by their name, your to_stored method could be simplified to:
def to_stored(self):
stored = self.stored.pop()
self.trigger(f"to_{stored}")

How do I interact with my chatbot? (made with chatterbot library in python)

from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer
from chatterbot.trainers import ChatterBotCorpusTrainer
my_bot = ChatBot(name='Chatbot', read_only=True,
logic_adapters=
['chatterbot.logic.MathematicalEvaluation',
'chatterbot.logic.BestMatch'])
list_trainer = ListTrainer(my_bot)
small_talk = ['hi there',
'hi!',
'how do you do',
'how are you',
'i\'m fine',
'glad to hear that.',
'i feel awesome',
'excellent, glad to hear that.',
'not so good',
'sorry to hear that.',
'wha\'s your name',
'My name is Chatbot, ask me a math question.']
math_talk_1 = ['pythagorean theorem',
'a squared plus b squared equals c squared']
math_talk_2 = ['2+2',
'4 ask me a harder one.']
math_talk_3 = ['law of cosines',
'c**2 = a**2 + b**2 - 2 * a * b * cos(gamma)']
list_trainer = ListTrainer(my_bot)
for item in (small_talk, math_talk_1, math_talk_2, math_talk_3):
list_trainer.train(item)
corpus_trainer = ChatterBotCorpusTrainer(my_bot)
corpus_trainer.train('chatterbot.corpus.english')
So I made this chatbot following a tutorial online in this website: https://www.upgrad.com/blog/how-to-make-chatbot-in-python/
But when I try running it it displays this error:
Traceback (most recent call last):
File "c:\Users\isaig\Dropbox\My PC (DESKTOP-K5PUFQD)\Documents\BYUI\2022\Fall\AI Society\chatbot\chatbot.py", line 5, in <module>
my_bot = ChatBot(name='Chatbot', read_only=True,
File "C:\Users\isaig\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\chatterbot\chatterbot.py", line 28, in __init__
self.storage = utils.initialize_class(storage_adapter, **kwargs)
File "C:\Users\isaig\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\chatterbot\utils.py", line 33, in initialize_class
return Class(*args, **kwargs)
File "C:\Users\isaig\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\chatterbot\storage\sql_storage.py", line 46, in __init__
if not self.engine.dialect.has_table(self.engine, 'Statement'):
File "C:\Users\isaig\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\sqlalchemy\dialects\sqlite\base.py", line 2016, in has_table
self._ensure_has_table_connection(connection)
File "C:\Users\isaig\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\sqlalchemy\engine\default.py", line 342, in _ensure_has_table_connection
raise exc.ArgumentError(
sqlalchemy.exc.ArgumentError: The argument passed to Dialect.has_table() should be a <class 'sqlalchemy.engine.base.Connection'>, got <class 'sqlalchemy.engine.base.Engine'>. Additionally, the Dialect.has_table() method is for internal dialect use only; please use ``inspect(some_engine).has_table(<tablename>>)`` for public API use.
I don't know what is wrong. please help if you know.

Getting Import Error quite randomly when using plotly express and having multiple graphs on one page

Relatively new to Dash, and this is a problem that has been vexing me for months now. I am making a multi-page app that shows some basic data trends using cards, and graphs embedded within cardbody. 30% of the time, the app works well without any errors and the other 70% it throws either one of the following:
ImportError: cannot import name 'ValidatorCache' from partially initialized module 'plotly.validator_cache' (most likely due to a circular import)
OR
ImportError: cannot import name 'Layout' from partially initialized module 'plotly.graph_objects' (most likely due to a circular import)
Both these appear quite randomly and I usually refresh the app to make them go away. But obviously I am doing something wrong. I have a set of dropdowns that trigger callbacks on graphs. I have been wracking my head about this. Any help/leads would be appreciated. The only pattern I see in the errors is they seem to emerge when the plotly express graphs are being called in the callbacks.
What am I doing wrong? I have searched all over online for help but nothing yet.
Sharing with some relevant snippets of code (this may be too long and many parts not important to the question, but to give you a general idea of what I have been working towards)
import dash
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from plotly.subplots import make_subplots
import plotly.express as px
card_content1_1 = [
dbc.CardHeader([dbc.Row([html.H5("SALES VOLUME TREND", className = "font-weight-bold text-success"),
dbc.Button(
html.I(className="fa fa-window-maximize"),
color="success",
id="sales_maximize",
className="ml-auto",
# href="www.cogitaas.com"
)
])]),
dbc.CardBody(
[dcc.Graph(
id='sales_graph',
figure={},
style={'height':'30vh'}
# className="mt-5"
)])]
card_stacked_discount = [
dbc.CardHeader([dbc.Row([html.H5("VOLUMES UNDER DIFFERENT DISCOUNT LEVELS", className="font-weight-bold text-info text-center"),
dbc.Button(
html.I(className="fa fa-window-maximize"),
color="info",
id="discount_maximize",
className="ml-auto",
# href="www.cogitaas.com"
)
])]),
dbc.CardBody(
[dcc.Dropdown(
id = 'stacked_discount_dropdown',
options =stacked_discount_options,
value=stacked_discount_options[0].get('value'),
style={'color':'black'},
# multi=True
),
dcc.Graph(
id='stacked_discount_graph',
figure={},
style={'height':'30vh'}
)])]
cards = html.Div(
[
dbc.Row(
[
dbc.Col(dbc.Card(card_content1_1, color="success", outline=True,
style={'height':'auto'}), width=8),
],
className="mb-4",
),
dbc.Row(
[
dbc.Col(dbc.Card(card_stacked_discount, color="info", outline=True), width=8),
dbc.Col(dbc.Card([
dbc.Row([
dbc.Col(dbc.Card(disc_sub_title, color="info", inverse=True)),
]),
html.Br(),
dbc.Row([
dbc.Col(dbc.Card(disc_sub_card1, color="info", outline=True)),
]),
]), width=4)
],
className="mb-4",
),
]
)
tab1_content = dbc.Card(
dbc.CardBody(
[cards,]
),
className="mt-3",
)
tabs = dbc.Tabs(dbc.Tab(tab1_content, label="Data", label_style={'color':'blue'}, tab_style={"margin-left":"auto"}),])
content = html.Div([
html.Div([tabs]),
],id="page-content")
app.layout = html.Div([dcc.Location(id="url"), content])
#app.callback(
dash.dependencies.Output('sales_graph', 'figure'),
[dash.dependencies.Input('platform-dropdown', 'value'),
dash.dependencies.Input('signature-dropdown', 'value'),
dash.dependencies.Input('franchise-dropdown', 'value'),
dash.dependencies.Input('sales_maximize', 'n_clicks'),
dash.dependencies.Input('time-dropdown', 'value'),
])
def update_sales_graph(plat, sign, fran, maximize, time_per):
print(str(time_per)+"Test")
time_ax=[]
if isinstance(time_per,str):
time_ax.append(time_per)
time_per=time_ax
if (time_per==None) or ('Full Period' in (time_per)):
dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran)]
elif ('YTD' in time_per):
dff = df[(df.Platform == plat) & (df.Signature == sign) & (df.Franchise == fran) & (df.year==2020)]
else:
dff = df[(df.Platform==plat) & (df.Signature==sign) & (df.Franchise==fran) & (df.Qtr_yr.isin(time_per))]
fig = px.area(dff, x='Date', y='Qty_Orig', color_discrete_sequence=px.colors.qualitative.Dark2)
fig.add_trace(go.Scatter(x=dff['Date'], y=dff['Outliers'], mode = 'markers', name='Outliers',
line=dict(color='darkblue')))
fig.add_trace(go.Scatter(x=dff['Date'], y=dff['bestfit'], name='Long Term Trend',
line=dict(color='darkblue')))
fig.update_layout(font_family="Rockwell",
title={'text': fran + " Volume Trend",
'y': 0.99,
# 'x': 0.15,
# 'xanchor': 'auto',
'yanchor': 'top'
},
legend=dict(
orientation="h",
# y=-.15, yanchor="bottom", x=0.5, xanchor="center"
),
yaxis_visible=False, yaxis_showticklabels=False,
xaxis_title=None,
margin=dict(l=0, r=0, t=0, b=0, pad=0),
plot_bgcolor='White',
paper_bgcolor='White',
)
fig.update_xaxes(showgrid=False, zeroline=True)
fig.update_yaxes(showgrid=False, zeroline=True)
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'maximize' in changed_id:
fig.show()
return fig
#app.callback(
dash.dependencies.Output('stacked_discount_graph', 'figure'),
[dash.dependencies.Input('platform-dropdown', 'value'),
dash.dependencies.Input('signature-dropdown', 'value'),
dash.dependencies.Input('franchise-dropdown', 'value'),
dash.dependencies.Input('discount_maximize', 'n_clicks'),
dash.dependencies.Input('stacked_discount_dropdown', 'value'),
dash.dependencies.Input('time-dropdown', 'value'),
])
def stacked_discount(plat, sign, fran, maximize, sales_days, time_per):
time_ax=[]
if isinstance(time_per,str):
time_ax.append(time_per)
time_per=time_ax
# else:
# time_per=list(time_per)
if (time_per==None) or ('Full Period' in (time_per)):
df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran)]
elif ('YTD' in time_per):
df_promo = df_promo_vol[(df_promo_vol.Platform == plat) & (df_promo_vol.Signature == sign) & (df_promo_vol.Franchise == fran) & (df_promo_vol.Year==2020)]
else:
df_promo = df_promo_vol[(df_promo_vol.Platform==plat) & (df_promo_vol.Signature==sign) & (df_promo_vol.Franchise==fran) & (df_promo_vol.Qtr_yr.isin(time_per))]
color_discrete_map = {
"0 - 10": "orange",
"10 - 15": "green",
"15 - 20": "blue",
"20 - 25": "goldenrod",
"25 - 30": "magenta",
"30 - 35": "red",
"35 - 40": "aqua",
"40 - 45": "violet",
"45 - 50": "brown",
"50 + ": "black"
}
category_orders = {'disc_range': ['0 - 10', '10 - 15', '15 - 20', '20 - 25', '25 - 30', '30 - 35', '35 - 40',
'40 - 45', '45 - 50', '50 + ']}
if (sales_days == None) or (sales_days == 'sales_act'):
fig = px.bar(df_promo, x='start', y='units_shipped', color='disc_range',
color_discrete_map=color_discrete_map,
category_orders=category_orders,
)
else:
fig = px.bar(df_promo, x='start', y='Date', color="disc_range",
color_discrete_map=color_discrete_map,
category_orders=category_orders,
)
fig.update_layout(font_family="Rockwell",
title={'text': fran + " Sales Decomposition",
'y': 0.99,
'x': 0.1,
# 'xanchor': 'auto',
'yanchor': 'top'
},
legend=dict(
orientation="h",
# y=-.15, yanchor="bottom", x=0.5, xanchor="center"
),
# yaxis_visible=False, yaxis_showticklabels=False,
xaxis_title=None,
margin=dict(l=0, r=0, t=30, b=30, pad=0),
plot_bgcolor='White',
paper_bgcolor='White',
)
fig.update_xaxes(showgrid=False, zeroline=True)
fig.update_yaxes(showgrid=False, zeroline=True)
changed_id = [p['prop_id'] for p in dash.callback_context.triggered][0]
if 'maximize' in changed_id:
fig.show()
return fig
Well, it appears I may have stumbled on to an answer. I was using the pretty much the same inputs for multiple callbacks and that could have been causing some interference with the sequencing of inputs. Once I integrated the code into one callback with multiple outputs, the problem seems to have disappeared.
Was dealing with this same issue where everything in my app worked fine, then I made an entirely separate section & callback that started throwing those circular import errors.
Was reluctant to re-arrange my (rightfully) separated callbacks to be just a single one and found you can fix the issue by just simply importing what the script says it's failing to get. In my case, plotly was trying to import the ValidatorCache and Layout so adding these to the top cleared the issue and now my app works as expected. Hope this helps someone experiencing a similar issue.
from plotly.graph_objects import Layout
from plotly.validator_cache import ValidatorCache

Ionic BLE Manufacturer specific Data

Using #ionic-native/ble I'm able to scan and discover a BLE device which has manufacturer specific data.
According to the lib (https://github.com/don/cordova-plugin-ble-central#ios-1) here is the way to get this data
const mfgData = new Uint8Array(device.advertising.kCBAdvDataManufacturerData);
console.log('Manufacturer Data: ', mfgData);
const hex = Buffer.from(mfgData).toString('hex');
console.log(hex);
The encode to hex result being 2604 0504 386 55c0b
What I don't understand is the proper way to use this result to decode the manufacturer (company) id, which is supposed to be "0x0426"
You can try the following :
const toHexString = bytes =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
console.log(toHexString(new Uint8Array([0, 1, 2, 42, 100, 101, 102, 255])))

D3 Multi Line Graph with Dots

I am new to D3.js. I love it but I am having real trouble figuring out the best approach to structuring data.
I would ideally like to create a simple multiline graph that has over points over the selected points. Firstly I have the multiple lines created but trying to add the points has stumped me, and I think it has to do with the structure of my data.
Here is my working fiddle. I'm not sure if I should be trying to use d3.nest to re-arrange the data
I have a json object that I am retrieving from a google form which is all nice and smooth. This is what it looks like:
var data = [{
"description": "Global warming is a serious and pressing problem. We should begin taking steps now even if this involves significant costs",
"year2013": 40,
"year2012": 36,
"year2011": 41,
"year2010": 46,
"year2009": 48,
"year2008": 60,
"year2006": 68,
}, {
"description": "The problem of global warming should be addressed, but its effects will be gradual, so we can deal with the problem gradually by taking steps that are low in cost",
"year2013": 44,
"year2012": 45,
"year2011": 40,
"year2010": 40,
"year2009": 39,
"year2008": 32,
"year2006": 24,
}, {
"description": "Until we are sure that global warming is really a problem, we should not take any steps that would have economic costs",
"year2013": 16,
"year2012": 18,
"year2011": 19,
"year2010": 13,
"year2009": 13,
"year2008": 8,
"year2006": 7,
}, {
"description": "Don't know / refused",
"year2013": 1,
"year2012": 1,
"year2011": 1,
"year2010": 1,
"year2009": 1,
"year2008": 0,
"year2006": 1,
}]
Any help would be appreciated, I have been at it for days.
Cheers!
First - I would flatten your data
data = [
{date:"2011",type: "line0", amount:20}
...
]
Then nest your data by type
nested = d3.nest()
.key( (d) -> return d.type )
.entries(data)
Then append your line groups
# Line Groups
groups = container.selectAll('g.full-line')
.data(nested, (d) -> return d.key )
# ENTER
groups.enter().append('svg:g')
.attr( 'class', (d,i) -> "full-line#{i}" )
# EXIT
d3.transition(groups.exit()).remove()
# TRANSITION
d3.transition(groups)
Then append your chart lines
# Individual Lines
lines = groups.selectAll('.line').data (d)-> [d.values]
# ENTER
lines.enter().append("svg:path")
.attr("class","line")
.attr("d", d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
# EXIT
d3.transition( groups.exit().selectAll('.line') )
.attr("d",
d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
# TRANSITION
d3.transition(lines)
.attr("d",
d3.svg.line()
.interpolate(interpolate)
.defined(defined)
.x( (d,i) -> return xScale(d,i) )
.y( (d,i) -> return yScale(d,i) ) )
Thanks
I ended up using something similar.
/* Transform Data */
data = data.map(function (d) {
return {
country: d.country,
date: new Date(d.year.toString()),
value: d.value
};
});
/* Nest Data */
data = d3.nest().key(function (d) {
return d.country;
}).entries(data);`