12from PySide2.QtWidgets
import QApplication
13from PySide2.QtQuick
import QQuickView
14from PySide2.QtCore
import QObject, Signal, Slot, Property, QUrl
15from PySide2.QtGui
import QIcon
18 def __init__(self, stream):
19 super().__init__(stream)
21 def handle_async_wait_result(self, image, status):
22 if status
is cvb.WaitStatus.Ok:
23 print(
"New image arrived")
25 if BackEnd.grab_image
or BackEnd.single_shot:
26 BackEnd.single_shot =
False
27 BackEnd.image_controller.refresh(image)
29 if BackEnd.has_ring_buffer:
32 elif BackEnd.server_started
and (
not BackEnd.server_single_shot_send_enabled
or BackEnd.server_single_shot_send)
and BackEnd.server.stream.is_running:
34 print(
'Sending image')
35 BackEnd.server_single_shot_send =
False
36 if BackEnd.has_ring_buffer:
37 BackEnd.buffer_index = image.buffer_index
38 BackEnd.img_list[image.buffer_index] = image
40 def image_release(img):
42 BackEnd.img_list[BackEnd.buffer_index].unlock()
43 print(
'Unlocked buffer at index ' + str(img.buffer_index))
45 BackEnd.server.stream.send(image, image_release)
46 BackEnd.image_controller.refresh(BackEnd.img_list[BackEnd.buffer_index])
48 BackEnd.server.stream.send(image)
49 BackEnd.image_controller.refresh(image)
51 print(
'Failed to send image')
52 elif BackEnd.has_ring_buffer:
53 print(
"Unlock unused image")
57class BackEnd(QObject):
58 connection_list_changed = Signal()
59 server_started =
False
60 server_single_shot_send_enabled =
False
61 server_single_shot_send =
False
65 has_ring_buffer =
False
72 QObject.__init__(self)
73 self.start_in_progress =
False
74 self.window_state_reg_node =
None
75 self.event_cookie =
None
80 cvb.AcquisitionStack.Vin)
81 self.stream = self.device.stream()
83 BackEnd.has_ring_buffer = bool(self.stream.ring_buffer)
84 if BackEnd.has_ring_buffer:
85 self.stream.ring_buffer.lock_mode = cvb.RingBufferLockMode.On
86 BackEnd.img_list = [
None] * self.stream.ring_buffer.count
89 self.handler = StreamProcessingHandler(self.stream)
91 self.view.rootContext().setContextProperty(
'mainImage', BackEnd.image_controller)
93 self.stream.get_snapshot()
94 BackEnd.image_controller.refresh(self.device.device_image)
98 self.connections = gs.LogicalNetworkInterface.get_all_available()
99 self.m_connection_list = list()
100 for connection
in self.connections:
101 self.m_connection_list.append(connection.ip_address() +
' (' + connection.ipv4_mask() +
')')
102 self.connection_list_changed.emit()
107 def __exit__(self, exc_type, exc_val, exc_tb):
108 if self.handler.is_active:
109 self.handler.finish()
111 BackEnd.server =
None
113 @Property('QVariantList', notify=connection_list_changed)
114 def connection_list(self):
115 return self.m_connection_list
118 def driver_path(self):
122 def desktop_path(self):
123 return QUrl.fromLocalFile(os.path.expanduser(
"~/Desktop"))
126 def switch_grab(self, switched):
127 if switched
and not self.handler.is_active:
129 BackEnd.grab_image = switched
132 print(
"Resetting ui")
133 if BackEnd.grab_image:
134 print(
'Resetting grab switch')
135 BackEnd.grab_image =
False
136 switch = self.view.findChild(QObject,
'switchGrab')
137 switch.setProperty(
'checked',
False)
140 print(
'Stopping grab')
142 if self.stream.is_running:
143 self.handler.try_finish()
144 print(
'Grab stopped')
147 def load_device(self, path):
148 print(
'Loading device from ' + path)
152 self.device = tmp_device
153 self.stream = self.device.stream()
154 self.handler = StreamProcessingHandler(self.stream)
156 self.stream.get_snapshot()
157 BackEnd.has_ring_buffer = bool(self.stream.ring_buffer)
158 if BackEnd.has_ring_buffer:
159 self.stream.ring_buffer.lock_mode = cvb.RingBufferLockMode.On
160 BackEnd.img_list = [
None] * self.stream.ring_buffer.count
161 BackEnd.image_controller.refresh(self.device.device_image)
164 print(
'Failed to load new device')
167 def save_image(self, path):
168 print(
'Saving image to ' + path)
169 self.device.device_image.save(path)
172 def btn_single_shot(self):
175 if not self.handler.is_active:
177 BackEnd.single_shot =
True
180 def switch_enable_single_shot_send(self, enabled):
181 BackEnd.server_single_shot_send_enabled = enabled
184 def btn_single_shot_send(self):
185 if BackEnd.server.stream.is_running:
186 print(
'Send single shot')
187 BackEnd.server_single_shot_send =
True
189 print(
'Acquisition not enabled on server')
192 def start_server(self, connection_index, driver_type_index):
193 print(
'connection_index', connection_index,
'driver_type_index', driver_type_index)
195 if self.start_in_progress:
198 self.start_in_progress =
True
201 if BackEnd.server_started
is False:
205 BackEnd.server = gs.Server.create_with_const_size(self.device.device_image.size,
206 self.device.device_image.color_model,
207 self.device.device_image.planes[0].data_type,
210 self.add_genicam_features()
212 BackEnd.server.user_version =
'Python GevServer'
215 if(BackEnd.has_ring_buffer):
216 BackEnd.server.stream.resend_buffers_count = 2
218 BackEnd.server.stream.resend_buffers_count = 0
221 BackEnd.server.start(self.connections[connection_index].ip_address())
224 BackEnd.server_started =
True
225 print(
'Started server')
227 BackEnd.server.stop()
228 BackEnd.server_started =
False
229 print(
'Server stopped')
232 print(
'Failed to start/stop the server')
233 self.start_in_progress =
False
235 def on_window_size_changed(self, valueNode):
236 print(
'Window size change callback called')
237 val = self.window_state_reg_node.value
239 self.view.showMinimized()
241 self.view.showNormal()
246 def add_genicam_features(self):
247 cat_node = gs.CategoryNode.create(
'Cust::CustomFeatures')
248 BackEnd.server.node_map.add_node(cat_node)
249 cat_node.display_name =
'Custom Features'
250 cat_node.tool_tip =
'Contains all application defined features'
251 root_node = BackEnd.server.node_map[
'Root']
252 root_node.add(cat_node, gs.NodeList.Child)
254 self.window_state_reg_node = gs.Int32RegNode.create(
'Cust::WindowStateReg')
255 BackEnd.server.node_map.add_node(self.window_state_reg_node)
256 self.window_state_reg_node.visibility = cvb.Visibility.Invisible
258 self.window_state_reg_node.cache_mode = cvb.CacheMode.NoCache
260 self.window_state_reg_node.polling_time = 333
261 self.window_state_reg_node.value = 1
262 self.event_cookie = self.window_state_reg_node.register_event_written_updated(self.on_window_size_changed)
264 enumeration_node = gs.EnumerationNode.create(
'Cust::WindowState')
265 BackEnd.server.node_map.add_node(enumeration_node)
266 enumeration_node.display_name =
'Window State'
267 enumeration_node.tool_tip =
'Current window state of server application'
268 enumeration_node.value_config_as_node = self.window_state_reg_node
270 minimized_node = gs.EnumEntryNode.create(
'Cust::Minimized')
271 minimized_node.numeric_value = 0
272 enumeration_node.add(minimized_node, gs.NodeList.Child)
274 normal_node = gs.EnumEntryNode.create(
'Cust::Normal')
275 normal_node.numeric_value = 1
276 enumeration_node.add(normal_node, gs.NodeList.Child)
278 max_node = gs.EnumEntryNode.create(
'Cust::Maximized')
279 max_node.numeric_value = 2
280 enumeration_node.add(max_node, gs.NodeList.Child)
282 cat_node.add(enumeration_node, gs.NodeList.Child)
285if __name__ ==
'__main__':
286 sys.argv += [
'--style',
'Windows']
287 app = QApplication(sys.argv)
288 app.setOrganizationName(
'STEMMER IMAGING')
289 app.setOrganizationDomain(
'https://www.stemmer-imaging.com/')
290 app.setApplicationName(
'GevServer Python tutorial')
293 if sys.platform ==
'win32':
295 myappid =
u'stemmerimaging.commonvisionblox.pygevserver.0'
296 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
298 app.setWindowIcon(QIcon(
'Tutorial-Python_32x32.png'))
305 view.setResizeMode(QQuickView.SizeRootObjectToView)
308 with BackEnd()
as backEnd:
311 view.rootContext().setContextProperty(
'backEnd', backEnd)
313 view.setSource(QUrl(
'main.qml'))
315 backEnd.btn_single_shot()
317 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:1327
Version information for GenICam related objects.
Definition: __init__.py:1707
Handler object for a single stream.
Definition: __init__.py:4380
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:193
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:7146
str expand_path(str path)
Expands a path containing an environment variable.
Definition: __init__.py:7046
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)