Python Dash live update with dcc.Interval not working - python

I am trying to update a Dash chart opened in my web browser with datas coming from a websocket server I made.
I have no issue with the websocket part. My server sends to the client datas that I add to a list and convert this list to a pandas DataFrame object. Then this object is added to a multiprocess.Queue.
In my Dash callback from dcc.Interval, I get the data from the previous Queue (this part seems to work because I can print correctly the data) and update my Graph.
But the Graph in my browser is not updating. I do not see any error raised in my python code or in the browser console. (I tried with Firefox, Chrome and Brave)
My python console print dash-update-component HTTP/1.1" 200 - with my data at each callback interval and the tab name is Updating...
Worse, sometimes I can see one or 10 datas correctly displayed then nothing without changing the code, just by restarting my code...
Here the code (client side) :
import multiprocess as mp
import threading
import dash
from dash.dependencies import Output, Input
from dash import dcc
from dash import html
import plotly.graph_objs as go
from socket import *
import time
import json
import pandas as pd
from datetime import datetime
FLAG_QUIT = False
FLAG_ASK_DATA = False
MAX_SIZE = 10
queue = mp.Queue()
datas = []
app = dash.Dash(__name__)
app.layout = html.Div(
[
html.H4('Live Chart'),
dcc.Graph(id='live-update-graph', animate =True),
dcc.Interval(
id='interval-component',
interval=200,
n_intervals=0
)
]
)
#app.callback(Output('live-update-graph', 'figure'),
[Input('interval-component', 'n_intervals')])
def update_graph(n):
df = queue.get()
print(df)
fig = go.Scatter(x=df.index, y = df['Data'])
return fig
# function for receiving message from client
def send_to_server(clsock):
global FLAG_QUIT
global FLAG_ASK_DATA
while True:
try:
if FLAG_QUIT == True:
break
if FLAG_ASK_DATA:
time.sleep(0.5)
clsock.sendall("ok".encode())
except Exception as ex:
print(str(ex))
# function for receiving message from server
def recv_from_server(clsock):
global FLAG_QUIT
global FLAG_ASK_DATA
while True:
try:
data = clsock.recv(1024).decode()
if data == 'q':
print('Closing connection')
FLAG_QUIT = True
break
process_data(data)
FLAG_ASK_DATA = True
except Exception as ex:
print(str(ex))
def process_data(data):
global datas
global queue
process_websocket_data(data)
datas_dataframe = convert_data_to_dataframe(datas)
queue.put(datas_dataframe)
def convert_data_to_dataframe(data):
data_frame = pd.DataFrame(data)
data_frame = data_frame.drop_duplicates(0)
data_frame_date = data_frame[0]
final_date = []
for time in data_frame_date.unique():
readable = datetime.strptime(time, '%Y-%m-%d-%H-%M')
final_date.append(readable)
data_frame.pop(0)
dataframe_final_date = pd.DataFrame(final_date)
dataframe_final_date.columns = ['Date']
final_dataframe = data_frame.join(dataframe_final_date)
final_dataframe.set_index('Date', inplace = True)
final_dataframe.columns = ['Data']
return final_dataframe
def process_websocket_data(raw_data):
data_json = json.loads(raw_data)
data = [data_json['Date'],data_json['Data']]
datas.append(data)
while len(datas) > MAX_SIZE:
datas.pop(0)
def main():
threads = []
HOST = 'localhost'
PORT = 8765
clientSocket = socket(AF_INET, SOCK_STREAM)
connected = False
while not connected:
try:
print("Waiting connecting server...")
time.sleep(1)
clientSocket.connect((HOST, PORT))
connected = True
except :
pass
print('Client is connected to a chat sever!\n')
clientSocket.send('start'.encode())
t_send = threading.Thread(target=send_to_server, args=(clientSocket,))
t_rcv = threading.Thread(target=recv_from_server, args=(clientSocket,))
t_send.start()
t_rcv.start()
if __name__ == '__main__':
main()
app.run_server()
What did I code wrong or miss?
Edit :
This is the datas I get from the queue in the callback update_graph(n) method.
Data
Date
2019-09-16 10302.00
2019-09-23 8061.98
2019-09-30 8042.08
2019-10-07 7851.01
2019-10-14 8274.33
2019-10-21 8218.23
2019-10-28 9534.37
2019-11-04 9197.86
2019-11-11 9041.31
2019-11-18 8504.13
I also tried to replace my X axis by a list of int instead of a list of dates but the result is the same. No refreshing.
Thanks in advance.

I finally managed to find the problem and it seems to be the Queue in the callback. So I removed the queue mechanism and instead use a classic list with a lock.
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Graph(id="live_graph", animate=False),
dcc.Interval(id='interval_component',
interval=INTERVAL,
)])
#app.callback(Output('live_graph', 'figure'),
[Input('interval_component', 'n_intervals')])
def update_graph(num):
global datas_dataframe
lock.acquire()
if len(datas_dataframe) == 0:
print("No data")
lock.release()
return no_update
try:
df = datas_dataframe.pop(0)
fig = go.Figure(data=[go.Scatter(x=df.index,y=df['Data'])],
layout=go.Layout(yaxis=dict(tickfont=dict(size=22))))
lock.release()
return fig
except Exception as ex:
print(str(ex))
lock.release()
return no_update

Related

Linux NoHup fails for Streaming API IG Markets where file is python

This is quite a specific question regarding nohup in linux, which runs a python file.
Back-story, I am trying to save down streaming data (from IG markets broadcast signal). And, as I am trying to run it via a remote-server (so I don't have to keep my own local desktop up 24/7),
somehow, the nohup will not engage when it 'listen's to a broadcast signal.
Below, is the example python code
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
IG Markets Stream API sample with Python
"""
user_ = 'xxx'
password_ = 'xxx'
api_key_ = 'xxx' # this is the 1st api key
account_ = 'xxx'
acc_type_ = 'xxx'
fileLoc = 'marketdata_IG_spx_5min.csv'
list_ = ["CHART:IX.D.SPTRD.DAILY.IP:5MINUTE"]
fields_ = ["UTM", "LTV", "TTV", "BID_OPEN", "BID_HIGH", \
"BID_LOW", "BID_CLOSE",]
import time
import sys
import traceback
import logging
import warnings
warnings.filterwarnings('ignore')
from trading_ig import (IGService, IGStreamService)
from trading_ig.lightstreamer import Subscription
cols_ = ['timestamp', 'data']
# A simple function acting as a Subscription listener
def on_prices_update(item_update):
# print("price: %s " % item_update)
print("xxxxxxxx
))
# A simple function acting as a Subscription listener
def on_charts_update(item_update):
# print("price: %s " % item_update)
print(xxxxxx"\
.format(
stock_name=item_update["name"], **item_update["values"]
))
res_ = [xxxxx"\
.format(
stock_name=item_update["name"], **item_update["values"]
).split(' '))]
# display(pd.DataFrame(res_))
try:
data_ = pd.read_csv(fileLoc)[cols_]
data_ = data_.append(pd.DataFrame(res_, columns = cols_))
data_.to_csv(fileLoc)
print('there is data and we are reading it')
# display(data_)
except:
pd.DataFrame(res_, columns = cols_).to_csv(fileLoc)
print('there is no data and we are saving first time')
time.sleep(60) # sleep for 1 min
def main():
logging.basicConfig(level=logging.INFO)
# logging.basicConfig(level=logging.DEBUG)
ig_service = IGService(
user_, password_, api_key_, acc_type_
)
ig_stream_service = IGStreamService(ig_service)
ig_session = ig_stream_service.create_session()
accountId = account_
################ my code to set sleep function to sleep/read at only certain time intervals
s_time = time.time()
############################
# Making a new Subscription in MERGE mode
subscription_prices = Subscription(
mode="MERGE",
# make sure to put L1 in front of the instrument name
items= list_,
fields= fields_
)
# adapter="QUOTE_ADAPTER")
# Adding the "on_price_update" function to Subscription
subscription_prices.addlistener(on_charts_update)
# Registering the Subscription
sub_key_prices = ig_stream_service.ls_client.subscribe(subscription_prices)
print('this is the line here')
input("{0:-^80}\n".format("HIT CR TO UNSUBSCRIBE AND DISCONNECT FROM \
LIGHTSTREAMER"))
# Disconnecting
ig_stream_service.disconnect()
if __name__ == '__main__':
main()
#######
Then, I try to run it on linux using this command : nohup python marketdata.py
where marketdata.py is basically the python code above.
Somehow, the nohup will not engage....... Any experts/guru who might see what I am missing in my code?

python configuration for SR830 via RS232

I am currently working working on a program that tries to replicate labview functionality by establishing a connection to SR830 via RS-232. For some reasons, I am not able to record data and I cannot seem to figure out why. I tried to google about it but I did not find anything. If someone could please help me figure out what I am doing wrong.
Here is my code:
import serial
import tkinter as tk
from datetime import datetime
import time
import os
import atexit
import threading
import numpy as np
import math
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
class SR830:
'''SR830 Class creates an object that can interface with and control an SR830 via RS-232.
Class data structure allows the SR830 code to be imported into other scripts via "from SSTR_Serial import SR830"
This can be used to encapsulate SR830 interface mechanics into a larger control scheme...
by passing the class a frame to pack itself into as the frame argument.'''
def command(self, command_string, command_value): #Basic command protocol, can be used as a template for other command functions.
#Note: queries should have null command_value arguments (command_value = '').
packet = command_string+str(command_value)+'\r' #Can be adjusted to include checksums/parity if needed.
try:
self.inst.write(packet.encode('ascii'))
except AttributeError as AE:
print(AE)
def create_log(self):
try: #Makes log file if it does not exist already.
log_dir = self.name+'_logs'
os.mkdir(log_dir)
except OSError:#Log file already exists
pass
self.log_open = datetime.now() #Var to hold time log was created (Different from when timer is started.)
date = str(self.log_open).split(' ')[0] #Date that log file was opened.
h = 0
a = False
while not a:
h += 1
log_str = log_dir+'/'+self.name+"_"+date+'_'+str(h)+'.txt'
log_path = os.path.join(os.getcwd(), log_str)
if os.path.exists(log_path):
pass
else:
a = True
self.log = open(log_str, "w+")
self.log.write(self.name+" log created on "+ str(self.log_open)+"\nTime \tShift (Degrees)\tAmplitude (mV)\tR ()\tTheta ()\n") #Writing opening lines.
def start_graph(self): #Should be threaded at begining. This begins making the graph and recording data when the start/record buttons are pressed.
def get_vals(self):
current_time = time.time()#Get current time
if (current_time - self.previous_time) >= self.sample_time: #Time is equal or passed sampling time.
time_elapsed = current_time - self.start_time #Total time elapsed since transmission begun.
#Transmission zone:
try: #Querying and storing commands below
self.command("PHAS?",'')
phase_shift = self.inst.read(5).decode('ascii').replace('\r','')
self.command('SLVL?','')
phase_amplitude = self.inst.read(5).decode('ascii').replace('\r','')
self.shifts = np.append(self.shifts,[phase_shift]); self.amplitudes = np.append(self.amplitudes,[phase_amplitude]); self.times = np.append(self.times,[time_elapsed]) #Will only append time if both other arrays are appended.
#^ Append on one line to keep all lengths consistant when switching threads.
#Variables that are queried but not logged.
self.command('OEXP?1','')
R = self.inst.read(5).decode('ascii').replace('\r','')
self.command('OEXP?2','')
Theta = self.inst.read(5).decode('ascii').replace('\r','')
except AttributeError as AE:#Exceptions for testing without connection to device
print(AE)
'''
#This area generates random sine waves. Only use for testing without connection to actual device.---------
self.times = np.append(self.times,[len(self.times)])
self.shifts = np.append(self.shifts,[math.sin(len(self.times))])
self.amplitudes = np.append(self.amplitudes, [5*math.cos(len(self.times))])
phase_shift = 5
phase_amplitude = 7
R = 10
Theta = 11
print(str(len(self.times)))
print(str(len(self.shifts)))
print(str(len(self.amplitudes)))
#End test zone ---------------------------------------------------------------------------------------
'''
#Remove extra elements to reduce memeory usage for long runs
while len(self.times) > 200: #Integer on RHS of inequality is max number of elements.
self.shifts = self.shifts[2:-1:1]; self.amplitudes = self.amplitudes[2:-1:1]; self.times=self.times[2:-1:1]
#Log writing zone:
if self.recording is True: #Updates log, otherwise only updates graph.
self.log.write(str(round(time_elapsed-self.pause_time,4))+'\t'+str(phase_shift)+'\t\t'+str(phase_amplitude)+'\t\t'+str(R)+'\t'+str(Theta)+'\n')
self.previous_time = current_time #Resets time value for calculating interval.
while self.running: #Loops as long as running is true and kills thread once runing is false.
get_vals(self)
def animate(self, i): #Only redraw matplotlib aspects that get cleared with a.clear() and a2.clear() to conserve memory.
#Todo - optimize drawing order
try:
self.a.clear()
self.a.plot(self.times,self.shifts, label='Phase shift ()', color='tab:blue')
self.a.set_ylabel('Shift')
self.a.set_ylabel('Shift', color='tab:blue')
self.a.set_xlabel('Time (s)')
self.a.legend(loc='upper left')
self.a2.clear()
self.a2.plot(self.times,self.amplitudes,label='Phase amplitude ()',color='tab:red')
self.a2.set_ylabel('Amplitude', color='tab:red')
self.a2.legend(loc='upper right')
self.a.get_yaxis_transform()#Fits axis to widget area.
except ValueError as VE:
print(VE)
def clear_graph(self): #Clears all arrays to make graph reset. Does not erase logged data.
self.times = np.array([])
self.shifts = np.array([])
self.amplitudes = np.array([])
def startstop(self): #Flips button to begin or stop graphing.
if self.running is False: #Recording and graphing is off. Calling function will turn it on and activate the following lines
self.running = True
self.clear_graph() #Clears last run.
self.run_btn.config(text='Stop', bg='red')
#self.start_graph()
self.sample_time = float(self.sample_box.get())
self.pause_time = 0 #Resets pause adjustment time.
self.start_time = time.time() #Resets start time. TODO-Add adjustments for pauses
self.pause_point = time.time() #Last time that pause activated. Used to calculate pause_time later.
self.graph_thread = threading.Thread(target=self.start_graph)
self.graph_thread.start()
else: #graphing is on. Calling function will turn it off and activate the following line
self.running = False
self.run_btn.config(text='Start',bg='green')
self.recording=False
self.record_btn.config(text='Start recording', bg="green")
try: #Shuts down log file if it exists.
self.log.close()
except AttributeError as AE:
print(AE)
def startstop_record(self): #Flips recording button.
if self.running is False:
self.startstop() #Makes sure data is being transmitted before recording starts.
if self.recording is False: #Device is running, but not recording. Command turns recording on.
self.record_btn.config(text='Pause recording', bg="yellow")
try:
self.log.write('') #Attempts to write an empty string to test if file is open.
except AttributeError: #File has not been created -> run function to open a new log.
self.create_log()
except ValueError: #Previous log file has been closed -> run function to open new log.
self.create_log()
self.recording = True
self.pause_time += time.time() - self.pause_point #Adjusts for
else: #Device is running AND recording. Command pauses recording.
self.recording = False
self.pause_point = time.time()
self.record_btn.config(text='Start recording', bg="green")
def __init__(self, name, frame):
self.name = name
self.running = False
self.recording = False
#Read config -----------------------------------------------------------------------
config = open('SR830 config.txt','w+')
try:
config_read = config.readlines()
config_read = config_read[1].split('\t')
except IndexError: #config coudl not be read - creates default file.
config.write('COM Baudrate\ttimeout\tsampling time\n10\t9600\t0.1\t1')
config.close()
config = open('SR830 config.txt','r')
config_read = config.readlines()
config_read = config_read[1].split('\t')
com = config_read[0]
baud = int(config_read[1])
self.config_timeout = float(config_read[2])
self.sample_time = float(config_read[3])
config.close()
#establish communication -----------------------------------------------------------
com = 'COM'+str(com) #TODO- make this read from config
try:
SR_inst = serial.Serial(port=com, baudrate=baud, timeout=self.config_timeout) #Opens communication.
#Note: SR830 has adjustablebaud rate and parity, but defaults to 9600 and none, respectively.
self.inst = SR_inst
self.command('OUTX',0) #Tells device to output responses via RS-232.
print(self.inst.read(5)) #Prints response to OUTX command.
#self.command('OUTX?','') #TEST - see if communication has switched.
#print(self.inst.read(10)) #Should print b'0\r' for RS-232 communication.
self.command('DDEF',110)#Sets CH1 to R.
print(self.inst.read(5))
self.command('DDEF',210)#Sets CH2 to Theta.
print(self.inst.read(5))
except ValueError as ve:
SR_inst = 0
print(ve)
except AttributeError as ae:
SR_inst = 0
print (ae)
except NameError as ne:
print(ne)
SR_inst = 0
except serial.SerialException as SE:
SR_inst = 0
print(SE)
#Create Tkinter GUI------------------------------------------------------------------
id_label=tk.Label(frame, text=name)
id_label.grid(row=0,column=0, sticky=tk.N+tk.S+tk.E+tk.W)
version_label = tk.Label(frame, text = 'Version 0.1')
version_label.grid(row=0, column=1, sticky=tk.N+tk.S+tk.E+tk.W)
sample_label = tk.Label(frame, text="Sampling time =")
sample_label.grid(row=1,column=0,sticky=tk.N+tk.S+tk.E+tk.W)
self.sample_box = tk.Entry(frame)
self.sample_box.insert(tk.END, self.sample_time)
self.sample_box.grid(row=1,column=1,sticky=tk.N+tk.S+tk.E+tk.W)
self.run_btn = tk.Button(frame, text='Start', command = self.startstop)
self.run_btn.grid(row=2,column=0,sticky=tk.N+tk.S+tk.E+tk.W)
self.run_btn.config(bg="green")
self.record_btn = tk.Button(frame, text='Start recording', command = self.startstop_record)
self.record_btn.grid(row=2,column=1,sticky=tk.N+tk.S+tk.E+tk.W)
self.record_btn.config(bg="green")
#Graph setup:
self.f = Figure(figsize=(5,5),dpi=100) #Figure that graphs appears in
self.a = self.f.add_subplot() #111 means there is only 1 chart. use a.plot to plot lists.
self.a2 = self.a.twinx()
self.graph_canvas = FigureCanvasTkAgg(self.f, frame)
self.graph_canvas.get_tk_widget().grid(row=3, column=0,columnspan=2)
#Make grid resizeable
for row in range(4): #Number of rows
try:
tk.Grid.rowconfigure(frame,row,weight=2)
except AttributeError as AE: #FOr unfilled rows & columns
print(AE)
for column in range(2): #Number of columns
try:
#root.grid_columnconfigure(column, weight=1)
tk.Grid.columnconfigure(frame,column,weight=1)
except AttributeError as AE: #For unfilled rows & columns
print(AE)
#Extra declarations
self.sample_time = float(self.sample_box.get())-2*self.config_timeout #Adjusts for timeout of device.
self.times = np.array([]) #Holds time values queried (May not be recorded)
self.shifts = np.array([])#Holds shift values queried.
self.amplitudes = np.array([])#Holds amplitude values queried.
#self.start_time = time.time() #TODO - Move this somewhere better
self.previous_time = 0
time.sleep(1)
self.ani = animation.FuncAnimation(self.f,self.animate, interval=1000)
def SR830_exit(self): #Exit handler.
self.running = False
self.recording = False
try:
self.inst.close()
except AttributeError as AE:
print(AE)
root.destroy()
exit()
#TEST AREA: ------------------------------------------------------------------------------
# Create tk root -------------------
root = tk.Tk()
root.title('SR830')
frame1 = tk.Frame(root)
frame1.pack()
#Create instrument object ---------
sr1 = SR830("SSTR", frame1) #TODO - remove once testing is done.
atexit.register(sr1.SR830_exit)
root.protocol('WM_DELETE_WINDOW', sr1.SR830_exit)
root.mainloop()

QML gauges not updating from Python

I'm still getting to grips with QT... I've made a python file and a QML file. the Python file updates the gauge's value from data it gets over UDP.
This only works once though... the first UDP packet comes in and updates the gauge, but when it gets the next packet,despite the value updating, the gauge itself does not.
QML
CircularGauge {
id: circularGauge
x: 30
y: 30
value: itt1value
minimumValue: 0
maximumValue: 1200
tickmarksVisible: false
style: CircularGaugeStyle {
maximumValueAngle: 400
minimumValueAngle: 90
}
}
Python:
def configureApplication():
# Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
view.setTitle("my title")
# Load the QML file
qml_file = os.path.join(os.path.dirname(__file__), "maingui.qml")
view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))
# load the slots into the QML file
view.rootContext().setContextProperty("itt1value", 0)
t = threading.Thread(target=receivedata, args=(view,))
t.start()
# Show the window
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
# execute and cleanup
app.exec_()
del view
In the threaded method receivedata() I get the data from UDP, process it, then send it to the gauge like so:
view.rootContext().setContextProperty("itt1value", itt)
receivedata() has a while loop in it with the above details, but the gauge only actually updates once. If I put a statement in the QML file to display itt1value, it always has the correct value, so do I need to put in a method to detect the change to this value and re-paint the gauge?
Edit: I was asked for the details of receivedata(), so I have attached it here:
def receivedata(view):
print("Starting UDP server...")
UDP_IP = "192.168.0.14"
UDP_PORT = 49000
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
olditt = 0
loopruns = 0 # for debugging
while True:
rawstring = sock.recv(1024)
hexarray = []
#lots of irrelevent formatting here, result is int(value)
itt = float(hextoint(value, olditt))
olditt = itt
itt = format(itt, '.3f')
current = str(loopruns) # for debugging
view.setTitle(current) # for debugging
view.rootContext().setContextProperty("itt1value", itt)
loopruns = loopruns + 1
print(itt)
You have the following errors:
You cannot directly modify the GUI from another thread.
A value can be exported again with setContextProperty(), this will not change the previous value unless the QML is reloaded.
If you want "itt" to modify any value in QML it must be of compatible types, in this case the value of CircularGauge is "real" and therefore the type of data supported in python is float.
Considering the above, I have created a QObject since it can notify changes through signals since it is thread-safe, and export the QObject making connections using Connections.
main.py
import os
import random
import sys
import threading
import time
from PySide2.QtCore import QObject, QUrl, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQuick import QQuickView
class Connections(QObject):
titleChanged = Signal(str, arguments=["title"])
valueChanged = Signal(float, arguments=["value"])
def receivedata(connector):
# configurations
loopruns = 0
while True:
# other stuff
time.sleep(0.1)
itt = random.uniform(0.0, 1200.0)
connector.valueChanged.emit(itt)
connector.titleChanged.emit(str(loopruns))
loopruns += 1
def main(args):
app = QGuiApplication(args)
view = QQuickView(title="my title", resizeMode=QQuickView.SizeRootObjectToView)
connector = Connections()
connector.titleChanged.connect(view.setTitle)
view.rootContext().setContextProperty("connector", connector)
# Load the QML file
qml_file = os.path.join(os.path.dirname(__file__), "maingui.qml")
view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))
# start thread
threading.Thread(target=receivedata, args=(connector,)).start()
# Show the window
if view.status() == QQuickView.Error:
return -1
view.show()
# execute and cleanup
ret = app.exec_()
del view
return ret
if __name__ == "__main__":
sys.exit(main(sys.argv))
maingui.qml
import QtQml 2.13
import QtQuick.Extras 1.4
import QtQuick.Controls.Styles 1.4
CircularGauge {
id: circularGauge
value: 100
minimumValue: 0
maximumValue: 1200
tickmarksVisible: false
style: CircularGaugeStyle {
maximumValueAngle: 400
minimumValueAngle: 90
}
Connections{
target: connector
onValueChanged: circularGauge.value = value
}
}

Python, websocket auto-close on some machines

I've written some api to communicate with a website using websocketapp. It works fine only on 2 pc. If i put my code on every other pc the websocket doesnt receive any message and closes. I've tried a lot of different machines and operating systems, many version of python (included the same that works), wireless and wired connection but nothing changed. There's no error or exception. What can it be?
EDIT: i don't own the website or the server. All other methods send messages and parse the response in on_socket_message
import requests
import websocket
import time
from threading import Thread
from datetime import datetime
import json
from position import Position
from constants import ACTIVES
class IQOption():
practice_balance = 0
real_balance = 0
server_time = 0
positions = {}
instruments_categories = ["cfd","forex","crypto"]
top_assets_categories = ["forex","crypto","fx-option"]
instruments_to_id = ACTIVES
id_to_instruments = {y:x for x,y in ACTIVES.items()}
market_data = {}
binary_expiration_list = {}
open_markets = {}
digital_strike_list = {}
candle_data = []
latest_candle = 0
position_id = 0
quotes =[]
position_id_list=[]
def __init__(self,username,password,host="iqoption.com"):
self.username = username
self.password = password
self.host = host
self.session = requests.Session()
self.generate_urls()
self.socket = websocket.WebSocketApp(self.socket_url,on_open=self.on_socket_connect,on_message=self.on_socket_message,on_close=self.on_socket_close,on_error=self.on_socket_error)
def generate_urls(self):
"""Generates Required Urls to operate the API"""
#https://auth.iqoption.com/api/v1.0/login
self.api_url = "https://{}/api/".format(self.host)
self.socket_url = "wss://{}/echo/websocket".format(self.host)
self.login_url = self.api_url+"v1.0/login"
self.profile_url = self.api_url+"profile"
self.change_account_url = self.profile_url+"/"+"changebalance"
self.getprofile_url = self.api_url+"getprofile"
def login(self):
"""Login and set Session Cookies"""
print("LOGIN")
data = {"email":self.username,"password":self.password}
self.log_resp = self.session.request(url="https://auth.iqoption.com/api/v1.0/login",data=data,method="POST")
requests.utils.add_dict_to_cookiejar(self.session.cookies, dict(platform="9"))
self.__ssid = self.log_resp.cookies.get("ssid")
print(self.__ssid)
self.start_socket_connection()
time.sleep(1) ## artificial delay to complete socket connection
self.log_resp2 = self.session.request(url="https://eu.iqoption.com/api/getprofile",method="GET")
ss = self.log_resp2._content.decode('utf-8')
js_ss=json.loads(ss)
self.parse_account_info(js_ss)
self.balance_id = js_ss["result"]["balance_id"]
self.get_instruments()
self.get_top_assets()
self.setOptions()
#self.getFeatures()
time.sleep(1)
print(js_ss["isSuccessful"])
return js_ss["isSuccessful"]
def on_socket_message(self,socket,message):
#do things
def on_socket_connect(self,socket):
"""Called on Socket Connection"""
self.initial_subscriptions()
print("On connect")
def initial_subscriptions(self):
self.send_socket_message("ssid",self.__ssid)
self.send_socket_message("subscribe","tradersPulse")
def on_socket_error(self,socket,error):
"""Called on Socket Error"""
print(message)
def on_socket_close(self,socket):
"""Called on Socket Close, does nothing"""
def start_socket_connection(self):
"""Start Socket Connection"""
self.socket_thread = Thread(target=self.socket.run_forever)
self.socket_thread.start()
def send_socket_message(self,name,msg):
#print(msg)
data = {"name":name,"msg":msg}
self.socket.send(json.dumps(data))
Here is an example running under Gevent Websockets. This makes it ASYNC (which I suspect is part of your problem) and allows for bidirectional communication.
import gevent
from gevent import monkey, signal, Timeout, sleep, spawn as gspawn
monkey.patch_all()
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from geventwebsocket import WebSocketError
import bottle
from bottle import get, route, template, request, response, abort, static_file
import ujson as json
#route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='static')
#route('/ws/remote')
def handle_websocket():
wsock = request.environ.get('wsgi.websocket')
if not wsock:
abort(400, 'Expected WebSocket request.')
while 1:
try:
message = ''
with Timeout(2, False) as timeout:
message = wsock.receive()
if message:
message = json.loads(message)
if 'command' in message:
r.command(message['command'])
except WebSocketError:
break
except Exception as exc:
print(str(exc))
#get('/')
def remote():
return template('templates/remote.tpl', title='WebsocketTest', websocket=WEBSOCKET, command='command', status=status)
if __name__ == '__main__':
r=None
status="Connecting..."
gspawn(initialize)
print 'Started...'
HOST = socket.gethostbyname(socket.gethostname())
HOST = 'localhost'
WEBSOCKET = 'ws://{}/ws/remote'.format(HOST)
botapp = bottle.app()
server = WSGIServer(("0.0.0.0", 80), botapp, handler_class=WebSocketHandler)
def shutdown():
print('Shutting down ...')
server.stop(timeout=60)
exit(signal.SIGTERM)
gevent.signal(signal.SIGTERM, shutdown)
gevent.signal(signal.SIGINT, shutdown) #CTRL C
server.serve_forever()
Then in your HTML you really should use reconnecting websocket library
https://github.com/joewalnes/reconnecting-websocket
<button id="TRIGGERED" type="button" class="btn btn-outline-primary">TRIGGER</button>
<script type="text/javascript" src="/static/reconnecting-websocket.min.js"></script>
<script>
var ws = new ReconnectingWebSocket('{{websocket}}');
ws.reconnectInterval = 3000;
ws.maxReconnectAttempts = 10;
ws.onmessage = function (evt) {
var wsmsg = JSON.parse(evt.data);
console.log(evt.data)
};
$("button").click(function() {
<!--console.log(this.id);-->
ws.send(JSON.stringify({'{{command}}': this.id}));
});
</script>

How do I live update a bokeh chart in a flask app every time the data changes?

If I have a flask app with a couple of charts made from bokeh, how do I update the chart live while its in the browser as new data gets added or changed? I am new to bokeh and read the documentation that I have to start a bokeh server, I still do not understand what I should be adding to my code to make it update.
This is a Bokeh server app with a callback executed every 1s and data updated every 5s. Looking at the current plot_data length and compering it with last_data_length let us determine when we need to update the plot (code works for Bokeh v1.0.4)
import random, time
from tornado.ioloop import IOLoop
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
from threading import Thread
class BokehApp():
plot_data = []
last_data_length = None
def __init__(self):
thread = Thread(target = self.startDataAcquisition)
thread.start()
io_loop = IOLoop.current()
server = Server(applications = {'/myapp': Application(FunctionHandler(self.make_document))}, io_loop = io_loop, port = 5001)
server.start()
server.show('/myapp')
io_loop.start()
def startDataAcquisition(self):
while True:
self.plot_data.append({'x': [random.random()], 'y': [random.random()], 'color': [random.choice(['red', 'blue', 'green'])]})
time.sleep(5)
def make_document(self, doc):
source = ColumnDataSource({'x': [], 'y': [], 'color': []})
fig = figure(title = 'Streaming Circle Plot!', sizing_mode = 'scale_both')
fig.circle(source = source, x = 'x', y = 'y', color = 'color', size = 10)
def update():
if self.last_data_length is not None and self.last_data_length != len(self.plot_data):
source.stream(self.plot_data[-1])
self.last_data_length = len(self.plot_data)
doc.add_root(fig)
doc.add_periodic_callback(update, 1000)
if __name__ == '__main__':
app = BokehApp()
Result:

Categories