I needed a start and stop button for reading data continuously with a while loop, so I used the first example in here https://wiki.wxpython.org/LongRunningTasks. Then I wanted to change a graphic in continuous as well but I couldn't do it so to simulate that I tried to just change a TextCtrl. So I used wx.CallAfter to write on the TextCtrl, and I see that 'internaly' it changes but it doesn´t update the frame. I also tried using the Update() and Refresh() but that didn't work either. So I have no idea what to do and I've search all the places I could and don't know what to do anymore, so any help is appreciated!
import nidaqmx
import wx
import wxmplot
import time
from threading import Thread
from numpy import mean, std
from nidaqmx.constants import AcquisitionType, DataTransferActiveTransferMode, TerminalConfiguration
ID_START = wx.NewId()
ID_STOP = wx.NewId()
EVT_RESULT_ID = wx.NewId()
def EVT_RESULT(win, func):
"""Define Result Event."""
win.Connect(-1, -1, EVT_RESULT_ID, func)
class ResultEvent(wx.PyEvent):
"""Simple event to carry arbitrary result data."""
def __init__(self, data):
"""Init Result Event."""
wx.PyEvent.__init__(self)
self.SetEventType(EVT_RESULT_ID)
self.data = data
myEVT = wx.NewEventType()
EVT = wx.PyEventBinder(myEVT, 1)
class MyEvent(wx.PyCommandEvent):
def __init__(self, evtType, id):
wx.PyCommandEvent.__init__(self, evtType, id)
myVal = None
def SetMyVal(self, val):
self.myVal = val
def GetMyVal(self):
return self.myVal
# janela para ler canais
class NewTaskWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(1000,500))
self.parent = parent
self.title = title
self.task_panel = wx.Panel(self, wx.ID_ANY, size = (1000,500))
self.canais = ['Canal', 'Dev1/ai0','Dev1/ai1','Dev1/ai2','Dev1/ai3','Dev1/ai4','Dev1/ai5',
'Dev1/ai6','Dev1/ai7','Dev1/ai8','Dev1/ai9','Dev1/ai10','Dev1/ai11',
'Dev1/ai12','Dev1/ai13','Dev1/ai14','Dev1/ai15']
self.cfgs = [TerminalConfiguration.DEFAULT,
TerminalConfiguration.DEFAULT,
TerminalConfiguration.DIFFERENTIAL,
TerminalConfiguration.NRSE,
TerminalConfiguration.PSEUDODIFFERENTIAL,
TerminalConfiguration.RSE]
self.configs = ['Modo', 'DEFAULT', 'DIFFERENTIAL', 'NRSE', 'PSEUDODIFFERENTIAL', 'RSE']
self.config1 = TerminalConfiguration.DEFAULT
self.config2 = TerminalConfiguration.DEFAULT
self.config3 = TerminalConfiguration.DEFAULT
self.config4 = TerminalConfiguration.DEFAULT
self.chan1 = 'canal1'
self.chan2 = 'canal2'
self.chan3 = 'canal3'
self.chan4 = 'canal4'
wx.Button(self.task_panel, ID_START, 'Start', pos=(10,10))
wx.Button(self.task_panel, ID_STOP, 'Stop', pos=(10,90))
self.status = wx.StaticText(self.task_panel, -1, '', pos=(800,0))
self.lista_canais_1 = wx.ComboBox(self.task_panel, id=1, pos=(106,10),
size=(100,80), choices=self.canais, style=wx.CB_READONLY)
self.lista_canais_1.SetSelection(0)
self.lista_canais_2 = wx.ComboBox(self.task_panel, id=2, pos=(106,40),
size=(100,80), choices=self.canais, style=wx.CB_READONLY)
self.lista_canais_2.SetSelection(0)
self.lista_canais_3 = wx.ComboBox(self.task_panel, id=3, pos=(106,70),
size=(100,80), choices=self.canais, style=wx.CB_READONLY)
self.lista_canais_3.SetSelection(0)
self.lista_canais_4 = wx.ComboBox(self.task_panel, id=4, pos=(106,100),
size=(100,80), choices=self.canais, style=wx.CB_READONLY)
self.lista_canais_4.SetSelection(0)
self.lista_configs_1 = wx.ComboBox(self.task_panel, id=5, pos=(216,10),
size=(100,80), choices=self.configs, style= wx.CB_READONLY)
self.lista_configs_1.SetSelection(0)
self.lista_configs_2 = wx.ComboBox(self.task_panel, id=6, pos=(216,40),
size=(100,80), choices=self.configs, style= wx.CB_READONLY)
self.lista_configs_2.SetSelection(0)
self.lista_configs_3 = wx.ComboBox(self.task_panel, id=7, pos=(216,70),
size=(100,80), choices=self.configs, style= wx.CB_READONLY)
self.lista_configs_3.SetSelection(0)
self.lista_configs_4 = wx.ComboBox(self.task_panel, id=8, pos=(216,100),
size=(100,80), choices=self.configs, style= wx.CB_READONLY)
self.lista_configs_4.SetSelection(0)
self.check1 = wx.CheckBox(self.task_panel, wx.ID_ANY, "ON", pos=(330,10), size=(40,20))
self.check2 = wx.CheckBox(self.task_panel, wx.ID_ANY, "ON", pos=(330,40), size=(40,20))
self.check3 = wx.CheckBox(self.task_panel, wx.ID_ANY, "ON", pos=(330,70), size=(40,20))
self.check4 = wx.CheckBox(self.task_panel, wx.ID_ANY, "ON", pos=(330,100), size=(40,20))
self.input_pontos = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_PROCESS_ENTER,
value = "0", size =(90,20), pos = (10,70))
self.media1_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (420,12)) #<<<<-----------------------
self.media2_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (420,42))
self.media3_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (420,72))
self.media4_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (420,102))
self.desvio1_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (560,12))
self.desvio2_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (560,42))
self.desvio3_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (560,72))
self.desvio4_text = wx.TextCtrl(self.task_panel , wx.ID_ANY, style = wx.TE_READONLY,
size =(90,20), pos = (560,102))
wx.StaticText(self.task_panel, wx.ID_ANY, "Nº Pontos:", (10,50))
wx.StaticText(self.task_panel, wx.ID_ANY, "Média:", (380,12))
wx.StaticText(self.task_panel, wx.ID_ANY, "Média:", (380,42))
wx.StaticText(self.task_panel, wx.ID_ANY, "Média:", (380,72))
wx.StaticText(self.task_panel, wx.ID_ANY, "Média:", (380,102))
wx.StaticText(self.task_panel, wx.ID_ANY, "Desvio: ", (520,12))
wx.StaticText(self.task_panel, wx.ID_ANY, "Desvio: ", (520,42))
wx.StaticText(self.task_panel, wx.ID_ANY, "Desvio: ", (520,72))
wx.StaticText(self.task_panel, wx.ID_ANY, "Desvio: ", (520,102))
self.Bind(wx.EVT_COMBOBOX, self.SelectChannel, self.lista_canais_1)
self.Bind(wx.EVT_COMBOBOX, self.SelectChannel, self.lista_canais_2)
self.Bind(wx.EVT_COMBOBOX, self.SelectChannel, self.lista_canais_3)
self.Bind(wx.EVT_COMBOBOX, self.SelectChannel, self.lista_canais_4)
self.Bind(wx.EVT_COMBOBOX, self.SelectConfig, self.lista_configs_1)
self.Bind(wx.EVT_COMBOBOX, self.SelectConfig, self.lista_configs_2)
self.Bind(wx.EVT_COMBOBOX, self.SelectConfig, self.lista_configs_3)
self.Bind(wx.EVT_COMBOBOX, self.SelectConfig, self.lista_configs_4)
self.Bind(wx.EVT_BUTTON, self.OnStart, id=ID_START)
self.Bind(wx.EVT_BUTTON, self.OnStop, id=ID_STOP)
EVT_RESULT(self,self.OnResult)
self.worker = None
self.Bind(EVT, self.UpdateMedia1)
def OnStart(self, event): #<<<<<<<<<<<--------------
"""Start Computation."""
# Trigger the worker thread unless it's already busy
global pontos, checks, chans, configz
pontos=int(self.input_pontos.GetValue())
checks = [self.check1.GetValue(), self.check2.GetValue(),
self.check3.GetValue(), self.check4.GetValue()]
chans = [self.chan1, self.chan2, self.chan3, self.chan4]
configz = [self.config1, self.config2, self.config3, self.config4]
#self.media1_text.write('adsdsad')
if len(chans) == len(set(chans)):
self.graph_panel = wxmplot.plotpanel.PlotPanel(self.task_panel, pos=(0,150),
size=(500,350))
self.graph_panel.plot([0],[0])
if not self.worker:
self.status.SetLabel('Starting computation')
self.worker = WorkerThread(self, self.parent, self.title, 1)
else:
self.ErroCanais()
def OnStop(self, event):
"""Stop Computation."""
# Flag the worker thread to stop if running
if self.worker:
self.status.SetLabel('Trying to abort computation')
self.worker.abort()
def OnResult(self, event):
"""Show Result status."""
if event.data is None:
# Thread aborted (using our convention of None return)
self.status.SetLabel('Computation aborted')
else:
# Process results here
self.status.SetLabel('Computation Result: %s' % event.data)
# In either event, the worker is done
self.worker = None
def UpdateMedia1(self, x): # <<<<<<<----------------------
self.media1_text.write(x)
print(self.media1_text.GetValue())
print(wx.IsMainThread())
def ErroCanais(self):
chan_erro_frame = wx.Frame(self, wx.ID_ANY, "ERRO", size=(200,100))
chan_erro_panel = wx.Panel(chan_erro_frame, wx.ID_ANY, size=(200,100))
wx.StaticText(chan_erro_panel, wx.ID_ANY, "CANAIS IGUAIS", (30,20))
chan_erro_frame.Show()
def SelectChannel(self, event):
if event.GetId() == 1:
if event.GetString() == 'Canal':
self.chan1 = 'canal1'
else:
self.chan1 = event.GetString()
elif event.GetId() == 2:
if event.GetString() == 'Canal':
self.chan2 = 'canal2'
else:
self.chan2 = event.GetString()
elif event.GetId() == 3:
if event.GetString() == 'Canal':
self.chan3 = 'canal3'
else:
self.chan3 = event.GetString()
elif event.GetId() == 4:
if event.GetString() == 'Canal':
self.chan4 = 'canal4'
else:
self.chan4 = event.GetString()
def SelectConfig(self, event):
if event.GetId() == 5:
self.config1 = self.cfgs[event.GetSelection()]
elif event.GetId() == 6:
self.config2 = self.cfgs[event.GetSelection()]
elif event.GetId() == 7:
self.config3 = self.cfgs[event.GetSelection()]
elif event.GetId() == 8:
self.config4 = self.cfgs[event.GetSelection()]
class WorkerThread(Thread,NewTaskWindow):
"""Worker Thread Class."""
def __init__(self, notify_window, parent, title, value):
"""Init Worker Thread Class."""
Thread.__init__(self)
NewTaskWindow.__init__(self, parent, title)
self._notify_window = notify_window
self._want_abort = 0
# This starts the thread running on creation, but you could
# also make the GUI thread responsible for calling this
self.start()
def run(self):
"""Run Worker Thread."""
# This is the code executing in the new thread. Simulation of
# a long process (well, 10s here) as a simple loop - you will
# need to structure your processing so that you periodically
# peek at the abort variable
global pontos, checks, chans, configz
#chann1, chann2, chann3, chann4 = "","","",""
#chanz = [chann1, chann2, chann3, chann4]
#task = nidaqmx.Task()
#for i in range(4):
#if checks[i]==True:
#pass
# task.ai_channels.add_ai_voltage_chan(chans[i],
# terminal_config=configz[i])
# task.timing.cfg_samp_clk_timing(rate=10123,
# sample_mode=AcquisitionType.CONTINUOUS,
# samps_per_chan=10000)
# chanz[i] = nidaqmx._task_modules.channels.ai_channel.AIChannel(task._handle,
# chans[i])
# chanz[i].ai_data_xfer_mech = DataTransferActiveTransferMode.INTERRUPT
#data = []
pontos = 10
for i in range(pontos):
#p = []
#r = task.read(1)
#p.append(r)
#data.append(mean(p))
#event = MyEvent(myEVT, self.GetId())
#event.SetMyVal(i)
#self.GetEventHandler().ProcessEvent(event)
wx.CallAfter(self.UpdateMedia1, str(i)) #<<<<<<------------------
time.sleep(1)
if self._want_abort:
# Use a result of None to acknowledge the abort (of
# course you can use whatever you'd like or even
# a separate event type)
wx.PostEvent(self._notify_window, ResultEvent(None))
return
# Here's where the result would be returned (this is an
# example fixed result of the number 10, but it could be
# any Python object)
#wx.PostEvent(self._notify_window, ResultEvent(10))
def abort(self):
"""abort worker thread."""
# Method for use by main thread to signal an abort
self._want_abort = 1
class MainWindow(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title, size=(500,500))
self.CreateStatusBar() # A StatusBar in the bottom of the window
self.panel = wx.Panel(self, wx.ID_ANY)
new_task_button = wx.Button(self.panel, wx.ID_ANY, 'New Task', (10, 10))
# Setting up the menu.
filemenu= wx.Menu()
# wx.ID_ABOUT and wx.ID_EXIT are standard ids provided by wxWidgets.
menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
# Creating the menubar.
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
self.SetMenuBar(menuBar) # Adding the MenuBar to the Frame content.
self.v1_check = wx.CheckBox(self.panel, id=20, label="Valvula 1", pos=(20,50), size=(70,20))
self.v2_check = wx.CheckBox(self.panel, id=21, label="Valvula 2", pos=(20,70), size=(70,20))
self.v3_check = wx.CheckBox(self.panel, id=22, label="Valvula 3", pos=(20,90), size=(70,20))
self.v4_check = wx.CheckBox(self.panel, id=23, label="Valvula 4", pos=(20,110), size=(70,20))
self.v5_check = wx.CheckBox(self.panel, id=24, label="Porta 5", pos=(20,130), size=(70,20))
self.v6_check = wx.CheckBox(self.panel, id=25, label="Porta 6", pos=(20,150), size=(70,20))
self.v7_check = wx.CheckBox(self.panel, id=26, label="Porta 7", pos=(20,170), size=(70,20))
self.v8_check = wx.CheckBox(self.panel, id=27, label="Porta 8", pos=(20,190), size=(70,20))
self.v9_check = wx.CheckBox(self.panel, id=28, label="Porta 9", pos=(20,210), size=(70,20))
self.v10_check = wx.CheckBox(self.panel, id=29, label="Porta 10", pos=(20,230), size=(70,20))
# Set events.
self.Bind(wx.EVT_BUTTON, self.OnNewTask, new_task_button)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v1_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v2_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v3_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v4_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v5_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v6_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v7_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v8_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v9_check)
self.Bind(wx.EVT_CHECKBOX, self.AbrirFecharValvula, self.v10_check)
self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
self.Show(True)
def AbrirFecharValvula(self, event):
vchecksid=[20,21,22,23,24,25,26,27,28,29]
vchecks = [self.v1_check,self.v2_check,self.v3_check,self.v4_check,self.v5_check,
self.v6_check,self.v7_check,self.v8_check,self.v9_check,self.v10_check]
#num = str(vchecksid.index(event.GetId()))
#porta = 'Dev1/port'+num+'/line1'
for check in vchecks:
if check.GetId()==event.GetId():
#with nidaqmx.Task() as task:
#task.do_channels.add_do_chan(porta)
#task.write(check.GetValue())
print(check.GetValue())
def OnNewTask(self, event):
newin = NewTaskWindow(self.panel, 'Task')
newin.Show()
def OnAbout(self,e):
# A message dialog box with an OK button. wx.OK is a standard ID in wxWidgets.
dlg = wx.MessageDialog( self, "A user interface with nidaqmx",
"About NIDAQMX GUI", wx.OK)
dlg.ShowModal() # Show it
dlg.Destroy() # finally destroy it when finished.
def OnExit(self,e):
self.Close(True) # Close the frame.
# self.graph_panel.plot_many([(range(5),range(5)),(range(5),[0,0,0,0,0])])#[(x1,y1),(x2,y2)]
if __name__=='__main__':
app = wx.App(False)
frame = MainWindow(None, "NIDAQMX GUI")
frame.Centre()
app.MainLoop()
del app
I commented with <<<<<----- the parts of the code at cause. And you can run it to see for your selves - click New Task and then Start. So the CallAfter is calling to UpdateMedia1 method and it shows that it is in the main thread and that it is changing the value of the media1 textctrl but its not changing in the window and I don't understand why. I've also tried creating my own event and use PostEvent but that didn't work either. I know this is a long question so thanks for your time reading!!
Your WorkerThread
class is derived from NewTaskWindow
so when you create WorkerThread
you also create a new invisible NewTaskWindow
In
wx.CallAfter(self.UpdateMedia1, str(i))
the self
refers to the invisible second frame and therefore its (invisible) text control is updated.
Solution
Don't inherit NewTaskWindow
in WorkerThread
and call the UpdateMedia1
of the visible frame object instead (you already gave it as parameter when creating WorkerThread
).