Coverage for pyilper/pilwidgets.py: 87%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1219 statements  

1#!/usr/bin/python3 

2# -*- coding: utf-8 -*- 

3# pyILPER 1.2.1 for Linux 

4# 

5# An emulator for virtual HP-IL devices for the PIL-Box 

6# derived from ILPER 1.4.5 for Windows 

7# Copyright (c) 2008-2013 Jean-Francois Garnier 

8# C++ version (c) 2013 Christoph Gießelink 

9# Python Version (c) 2015 Joachim Siebold 

10# 

11# This program is free software; you can redistribute it and/or 

12# modify it under the terms of the GNU General Public License 

13# as published by the Free Software Foundation; either version 2 

14# of the License, or (at your option) any later version. 

15# 

16# This program is distributed in the hope that it will be useful, 

17# but WITHOUT ANY WARRANTY; without even the implied warranty of 

18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19# GNU General Public License for more details. 

20# 

21# You should have received a copy of the GNU General Public License 

22# along with this program; if not, write to the Free Software 

23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 

24# 

25# pyILPER widget classes ---------------------------------------------------- 

26# 

27# Changelog 

28# 24.09.2015 cg 

29# - fixed loading file in cls_HelpWindow() when file path contain special characters 

30# - fixed name in cls_AboutWindow() text 

31# - fixed some errors at error condition 

32# 05.10.2015 jsi:  

33# - new style signal/slot handling for quit, crash and show_message 

34# - new style signal/slot handling for bottonBox widget 

35# - adjust super statements to python3+ syntax 

36# - removed unusable code to resize main window 

37# 20.10.2015 jsi 

38# - fix leading // for docpath (OSX) 

39# 24.10.2015 jsi 

40# - use non native menus only (OSX issues) 

41# - removed ) from date of medium formatted 

42# 30.11.2015 jsi 

43# - introduced idy frame option 

44# 22.12.2015 jsi 

45# - added navigation buttons to the help window  

46# - make help window resizeable 

47# 28.12.2015 jsi 

48# - do_cbActive: check for method toggle_active fixed 

49# 06.01.2016 jsi 

50# - initialize charset properly at program start 

51# - use utf-8-sig as charset for logging 

52# 29.01.2016 jsi 

53# - improve os detection 

54# 30.01.2016 jsi 

55# - use font metrics to determine terminal window size 

56# - removed experimental mark from TCP/IP configuration 

57# 01.02.2016 jsi 

58# - added InstallCheck menu callback 

59# 26.02.2016 jsi: 

60# - do not update terminal, if not visible 

61# 28.02.2016 jsi: 

62# - removed insert/replace indicator for the terminal widget 

63# 02.03.2016 jsi: 

64# - removed the terminal size combobox for the terminal widget 

65# - added the following configuration options to the pyilper configuration for 

66# the generic terminal: terminal size, color scheme, character size 

67# 05.03.2016 jsi: 

68# - improved HP-IL device status window 

69# 12.03.2016 jsi: 

70# - set terminal output queue poll timer to 25ms 

71# 13.03.2016 jsi: 

72# - modified exit of modal dialogs 

73# 21.03.2016 jsi: 

74# - refactored interface to HPTerm 

75# - modified parameters of cls_ui 

76# 27.03.2016 jsi: 

77# - moved virtual HP-IL device status to utilities menu 

78# 01.04..2016 jsi: 

79# - added copy function for PILIMAGE.DAT to the utlity menu 

80# 07.04.2016 cg 

81# added 230400 baud to baudrate combo box 

82# 12.04.2016 cg 

83# get available Windows COM ports from registry 

84# 14.04.2016 jsi 

85# - corrected all files filter to * in the file dialog 

86# 17.04.2016 jsi 

87# - catch winreg access error if no COM port is available 

88# 19.04.2016 jsi 

89# - modified serial device filtering for MAC OS X 

90# 25.04.2016 jsi 

91# - remove baudrate configuration 

92# 27.04.2016 jsi 

93# - do not set path for QFileDialog, it remembers the last directory automatically 

94# 28.04.2016 jsi 

95# - enable tabscope to log inbound, outbound and both traffic 

96# 29.04.2016 jsi 

97# - log scope unbuffered 

98# 07.05.2016 jsi 

99# - make scroll up buffer size configurable 

100# 08.05.2016 jsi 

101# - refactoring of PilConfigWindow, make autobaud/baudrate setting configurable again 

102# 17.06.2016 jsi 

103# - refactoring, use constant to access configuration 

104# - refactoring, move constants to pilcore.py 

105# - refactoring, use platform functions from pilcore.py 

106# 27.08.2016 jsi 

107# - tab configuration rewritten 

108# 18.09.2016 jsi 

109# - configurable device sequence added 

110# 04.10.2016 jsi 

111# - renamed sequence to position in the menu entry 

112# 05.10.2016 jsi 

113# - redraw terminal widgets if parent window was resized 

114# 13.10.2016 jsi 

115# - device configuration rewritten, configure devices and their position in one dialogue 

116# now. 

117# 19.10.2016 jsi 

118# - plotter tab widget added (merged) 

119# - pen definition dialog added (merged) 

120# - webkit/webengine handling added (experimental) 

121# 24.10.2016 jsi 

122# - show python and qt version in the About window 

123# 04.12.2016 jsi 

124# - allow LIF directories not starting at record 2 

125# 11.12.2016 jsi 

126# - extend configuration regarding pipes (Linux and Mac OS only) 

127# 07.01.2016 jsi 

128# - extended cls_HelpWindow to load arbitrary html files 

129# 16.03.2017 jsi 

130# - catch exception if neither QtWebKitWidgets or QtWebEngineWidgets are found 

131# 01.08.2017 jsi 

132# - add HP82162A tab 

133# - refactoring: tab classes moved to pilxxxx.py  

134# 23.08.2017 jsi 

135# - socket config replaced by unix domain socket config 

136# 31.08.2017 jsi 

137# - changed config param terminalsize to terminalwidth 

138# 03.09.2017 jsi 

139# - getDevices is now method of commthread 

140# 07.09.2017 jsi 

141# - bugfixes: moved pen config classes to pilplotter, remove double addLayout in 

142# config window 

143# 08.09.2017 jsi 

144# - renamed paper format from US to Letter 

145# 14.04.2017 jsi 

146# - refactoring of cls_tabtermgeneric 

147# 20.09.2017 jsi 

148# - changes of the pyILPER configuration dialog. Determine which changes need a 

149# restart of the communication and wich need a restart of the application. 

150# Issue appropriate messages.  

151# 30.10.2017 jsi 

152# - added configuration of lifutils path 

153# - removed deprecated constant QFileDialog.DirectoryOnly 

154# 12.11.2017 jsi 

155# - removed editing of parameters winpipe and socketname 

156# - added editing of parameter serverport 

157# - two column layout of config window 

158# 17.11.2017 jsi 

159# - added missing reconfigure method to cls_tabtermgeneric 

160# 22.11.2017 jsi 

161# - renamed header of socket server configuration 

162# 01.12.2017 jsi 

163# - added HP82162A thermal printer display pixelsize configuration 

164# 27.12.2017 jsi 

165# - removed central widget 

166# - introduced floating virtual devices 

167# 04.01.2018 jsi 

168# - make buffer_log configurable 

169# - do a log buffer flush only if buffer_log is True (Log Checkbox Widget) 

170# - reconfigure log checkbox object (cls_tabtermgeneric) 

171# 16.01.2018 jsi 

172# - added class for cascading configuration menus 

173# - cls_tabgeneric and cls_tabtermgeneric rewritten, implemented new 

174# device tab/window configuration 

175# - removed configuration of terminal width and terminal color scheme 

176# from cls_PilConfigWindow 

177# 20.01.2018 jsi: 

178# - scrollupbuffersize is not a local parameter 

179# - terminalcharsize is now dual config parameter 

180# - cls_config_tool_button can now handle the "Default" option (only for 

181# T_INTEGER) 

182# 05.02.2018 jsi: 

183# - apply BOM to log file on Windows only at the beginning of a new file 

184# if usebom config variable is tru 

185# - make usebom variable configurable 

186# - allow smaller font sizes for terminal window 

187# 10.02.2018 jsi: 

188# - fixed bom handling 

189# 19.02.2018 jsi: 

190# - introduced "paper" color scheme 

191# 21.02.2018 jsi: 

192# - fixed crash in cls_DevStatusWindo.de_refresh() if pyILPER status is  

193# disabled 

194# 08.07.2018: 

195# - fixed closeEvent of cls_ui 

196# 11.08.2018 jsi: 

197# - terminal custom shortcut configuration added in main menu 

198# 06.01.2018 jsi 

199# - added global configuration for HP2225B screenwidth 

200# 13.02.2020 cg 

201# - fixed wrong address view for addresses > 15 and changed device 

202# address view to HP71 style in cls_DevStatusWindow() 

203# 19.12.2021 jsi 

204# - tab title text color fixed (macOS problem) 

205# 

206import os 

207import glob 

208import datetime 

209import re 

210import sys 

211import functools 

212import pyilper 

213from PySide6 import QtCore, QtGui, QtWidgets,QtWebEngineWidgets 

214from .pilqterm import QScrolledTerminalWidget 

215from .pilcharconv import CHARSET_HP71, charsets 

216from .pilconfig import PILCONFIG 

217from .pilcore import * 

218if isWINDOWS(): 

219 import winreg 

220# 

221# constants for color schemes 

222# 

223COLOR_SCHEME_WHITE=0 

224COLOR_SCHEME_GREEN=1 

225COLOR_SCHEME_AMBER=2 

226COLOR_SCHEME_PAPER=3 

227color_scheme_names= ["white","amber","green","paper"] 

228# 

229# constants for cls_config_tool_button 

230# 

231T_BOOLEAN=1 

232T_STRING=2 

233T_INTEGER=3 

234O_DEFAULT= -1 

235 

236# 

237# class for cascading config menus 

238# 

239class cls_config_tool_button(QtWidgets.QToolButton): 

240 

241 config_changed_signal= QtCore.Signal() 

242 

243 def __init__(self,name,text): 

244 super().__init__() 

245 self.name= name 

246 self.menu= QtWidgets.QMenu() 

247 self.setText(text) 

248 self.setMenu(self.menu) 

249 self.setPopupMode(QtWidgets.QToolButton.InstantPopup) 

250 self.options= [] 

251 self.menu.aboutToShow.connect(self.do_before_show_menu) 

252 self.config_changed=None 

253 

254 

255 def add_option(self,option_text, option_name, option_type, option_choices): 

256 n=len(self.options) 

257 submenu=QtWidgets.QMenu(option_text,parent=self.menu) 

258 self.menu.addMenu(submenu) 

259 submenu.aboutToShow.connect(functools.partial(self.do_before_show_submenu,n)) 

260 k=0 

261 option_actions=[] 

262 for choice in option_choices: 

263 if option_type== T_BOOLEAN: 

264 if choice== True: 

265 choice="On" 

266 else: 

267 choice="Off" 

268 if option_type== T_INTEGER: 

269 if choice== -1: 

270 choice="Default" 

271 else: 

272 choice= str(choice) 

273 action= submenu.addAction(choice) 

274 option_actions.append(action) 

275 action.triggered.connect(functools.partial(self. do_choice_submenu,n,k)) 

276 k+=1 

277 self.options.append([option_name, option_type, option_choices, option_actions]) 

278# 

279# clears the variable which contains the changed option 

280# 

281 def do_before_show_menu(self): 

282 self.config_changed=None 

283# 

284# returns the name of the changed option or None 

285# 

286 def get_changed_option_name(self): 

287 if self.config_changed==None: 

288 return None 

289 else: 

290 return self.options[self.config_changed][0] 

291# 

292# Action: get the value of the selected choice 

293# 

294 def do_choice_submenu(self,n,k): 

295 v=self.options[n][2][k] 

296 option_type=self.options[n][1] 

297 

298 self.config_changed=n 

299# store parameter value 

300 if option_type== T_INTEGER: 

301 option_value= int(v) 

302 elif option_type== T_BOOLEAN: 

303 option_value= v 

304 elif option_type== T_STRING: 

305 option_value=k 

306 PILCONFIG.put(self.name, self.options[n][0],option_value) 

307 self.config_changed_signal.emit() 

308# 

309# Action: disable the current option value in the choices 

310#  

311 def do_before_show_submenu(self,n): 

312 option_type=self.options[n][1] 

313 

314# get parameter value 

315 option_value=PILCONFIG.get(self.name,self.options[n][0]) 

316 if option_type== T_STRING: 

317 option_value= self.options[n][2][option_value] 

318 self.do_enable_disable(n,option_value) 

319 

320 

321 def do_enable_disable(self,n,option_value): 

322 k=0 

323 for choice in self.options[n][2]: 

324 if choice== option_value: 

325 self.options[n][3][k].setEnabled(False) 

326 else: 

327 self.options[n][3][k].setEnabled(True) 

328 k+=1 

329 

330# 

331# custom class float button for tab corner ----------------------------------- 

332# 

333class cls_FloatButton(QtWidgets.QPushButton): 

334 

335 def __init__(self,parent=None): 

336 super().__init__(parent) 

337 self.button_size=20 

338 self.icon=self.style().standardIcon(QtWidgets.QStyle.SP_TitleBarNormalButton) 

339 self.pixmap= self.icon.pixmap(self.button_size, self.button_size) 

340 self.setFixedSize(self.button_size, self.button_size) 

341 

342 def paintEvent(self, ev): 

343 QtWidgets.QPushButton.paintEvent(self, ev) 

344 p = QtGui.QPainter(self) 

345 p.drawPixmap(self.button_size-self.pixmap.width(),0,self.pixmap) 

346# 

347# custom class for floating dialog widget ------------------------------------ 

348#  

349class cls_FloatWindow(QtWidgets.QDialog): 

350 

351 def __init__(self,parent,position,pilwidget,name): 

352 super().__init__() 

353 self.parent=parent 

354 self.name= name 

355 wlayout= QtWidgets.QVBoxLayout() 

356 wlayout.addWidget(pilwidget) 

357 self.setLayout(wlayout) 

358 self.setWindowTitle(name) 

359 if position !="": 

360 self.move(QtCore.QPoint(position[0],position[1])) 

361 if len(position)==4: 

362 self.resize(position[2],position[3]) 

363# 

364# catch close event, dock widget and save position and size 

365# 

366 def closeEvent(self,e): 

367 pos_y=self.pos().y() 

368 pos_x=self.pos().x() 

369 if pos_x < 50: 

370 pos_x=50 

371 if pos_y < 50: 

372 pos_y=50 

373 width= self.width() 

374 height= self.height() 

375 self.parent.setPosition([pos_x, pos_y, width, height]) 

376 self.parent.dock() 

377 e.ignore() 

378 return 

379# 

380# custom class dockable tab, handels all docking/undocking stuff ------------- 

381# 

382class cls_DockableTab(QtWidgets.QWidget): 

383 

384 def __init__(self,tabs,index,pilwidget,name): 

385 super().__init__() 

386 self.pilwidget=pilwidget 

387 self.tabs=tabs 

388 self.index=index 

389 self.name=name 

390 self.vLayout=QtWidgets.QVBoxLayout() 

391 self.vLayout.addWidget(self.pilwidget) 

392 self.setLayout(self.vLayout) 

393 self.window= None 

394 self.position= PILCONFIG.get(self.name,"position","") 

395 self.is_visible= False 

396 self.is_floating= PILCONFIG.get(self.name,"floating",False) 

397 if self.is_floating: 

398 self.undock() 

399# 

400# undock virtual device widget. A floating widget is always "visible" 

401# 

402 def undock(self): 

403 if self.window is None: 

404 self.vLayout.removeWidget(self.pilwidget) 

405 self.window=cls_FloatWindow(self,self.position,self.pilwidget,self.name) 

406 if not self.is_visible: 

407 self.pilwidget.becomes_visible() 

408 self.window.show() 

409 self.window.raise_() 

410 self.is_floating= True 

411 self.tabs.setTabEnabled(self.index,False) 

412 return 

413# 

414# dock virtual device widget 

415# 

416 def dock(self): 

417 if not (self.window is None): 

418 self.window.layout().removeWidget(self.pilwidget) 

419 self.vLayout.addWidget(self.pilwidget) 

420 self.window= None 

421 self.tabs.setTabEnabled(self.index,True) 

422 if not self.is_visible: 

423 self.pilwidget.becomes_invisible() 

424 self.is_floating= False 

425# 

426# check disable dock if widget is floating: setting tab disabled does 

427# not work from the __init__ method 

428# 

429 def checkDisabled(self): 

430 if self.is_floating: 

431 self.tabs.setTabEnabled(self.index,False) 

432# 

433# tab becomes visible, do not trigger visibility if tab is floating 

434# 

435 def becomes_visible(self): 

436 self.is_visible= True 

437 if self.window is None: 

438 self.pilwidget.becomes_visible() 

439# 

440# tab becomes invisible, do not trigger invisibility if tab is floating 

441# 

442 def becomes_invisible(self): 

443 self.is_visible= False 

444 if self.window is None: 

445 self.pilwidget.becomes_invisible() 

446# 

447# set position and size information, called by floating window on close 

448# 

449 def setPosition(self,position): 

450 self.position=position 

451# 

452# force close of floating tab on program exit, preserve the is_floating flag 

453# 

454 def closeFloatWindow(self): 

455 if self.window is not None: 

456 self.window.close() 

457 self.is_floating= True 

458 PILCONFIG.put(self.name,"position",self.position) 

459 PILCONFIG.put(self.name,"floating",self.is_floating) 

460# 

461# return positon 

462# 

463 def getPosition(self): 

464 return self.position 

465# 

466# return if tab is floating 

467# 

468 def isFloating(self): 

469 return self.is_floating 

470# 

471# custom tab class, hides the dockable tab widget ---------------------------- 

472# 

473class cls_Tabs(QtWidgets.QTabWidget): 

474 

475 def __init__(self): 

476 super().__init__() 

477 self.old_index=-1 

478 self.floatbutton=cls_FloatButton(self) 

479 self.setCornerWidget(self.floatbutton,QtCore.Qt.TopRightCorner) 

480 self.floatbutton.clicked.connect(self.do_undock) 

481 self.currentChanged[int].connect(self.tab_current_changed) 

482# 

483# add virtual device via a dockable tab widget 

484# 

485 def addTab(self,pilwidget,name): 

486 n=self.count() 

487 dockable=cls_DockableTab(self,n,pilwidget,name) 

488 super().addTab(dockable,name) 

489 dockable.checkDisabled() 

490# 

491# force close of floating Windows (on program exit) 

492# 

493 def closeFloatingWindows(self): 

494 for j in range(self.count()): 

495 dockable=self.widget(j) 

496 dockable.closeFloatWindow() 

497# 

498# undock virtual device widget triggerd by the undock corner button 

499# 

500 def do_undock(self): 

501 i=self.currentIndex() 

502 t=self.widget(i) 

503 t.undock() 

504 self.setTabEnabled(i,False) 

505# 

506# get virtual device widget of tab index i 

507# 

508 def pilWidget(self,i): 

509 t=self.widget(i) 

510 return t.pilwidget 

511# 

512# signal handler if tab index changes, handle visible/invisible stuff 

513# 

514 def tab_current_changed(self,index): 

515# 

516# on macOS bigSur the text of a selected tab is unreadable, so 

517# we need to force the text color here 

518# 

519 self.tabBar().setTabTextColor(self.old_index,QtGui.QColor("#000")) 

520 self.tabBar().setTabTextColor(index,QtGui.QColor("#aaa")) 

521 if self.old_index >=0: 

522 self.widget(self.old_index).becomes_invisible() 

523 self.old_index=index 

524 self.widget(self.old_index).becomes_visible() 

525 PILCONFIG.put("pyilper","active_tab",index) 

526 

527# 

528# Logging check box class -------------------------------------------------- 

529# 

530class LogCheckboxWidget(QtWidgets.QCheckBox): 

531 def __init__(self,name): 

532 super().__init__("Log "+name) 

533 self.name=name 

534 self.filename=self.name+".log" 

535 self.log= None 

536 self.buffer_log=PILCONFIG.get(self.name,"buffer_log",True) 

537# 

538# configure log buffering 

539# 

540 def set_buffering(self,buffer_log): 

541 self.buffer_log= buffer_log 

542# 

543# open log file, output BOM only on Windows at the beginning of the file 

544# 

545 def logOpen(self): 

546 try: 

547 if isWINDOWS() and PILCONFIG.get("pyilper","usebom"): 

548 self.log=open(self.filename,"a",encoding="UTF-8-SIG") 

549 else: 

550 self.log=open(self.filename,"a",encoding="UTF-8") 

551 self.log.write("\nBegin log "+self.filename+" at ") 

552 self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 

553 self.log.write("\n") 

554 except OSError as e: 

555 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot open log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

556 

557 def logClose(self): 

558 try: 

559 self.log.write("\nEnd log "+self.filename+" at ") 

560 self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 

561 self.log.write("\n") 

562 self.log.close() 

563 self.log= None 

564 except OSError as e: 

565 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot close log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

566 

567 def logWrite(self,line): 

568 if self.log is None: 

569 return 

570 try: 

571 self.log.write(line) 

572 except OSError as e: 

573 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot write to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

574 try: 

575 self.log.close() 

576 except OSError: 

577 pass 

578 self.log = None 

579 

580 def logFlush(self): 

581 if self.log is None: 

582 return 

583 if self.buffer_log: 

584 return 

585 try: 

586 self.log.flush() 

587 except OSError as e: 

588 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot flush to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

589 try: 

590 self.log.close() 

591 except OSError: 

592 pass 

593 self.log = None 

594# 

595# abstract generic tab class ------------------------------------------------- 

596# 

597class cls_tabgeneric(QtWidgets.QWidget): 

598 

599 def __init__(self,parent,name): 

600 super().__init__() 

601 self.name= name 

602 self.active= PILCONFIG.get(self.name,"active",False) 

603 self.parent=parent 

604 self.font_name=FONT 

605 self.font_size=0 

606 self.font_width=0 

607 self.font_height=0 

608 self.width= 0 

609 self.height= 0 

610 self.guiobject= None 

611 self.cbLogging= None 

612 self.logging= False 

613 self.pildevice=None 

614 self.widget_index=1 

615 self.cBut= None 

616# 

617# Build basic layout 

618# 

619 self.vbox= QtWidgets.QVBoxLayout() 

620 self.setLayout(self.vbox) 

621# 

622# hbox1: container of GUI Object 

623# 

624 self.hbox1=QtWidgets.QHBoxLayout() 

625 self.hbox1.setContentsMargins(10,10,10,10) 

626# 

627# hbox2: container of control widgets 

628#  

629 self.hbox2= QtWidgets.QHBoxLayout() 

630 self.hbox2.setContentsMargins(10,3,10,3) 

631 

632 self.cbActive= QtWidgets.QCheckBox('Device enabled') 

633 self.cbActive.setChecked(self.active) 

634 self.cbActive.setEnabled(False) 

635 self.cbActive.stateChanged.connect(self.do_cbActive) 

636 

637 self.hbox2.addWidget(self.cbActive) 

638 self.hbox2.addStretch(1) 

639 

640 self.vbox.addLayout(self.hbox1) 

641 self.vbox.addLayout(self.hbox2) 

642# 

643# add the gui object to hbox1 

644# 

645 def add_guiobject(self,guiobject): 

646 self.guiobject= guiobject 

647 self.hbox1.addWidget(self.guiobject) 

648# 

649# insert a control widget 

650# 

651 def add_controlwidget(self,widget): 

652 self.hbox2.insertWidget(self.widget_index,widget) 

653 self.widget_index+=1 

654# 

655# add a config cascading menu: always at the end of the control box 

656# 

657 def add_configwidget(self): 

658 self.cBut= cls_config_tool_button(self.name,"Configuration") 

659 self.hbox2.addWidget(self.cBut) 

660# 

661# insert the cbLogging widget in hbox2 after the cbActive Widget 

662# 

663 def add_logging(self): 

664 self.cbLogging= LogCheckboxWidget(self.name) 

665 self.hbox2.insertWidget(1,self.cbLogging) 

666 self.logging= PILCONFIG.get(self.name,"logging",False) 

667 self.cbLogging.setChecked(self.logging) 

668 self.cbLogging.setEnabled(False) 

669 self.cbLogging.stateChanged.connect(self.do_cbLogging) 

670 self.widget_index+=1 

671# 

672# if config button present, then add log buffer option 

673# 

674 if self.cBut is not None: 

675 self.cBut.add_option("Log buffering","buffer_log",T_BOOLEAN,[True,False]) 

676# 

677# insert a status widget 

678# 

679 def add_statuswidget(self,statuswidget): 

680 self.hbox2.insertWidget(self.widget_index,statuswidget) 

681 self.hbox2.insertStretch(self.widget_index,1) 

682# 

683# tab config changed, handle buffer_log change 

684# 

685 def do_tabconfig_changed(self): 

686 param= self.cBut.get_changed_option_name() 

687 if param== "buffer_log": 

688 self.cbLogging.set_buffering(PILCONFIG.get(self.name,"buffer_log")) 

689# 

690# reconfigure, nothing to do here 

691# 

692 def reconfigure(self): 

693 return 

694#  

695# disable tab 

696# 

697 def disable(self): 

698 if self.cbLogging is not None: 

699 if self.logging: 

700 self.cbLogging.logClose() 

701 self.cbLogging.setEnabled(False) 

702 self.cbActive.setEnabled(False) 

703# 

704# enable tab 

705# 

706 def enable(self): 

707 self.cbActive.setEnabled(True) 

708 if self.cbLogging is not None: 

709 if self.logging: 

710 self.cbLogging.logOpen() 

711 self.cbLogging.setEnabled(True) 

712# 

713# toggle active/inactive 

714# 

715 def toggle_active(self): 

716 return 

717# 

718# action: toogle active checkbox 

719# 

720 def do_cbActive(self): 

721 self.active= self.cbActive.isChecked() 

722 PILCONFIG.put(self.name,"active",self.active) 

723 self.pildevice.setactive(self.active) 

724 self.toggle_active() 

725# 

726# action: toggle log checkbox 

727# 

728 def do_cbLogging(self): 

729 self.cbLogging.setEnabled(False) 

730 self.logging= self.cbLogging.isChecked() 

731 self.pildevice.setlocked(True) 

732 if self.logging: 

733 self.cbLogging.logOpen() 

734 else: 

735 self.cbLogging.logClose() 

736 PILCONFIG.put(self.name,"logging",self.logging) 

737 self.pildevice.setlocked(False) 

738 self.cbLogging.setEnabled(True) 

739# 

740# generic terminal tab widget ------------------------------------------------ 

741# 

742class cls_tabtermgeneric(cls_tabgeneric): 

743 

744 def __init__(self,parent,name): 

745 super().__init__(parent,name) 

746 self.kbd_delay=False 

747# 

748# init local configuration parameters 

749# 

750 self.terminalwidth= PILCONFIG.get(self.name,"terminalwidth",80) 

751 self.colorscheme= PILCONFIG.get(self.name,"colorscheme",COLOR_SCHEME_WHITE) 

752 self.scrollupbuffersize=PILCONFIG.get(self.name,"scrollupbuffersize",1000) 

753 self.termcharsize=PILCONFIG.get(self.name,"terminalcharsize",-1) 

754# 

755# Build GUI  

756# 

757 self.guiobject=QScrolledTerminalWidget(self,self.name) 

758# 

759# add guiobject to tab 

760# 

761 self.add_guiobject(self.guiobject) 

762# 

763# add tab config widget 

764# 

765 self.add_configwidget() 

766# 

767# add basic terminal config options to cascading menu 

768# 

769 self.cBut.add_option("Terminal width","terminalwidth",T_INTEGER,[80,120]) 

770 self.cBut.add_option("Color scheme","colorscheme",T_STRING,color_scheme_names) 

771 self.cBut.add_option("Font size","terminalcharsize",T_INTEGER,[O_DEFAULT,13,14,15,16,17,18,19,20]) 

772 self.cBut.add_option("Scrollup buffer","scrollupbuffersize",T_INTEGER,[1000,2000,5000,10000]) 

773 

774 self.statuswidget=QtWidgets.QLabel("") 

775 self.statuswidget.setText("Display size :") 

776 self.add_statuswidget(self.statuswidget) 

777# 

778# handle changes of tab configuration 

779# 

780 def do_tabconfig_changed(self): 

781 param= self.cBut.get_changed_option_name() 

782 if param=="terminalwidth": 

783 self.guiobject.reconfigure() 

784 elif param=="colorscheme": 

785 self.guiobject.reconfigure() 

786 elif param=="scrollupbuffersize": 

787 self.guiobject.reconfigure() 

788 elif param=="terminalcharsize": 

789 self.guiobject.reconfigure() 

790 super().do_tabconfig_changed() 

791# 

792# reconfigure tab: reconfigure gui object 

793# 

794 def reconfigure(self): 

795 self.guiobject.reconfigure() 

796 super().reconfigure() 

797# 

798# catch resize event to redraw the terminal window 

799# 

800 def resizeEvent(self,event): 

801 self.guiobject.redraw() 

802# 

803# update status widget 

804# 

805 def update_status(self,rows,cols): 

806 self.statuswidget.setText("Display size: {:d}x{:d}".format(rows,cols)) 

807# 

808# enable/disable 

809# 

810 def enable(self): 

811 super().enable() 

812 self.guiobject.enable() 

813 

814 def disable(self): 

815 super().disable() 

816 self.guiobject.disable() 

817# 

818# becomes visible, refresh content, activate update and blink 

819# 

820 def becomes_visible(self): 

821 self.guiobject.becomes_visible() 

822 return 

823# 

824# becomes invisible, deactivate update and blink 

825# 

826 def becomes_invisible(self): 

827 self.guiobject.becomes_invisible() 

828 return 

829# 

830# Help Dialog class ---------------------------------------------------------- 

831# 

832class HelpError(Exception): 

833 def __init__(self,value): 

834 self.value=value 

835 

836 def __str__(self): 

837 return repr(self.value) 

838 

839 

840class cls_HelpWindow(QtWidgets.QDialog): 

841 

842 def __init__(self,parent=None): 

843# 

844 super().__init__() 

845 self.setWindowTitle('pyILPER Manual') 

846 

847 self.vlayout = QtWidgets.QVBoxLayout() 

848 self.setLayout(self.vlayout) 

849 self.view = QtWebEngineWidgets.QWebEngineView() 

850 self.view.setMinimumWidth(600) 

851 self.vlayout.addWidget(self.view) 

852 self.buttonExit = QtWidgets.QPushButton('Exit') 

853 self.buttonExit.setFixedWidth(60) 

854 self.buttonExit.clicked.connect(self.do_exit) 

855 self.buttonBack = QtWidgets.QPushButton('<') 

856 self.buttonBack.setFixedWidth(60) 

857 self.buttonForward = QtWidgets.QPushButton('>') 

858 self.buttonForward.setFixedWidth(60) 

859 self.hlayout = QtWidgets.QHBoxLayout() 

860 self.hlayout.addWidget(self.buttonBack) 

861 self.hlayout.addWidget(self.buttonExit) 

862 self.hlayout.addWidget(self.buttonForward) 

863 self.vlayout.addLayout(self.hlayout) 

864 self.buttonBack.clicked.connect(self.do_back) 

865 self.buttonForward.clicked.connect(self.do_forward) 

866 

867 def do_exit(self): 

868 self.hide() 

869 

870 def do_back(self): 

871 self.view.back() 

872 

873 def do_forward(self): 

874 self.view.forward() 

875 

876 def loadDocument(self,subdir,document): 

877 if subdir=="": 

878 docpath=os.path.join(os.path.dirname(pyilper.__file__),"Manual",document) 

879 else: 

880 docpath=os.path.join(os.path.dirname(pyilper.__file__),"Manual",subdir,document) 

881 docpath=re.sub("//","/",docpath,1) 

882 self.view.load(QtCore.QUrl.fromLocalFile(docpath)) 

883# 

884# Release Info Dialog class -------------------------------------------------- 

885# 

886class cls_ReleaseWindow(QtWidgets.QDialog): 

887 

888 def __init__(self,version): 

889 super().__init__() 

890 self.setWindowTitle('Release Information for pyILPER '+version) 

891 self.vlayout = QtWidgets.QVBoxLayout() 

892 self.setLayout(self.vlayout) 

893 self.view = QtWidgets.QLabel() 

894 self.view.setFixedWidth(500) 

895 self.view.setWordWrap(True) 

896 self.view.setText("Release Info Text") 

897 self.button = QtWidgets.QPushButton('OK') 

898 self.button.setFixedWidth(60) 

899 self.button.clicked.connect(self.do_exit) 

900 self.vlayout.addWidget(self.view) 

901 self.hlayout = QtWidgets.QHBoxLayout() 

902 self.hlayout.addWidget(self.button) 

903 self.vlayout.addLayout(self.hlayout) 

904 

905 def do_exit(self): 

906 self.hide() 

907 

908# 

909# 

910# About Dialog class -------------------------------------------------------- 

911# 

912class cls_AboutWindow(QtWidgets.QDialog): 

913 

914 def __init__(self,version): 

915 super().__init__() 

916 self.qtversion=QtCore.__version__ 

917 self.pyversion=str(sys.version_info.major)+"."+str(sys.version_info.minor)+"."+str(sys.version_info.micro) 

918 self.setWindowTitle('pyILPER About ...') 

919 self.vlayout = QtWidgets.QVBoxLayout() 

920 self.setLayout(self.vlayout) 

921 self.view = QtWidgets.QLabel() 

922 self.view.setFixedWidth(300) 

923 self.view.setWordWrap(True) 

924 self.view.setText("pyILPER "+version+ "\n\nAn emulator for virtual HP-IL devices for the PIL-Box derived from ILPER 1.4.5 for Windows\n\nCopyright (c) 2008-2013 Jean-Francois Garnier\nC++ version (c) 2017 Christoph Gießelink\nTerminal emulator code Henning Schröder\nPython Version (c) 2015-2020 Joachim Siebold\n\nGNU General Public License Version 2\n\nYou run Python "+self.pyversion+" and Qt "+self.qtversion+"\n") 

925 

926 

927 self.button = QtWidgets.QPushButton('OK') 

928 self.button.setFixedWidth(60) 

929 self.button.clicked.connect(self.do_exit) 

930 self.vlayout.addWidget(self.view) 

931 self.hlayout = QtWidgets.QHBoxLayout() 

932 self.hlayout.addWidget(self.button) 

933 self.vlayout.addLayout(self.hlayout) 

934 

935 def do_exit(self): 

936 self.hide() 

937# 

938# Get TTy Dialog class ------------------------------------------------------ 

939# 

940 

941class cls_TtyWindow(QtWidgets.QDialog): 

942 

943 def __init__(self, parent=None): 

944 super().__init__() 

945 

946 self.setWindowTitle("Select serial device") 

947 self.vlayout= QtWidgets.QVBoxLayout() 

948 self.setLayout(self.vlayout) 

949 

950 self.label= QtWidgets.QLabel() 

951 self.label.setText("Select or enter serial port") 

952# self.label.setAlignment(QtCore.Qt.AlignCenter) 

953 

954 self.__ComboBox__ = QtWidgets.QComboBox() 

955 self.__ComboBox__.setEditable(True) 

956 

957 if isWINDOWS(): 

958# 

959# Windows COM ports from registry 

960# 

961 try: 

962 with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Hardware\DeviceMap\SerialComm",0,winreg.KEY_QUERY_VALUE|winreg.KEY_ENUMERATE_SUB_KEYS) as key: 

963 for i in range (0, winreg.QueryInfoKey(key)[1]): 

964 port = winreg.EnumValue(key, i)[1] 

965 self.__ComboBox__.addItem( port, port ) 

966 except FileNotFoundError: 

967 pass 

968 elif isLINUX(): 

969# 

970# Linux /dev/ttyUSB? 

971# 

972 devlist=glob.glob("/dev/ttyUSB*") 

973 for port in devlist: 

974 self.__ComboBox__.addItem( port, port ) 

975# 

976# Mac OS X /dev/tty.usbserial-* 

977# 

978 elif isMACOS(): 

979 devlist=glob.glob("/dev/tty.usbserial-*") 

980 for port in devlist: 

981 self.__ComboBox__.addItem( port, port ) 

982 

983 else: 

984# 

985# Other 

986# 

987 devlist=glob.glob("/dev/tty*") 

988 for port in devlist: 

989 self.__ComboBox__.addItem( port, port ) 

990 

991# self.__ComboBox__.activated['QString'].connect(self.combobox_choosen) 

992 self.__ComboBox__.editTextChanged.connect(self.combobox_textchanged) 

993 self.buttonBox = QtWidgets.QDialogButtonBox() 

994 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

995 self.buttonBox.setCenterButtons(True) 

996 self.buttonBox.accepted.connect(self.do_ok) 

997 self.buttonBox.rejected.connect(self.do_cancel) 

998 self.vlayout.addWidget(self.label) 

999 self.vlayout.addWidget(self.__ComboBox__) 

1000 self.hlayout = QtWidgets.QHBoxLayout() 

1001 self.hlayout.addWidget(self.buttonBox) 

1002 self.vlayout.addWidget(self.buttonBox) 

1003 self.__device__= "" 

1004 

1005 def do_ok(self): 

1006 if self.__device__=="": 

1007 self.__device__= self.__ComboBox__.currentText() 

1008 if self.__device__=="": 

1009 return 

1010 super().accept() 

1011 

1012 def do_cancel(self): 

1013 super().reject() 

1014 

1015 

1016 def combobox_textchanged(self, device): 

1017 self.__device__= device 

1018 

1019 def combobox_choosen(self, device): 

1020 self.__device__= device 

1021 

1022 def getDevice(self): 

1023 return self.__device__ 

1024 

1025 @staticmethod 

1026 def getTtyDevice(parent=None): 

1027 dialog= cls_TtyWindow(parent) 

1028 dialog.resize(200,100) 

1029 result= dialog.exec() 

1030 if result== QtWidgets.QDialog.Accepted: 

1031 return dialog.getDevice() 

1032 else: 

1033 return "" 

1034# 

1035# Main pyILPER configuration dialog ------------------------------------------ 

1036# 

1037class cls_PilConfigWindow(QtWidgets.QDialog): 

1038 

1039 def __init__(self,parent): 

1040 super().__init__() 

1041 self.__needs_reconnect__= False 

1042 self.__needs_reconfigure__= False 

1043 self.__needs_restart__= False 

1044 self.__name__=parent.name 

1045 self.__parent__= parent 

1046 self.__mode__= PILCONFIG.get(self.__name__,"mode") 

1047 self.__tty__= PILCONFIG.get(self.__name__,"tty") 

1048 self.__ttyspeed__= PILCONFIG.get(self.__name__,"ttyspeed") 

1049 self.__port__= PILCONFIG.get(self.__name__,"port") 

1050 self.__idyframe__= PILCONFIG.get(self.__name__,"idyframe") 

1051 self.__remotehost__= PILCONFIG.get(self.__name__,"remotehost") 

1052 self.__remoteport__= PILCONFIG.get(self.__name__,"remoteport") 

1053 self.__serverport__= PILCONFIG.get(self.__name__,"serverport") 

1054 self.__workdir__= PILCONFIG.get(self.__name__,"workdir") 

1055 self.__termcharsize__=PILCONFIG.get(self.__name__,"terminalcharsize") 

1056 self.__dircharsize__=PILCONFIG.get(self.__name__,"directorycharsize") 

1057 self.__papersize__=PILCONFIG.get(self.__name__,"papersize") 

1058 self.__lifutilspath__=PILCONFIG.get(self.__name__,"lifutilspath") 

1059 self.__hp82162a_pixelsize__=PILCONFIG.get(self.__name__,"hp82162a_pixelsize") 

1060 self.__hp2225b_screenwidth__=PILCONFIG.get(self.__name__,"hp2225b_screenwidth") 

1061 self.__usebom__= PILCONFIG.get(self.__name__,"usebom") 

1062 

1063 self.setWindowTitle("pyILPER configuration") 

1064 self.vbox0= QtWidgets.QVBoxLayout() 

1065 self.setLayout(self.vbox0) 

1066 self.hbox0= QtWidgets.QHBoxLayout() 

1067 self.vbox0.addLayout(self.hbox0) 

1068 self.vbox1= QtWidgets.QVBoxLayout() 

1069 self.vbox2= QtWidgets.QVBoxLayout() 

1070 self.hbox0.addLayout(self.vbox1) 

1071 self.hbox0.addLayout(self.vbox2) 

1072 

1073# 

1074# Group box with radio buttons for communication typ 

1075# 

1076 

1077 self.gbox = QtWidgets.QGroupBox() 

1078 self.gbox.setFlat(True) 

1079 self.gbox.setTitle("Communication configuration") 

1080 self.vboxgbox= QtWidgets.QVBoxLayout() 

1081 self.gbox.setLayout(self.vboxgbox) 

1082# 

1083# Section PIL-Box 

1084# 

1085 self.radbutPIL = QtWidgets.QRadioButton(self.gbox) 

1086 self.radbutPIL.setText("PIL-Box") 

1087 self.radbutPIL.clicked.connect(self.setCheckBoxes) 

1088 self.vboxgbox.addWidget(self.radbutPIL) 

1089# 

1090# serial device 

1091# 

1092 self.hboxtty= QtWidgets.QHBoxLayout() 

1093 self.lbltxt1=QtWidgets.QLabel("Serial Device: ") 

1094 self.hboxtty.addWidget(self.lbltxt1) 

1095 self.lblTty=QtWidgets.QLabel() 

1096 self.lblTty.setText(self.__tty__) 

1097 self.hboxtty.addWidget(self.lblTty) 

1098 self.hboxtty.addStretch(1) 

1099 self.butTty=QtWidgets.QPushButton() 

1100 self.butTty.setText("change") 

1101 self.butTty.pressed.connect(self.do_config_Interface) 

1102 self.hboxtty.addWidget(self.butTty) 

1103 self.vboxgbox.addLayout(self.hboxtty) 

1104# 

1105# tty speed combo box 

1106# 

1107 self.hboxbaud= QtWidgets.QHBoxLayout() 

1108 self.lbltxt2=QtWidgets.QLabel("Baud rate ") 

1109 self.hboxbaud.addWidget(self.lbltxt2) 

1110 self.comboBaud=QtWidgets.QComboBox() 

1111 i=0 

1112 for baud in BAUDRATES: 

1113 self.comboBaud.addItem(baud[0]) 

1114 if self.__ttyspeed__== baud[1]: 

1115 self.comboBaud.setCurrentIndex(i) 

1116 i+=1 

1117 

1118 self.hboxbaud.addWidget(self.comboBaud) 

1119 self.hboxbaud.addStretch(1) 

1120 self.vboxgbox.addLayout(self.hboxbaud) 

1121 

1122# 

1123# idy frames 

1124# 

1125 self.cbIdyFrame= QtWidgets.QCheckBox('Enable IDY frames') 

1126 self.cbIdyFrame.setChecked(self.__idyframe__) 

1127 self.cbIdyFrame.setEnabled(True) 

1128 self.cbIdyFrame.stateChanged.connect(self.do_cbIdyFrame) 

1129 self.vboxgbox.addWidget(self.cbIdyFrame) 

1130# 

1131# section TCP/IP communication 

1132# 

1133 self.radbutTCPIP = QtWidgets.QRadioButton(self.gbox) 

1134 self.radbutTCPIP.setText("HP-IL over TCP/IP") 

1135 self.radbutTCPIP.clicked.connect(self.setCheckBoxes) 

1136 self.vboxgbox.addWidget(self.radbutTCPIP) 

1137# 

1138# TCP/IP Parameter input (port, remote host, remote port) 

1139# 

1140 self.intvalidator= QtGui.QIntValidator() 

1141 self.glayout=QtWidgets.QGridLayout() 

1142 self.lbltxt3=QtWidgets.QLabel("Port:") 

1143 self.glayout.addWidget(self.lbltxt3,0,0) 

1144 self.lbltxt4=QtWidgets.QLabel("Remote host:") 

1145 self.glayout.addWidget(self.lbltxt4,1,0) 

1146 self.lbltxt5=QtWidgets.QLabel("Remote port:") 

1147 self.glayout.addWidget(self.lbltxt5,2,0) 

1148 self.edtPort= QtWidgets.QLineEdit() 

1149 self.glayout.addWidget(self.edtPort,0,1) 

1150 self.edtPort.setText(str(self.__port__)) 

1151 self.edtPort.setValidator(self.intvalidator) 

1152 self.edtRemoteHost= QtWidgets.QLineEdit() 

1153 self.glayout.addWidget(self.edtRemoteHost,1,1) 

1154 self.edtRemoteHost.setText(self.__remotehost__) 

1155 self.edtRemotePort= QtWidgets.QLineEdit() 

1156 self.glayout.addWidget(self.edtRemotePort,2,1) 

1157 self.edtRemotePort.setText(str(self.__remoteport__)) 

1158 self.edtRemotePort.setValidator(self.intvalidator) 

1159 self.vboxgbox.addLayout(self.glayout) 

1160 self.vbox1.addWidget(self.gbox) 

1161# 

1162# Section TCP/IP server port 

1163# 

1164 self.radbutServerport = QtWidgets.QRadioButton(self.gbox) 

1165 self.radbutServerport.setText("TCP/IP socket Server (PIL-Box emulator)") 

1166 self.radbutServerport.clicked.connect(self.setCheckBoxes) 

1167 self.vboxgbox.addWidget(self.radbutServerport) 

1168 self.splayout=QtWidgets.QGridLayout() 

1169 self.splayout.addWidget(QtWidgets.QLabel("Server port:"),0,0) 

1170 self.edtServerport=QtWidgets.QLineEdit() 

1171 self.edtServerport.setValidator(self.intvalidator) 

1172 self.splayout.addWidget(self.edtServerport,0,1) 

1173 self.edtServerport.setText(str(self.__serverport__)) 

1174 self.vboxgbox.addLayout(self.splayout) 

1175 

1176# 

1177# Init radio buttons 

1178# 

1179 if self.__mode__==0: 

1180 self.radbutPIL.setChecked(True) 

1181 elif self.__mode__==1: 

1182 self.radbutTCPIP.setChecked(True) 

1183 else: 

1184 self.radbutServerport.setChecked(True) 

1185 self.setCheckBoxes() 

1186# 

1187# Section Working Directory 

1188# 

1189 self.gboxw = QtWidgets.QGroupBox() 

1190 self.gboxw.setFlat(True) 

1191 self.gboxw.setTitle("Working directory") 

1192 self.vboxgboxw= QtWidgets.QVBoxLayout() 

1193 self.gboxw.setLayout(self.vboxgboxw) 

1194 self.hboxwdir= QtWidgets.QHBoxLayout() 

1195 self.lbltxt6=QtWidgets.QLabel("Directory: ") 

1196 self.hboxwdir.addWidget(self.lbltxt6) 

1197 self.lblwdir=QtWidgets.QLabel() 

1198 self.lblwdir.setText(self.__workdir__) 

1199 self.hboxwdir.addWidget(self.lblwdir) 

1200 self.hboxwdir.addStretch(1) 

1201 self.butwdir=QtWidgets.QPushButton() 

1202 self.butwdir.setText("change") 

1203 self.butwdir.pressed.connect(self.do_config_Workdir) 

1204 self.hboxwdir.addWidget(self.butwdir) 

1205 self.vboxgboxw.addLayout(self.hboxwdir) 

1206 self.vbox1.addWidget(self.gboxw) 

1207 

1208 self.vbox1.addStretch(1) 

1209# 

1210# section lifutils path 

1211# 

1212 self.gboxlifpath = QtWidgets.QGroupBox() 

1213 self.gboxlifpath.setFlat(True) 

1214 self.gboxlifpath.setTitle("Custom LIFUTILS location") 

1215 self.vboxgboxlifpath= QtWidgets.QVBoxLayout() 

1216 self.gboxlifpath.setLayout(self.vboxgboxlifpath) 

1217 self.hboxlifpath= QtWidgets.QHBoxLayout() 

1218 self.lbltxt7=QtWidgets.QLabel("Path to lifversion program: ") 

1219 self.hboxlifpath.addWidget(self.lbltxt7) 

1220 self.lbllifpath=QtWidgets.QLabel() 

1221 self.lbllifpath.setText(self.__lifutilspath__) 

1222 self.hboxlifpath.addWidget(self.lbllifpath) 

1223 self.hboxlifpath.addStretch(1) 

1224 

1225 self.vboxlifbut= QtWidgets.QVBoxLayout() 

1226 self.butlifpathchange=QtWidgets.QPushButton() 

1227 self.butlifpathchange.setText("change") 

1228 self.butlifpathchange.pressed.connect(self.do_config_lifutilspath_change) 

1229 self.butlifpathclear=QtWidgets.QPushButton() 

1230 self.butlifpathclear.setText("clear") 

1231 self.butlifpathclear.pressed.connect(self.do_config_lifutilspath_clear) 

1232 self.vboxlifbut.addWidget(self.butlifpathchange) 

1233 self.vboxlifbut.addWidget(self.butlifpathclear) 

1234 

1235 self.hboxlifpath.addLayout(self.vboxlifbut) 

1236 self.vboxgboxlifpath.addLayout(self.hboxlifpath) 

1237 self.vbox2.addWidget(self.gboxlifpath) 

1238# 

1239# Section Terminal configuration: scroll up buffer, font size 

1240# 

1241 self.gboxt= QtWidgets.QGroupBox() 

1242 self.gboxt.setFlat(True) 

1243 self.gboxt.setTitle("Terminal Settings") 

1244 self.gridt= QtWidgets.QGridLayout() 

1245 self.gridt.setSpacing(3) 

1246 self.gridt.addWidget(QtWidgets.QLabel("Font Size"),0,0) 

1247 

1248 self.spinTermCharsize=QtWidgets.QSpinBox() 

1249 self.spinTermCharsize.setMinimum(13) 

1250 self.spinTermCharsize.setMaximum(20) 

1251 self.spinTermCharsize.setValue(self.__termcharsize__) 

1252 self.gridt.addWidget(self.spinTermCharsize,0,1) 

1253 

1254 self.gboxt.setLayout(self.gridt) 

1255 self.vbox2.addWidget(self.gboxt) 

1256# 

1257# HP82162A thermal printer settings 

1258# 

1259 self.gbox82162a= QtWidgets.QGroupBox() 

1260 self.gbox82162a.setFlat(True) 

1261 self.gbox82162a.setTitle("HP82162A Settings") 

1262 self.grid82162a= QtWidgets.QGridLayout() 

1263 self.grid82162a.setSpacing(3) 

1264 self.grid82162a.addWidget(QtWidgets.QLabel("Pixel size"),0,0) 

1265 

1266 self.spinHP82162APixelsize=QtWidgets.QSpinBox() 

1267 self.spinHP82162APixelsize.setMinimum(1) 

1268 self.spinHP82162APixelsize.setMaximum(2) 

1269 self.spinHP82162APixelsize.setValue(self.__hp82162a_pixelsize__) 

1270 self.grid82162a.addWidget(self.spinHP82162APixelsize,0,1) 

1271 

1272 self.gbox82162a.setLayout(self.grid82162a) 

1273 self.vbox2.addWidget(self.gbox82162a) 

1274# 

1275# HP2225B thermal printer settings 

1276# 

1277 self.gbox2225B= QtWidgets.QGroupBox() 

1278 self.gbox2225B.setFlat(True) 

1279 self.gbox2225B.setTitle("HP2225B Settings") 

1280 self.grid2225B= QtWidgets.QGridLayout() 

1281 self.grid2225B.setSpacing(3) 

1282 self.grid2225B.addWidget(QtWidgets.QLabel("Screen width size"),0,0) 

1283 

1284 self.spinHP2225Bscreenwidth=QtWidgets.QSpinBox() 

1285 self.spinHP2225Bscreenwidth.setMinimum(640) 

1286 self.spinHP2225Bscreenwidth.setMaximum(1280) 

1287 self.spinHP2225Bscreenwidth.setSingleStep(320) 

1288 self.spinHP2225Bscreenwidth.setValue(self.__hp2225b_screenwidth__) 

1289 self.grid2225B.addWidget(self.spinHP2225Bscreenwidth,0,1) 

1290 

1291 self.gbox2225B.setLayout(self.grid2225B) 

1292 self.vbox2.addWidget(self.gbox2225B) 

1293# 

1294# Section Directory listing configuration: font size 

1295# 

1296 self.gboxd= QtWidgets.QGroupBox() 

1297 self.gboxd.setFlat(True) 

1298 self.gboxd.setTitle("Directory Listing Settings") 

1299 self.gridd= QtWidgets.QGridLayout() 

1300 self.gridd.setSpacing(3) 

1301 self.gridd.addWidget(QtWidgets.QLabel("Font Size"),0,0) 

1302 self.spinDirCharsize=QtWidgets.QSpinBox() 

1303 self.spinDirCharsize.setMinimum(11) 

1304 self.spinDirCharsize.setMaximum(18) 

1305 self.spinDirCharsize.setValue(self.__dircharsize__) 

1306 self.gridd.addWidget(self.spinDirCharsize,0,1) 

1307 

1308 self.gboxd.setLayout(self.gridd) 

1309 self.vbox2.addWidget(self.gboxd) 

1310# 

1311# Section Papersize 

1312# 

1313 self.gboxps= QtWidgets.QGroupBox() 

1314 self.gboxps.setFlat(True) 

1315 self.gboxps.setTitle("Papersize (Plotter and PDF output)") 

1316 self.gridps=QtWidgets.QGridLayout() 

1317 self.gridps.setSpacing(3) 

1318 

1319 self.gridps.addWidget(QtWidgets.QLabel("Papersize:"),0,0) 

1320 self.combops=QtWidgets.QComboBox() 

1321 self.combops.addItem("A4") 

1322 self.combops.addItem("Letter") 

1323 self.combops.setCurrentIndex(self.__papersize__) 

1324 self.gridps.addWidget(self.combops,0,1) 

1325 self.gboxps.setLayout(self.gridps) 

1326 self.vbox2.addWidget(self.gboxps) 

1327# 

1328# section log file options (Windows only) 

1329# 

1330 self.gboxbom= QtWidgets.QGroupBox() 

1331 self.gboxbom.setFlat(True) 

1332 self.gboxbom.setTitle("UTF-8 encoding") 

1333 self.vboxbom= QtWidgets.QVBoxLayout() 

1334 self.cbUseBom= QtWidgets.QCheckBox('Use BOM (Windows only)') 

1335 self.cbUseBom.setChecked(self.__usebom__) 

1336 self.cbUseBom.stateChanged.connect(self.do_cbUseBom) 

1337 self.vboxbom.addWidget(self.cbUseBom) 

1338 self.gboxbom.setLayout(self.vboxbom) 

1339 if isWINDOWS(): 

1340 self.vbox2.addWidget(self.gboxbom) 

1341 

1342 self.vbox2.addStretch(1) 

1343# 

1344# add ok/cancel buttons 

1345# 

1346 self.gbox_buttonlist=[self.radbutPIL, self.radbutTCPIP] 

1347 

1348 self.buttonBox = QtWidgets.QDialogButtonBox() 

1349 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1350 self.buttonBox.setCenterButtons(True) 

1351 self.buttonBox.accepted.connect(self.do_ok) 

1352 self.buttonBox.rejected.connect(self.do_cancel) 

1353 self.hlayout = QtWidgets.QHBoxLayout() 

1354 self.hlayout.addWidget(self.buttonBox) 

1355 self.vbox0.addLayout(self.hlayout) 

1356 

1357 def setCheckBoxes(self): 

1358 if self.radbutPIL.isChecked(): 

1359 self.__mode__=0 

1360 self.butTty.setEnabled(True) 

1361 self.edtPort.setEnabled(False) 

1362 self.edtRemoteHost.setEnabled(False) 

1363 self.edtRemotePort.setEnabled(False) 

1364 self.cbIdyFrame.setEnabled(True) 

1365 self.edtServerport.setEnabled(False) 

1366 self.comboBaud.setEnabled(True) 

1367 elif self.radbutTCPIP.isChecked(): 

1368 self.__mode__=1 

1369 self.butTty.setEnabled(False) 

1370 self.edtPort.setEnabled(True) 

1371 self.edtRemoteHost.setEnabled(True) 

1372 self.edtRemotePort.setEnabled(True) 

1373 self.cbIdyFrame.setEnabled(True) 

1374 self.edtServerport.setEnabled(False) 

1375 self.comboBaud.setEnabled(False) 

1376 elif self.radbutServerport.isChecked(): 

1377 self.__mode__=2 

1378 self.butTty.setEnabled(False) 

1379 self.edtPort.setEnabled(False) 

1380 self.edtRemoteHost.setEnabled(False) 

1381 self.edtRemotePort.setEnabled(False) 

1382 self.cbIdyFrame.setEnabled(False) 

1383 self.edtServerport.setEnabled(True) 

1384 self.comboBaud.setEnabled(False) 

1385 

1386 def do_config_Interface(self): 

1387 interface= cls_TtyWindow.getTtyDevice() 

1388 if interface == "" : 

1389 return 

1390 self.__tty__= interface 

1391 self.lblTty.setText(self.__tty__) 

1392 

1393 def getWorkDirName(self): 

1394 dialog=QtWidgets.QFileDialog() 

1395 dialog.setWindowTitle("Select pyILPER working directory") 

1396 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

1397 dialog.setFileMode(QtWidgets.QFileDialog.Directory) 

1398 dialog.setOption(QtWidgets.QFileDialog.ShowDirsOnly,True) 

1399 if dialog.exec(): 

1400 return dialog.selectedFiles() 

1401 

1402 def do_cbIdyFrame(self): 

1403 self.__idyframe__= self.cbIdyFrame.isChecked() 

1404 

1405 def do_cbUseBom(self): 

1406 self.__usebom__= self.cbUseBom.isChecked() 

1407 

1408 def do_config_Workdir(self): 

1409 flist=self.getWorkDirName() 

1410 if flist is None: 

1411 return 

1412 self.__workdir__= flist[0] 

1413 self.lblwdir.setText(self.__workdir__) 

1414 

1415 def getLifutilsDirName(self): 

1416 dialog=QtWidgets.QFileDialog() 

1417 dialog.setWindowTitle("Select Path to lifversion executable") 

1418 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

1419 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 

1420 if dialog.exec(): 

1421 return dialog.selectedFiles() 

1422 

1423 def do_config_lifutilspath_clear(self): 

1424 self.__lifutilspath__= "" 

1425 self.lbllifpath.setText(self.__lifutilspath__) 

1426 

1427 def do_config_lifutilspath_change(self): 

1428 flist=self.getLifutilsDirName() 

1429 if flist is None: 

1430 return 

1431 self.__lifutilspath__= flist[0] 

1432 self.lbllifpath.setText(self.__lifutilspath__) 

1433# 

1434# check if configuration parameter was changed 

1435# 

1436 def check_param(self,param,value): 

1437 oldvalue= PILCONFIG.get(self.__name__,param,value) 

1438 return (value!= oldvalue) 

1439 

1440# 

1441# OK button pressed 

1442# 

1443 def do_ok(self): 

1444# 

1445# check if we need to restart the pyILPER communication 

1446# 

1447 self.__needs_reconnect__= False 

1448 self.__needs_reconnect__ |= self.check_param("mode",self.__mode__) 

1449 self.__needs_reconnect__ |= self.check_param("tty", self.lblTty.text()) 

1450 self.__needs_reconnect__ |= self.check_param("ttyspeed", BAUDRATES[self.comboBaud.currentIndex()][1]) 

1451 self.__needs_reconnect__ |= self.check_param("idyframe",self.__idyframe__) 

1452 self.__needs_reconnect__ |= self.check_param("port", int(self.edtPort.text())) 

1453 self.__needs_reconnect__ |= self.check_param("remotehost", self.edtRemoteHost.text()) 

1454 self.__needs_reconnect__ |= self.check_param("serverport", int(self.edtServerport.text())) 

1455 self.__needs_reconnect__ |= self.check_param("workdir", self.lblwdir.text()) 

1456# 

1457# we need to reconnect, so get confirmation 

1458# 

1459 if self.__needs_reconnect__ and PILCONFIG.get("pyilper","show_msg_commparams_changed",True): 

1460 msgbox= QtWidgets.QMessageBox() 

1461 msgbox.setText("The changes of communication parameters or the working directory require a disconnect and reconnect of the pyILPER communication. Continue?") 

1462 msgbox.setIcon(QtWidgets.QMessageBox.Warning) 

1463 msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) 

1464 msgbox.setDefaultButton(QtWidgets.QMessageBox.Cancel) 

1465 cb=QtWidgets.QCheckBox("Do not show this message again") 

1466 msgbox.setCheckBox(cb) 

1467 msgbox.setWindowTitle("Warning") 

1468 reply=msgbox.exec() 

1469 PILCONFIG.put("pyilper","show_msg_commparams_changed",cb.checkState()!=QtCore.Qt.Checked) 

1470# 

1471# not confirmed, cancel everything 

1472# 

1473 if reply == QtWidgets.QMessageBox.Cancel: 

1474 super().reject 

1475# 

1476# store parameters 

1477# 

1478 PILCONFIG.put(self.__name__,"mode",self.__mode__) 

1479 PILCONFIG.put(self.__name__,"tty", self.lblTty.text()) 

1480 PILCONFIG.put(self.__name__,"ttyspeed", BAUDRATES[self.comboBaud.currentIndex()][1]) 

1481 PILCONFIG.put(self.__name__,"idyframe",self.__idyframe__) 

1482 PILCONFIG.put(self.__name__,"port", int(self.edtPort.text())) 

1483 PILCONFIG.put(self.__name__,"remotehost", self.edtRemoteHost.text()) 

1484 PILCONFIG.put(self.__name__,"remoteport", int(self.edtRemotePort.text())) 

1485 PILCONFIG.put(self.__name__,"serverport", int(self.edtServerport.text())) 

1486 PILCONFIG.put(self.__name__,"workdir", self.lblwdir.text()) 

1487# 

1488# these parameters require a reconfiguration  

1489# 

1490 self.__needs_reconfigure__= False 

1491 self.__needs_reconfigure__ |= self.check_param("terminalcharsize",self.spinTermCharsize.value()) 

1492 self.__needs_reconfigure__ |= self.check_param("directorycharsize",self.spinDirCharsize.value()) 

1493 self.__needs_reconfigure__ |= self.check_param("hp82162a_pixelsize",self.spinHP82162APixelsize.value()) 

1494 self.__needs_reconfigure__ |= self.check_param("hp2225b_screenwidth",self.spinHP2225Bscreenwidth.value()) 

1495# 

1496# These parameters need a restart, display message 

1497# 

1498 self.__needs_restart__= False 

1499 self.__needs_restart__ |= self.check_param("papersize",self.combops.currentIndex()) 

1500 self.__needs_restart__ |= self.check_param("lifutilspath",self.lbllifpath.text()) 

1501# 

1502# some parameters need a restart of the application, inform user 

1503# 

1504 if self.__needs_restart__ and PILCONFIG.get("pyilper","show_msg_restartparams_changed",True): 

1505 msgbox= QtWidgets.QMessageBox() 

1506 msgbox.setText("Changes of the papersize, the scrollup buffer size or the lifutils path require a restart of pyILPER for take the changes to effect.") 

1507 msgbox.setIcon(QtWidgets.QMessageBox.Information) 

1508 msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok) 

1509 msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok) 

1510 cb=QtWidgets.QCheckBox("Do not show this message again") 

1511 msgbox.setCheckBox(cb) 

1512 msgbox.setWindowTitle("Information") 

1513 reply=msgbox.exec() 

1514 PILCONFIG.put("pyilper","show_msg_restartparams_changed",cb.checkState()!=QtCore.Qt.Checked) 

1515# 

1516# store parameters 

1517# 

1518 PILCONFIG.put(self.__name__,"terminalcharsize",self.spinTermCharsize.value()) 

1519 PILCONFIG.put(self.__name__,"directorycharsize",self.spinDirCharsize.value()) 

1520 PILCONFIG.put(self.__name__,"papersize",self.combops.currentIndex()) 

1521 PILCONFIG.put(self.__name__,"lifutilspath",self.lbllifpath.text()) 

1522 PILCONFIG.put(self.__name__,"hp82162a_pixelsize",self.spinHP82162APixelsize.value()) 

1523 PILCONFIG.put(self.__name__,"hp2225b_screenwidth",self.spinHP2225Bscreenwidth.value()) 

1524 PILCONFIG.put(self.__name__,"usebom",self.__usebom__) 

1525 super().accept() 

1526 

1527 def do_cancel(self): 

1528 super().reject() 

1529 

1530 def get_status(self): 

1531 return (self.__needs_reconnect__, self.__needs_reconfigure__) 

1532 

1533 

1534 @staticmethod 

1535 def getPilConfig(parent): 

1536 dialog= cls_PilConfigWindow(parent) 

1537 result= dialog.exec() 

1538 (reconnect,reconfigure)= dialog.get_status() 

1539 if result== QtWidgets.QDialog.Accepted: 

1540 return True, reconnect, reconfigure 

1541 else: 

1542 return False, False, False 

1543# 

1544# HP-IL virtual device configuration class ---------------------------------- 

1545# 

1546 

1547class cls_DeviceConfigWindow(QtWidgets.QDialog): 

1548 

1549 def __init__(self,parent): 

1550 super().__init__() 

1551 self.parent=parent 

1552 self.setWindowTitle('Virtual HP-IL device config') 

1553 self.vlayout = QtWidgets.QVBoxLayout() 

1554# 

1555# item list and up/down buttons 

1556# 

1557 self.hlayout = QtWidgets.QHBoxLayout() 

1558 self.devList = QtWidgets.QListWidget() 

1559 self.hlayout.addWidget(self.devList) 

1560 self.vlayout2= QtWidgets.QVBoxLayout() 

1561 self.buttonUp= QtWidgets.QPushButton("^") 

1562 self.vlayout2.addWidget(self.buttonUp) 

1563 self.buttonDown= QtWidgets.QPushButton("v") 

1564 self.vlayout2.addWidget(self.buttonDown) 

1565 self.buttonAdd= QtWidgets.QPushButton("Add") 

1566 self.vlayout2.addWidget(self.buttonAdd) 

1567 self.buttonRemove= QtWidgets.QPushButton("Remove") 

1568 self.vlayout2.addWidget(self.buttonRemove) 

1569 self.hlayout.addLayout(self.vlayout2) 

1570 self.vlayout.addLayout(self.hlayout) 

1571 self.buttonUp.clicked.connect(self.do_itemUp) 

1572 self.buttonDown.clicked.connect(self.do_itemDown) 

1573 self.buttonAdd.clicked.connect(self.do_itemAdd) 

1574 self.buttonRemove.clicked.connect(self.do_itemRemove) 

1575# 

1576# ok/cancel button box 

1577#  

1578 self.buttonBox = QtWidgets.QDialogButtonBox() 

1579 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1580 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1581 self.buttonBox.setCenterButtons(True) 

1582 self.buttonBox.accepted.connect(self.do_ok) 

1583 self.buttonBox.rejected.connect(self.do_cancel) 

1584 self.vlayout.addWidget(self.buttonBox) 

1585 self.setLayout(self.vlayout) 

1586# 

1587# fill list widget 

1588# 

1589 self.tabList=PILCONFIG.get(self.parent.name,"tabconfig") 

1590 for tab in self.tabList: 

1591 typ= tab[0] 

1592 name=tab[1] 

1593 self.devList.addItem(name+" ("+ TAB_NAMES[typ]+ ")") 

1594 self.devList.setCurrentRow(0) 

1595 

1596 def do_ok(self): 

1597 PILCONFIG.put(self.parent.name,"tabconfig",self.tabList) 

1598 super().accept() 

1599 

1600 def do_cancel(self): 

1601 super().reject() 

1602 

1603 def do_itemUp(self): 

1604 num_rows=self.devList.count() 

1605 if num_rows == 0: 

1606 return 

1607 row=self.devList.currentRow() 

1608 if row == 0: 

1609 return 

1610 item= self.devList.takeItem(row) 

1611 self.devList.insertItem(row-1,item) 

1612 item= None 

1613 self.devList.setCurrentRow(row-1) 

1614 temp=self.tabList[row] 

1615 self.tabList[row]= self.tabList[row-1] 

1616 self.tabList[row-1]=temp 

1617 return 

1618 

1619 

1620 def do_itemDown(self): 

1621 num_rows=self.devList.count() 

1622 if num_rows == 0: 

1623 return 

1624 row=self.devList.currentRow() 

1625 if row+1 == num_rows: 

1626 return 

1627 item= self.devList.takeItem(row) 

1628 self.devList.insertItem(row+1,item) 

1629 item= None 

1630 self.devList.setCurrentRow(row+1) 

1631 temp=self.tabList[row] 

1632 self.tabList[row]= self.tabList[row+1] 

1633 self.tabList[row+1]=temp 

1634 return 

1635 

1636 def do_itemRemove(self): 

1637 row=self.devList.currentRow() 

1638 del(self.tabList[row]) 

1639 item=self.devList.takeItem(row) 

1640 item= None 

1641 

1642 def do_itemAdd(self): 

1643 retval=cls_AddDeviceWindow.getAddDevice(self) 

1644 if retval== "": 

1645 return 

1646 typ=retval[0] 

1647 name=retval[1] 

1648 self.devList.addItem(name+" ("+ TAB_NAMES[typ]+ ")") 

1649 self.tabList.append([typ,name]) 

1650 

1651 @staticmethod 

1652 def getDeviceConfig(parent): 

1653 dialog= cls_DeviceConfigWindow(parent) 

1654 dialog.resize(350,100) 

1655 result= dialog.exec() 

1656 if result== QtWidgets.QDialog.Accepted: 

1657 return True 

1658 else: 

1659 return False 

1660# 

1661# validator checks for valid device name ------------------------------------- 

1662# 

1663class cls_Device_validator(QtGui.QValidator): 

1664 

1665 def validate(self,string,pos): 

1666 self.regexp = QtCore.QRegularExpression('[A-Za-z][A-Za-z0-9]*') 

1667 self.validator = QtGui.QRegularExpressionValidator(self.regexp) 

1668 result=self.validator.validate(string,pos) 

1669 return result[0], result[1], result[2] 

1670# 

1671# Add virtual device dialog class -------------------------------------------- 

1672# 

1673class cls_AddDeviceWindow(QtWidgets.QDialog): 

1674 

1675 def __init__(self,parent): 

1676 super().__init__() 

1677 self.typ= None 

1678 self.name=None 

1679 self.tabList=parent.tabList 

1680 self.setWindowTitle('New Virtual HP-IL device') 

1681# 

1682# Device name, allow only letter followed by letters or digits 

1683# 

1684 self.vlayout = QtWidgets.QVBoxLayout() 

1685 self.leditName= QtWidgets.QLineEdit(self) 

1686 self.leditName.setText("") 

1687 self.leditName.setMaxLength(10) 

1688 self.leditName.textChanged.connect(self.do_checkdup) 

1689 self.validator=cls_Device_validator() 

1690 self.leditName.setValidator(self.validator) 

1691 self.vlayout.addWidget(self.leditName) 

1692# 

1693# Combobox, omit the scope! 

1694# 

1695 self.comboTyp=QtWidgets.QComboBox() 

1696 for i in range(1,len(TAB_NAMES)): 

1697 self.comboTyp.addItem(TAB_NAMES[i]) 

1698 self.vlayout.addWidget(self.comboTyp) 

1699 

1700 self.buttonBox = QtWidgets.QDialogButtonBox() 

1701 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1702 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) 

1703 self.buttonBox.setCenterButtons(True) 

1704 self.buttonBox.accepted.connect(self.do_ok) 

1705 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) 

1706 self.buttonBox.rejected.connect(self.do_cancel) 

1707 self.vlayout.addWidget(self.buttonBox) 

1708 self.setLayout(self.vlayout) 

1709# 

1710# validate if name is not empty and unique 

1711# 

1712 def do_checkdup(self): 

1713 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) 

1714 tst=self.leditName.text() 

1715 if tst=="": 

1716 return 

1717 for tab in self.tabList: 

1718 if tst== tab[1]: 

1719 return 

1720 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True) 

1721# 

1722# return results 

1723# 

1724 def getResult(self): 

1725 return([self.typ,self.name]) 

1726 

1727# 

1728# only enabled if name is not empty and unique 

1729# 

1730 def do_ok(self): 

1731 self.name= self.leditName.text() 

1732 self.typ= self.comboTyp.currentIndex()+1 

1733 super().accept() 

1734 

1735 def do_cancel(self): 

1736 super().reject() 

1737 

1738 @staticmethod 

1739 def getAddDevice(parent): 

1740 dialog= cls_AddDeviceWindow(parent) 

1741 dialog.resize(250,100) 

1742 result= dialog.exec() 

1743 if result== QtWidgets.QDialog.Accepted: 

1744 return dialog.getResult() 

1745 else: 

1746 return "" 

1747# 

1748# HP-IL device Status Dialog class ------------------------------------------- 

1749# 

1750class cls_DevStatusWindow(QtWidgets.QDialog): 

1751 

1752 def __init__(self,parent): 

1753 super().__init__() 

1754 self.parent=parent 

1755 self.setWindowTitle('Virtual HP-IL device status') 

1756 self.vlayout = QtWidgets.QVBoxLayout() 

1757 self.setLayout(self.vlayout) 

1758 self.__timer__=QtCore.QTimer() 

1759 self.__timer__.timeout.connect(self.do_refresh) 

1760 self.rows=len(parent.pilwidgets)-1 

1761 self.cols=5 

1762 self.__table__ = QtWidgets.QTableWidget(self.rows,self.cols) # Table view for dir 

1763 self.__table__.setSortingEnabled(False) # no sorting 

1764# 

1765# switch off grid, no focus, no row selection 

1766# 

1767 self.__table__.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) 

1768 self.__table__.setFocusPolicy(QtCore.Qt.NoFocus) 

1769 self.__table__.setShowGrid(False) 

1770 h1= QtWidgets.QTableWidgetItem() 

1771 h1.setText("Device") 

1772 self.__table__.setHorizontalHeaderItem(0,h1) 

1773 h2= QtWidgets.QTableWidgetItem() 

1774 h2.setText("DID") 

1775 self.__table__.setHorizontalHeaderItem(1,h2) 

1776 h3= QtWidgets.QTableWidgetItem() 

1777 h3.setText("AID") 

1778 self.__table__.setHorizontalHeaderItem(2,h3) 

1779 h4= QtWidgets.QTableWidgetItem() 

1780 h4.setText("Addr.") 

1781 self.__table__.setHorizontalHeaderItem(3,h4) 

1782 h5= QtWidgets.QTableWidgetItem() 

1783 h5.setText("HP-IL Status") 

1784 self.__table__.setHorizontalHeaderItem(4,h5) 

1785 self.__table__.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch) 

1786 self.__table__.resizeColumnsToContents() 

1787# 

1788# no vertical header 

1789# 

1790 self.__table__.verticalHeader().setVisible(False) 

1791 self.__table__.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed) 

1792 self.__table__.verticalHeader().setDefaultSectionSize(16) 

1793# 

1794# populate 

1795# 

1796 self.__items__= { } 

1797 for row in range(self.rows): 

1798 for col in range(self.cols): 

1799 self.__items__[row,col]= QtWidgets.QTableWidgetItem() 

1800 if col > 1: 

1801 self.__items__[row,col].setTextAlignment(QtCore.Qt.AlignHCenter) 

1802 self.__items__[row,col].setText(" ") 

1803 self.__table__.setItem(row,col,self.__items__[row,col]) 

1804 

1805 self.__table__.resizeRowsToContents() 

1806 self.vlayout.addWidget(self.__table__) 

1807 self.button = QtWidgets.QPushButton('OK') 

1808 self.button.setFixedWidth(60) 

1809 self.button.clicked.connect(self.do_exit) 

1810 self.hlayout = QtWidgets.QHBoxLayout() 

1811 self.hlayout.addWidget(self.button) 

1812 self.vlayout.addLayout(self.hlayout) 

1813 self.resize(600,self.sizeHint().height()) 

1814 self.do_refresh() 

1815 

1816 def hideEvent(self,event): 

1817 self.__timer__.stop() 

1818 

1819 def showEvent(self,event): 

1820 self.__timer__.start(500) 

1821 

1822 def do_exit(self): 

1823 super().accept() 

1824 

1825 def do_refresh(self): 

1826 devices=self.parent.commthread.getDevices() 

1827 if not devices: 

1828 return 

1829 i=1 

1830 for row in range(self.rows): 

1831 pildevice= devices[i][0] 

1832 name=devices[i][1] 

1833 i+=1 

1834 self.__items__[row,0].setText(name) 

1835 for col in range (1,self.cols): 

1836 self.__items__[row,col].setText("") 

1837 if pildevice is None: 

1838 continue 

1839 (active, did, aid, addr, addr2nd, hpilstatus)= pildevice.getstatus() 

1840 if not active: 

1841 continue 

1842 devaddr = '' 

1843 if (addr & 0x80) != 0: 

1844 devaddr = str(addr & 0x1F) 

1845 if (addr2nd & 0x80) != 0: 

1846 devextaddr = ".{0:02d}".format((addr2nd & 0x1F) + 1) 

1847 devaddr += devextaddr 

1848 self.__items__[row,1].setText(did) 

1849 self.__items__[row,2].setText("{0:x}".format(aid)) 

1850 self.__items__[row,3].setText(devaddr) 

1851 self.__items__[row,4].setText("{0:s}".format(hpilstatus)) 

1852# 

1853# Main Window user interface class ------------------------------------------- 

1854# 

1855class cls_ui(QtWidgets.QMainWindow): 

1856 

1857 def __init__(self,parent,version,instance): 

1858 super().__init__() 

1859 if instance == "": 

1860 self.setWindowTitle("pyILPER "+version) 

1861 else: 

1862 self.setWindowTitle("pyILPER "+version+" Instance: "+instance) 

1863# 

1864# signals 

1865# 

1866 self.sig_crash= parent.sig_crash 

1867 self.sig_quit= parent.sig_quit 

1868 self.sig_show_message= parent.sig_show_message 

1869 

1870# 

1871# Menu 

1872# 

1873 self.menubar = self.menuBar() 

1874 self.menubar.setNativeMenuBar(False) 

1875 self.menuFile= self.menubar.addMenu('File') 

1876 self.menuUtil= self.menubar.addMenu('Utilities') 

1877 self.menuHelp= self.menubar.addMenu('Help') 

1878 

1879 self.actionConfig=self.menuFile.addAction("pyILPER configuration") 

1880 self.actionDevConfig=self.menuFile.addAction("Virtual HP-IL device configuration") 

1881 self.actionPenConfig=self.menuFile.addAction("Plotter pen configuration") 

1882 self.actionShortcutConfig=self.menuFile.addAction("Terminal keyboard shortcut configuration") 

1883 self.actionReconnect=self.menuFile.addAction("Reconnect") 

1884 self.actionExit=self.menuFile.addAction("Quit") 

1885 

1886 self.actionInit=self.menuUtil.addAction("Initialize LIF image file") 

1887 self.actionFix=self.menuUtil.addAction("Fix Header of LIF image file") 

1888 self.actionDevStatus=self.menuUtil.addAction("Virtual HP-IL device status") 

1889 self.actionCopyPilimage=self.menuUtil.addAction("Copy PILIMAGE.DAT to workdir") 

1890 self.actionInstallCheck=self.menuUtil.addAction("Check LIFUTILS installation") 

1891 self.actionInit.setEnabled(False) 

1892 self.actionFix.setEnabled(False) 

1893 

1894 self.actionAbout=self.menuHelp.addAction("About") 

1895 self.actionHelp=self.menuHelp.addAction("Manual") 

1896# 

1897 self.tabs=cls_Tabs() 

1898 self.setCentralWidget(self.tabs) 

1899# 

1900# Status bar 

1901# 

1902 self.statusbar=self.statusBar() 

1903# 

1904# Size policy 

1905# 

1906 self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 

1907 

1908# 

1909# queued emit of the signal to update the message text 

1910# 

1911 def emit_message(self,s): 

1912 self.sig_show_message.emit(s) 

1913# 

1914# queued emit of the signal to indicate crash 

1915# 

1916 def emit_crash(self): 

1917 self.sig_crash.emit() 

1918# 

1919# catch close event 

1920# 

1921 def closeEvent(self,event): 

1922 event.accept() 

1923 self.sig_quit.emit() 

1924# 

1925# enable controls that require lifutils 

1926# 

1927 def enableLIFControls(self): 

1928 self.actionInit.setEnabled(True) 

1929 self.actionFix.setEnabled(True)