Kivy & pyvisa - 如何在 GPIB 连接后将 ObjectProperty 设置为 None?

Kivy & pyvisa - How to make an ObjectProperty to None after a GPIB connection?

我正面临 kivy 和 pyvisa 的真正阻塞问题,我真的不知道如何找到解决方案。

在下面的代码中,我有一个名为 'device' 的 ObjectProperty,它被初始化为 None。我想用它来启动 GPIB 连接。当这个关闭时,我想再次将设备属性设置为None。

下面的所有代码都是一个简单的例子,试图找到解决这个问题的方法,但我在实际应用程序中确实遇到了这个问题。这个应用程序的功能之一是在列表中选择要使用的设备,我不可能提前知道哪一个可用或不可用,甚至 属性 'device'将在 GPIB 或其他协议中连接。这就是为什么将其重置为 None.

对我来说很重要

代码如下:

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()

所以,这是我的问题:当我从 GPIB 连接和断开设备时,一切都进行得很顺利,但是当我尝试在 'device' return 到 None 之后断开连接我有这个错误:

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

所以,这就是为什么它对我来说很奇怪:如果我尝试做同样的事情,而不使用 kivy,在 python 命令行中,它有效!

>>> 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

所以,我真的不明白为什么它不起作用... 有人知道如何解决这个问题吗?

N.B : 这是我第一次 post 在这里,所以我希望所有的解释都清楚并且我没有犯错 ^^ 如果是这样的话请告诉我!

https://kivy.org/doc/stable/api-kivy.properties.html#kivy.properties.Property

None是一个特例:可以将a属性的默认值设置为None,但不能将None设置为a属性 之后。如果你真的想这样做,你必须声明 属性 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.

改变

device          = ObjectProperty(None)

device          = ObjectProperty(None, allownone=True)