12if sys.version_info >= (3, 11):
13 from PySide6.QtWidgets
import QApplication
14 from PySide6.QtQuick
import QQuickView
15 from PySide6.QtCore
import QObject, Signal, Slot, Property, QUrl
16 from PySide6.QtGui
import QIcon
18 from PySide2.QtWidgets
import QApplication
19 from PySide2.QtQuick
import QQuickView
20 from PySide2.QtCore
import QObject, Signal, Slot, Property, QUrl
21 from PySide2.QtGui
import QIcon
24 def __init__(self, stream):
25 super().__init__(stream)
27 def handle_async_wait_result(self, image, status):
28 if status
is cvb.WaitStatus.Ok:
29 print(
"New image arrived")
31 if BackEnd.grab_image
or BackEnd.single_shot:
32 BackEnd.single_shot =
False
33 BackEnd.image_controller.refresh(image)
35 if BackEnd.has_ring_buffer:
38 elif BackEnd.server_started
and (
not BackEnd.server_single_shot_send_enabled
or BackEnd.server_single_shot_send)
and BackEnd.server.stream.is_running:
40 print(
'Sending image')
41 BackEnd.server_single_shot_send =
False
42 if BackEnd.has_ring_buffer:
43 BackEnd.buffer_index = image.buffer_index
44 BackEnd.img_list[image.buffer_index] = image
46 def image_release(img):
48 BackEnd.img_list[BackEnd.buffer_index].unlock()
49 print(
'Unlocked buffer at index ' + str(img.buffer_index))
51 BackEnd.server.stream.send(image, image_release)
52 BackEnd.image_controller.refresh(BackEnd.img_list[BackEnd.buffer_index])
54 BackEnd.server.stream.send(image)
55 BackEnd.image_controller.refresh(image)
57 print(
'Failed to send image')
58 elif BackEnd.has_ring_buffer:
59 print(
"Unlock unused image")
63class BackEnd(QObject):
64 connection_list_changed = Signal()
65 server_started =
False
66 server_single_shot_send_enabled =
False
67 server_single_shot_send =
False
71 has_ring_buffer =
False
78 QObject.__init__(self)
79 self.start_in_progress =
False
80 self.window_state_reg_node =
None
81 self.event_cookie =
None
86 cvb.AcquisitionStack.Vin)
87 self.stream = self.device.stream()
89 BackEnd.has_ring_buffer = bool(self.stream.ring_buffer)
90 if BackEnd.has_ring_buffer:
91 self.stream.ring_buffer.lock_mode = cvb.RingBufferLockMode.On
92 BackEnd.img_list = [
None] * self.stream.ring_buffer.count
95 self.handler = StreamProcessingHandler(self.stream)
97 self.view.rootContext().setContextProperty(
'mainImage', BackEnd.image_controller)
99 self.stream.get_snapshot()
100 BackEnd.image_controller.refresh(self.device.device_image)
104 self.connections = gs.LogicalNetworkInterface.get_all_available()
105 self.m_connection_list = list()
106 for connection
in self.connections:
107 self.m_connection_list.append(connection.ip_address() +
' (' + connection.ipv4_mask() +
')')
108 self.connection_list_changed.emit()
113 def __exit__(self, exc_type, exc_val, exc_tb):
114 if self.handler.is_active:
115 self.handler.finish()
117 BackEnd.server =
None
119 @Property('QVariantList', notify=connection_list_changed)
120 def connection_list(self):
121 return self.m_connection_list
124 def driver_path(self):
128 def desktop_path(self):
129 return QUrl.fromLocalFile(os.path.expanduser(
"~/Desktop"))
132 def switch_grab(self, switched):
133 if switched
and not self.handler.is_active:
135 BackEnd.grab_image = switched
138 print(
"Resetting ui")
139 if BackEnd.grab_image:
140 print(
'Resetting grab switch')
141 BackEnd.grab_image =
False
142 switch = self.view.findChild(QObject,
'switchGrab')
143 switch.setProperty(
'checked',
False)
146 print(
'Stopping grab')
148 if self.stream.is_running:
149 self.handler.try_finish()
150 print(
'Grab stopped')
153 def load_device(self, path):
154 print(
'Loading device from ' + path)
158 self.device = tmp_device
159 self.stream = self.device.stream()
160 self.handler = StreamProcessingHandler(self.stream)
162 self.stream.get_snapshot()
163 BackEnd.has_ring_buffer = bool(self.stream.ring_buffer)
164 if BackEnd.has_ring_buffer:
165 self.stream.ring_buffer.lock_mode = cvb.RingBufferLockMode.On
166 BackEnd.img_list = [
None] * self.stream.ring_buffer.count
167 BackEnd.image_controller.refresh(self.device.device_image)
170 print(
'Failed to load new device')
173 def save_image(self, path):
174 print(
'Saving image to ' + path)
175 self.device.device_image.save(path)
178 def btn_single_shot(self):
181 if not self.handler.is_active:
183 BackEnd.single_shot =
True
186 def switch_enable_single_shot_send(self, enabled):
187 BackEnd.server_single_shot_send_enabled = enabled
190 def btn_single_shot_send(self):
191 if BackEnd.server.stream.is_running:
192 print(
'Send single shot')
193 BackEnd.server_single_shot_send =
True
195 print(
'Acquisition not enabled on server')
198 def start_server(self, connection_index, driver_type_index):
199 print(
'connection_index', connection_index,
'driver_type_index', driver_type_index)
201 if self.start_in_progress:
204 self.start_in_progress =
True
207 if BackEnd.server_started
is False:
211 BackEnd.server = gs.Server.create_with_const_size(self.device.device_image.size,
212 self.device.device_image.color_model,
213 self.device.device_image.planes[0].data_type,
216 self.add_genicam_features()
218 BackEnd.server.user_version =
'Python GevServer'
221 if(BackEnd.has_ring_buffer):
222 BackEnd.server.stream.resend_buffers_count = 2
224 BackEnd.server.stream.resend_buffers_count = 0
227 BackEnd.server.start(self.connections[connection_index].ip_address())
230 BackEnd.server_started =
True
231 print(
'Started server')
233 BackEnd.server.stop()
234 BackEnd.server_started =
False
235 print(
'Server stopped')
238 print(
'Failed to start/stop the server')
239 self.start_in_progress =
False
241 def on_window_size_changed(self, valueNode):
242 print(
'Window size change callback called')
243 val = self.window_state_reg_node.value
245 self.view.showMinimized()
247 self.view.showNormal()
252 def add_genicam_features(self):
253 cat_node = gs.CategoryNode.create(
'Cust::CustomFeatures')
254 BackEnd.server.node_map.add_node(cat_node)
255 cat_node.display_name =
'Custom Features'
256 cat_node.tool_tip =
'Contains all application defined features'
257 root_node = BackEnd.server.node_map[
'Root']
258 root_node.add(cat_node, gs.NodeList.Child)
260 self.window_state_reg_node = gs.Int32RegNode.create(
'Cust::WindowStateReg')
261 BackEnd.server.node_map.add_node(self.window_state_reg_node)
262 self.window_state_reg_node.visibility = cvb.Visibility.Invisible
264 self.window_state_reg_node.cache_mode = cvb.CacheMode.NoCache
266 self.window_state_reg_node.polling_time = 333
267 self.window_state_reg_node.value = 1
268 self.event_cookie = self.window_state_reg_node.register_event_written_updated(self.on_window_size_changed)
270 enumeration_node = gs.EnumerationNode.create(
'Cust::WindowState')
271 BackEnd.server.node_map.add_node(enumeration_node)
272 enumeration_node.display_name =
'Window State'
273 enumeration_node.tool_tip =
'Current window state of server application'
274 enumeration_node.value_config_as_node = self.window_state_reg_node
276 minimized_node = gs.EnumEntryNode.create(
'Cust::Minimized')
277 minimized_node.numeric_value = 0
278 enumeration_node.add(minimized_node, gs.NodeList.Child)
280 normal_node = gs.EnumEntryNode.create(
'Cust::Normal')
281 normal_node.numeric_value = 1
282 enumeration_node.add(normal_node, gs.NodeList.Child)
284 max_node = gs.EnumEntryNode.create(
'Cust::Maximized')
285 max_node.numeric_value = 2
286 enumeration_node.add(max_node, gs.NodeList.Child)
288 cat_node.add(enumeration_node, gs.NodeList.Child)
291if __name__ ==
'__main__':
292 sys.argv += [
'--style',
'Windows']
293 app = QApplication(sys.argv)
294 app.setOrganizationName(
'STEMMER IMAGING')
295 app.setOrganizationDomain(
'https://www.stemmer-imaging.com/')
296 app.setApplicationName(
'GevServer Python tutorial')
299 if sys.platform ==
'win32':
301 myappid =
u'stemmerimaging.commonvisionblox.pygevserver.0'
302 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
304 app.setWindowIcon(QIcon(
'Tutorial-Python_32x32.png'))
311 view.setResizeMode(QQuickView.SizeRootObjectToView)
314 with BackEnd()
as backEnd:
317 view.rootContext().setContextProperty(
'backEnd', backEnd)
319 view.setSource(QUrl(
'main.qml'))
321 backEnd.btn_single_shot()
323 view.resize(640, 390)
Union[cvb.GenICamDevice, cvb.VinDevice, cvb.EmuDevice, cvb.VideoDevice, cvb.NonStreamingDevice] open(str provider, int acquisition_stack=cvb.AcquisitionStack.PreferVin)
Opens a device with the given provider and acquisition stack.
Definition: __init__.py:1629
Version information for GenICam related objects.
Definition: __init__.py:2067
Handler object for a single stream.
Definition: __init__.py:5459
Controller object for the QML image view item.
Definition: __init__.py:14
None register(cls, str uri="CvbQuick", int version_major=1, int version_minor=0, str qml_name="ImageView")
Convenience method to register this type or a derived type in QML.
Definition: __init__.py:196
Common Vision Blox GevServer module for Python.
Definition: __init__.py:1
Common Vision Blox UI module for Python.
Definition: __init__.py:1
str install_path()
Directory Common Vision Blox has been installed to.
Definition: __init__.py:8318
str expand_path(str path)
Expands a path containing an environment variable.
Definition: __init__.py:8202
2import CvbQuick 1.0 as CvbQuick
3import QtQuick.Controls 2.12
4import QtQuick.Layouts 1.12
5import QtQuick.Dialogs 1.3
6import QtQuick.Controls.Universal 2.4
7import QtQuick.Window 2.3
14 Layout.fillWidth: true
15 Layout.fillHeight: true
17 property var loadFile: true
18 property var enableSingleShotSend: false
20 Universal.theme: Universal.System
22 background: Rectangle {
25 color: Universal.background
31 title: loadFile ? "Select an image or driver to load" : "Save image"
32 selectExisting: loadFile
34 nameFilters: ["Drivers (*.vin *.emu)", "Image files (*png *.bmp)", "All files (*)"]
35 selectedNameFilter: loadFile ? "Drivers" : "Image files"
38 var path = fileUrl.toString();
39 // remove prefixed "file:///"
40 path = path.replace(/^(file:\/{3})/,"");
41 // unescape html codes like '%23' for '#'
42 var cleanPath = decodeURIComponent(path);
44 loadFile ? backEnd.load_device(path) : backEnd.save_image(path)
51 flow: GridLayout.TopToBottom
57 objectName: "connectionCombo"
58 enabled: btnStartServer.m_server_disabled
59 Layout.fillWidth: true
61 model: backEnd.connection_list
68 Layout.fillHeight: true
69 Layout.fillWidth: true
81 enabled: btnStartServer.m_server_disabled
82 Layout.fillWidth: true
84 (Qt.platform.os == "windows") ?
86 qsTr("Socket Driver (loopback)"),
87 qsTr("Filter Driver (default)")
97 objectName: "btnStartServer"
99 implicitWidth: btnLoadDevice.width
100 property var m_server_disabled: true
101 text: m_server_disabled ? qsTr("Start Server") : qsTr("Stop Server")
103 m_server_disabled = m_server_disabled ? false : true
104 backEnd.start_server(connectionCombo.currentIndex, driverTypeCombo.currentIndex)
114 objectName: "btnLoadDevice"
116 enabled: btnStartServer.m_server_disabled
117 text: qsTr("Load Device")
121 fileDialog.folder = backEnd.driver_path
129 objectName: "btnSaveImg"
131 implicitWidth: btnLoadDevice.width
132 text: qsTr("Save Image")
136 fileDialog.folder = backEnd.desktop_path
143 objectName: "btnSingleShot"
145 implicitWidth: btnLoadDevice.width
146 width: btnSaveImg.width
147 enabled: btnStartServer.m_server_disabled
148 text: qsTr("Single Shot")
149 onClicked: backEnd.btn_single_shot()
154 objectName: "switchEnableSingleShotSend"
155 id: switchEnableSingleShotSend
156 text: qsTr("Enable Single Shot Send")
158 enabled: !btnStartServer.m_server_disabled
161 enableSingleShotSend = enableSingleShotSend ? false : true
162 backEnd.switch_enable_single_shot_send(checked)
168 objectName: "btnSingleShotSend"
169 id: btnSingleShotSend
170 implicitWidth: btnLoadDevice.width
171 enabled: !btnStartServer.m_server_disabled && enableSingleShotSend
172 text: qsTr("Single Shot Send")
173 onClicked: backEnd.btn_single_shot_send()
179 objectName: "switchGrab"
183 enabled: btnStartServer.m_server_disabled
184 onCheckedChanged: backEnd.switch_grab(checked)