I'm facing a real blocking issue with kivy and pyvisa and I'm really lost about how to find a solution to this.
In the code below, I have an ObjectProperty called 'device' who's initialized to None. I want to use it to start a GPIB connection. When this one is closed, I want to set the device property to None again.
All the code below is a simple example to try to find a solution to this problem, but I'm facing actually this issue in a real application. One of the functions of this application is to choose which equipment to use in a list, and it is impossible for me to know in advance which one will be available or not, or even if the property 'device' will be connected in GPIB or in another protocol. This is why it is important for me to reset it to None.
Here is the code:
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty, BooleanProperty, StringProperty
from pyvisa import ResourceManager
class NoneScreen(Screen):
# Properties
address = StringProperty('GPIB0::10::INSTR')
isConnected = BooleanProperty(False)
device = ObjectProperty(None)
# Connect/Disconnect very basicaly to a GPIB device
def connect(self):
if not self.isConnected:
print('Connect to', self.address)
rm = ResourceManager()
self.device = rm.open_resource(self.address)
self.isConnected = True
else:
print('Disconnect from', self.address)
self.device.close()
self.isConnected = False
# Test the state of the connection
def testCon(self):
try:
self.device.query('*IDN?')
except:
print('You are not connected')
else:
print('You are connected :)')
# Try something to make device properties None again
def noneFct(self):
self.device = None
# App
class MainApp(App):
def build(self):
ns = NoneScreen()
return ns
if __name__ == '__main__':
MainApp().run()
Main.kv
<NoneScreen>:
BoxLayout:
orientation: 'vertical'
Button:
text: 'Print'
on_release: print('self.device =', root.device)
Button:
text: 'Connect'
on_release: root.connect()
Button:
text: 'Test Connexion'
on_release: root.testCon()
Button:
text: 'Become None'
on_release: root.noneFct()
So, here is my problem : when I connect and disconnect an equipment from the GPIB, all is going well BUT when I try to make 'device' return to None after disconnection I have this error :
self.device = None
Connect to GPIB0::10::INSTR
You are connected :)
self.device = GPIBInstrument at GPIB0::10::INSTR
Disconnect from GPIB0::10::INSTR
self.device = GPIBInstrument at GPIB0::10::INSTR
You are not connected
[INFO ] [Base ] Leaving application in progress...
Traceback (most recent call last):
File "C:\Users\Frederic\Desktop\debug\main.py", line 51, in <module>
MainApp().run()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\app.py", line 855, in run
runTouchApp()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\base.py", line 504, in runTouchApp
EventLoop.window.mainloop()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\core\window\window_sdl2.py", line 747, in mainloop
self._mainloop()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\core\window\window_sdl2.py", line 479, in _mainloop
EventLoop.idle()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\base.py", line 342, in idle
self.dispatch_input()
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\base.py", line 327, in dispatch_input
post_dispatch_input(*pop(0))
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\base.py", line 293, in post_dispatch_input
wid.dispatch('on_touch_up', me)
File "kivy\_event.pyx", line 707, in kivy._event.EventDispatcher.dispatch
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
self.dispatch('on_release')
File "kivy\_event.pyx", line 703, in kivy._event.EventDispatcher.dispatch
File "kivy\_event.pyx", line 1214, in kivy._event.EventObservers.dispatch
File "kivy\_event.pyx", line 1098, in kivy._event.EventObservers._dispatch
File "C:\Users\Frederic\AppData\Local\Programs\Python\Python37\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
exec(__kvlang__.co_value, idmap)
File "C:\Users\Frederic\Desktop\debug\main.kv", line 20, in <module>
on_release: root.noneFct()
File "C:\Users\Frederic\Desktop\debug\main.py", line 38, in noneFct
self.device = None
File "kivy\properties.pyx", line 497, in kivy.properties.Property.__set__
File "kivy\properties.pyx", line 541, in kivy.properties.Property.set
File "kivy\properties.pyx", line 532, in kivy.properties.Property.set
File "kivy\properties.pyx", line 1001, in kivy.properties.ObjectProperty.check
File "kivy\properties.pyx", line 570, in kivy.properties.Property.check
ValueError: None is not allowed for NoneScreen.device
So, here is why it's going weird for me : if I try to do the same thing, without using kivy, in a python command line, it works !
>>> import pyvisa
>>> rm = pyvisa.ResourceManager()
>>> device = None
>>> print(device)
None
>>> device = rm.open_resource('GPIB0::10::INSTR')
>>> print(device)
GPIBInstrument at GPIB0::10::INSTR
>>> device.query('*IDN?')
'Agilent Technologies,33250A,0,2.01-1.01-1.00-03-2\n'
>>> device.close()
>>> print(device)
GPIBInstrument at GPIB0::10::INSTR
>>> device = None
>>> print(device)
None
So, I really don't understand why it doesn't work... Does anyone have any idea on how to fix this ?
N.B : This my first post here, so I hope all is clearly explained and that I didn't make a mistake ^^ Please let me know if it is the case !
https://kivy.org/doc/stable/api-kivy.properties.html#kivy.properties.Property
None is a special case: you can set the default value of a Property to None, but you can’t set None to a property afterward. If you really want to do that, you must declare the Property with allownone=True:
class MyObject(Widget):
hello = ObjectProperty(None, allownone=True)
# then later
a = MyObject()
a.hello = 'bleh' # working
a.hello = None # working too, because allownone is True.
Change
device = ObjectProperty(None)
To
device = ObjectProperty(None, allownone=True)