itkdb-gtk 0.10.7__py3-none-any.whl → 0.10.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of itkdb-gtk might be problematic. Click here for more details.

@@ -1,33 +1,55 @@
1
1
  #!/usr/bin/env python3
2
2
  """PB/Hybrid panel Visual inspection GUI.."""
3
- import json
4
3
  import sys
4
+ import copy
5
+ from pathlib import Path
5
6
 
6
7
  try:
7
8
  import itkdb_gtk
8
9
 
9
10
  except ImportError:
10
- from pathlib import Path
11
11
  cwd = Path(__file__).parent.parent
12
12
  sys.path.append(cwd.as_posix())
13
13
 
14
14
  from itkdb_gtk import dbGtkUtils, ITkDBlogin, ITkDButils
15
+ from itkdb_gtk.ShowComments import ShowComments
16
+ from itkdb_gtk.ShowDefects import ShowDefects
17
+ from itkdb_gtk.UploadTest import create_json_data_editor
18
+
15
19
 
16
20
  import gi
17
21
  gi.require_version("Gtk", "3.0")
18
- from gi.repository import Gtk, Gdk, Gio
22
+ from gi.repository import Gtk, Gdk, Gio, GObject
23
+
24
+ HELP_LINK="https://itkdb-gtk.docs.cern.ch/panelVisualInspection.html"
25
+
26
+ class TestJson(GObject.Object):
27
+ """To store test JSOn."""
28
+ __gtype_name__ = "TestJson"
29
+
30
+ def __init__(self, js=None):
31
+ super().__init__()
32
+ self.js = copy.deepcopy(js)
33
+
34
+ def set_js(self, js):
35
+ """SEts the dictionary"""
36
+ self.js = copy.deepcopy(js)
19
37
 
20
- HELP_LINK="https://itkdb-gtk.docs.cern.ch"
21
38
 
22
39
  class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
23
40
  """PB/Hybryd panel visual inspection GUI."""
24
- SN, PASSED, ALL = range(3)
41
+ SN, ORDER, PASSED, N_FILES, F_LIST, TEST_J, ALL = range(7)
42
+ F_DEFECT, F_NAME, F_PATH = range(3)
25
43
 
26
- def __init__(self, session, help=HELP_LINK):
27
- super().__init__(title="ITkDB Dashboard",
44
+ def __init__(self, session, title="PanelVisualInspection", help_link=HELP_LINK):
45
+ super().__init__(title=title,
28
46
  session=session,
29
47
  show_search="Find object with given SN.",
30
- help=help)
48
+ help_link=help_link)
49
+
50
+ self.institute = "IFIC"
51
+ self.global_image = None
52
+ self.global_link = None
31
53
 
32
54
  # action button in header
33
55
  button = Gtk.Button()
@@ -41,17 +63,49 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
41
63
  grid = Gtk.Grid(column_spacing=5, row_spacing=1)
42
64
  self.mainBox.pack_start(grid, False, False, 5)
43
65
 
66
+ irow = 0
67
+ receiver = self.create_institute_combo()
68
+ receiver.connect("changed", self.on_institute)
69
+ receiver.set_tooltip_text("Select the Institute making the test.")
70
+ dbGtkUtils.set_combo_iter(receiver, self.institute)
71
+
72
+ lbl = Gtk.Label(label="Institute")
73
+ lbl.set_xalign(0)
74
+ grid.attach(lbl, 0, irow, 1, 1)
75
+ grid.attach(receiver, 1, irow, 1, 1)
76
+
77
+ irow += 1
44
78
  lbl = Gtk.Label(label="Serial Number")
45
79
  lbl.set_xalign(0)
46
- grid.attach(lbl, 0, 0, 1, 1)
80
+ grid.attach(lbl, 0, irow, 1, 1)
47
81
 
48
- self.SN = dbGtkUtils.TextEntry()
82
+ self.SN = dbGtkUtils.TextEntry(small=True)
49
83
  self.SN.connect("text_changed", self.SN_ready)
50
84
  self.SN.widget.set_tooltip_text("Enter SN of PWD or Hybrid panel.")
51
- grid.attach(self.SN.widget, 1, 0, 1, 1)
85
+ grid.attach(self.SN.widget, 1, irow, 1, 1)
52
86
 
53
87
  self.panel_type = Gtk.Label(label="")
54
- grid.attach(self.panel_type, 2, 0, 1, 1)
88
+ grid.attach(self.panel_type, 2, irow, 1, 1)
89
+
90
+ irow += 1
91
+ lbl = Gtk.Label(label="Date")
92
+ lbl.set_xalign(0)
93
+ grid.attach(lbl, 0, irow, 1, 1)
94
+
95
+ self.date = dbGtkUtils.TextEntry(small=True)
96
+ grid.attach(self.date.widget, 1, irow, 1, 1)
97
+ self.date.entry.set_text(ITkDButils.get_db_date())
98
+ self.date.connect("text_changed", self.new_date)
99
+
100
+ irow += 1
101
+ self.fC = Gtk.FileChooserButton()
102
+ self.fC.connect("file-set", self.on_global_image)
103
+
104
+ lbl = Gtk.Label(label="Global Image")
105
+ lbl.set_xalign(0)
106
+ grid.attach(lbl, 0, irow, 1, 1)
107
+ grid.attach(self.fC, 1, irow, 1, 1)
108
+
55
109
 
56
110
  # Paned object
57
111
  paned = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
@@ -70,33 +124,73 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
70
124
 
71
125
  dbGtkUtils.setup_scanner(self.get_qrcode)
72
126
 
127
+ def on_global_image(self, *args):
128
+ """We choose the global image."""
129
+ fnam = self.fC.get_filename()
130
+ if fnam is None or not Path(fnam).exists():
131
+ dbGtkUtils.complain("Could not find image", fnam, parent=self)
132
+ return
133
+
134
+ self.global_image = Path(fnam).expanduser().resolve()
135
+
136
+
137
+
138
+ def on_institute(self, combo):
139
+ """A new recipient has been chosen."""
140
+ name = self.get_institute_from_combo(combo)
141
+ if name:
142
+ self.institute = name
143
+
144
+ def new_date(self, entry, value):
145
+ """new date given at input."""
146
+ d = dbGtkUtils.parse_date_as_string(value)
147
+ if d is not None:
148
+ self.date.set_text(d)
149
+
150
+ def create_model(self):
151
+ """Create tree view model."""
152
+ return Gtk.ListStore(str, int, bool, int, Gtk.ListStore, TestJson)
153
+
154
+ def create_file_model(self):
155
+ """Create model for file list"""
156
+ return Gtk.ListStore(str, str, str)
157
+
73
158
  def create_tree_view(self, size=150):
74
159
  """Create the TreeView with the children."""
75
- model = Gtk.ListStore(str, bool)
160
+ model = self.create_model()
76
161
  self.tree = Gtk.TreeView(model=model)
162
+ self.tree.connect("button-press-event", self.button_pressed)
77
163
 
78
164
  scrolled = Gtk.ScrolledWindow()
79
165
  scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
80
166
  scrolled.add(self.tree)
81
167
  scrolled.set_size_request(-1, size)
82
-
168
+
83
169
  renderer = Gtk.CellRendererText()
84
170
  column = Gtk.TreeViewColumn("SN", renderer, text=PanelVisualInspection.SN)
85
171
  self.tree.append_column(column)
86
172
 
173
+ renderer = Gtk.CellRendererText()
174
+ column = Gtk.TreeViewColumn("position", renderer, text=PanelVisualInspection.ORDER)
175
+ self.tree.append_column(column)
176
+
87
177
  renderer = Gtk.CellRendererToggle()
88
178
  renderer.set_property("activatable", True)
89
179
  renderer.set_property("radio", True)
90
180
  renderer.set_padding(5, 0)
91
-
92
- x, y = renderer.get_alignment()
181
+
182
+ _, y = renderer.get_alignment()
93
183
  renderer.set_alignment(0, y)
94
184
  # renderer.set_property("inconsistent", True)
95
185
  renderer.connect("toggled", self.btn_toggled)
96
-
186
+
97
187
  column = Gtk.TreeViewColumn("Passed", renderer, active=PanelVisualInspection.PASSED)
98
188
  self.tree.append_column(column)
99
189
 
190
+ renderer = Gtk.CellRendererText()
191
+ column = Gtk.TreeViewColumn("N. Images", renderer, text=PanelVisualInspection.N_FILES)
192
+ self.tree.append_column(column)
193
+
100
194
  return scrolled
101
195
 
102
196
  def btn_toggled(self, renderer, path, *args):
@@ -104,50 +198,233 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
104
198
  model = self.tree.get_model()
105
199
  val = not model[path][PanelVisualInspection.PASSED]
106
200
  model[path][PanelVisualInspection.PASSED] = val
107
-
201
+
202
+
203
+ def get_iter_at_position(self, tree, event):
204
+ """Get the model and iterator at position."""
205
+ # Create popup menu
206
+ select = tree.get_selection()
207
+ model, lv_iter = select.get_selected()
208
+ values = None
209
+ if lv_iter:
210
+ values = model[lv_iter]
211
+
212
+ else:
213
+ P = tree.get_path_at_pos(event.x, event.y)
214
+ if P:
215
+ lv_iter = model.get_iter(P[0])
216
+ values = model[lv_iter]
217
+
218
+ return model, lv_iter, values
219
+
108
220
 
109
221
  def button_pressed(self, tree, event):
110
222
  """Button pressed on tree view."""
223
+ # Create popup menu
224
+ model, lv_iter, values = self.get_iter_at_position(tree, event)
225
+ if not values:
226
+ return
227
+
111
228
  # double click shows attachments
112
229
  if event.button == 1 and event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
113
- select = self.tree.get_selection()
114
- model, iter = select.get_selected()
115
- if not iter:
116
- return
117
-
118
- self.on_show_json(None, (model, iter, model[iter]))
119
- # self.on_show_attachments(None, (model, iter, model[iter]))
230
+ #self.write_message("This is a double click.\n")
231
+ self.on_upload_image(None, (model, lv_iter, values))
120
232
  return
121
233
 
122
234
  if event.button != 3:
123
235
  return
124
236
 
125
- # Create popup menu
126
- select = self.tree.get_selection()
127
- model, iter = select.get_selected()
128
- values = None
129
- if iter:
130
- values = model[iter]
237
+ menu = Gtk.Menu()
131
238
 
132
- if not iter:
133
- P = tree.get_path_at_pos(event.x, event.y)
134
- if P:
135
- print(P[0].to_string())
136
- iter = model.get_iter(P[0])
137
- values = model[iter]
239
+ item_show = Gtk.MenuItem(label="Upload Image")
240
+ item_show.connect("activate", self.on_upload_image, (model, lv_iter, values))
241
+ menu.append(item_show)
242
+
243
+ item_show_json = Gtk.MenuItem(label="Show JSOn")
244
+ item_show_json.connect("activate", self.on_show_json, (model, lv_iter, values))
245
+ menu.append(item_show_json)
246
+
247
+ item_show_com = Gtk.MenuItem(label="Edit Comments")
248
+ item_show_com.connect("activate", self.on_show_comments, (model, lv_iter, values))
249
+ menu.append(item_show_com)
250
+
251
+ item_show_def = Gtk.MenuItem(label="Edit Defects")
252
+ item_show_def.connect("activate", self.on_show_defects, (model, lv_iter, values))
253
+ menu.append(item_show_def)
254
+
255
+ menu.show_all()
256
+ menu.popup_at_pointer(event)
257
+
258
+ def on_upload_image(self, item, data):
259
+ """Add defects with images.."""
260
+
261
+ model, lv_iter, val = data
262
+
263
+ irow = 0
264
+ tree = Gtk.TreeView(model=val[self.F_LIST])
265
+ tree.connect("button-press-event", self.on_file_pressed)
266
+
267
+ btn = Gtk.Button(label="Add image")
268
+ btn.set_tooltip_text("Click to add a new image.")
269
+ btn.connect("clicked", self.on_add_image, val[PanelVisualInspection.F_LIST])
270
+
271
+ scrolled = Gtk.ScrolledWindow()
272
+ scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
273
+ scrolled.add(tree)
274
+ scrolled.set_size_request(-1, 150)
275
+
276
+ renderer = Gtk.CellRendererText()
277
+ column = Gtk.TreeViewColumn("Description", renderer, text=PanelVisualInspection.F_DEFECT)
278
+ tree.append_column(column)
279
+
280
+ renderer = Gtk.CellRendererText()
281
+ column = Gtk.TreeViewColumn("File", renderer, text=PanelVisualInspection.F_NAME)
282
+ tree.append_column(column)
283
+
284
+ dlg = Gtk.Dialog(title="Add Image")
285
+ dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
286
+ Gtk.STOCK_OK, Gtk.ResponseType.OK)
287
+ box = dlg.get_content_area()
288
+ box.add(btn)
289
+ box.add(scrolled)
290
+ dlg.show_all()
291
+ rc = dlg.run()
292
+ if rc == Gtk.ResponseType.OK:
293
+ f_model = tree.get_model()
294
+ n_files = f_model.iter_n_children()
295
+ model.set_value(lv_iter, PanelVisualInspection.F_LIST, f_model)
296
+ model.set_value(lv_iter, PanelVisualInspection.N_FILES, n_files)
297
+
298
+ dlg.hide()
299
+ dlg.destroy()
300
+
301
+ self.write_message("Defects added\n")
302
+
303
+ def on_file_pressed(self, tree, event):
304
+ """Called when right button clicked in add image dialog.
305
+
306
+ Opens a pop-up menu to delete the selected entry."""
307
+ # double click shows attachments
308
+ if event.button == 1 and event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
309
+ self.write_message("This is a double click.\n")
310
+ return
311
+
312
+
313
+ if event.button != 3:
314
+ return
138
315
 
316
+ model, lv_iter, values = self.get_iter_at_position(tree, event)
139
317
  if not values:
140
318
  return
319
+ menu = Gtk.Menu()
141
320
 
321
+ item_show = Gtk.MenuItem(label="Delete")
322
+ item_show.connect("activate", self.on_delete_image, (model, lv_iter, values))
323
+ menu.append(item_show)
324
+
325
+ menu.show_all()
326
+ menu.popup_at_pointer(event)
327
+
328
+ def on_delete_image(self, item, data):
329
+ """Delete a defect and image"""
330
+ model, lv_iter, _ = data
331
+ model.remove(lv_iter)
332
+
333
+ def on_add_image(self, btn, model):
334
+ """Adds a new image."""
335
+ dlg = Gtk.Dialog(title="Add Image")
336
+
337
+ dlg.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
338
+ Gtk.STOCK_OK, Gtk.ResponseType.OK)
339
+ box = dlg.get_content_area()
340
+ grid = Gtk.Grid(column_spacing=5, row_spacing=1)
341
+ box.add(grid)
342
+
343
+ irow = 0
344
+ lbl = Gtk.Label(label="Description")
345
+ lbl.set_xalign(0)
346
+ grid.attach(lbl, 0, irow, 1, 1)
347
+
348
+ desc = Gtk.Entry()
349
+ grid.attach(desc, 1, irow, 1, 1)
350
+
351
+ irow += 1
352
+ lbl = Gtk.Label(label="Image")
353
+ lbl.set_xalign(0)
354
+ grid.attach(lbl, 0, irow, 1, 1)
355
+
356
+ fC = Gtk.FileChooserButton()
357
+ grid.attach(fC, 1, irow, 1, 1)
358
+
359
+ dlg.show_all()
360
+
361
+ rc = dlg.run()
362
+ if rc == Gtk.ResponseType.OK:
363
+ D = desc.get_text()
364
+ P = Path(fC.get_filename()).expanduser().resolve()
365
+ model.append([D, P.name, P.as_posix()])
366
+
367
+ dlg.hide()
368
+ dlg.destroy()
369
+
370
+ def on_show_json(self, item, data):
371
+ """Test JSon."""
372
+ model, lv_iter, val = data
373
+ payload = val[PanelVisualInspection.TEST_J].js
374
+ value, dlg = create_json_data_editor(payload)
375
+ rc = dlg.run()
376
+ if rc == Gtk.ResponseType.OK:
377
+ payload = value.values
378
+ model.set_value(lv_iter, PanelVisualInspection.TEST_J, TestJson(payload))
379
+
380
+ dlg.hide()
381
+ dlg.destroy()
382
+
383
+ def on_show_comments(self, item, data):
384
+ """Show comments"""
385
+ model, lv_iter, val = data
386
+ js = val[PanelVisualInspection.TEST_J].js
387
+ SC = ShowComments("Test Comments", js["comments"], self)
388
+ rc = SC.run()
389
+ if rc == Gtk.ResponseType.OK:
390
+ js["comments"] = SC.comments
391
+ model.set_value(lv_iter, PanelVisualInspection.TEST_J, TestJson(js))
392
+
393
+ SC.hide()
394
+ SC.destroy()
395
+
396
+ def on_show_defects(self, item, data):
397
+ """Show comments"""
398
+ model, lv_iter, val = data
399
+ js = val[PanelVisualInspection.TEST_J].js
400
+ SD = ShowDefects("Test Defects", js["defects"], self)
401
+ rc = SD.run()
402
+ if rc == Gtk.ResponseType.OK:
403
+ js["defects"] = SD.defects
404
+ model.set_value(lv_iter, PanelVisualInspection.TEST_J, TestJson(js))
405
+
406
+ SD.hide()
407
+ SD.destroy()
142
408
 
143
409
  def SN_ready(self, *args):
144
410
  """SN is ready in the TextEnttry."""
145
411
  SN = self.SN.get_text()
146
- if not SN.startswith("20U") or len(SN)!=14:
147
- dbGtkUtils.complain("Invalid Serial Number",
148
- "{}: wrong SN".format(SN))
412
+ # GEt children.
413
+ panel = ITkDButils.get_DB_component(self.session, SN)
414
+ if panel is None:
415
+ self.write_message(ITkDButils.get_db_response())
149
416
  return
150
417
 
418
+ SN = panel["serialNumber"]
419
+ args[0].set_text(SN)
420
+ is_PWB = False
421
+ defaults = {
422
+ "institution": self.institute,
423
+ "runNumber": "1",
424
+ "date": self.date.get_text()
425
+ }
426
+ component_type = None
427
+ test_code = None
151
428
  if "USED" in SN:
152
429
  # Powerboard Carrier
153
430
  if not SN[6].isdigit():
@@ -157,15 +434,23 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
157
434
  return
158
435
 
159
436
  self.panel_type.set_text("PWB carrier")
437
+ is_PWB = True
438
+ component_type = "PWB"
439
+ test_code = "PICTURE"
160
440
 
161
441
  elif "USET" in SN:
162
442
  # Hybrid test panel
443
+ component_type = "HYBRID_TEST_PANEL"
444
+ test_code = "VISUAL_INSPECTION_RECEPTION"
445
+
163
446
  if not SN[6].isdigit or int(SN[6])>5:
164
447
  dbGtkUtils.complain("Not a Hybrid Test Panel",
165
448
  "{}: wrong SN for a hybrid test panel".format(SN))
166
449
  self.SN.widget.set_text("")
167
450
  return
168
451
 
452
+ self.panel_type.set_text("HYB test panel")
453
+
169
454
  else:
170
455
  dbGtkUtils.complain("Invalid SN.",
171
456
  "{}\nNot a PWB carrier not HYB test panel.".format(SN))
@@ -173,21 +458,24 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
173
458
  return
174
459
 
175
460
  # GEt children.
176
- panel = ITkDButils.get_DB_component(self.session, SN)
177
- if panel is None:
178
- self.write_message(ITkDButils.get_db_response())
179
- return
180
-
181
- children = []
461
+ skltn = ITkDButils.get_test_skeleton(self.session, component_type, test_code, defaults)
462
+ model = self.create_model()
182
463
  for child in panel["children"]:
183
464
  if child["component"] is not None:
184
- if child["componentType"]["name"] == "Powerboard":
185
- children.append(child["component"]["serialNumber"])
465
+ child_SN = child["component"]["serialNumber"]
466
+ skltn["component"] = child_SN
467
+ if is_PWB:
468
+ position = child["order"]
469
+ else:
470
+ position = -1
471
+ for P in child["properties"]:
472
+ if P["code"] == "HYBRID_POSITION":
473
+ if P["value"] is not None:
474
+ position = int(P["value"])
475
+ break
476
+
477
+ model.append([child_SN, position, True, 0, self.create_file_model(), TestJson(skltn)])
186
478
 
187
- model = Gtk.ListStore(str, bool)
188
- for child in children:
189
- model.append([SN, True])
190
-
191
479
  self.tree.set_model(model)
192
480
 
193
481
 
@@ -195,10 +483,78 @@ class PanelVisualInspection(dbGtkUtils.ITkDBWindow):
195
483
  """Upload the current test."""
196
484
  SN = self.SN.get_text()
197
485
 
486
+ model = self.tree.get_model()
487
+ lv_iter = model.get_iter_first()
488
+ n_items = 0
489
+ global_link = None
490
+ while lv_iter:
491
+ values = model[lv_iter]
492
+ payload = values[PanelVisualInspection.TEST_J].js
493
+
494
+ attachments = []
495
+ if global_link is None:
496
+ A = ITkDButils.Attachment(path=self.global_image, title="Global Image", desc="Image of whole panel")
497
+ attachments.append(A)
498
+
499
+ im_model = values[PanelVisualInspection.F_LIST]
500
+ im_iter = im_model.get_iter_first()
501
+ idef = 1
502
+ while im_iter:
503
+ defect, name, path = im_model[im_iter]
504
+ A = ITkDButils.Attachment(path=path, title="Defect {}".format(idef), desc=defect)
505
+ attachments.append(A)
506
+ idef += 1
507
+ im_iter = im_model.iter_next(im_iter)
508
+
509
+ rc = ITkDButils.upload_test(self.session, payload, attachments, check_runNumber=True)
510
+ if rc:
511
+ ipos = rc.find("The following details may help:")
512
+ msg = rc[ipos:]
513
+ dbGtkUtils.complain("Failed uploading test {}-{}".format(payload["component"], payload["testType"]), msg)
514
+ self.write_message(msg)
515
+
516
+ else:
517
+ self.write_message("Upload {}-{} successfull\n".format(payload["component"], payload["testType"]))
518
+ if global_link is None:
519
+ try:
520
+ global_link = ITkDButils.attachment_urls[self.global_image.name]
521
+ except KeyError:
522
+ pass
523
+
524
+ if payload["testType"] == "PICTURE":
525
+ rc = ITkDButils.set_test_run_parameter(self.session,
526
+ ITkDButils.uploaded_test_runs[0],
527
+ "COMMENT", "Picture of PW carrier")
528
+
529
+ rc = ITkDButils.set_test_run_parameter(self.session,
530
+ ITkDButils.uploaded_test_runs[0],
531
+ "LINK_TO_PICTURE", global_link)
532
+ if rc:
533
+ ipos = rc.find("The following details may help:")
534
+ msg = rc[ipos:]
535
+ dbGtkUtils.complain("Failed updating LINK_TO_PICTURE {}-{}".format(payload["component"], payload["testType"]), msg)
536
+ self.write_message(msg)
537
+
538
+ elif payload["testType"] == "VISUAL_INSPECTION_RECEPTION":
539
+ rc = ITkDButils.create_test_run_comment(self.session,
540
+ ITkDButils.uploaded_test_runs[0],
541
+ ["Link to global image:\n {}".format(global_link)])
542
+ if rc:
543
+ ipos = rc.find("The following details may help:")
544
+ msg = rc[ipos:]
545
+ dbGtkUtils.complain("Failed adding global link as comment: {}-{}".format(payload["component"], payload["testType"]), msg)
546
+ self.write_message(msg)
547
+
548
+
549
+ n_items += 1
550
+ lv_iter = model.iter_next(lv_iter)
551
+
552
+
198
553
  def get_qrcode(self, fd, state, reader):
199
554
  """Read SN from scanner."""
200
555
  txt = dbGtkUtils.scanner_get_line(reader)
201
556
  self.write_message("SN: {}\n".format(txt))
557
+ self.SN_ready(txt, self.SN.widget)
202
558
 
203
559
 
204
560
  def main():
@@ -225,6 +581,5 @@ def main():
225
581
 
226
582
  dlg.die()
227
583
 
228
-
229
584
  if __name__ == "__main__":
230
- main()
585
+ main()