I asked a question about this specific code a few days ago, however, some changes have been made since then. I am trying to write a mobile application where when the user presses a button, they can view various real time graphs. Currently, I am just working on a simple line plot. The issue is whenever I press the plot button the screen transitions correctly and the canvas is shown, but nothing is actually plotted on the canvas. It is just empty. Below is my python code and .kv file.
button_log = []
class HomeScreen(Screen):
def button_log(self,button):
button_text = button.text
global button_log
button_log.append(button_text)
return button_text, button_log
class LinePlotScreen(Screen):
def on_pre_enter(self):
graph_widget = self.ids.graph_widget
graph_widget.start_update()
class WindowManager(ScreenManager):
pass
class GraphWidget1(BoxLayout):
def __init__(self,**kwargs):
super(GraphWidget1,self).__init__(**kwargs)
self.fig , self.ax = plt.subplots()
self.line, = self.ax.plot([],[])
canvas = FigureCanvasKivyAgg(self.fig)
self.add_widget(canvas)
self.x_data = []
self.y_data = []
self.is_running = False
def start_update(self):
if not self.is_running:
self.is_running = True
#self.thread = threading.Thread(target=self.update_graph)
#self.thread.daemon = True
#self.thread.start()
#Clock.schedule_interval(self.update_graph, 0.1)
Clock.schedule_once(self.update_graph)
def stop_update(self):
if self.is_running():
self.is_running = False
def update_graph(self, dt):
x = np.linspace(0,10,100)
y = np.sin(x)
self.x_data.append(x)
self.y_data.append(y)
#self.ax.cla()
self.line.set_data(self.x_data,self.y_data)
self.ax.relim()
self.ax.autoscale_view(True,True,True)
self.canvas.draw
if self.is_running:
Clock.schedule_once(self.update_graph, 0.1)
kv = Builder.load_file('Simplified.kv')
class TestApp(App):
def build(self):
self.stop_value = 0
self.data_int = None
self.left_shoe = []
self.right_shoe = []
return kv
def on_button_press(self):
global button_log
if button_log[-1] == 'Start Data Collection':
UDP_IP = "192.168.1.100"
UDP_PORT = 3333
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((socket.gethostname(), UDP_PORT))
def collect_data(dt):
nonlocal sock
nonlocal self
global button_log
data, addr = sock.recvfrom(1024)
self.data_int = fmt_str(data)
if self.data_int[0] == 1:
self.left_shoe.append(self.data_int)
elif self.data_int[0] == 2:
self.right_shoe.append(self.data_int)
if self.stop_value == 1:
Clock.unschedule(collect_data)
Clock.schedule_interval(collect_data, 0.1)
elif button_log[-1] == 'Stop Data Collection':
self.stop_value = 1
elif button_log[-1] == 'Plot Line Charts':
self.root.current = 'lineplot'
line_plot_screen = self.root.get_screen('lineplot')
graph_widget = line_plot_screen.ids.graph_widget
Simplified.kv
WindowManager:
HomeScreen:
LinePlotScreen:
<HomeScreen>:
name: 'home'
BoxLayout:
orientation: 'vertical'
size: root.width,root.height
Label:
text: 'Home'
font_size: 32
Button:
id: Start
text: 'Start Data Collection'
font_size: 32
pos_hint: {'center_x':0.5}
size_hint: (0.5,1)
valign: 'center'
on_press: root.button_log(self)
on_release: app.on_button_press()
Button:
id: Stop
text: 'Stop Data Collection'
font_size: 32
pos_hint: {'center_x':0.5}
size_hint: (0.5,1)
valign: 'center'
on_press: root.button_log(self)
on_release: app.on_button_press()
Button:
id: plot_line
text: 'Plot Line Charts'
font_size: 32
pos_hint: {'center_x':0.5}
size_hint: (0.5,1)
valign: 'center'
on_press:
root.button_log(self)
app.on_button_press()
<LinePlotScreen>:
name: 'lineplot'
BoxLayout:
id: box
orientation: 'vertical'
size: root.width,root.height
Label:
text: 'Line Graphs'
font_size: 32
GraphWidget1:
id: graph_widget
I was having issues before trying to just transition the screen and have the figure displayed. Now, I am dealing with the issue of transitioning the screen successfully but the figure not plotting. I think the issue might be with the 'self.canvas.draw' line but I'm not totally sure. I tried adding parenthesis at the end of that line to make it a function call, but that just caused the window to crash when the plot button was pressed. I tried looking at other StackOverflow questions and couldn't really find one that fit my situation. Any help would be greatly appreciated, thanks.
You need to call the plot()
method of pyplot
in order for the new data to be plotted, and you must set the figure
attribute of the FigureCanvasKivyAgg
to the current updated plot.
One way to do that, is to start by saving a reference to the FigureCanvasKivyAgg
:
class GraphWidget1(BoxLayout):
def __init__(self,**kwargs):
super(GraphWidget1,self).__init__(**kwargs)
self.fig , self.ax = plt.subplots()
self.line, = self.ax.plot([],[])
self.plot = FigureCanvasKivyAgg(self.fig)
self.add_widget(self.plot)
self.x_data = []
self.y_data = []
self.is_running = False
Then, in the update_graph()
:
def update_graph(self, dt):
plt.cla()
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.grid(True)
self.plot.figure = plt.gcf()
self.plot.draw()