Coverage for pyilper/pyilpermain.py: 81%

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

394 statements  

1#!/usr/bin/python3 

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

3# pyILPER 1.2.4 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 main program ----------------------------------------------------- 

26# 

27# Changelog 

28# 05.10.2015 jsi: 

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

30# - adjust super statements to python3+ syntax 

31# 11.10.2015 jsi: 

32# - bump version number to 1.2.2 

33# 24.10.2015 jsi 

34# - raise gui (OS X issue) 

35# 28.10.2015 jsi 

36# - introduced separate version number for config file layout 

37# 08.11.2015 jsi 

38# - bump version number to 1.2.3 

39# 14.11.2015 jsi 

40# - bump version number to 1.3.0 

41# 29.11.2015 jsi 

42# - removed pause and resume pil loop 

43# 30.11.2015 jsi 

44# - introduced idyframe option 

45# - bump version number to 1.3.1 

46# 18.12.2015 jsi 

47# - bump version number to 1.3.2 

48# 26.12.2015 cg 

49# -fixed misspelling 

50# 10.1.2016 jsi 

51# - store last windows position as proposed by cg 

52# 11.1.2016 jsi 

53# - corrected error im my implementation of determining the windows position 

54# 22.1.2016 jsi 

55# - corrected typo in Message box if HP-IL device config was changed, hint by cg 

56# 01.02.2016 jsi 

57# - added InstallCheck menu entry 

58# 16.02.2016 jsi 

59# - bump version number to 1.3.3 (Development) 

60# 20.02.2016 jsi 

61# - dump stacks introduced 

62# 21.02.2016 jsi 

63# - dump stacks disabled for windows 

64# 02.03.2016 jsi 

65# - added generic terminal configuration 

66# 05.03.2016 jsi 

67# - renmoved dead code 

68# 22.02.2016 jsi 

69# - modified call of cls_ui 

70# 01.04.2016 jsi 

71# - added copy function of PILIMAGE.DAT to the utility menu 

72# 03.04.2016 jsi 

73# - 1.3.3 productio 

74# 05.04.2016 jsi 

75# - bump version number to 1.3.4 (Development) 

76# 07.04.2016 cg 

77# - added 230400 baud to supported baudrates 

78# 14.04.2016 jsi 

79# - store coordinate list of position in config instead of QPoint Object 

80# - set config file version to 2 

81# 21.04.2016 jsi 

82# - development version uses other config file than the production version. This  

83# is controlled by the PRODUCTION constant 

84# 26.04.2016 jsi 

85# - IDY frame processing now enabled by default 

86# - remove baudrate config parameter 

87# - TypeError in open tty device now handled in pilbox.py 

88# - show error if no serial device was configured. 

89# 28.04.2016 jsi 

90# - call post_enable to register outbound scope device 

91# 07.05.2016 jsi 

92# - introduce parameter for scroll up buffer size 

93# 08.05.2016 jsi 

94# - refactoring, make autobaud/baud rate setting configurable again (ttyspeed parameter) 

95# 03.07.2016 jsi 

96# - refactoring import reorganized 

97# 08.07.2016 jsi 

98# - refactoring, use constant for configuration 

99# - refactoring, move constants to pilcore.py 

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

101# 27.08.2016 jsi 

102# - tab configuration rewritten 

103# 18.09.2016 jsi 

104# - multiple instances capability added 

105# 13.10.2016 jsi 

106# - device configuration rewritten (add, remove and change position of devices) 

107# 19.10.2016 jsi 

108# - added pen config menu entry (merged) 

109# 19.11.2016 jsi 

110# - pipes thread added 

111# 11.12.2016 jsi 

112# - default configuration now includes the plotter 

113# 07.01.2016 jsi 

114# - store pyILPER version in config file 

115# - show "first run" information, if started unconfigures 

116# - show release information if a new pyILPER version was started the first time 

117# - show tty device unconfigured message in status bar instead in pop up window 

118# 19.02.2017 jsi: 

119# - directorycharsize parameter introduced 

120# 11.03.2017 jsi: 

121# - change document names of release notes and change log 

122# 16.03.2017 jsi: 

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

124# 17.03.2017 jsi: 

125# - do not load initial document, if online manual is re-opened 

126# 01.08.2017 jsi 

127# - HP82162A added 

128# 07.08.2017 jsi 

129# - papersize parameter now global 

130# - refactoring of tab classes 

131# - minimum display position is 50,50 - otherwise some display manager hide 

132# the menu bar (e.g. RASPBIAN) 

133# - error in exception handling of reading the config file fixed 

134# 23.08.2017 jsi: 

135# - used pilsocket instead of pilpipes 

136# 30.08.2017 jsi: 

137# - save and restore main window size 

138# 31.08.2017 jsi: 

139# - config parameter terminalsize changed to terminalwidth 

140# 04.09.2017 jsi: 

141# - refactoring of thread classes 

142# 20.09.2017 jsi: 

143# - configuration of tab parameters at runtime 

144# 28.10.2017 jsi: 

145# - detection of lifutils now in cls_pyilper 

146# 12.11.2017 jsi: 

147# - removed configuration parameters: winpipename and socketname 

148# - introduced configuration parameter: serverport 

149# - remove MODE_PIPE communication 

150# 17.11.2017 jsi: 

151# - put drive tabs at the end in the default configuration 

152# 05.12.2017 jsi 

153# - initialized parameter hp82162a_pixelsize 

154# 27.12.2017 jsi 

155# - changes because of new cls_Tabs widget 

156# 17.01.2018 jsi 

157# - remove global parameters for terminal width and color scheme which 

158# are now tab specific.  

159# - remove scrollupbuffersize as global parameter 

160# 05.02.2018 jsi 

161# - usebom config variable introduced 

162# 12.02.2018 jsi 

163# - added --clean startup option 

164# 01.03.2018 jsi 

165# - check minimum python version 

166# 10.08.2018 jsi 

167# - cls_PenConfigWindow moved to penconfig.py 

168# 11.08.2018 jsi 

169# - terminal custom shortcut config added 

170# 18.12.2018 jsi 

171# - added HP2225B tab 

172# 06.01.2018 jsi 

173# - added HP2225B screenwidth global parameter 

174# 30.11.2019 jsi 

175# - improved help text for command line parameters 

176# 26.11.2020 jsi 

177# - fix: disable filemanagement controls, if lifutils are not installed 

178# 15.11.2021 jsi 

179# - raw drive tab added to TAB_CLASSES dict 

180# 12.12.2021 jsi 

181# - copy config runtime option added 

182# - check if the configuration are from a newer pyILPER version 

183# 18.12.2021 jsi 

184# - copy config: display versions 

185# 19.12.2021 jsi 

186# - copy config: error processing added 

187# 

188import os 

189import sys 

190import signal 

191import traceback 

192import shutil 

193import pyilper 

194import re 

195import argparse 

196import time 

197from PySide6 import QtCore, QtWidgets 

198from .pilwidgets import cls_ui, cls_AboutWindow, cls_HelpWindow, HelpError, cls_DeviceConfigWindow, cls_DevStatusWindow, cls_PilConfigWindow 

199from .pilcore import * 

200from .pilconfig import PilConfigError, PILCONFIG, cls_pilconfig 

201from .penconfig import PenConfigError, PENCONFIG, cls_PenConfigWindow 

202from .shortcutconfig import ShortcutConfigError, SHORTCUTCONFIG, cls_ShortcutConfigWindow 

203from .pilthreads import cls_PilBoxThread, cls_PilTcpIpThread, cls_PilSocketThread, PilThreadError 

204from .lifexec import cls_lifinit, cls_liffix, cls_installcheck, check_lifutils 

205from .pilhp82162a import cls_tabhp82162a 

206from .pilplotter import cls_tabplotter 

207from .pildrive import cls_tabdrive,cls_tabrawdrive 

208from .pilscope import cls_tabscope 

209from .pilprinter import cls_tabprinter 

210from .pilterminal import cls_tabterminal 

211from .pilhp2225b import cls_tabhp2225b 

212 

213STAT_DISABLED = 0 # Application in cold state: not running 

214STAT_ENABLED = 1 # Application in warm state: running 

215 

216COMMTHREAD_CLASSES={MODE_PILBOX:cls_PilBoxThread,MODE_TCPIP:cls_PilTcpIpThread,MODE_SOCKET:cls_PilSocketThread} 

217 

218TAB_CLASSES={TAB_SCOPE:cls_tabscope,TAB_PRINTER:cls_tabprinter,TAB_DRIVE:cls_tabdrive,TAB_TERMINAL:cls_tabterminal,TAB_PLOTTER:cls_tabplotter,TAB_HP82162A:cls_tabhp82162a,TAB_HP2225B: cls_tabhp2225b, TAB_RAWDRIVE: cls_tabrawdrive} 

219 

220# 

221# Main application ------------------------------------------------------  

222# 

223 

224class cls_pyilper(QtCore.QObject): 

225 

226 sig_show_message=QtCore.Signal(str) 

227 sig_crash=QtCore.Signal() 

228 sig_quit=QtCore.Signal() 

229 

230 

231 def __init__(self,args): 

232 

233 super().__init__() 

234 self.name="pyilper" 

235 self.instance="" 

236 self.clean=False 

237 if args.instance: 

238 if args.instance.isalnum(): 

239 self.instance=args.instance 

240 if args.clean: 

241 self.clean=True 

242 self.status= STAT_DISABLED 

243 self.pilwidgets= [ ] 

244 self.commobject=None 

245 self.commthread= None 

246 self.helpwin= None 

247 self.aboutwin=None 

248 self.devstatuswin=None 

249 self.lifutils_installed= False 

250 self.message="" 

251 self.msgTimer=QtCore.QTimer() 

252 self.msgTimer.timeout.connect(self.show_refresh_message) 

253# 

254# create user interface instance 

255# 

256 self.ui= cls_ui(self,VERSION,self.instance) 

257# 

258# check minimum python version 

259# 

260 if sys.version_info < ( PYTHON_REQUIRED_MAJOR, PYTHON_REQUIRED_MINOR): 

261 required_version= str(PYTHON_REQUIRED_MAJOR)+"."+str(PYTHON_REQUIRED_MINOR) 

262 found_version=str(sys.version_info.major)+"."+str(sys.version_info.minor)+"."+str(sys.version_info.micro) 

263 reply=QtWidgets.QMessageBox.critical(self.ui,'Error','pyILPER requires at least Python version '+required_version+". You rund Python version "+found_version,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

264 sys.exit(1) 

265 

266# 

267# Initialize Main Window, connect callbacks 

268# 

269 self.ui.actionConfig.triggered.connect(self.do_pyilperConfig) 

270 self.ui.actionDevConfig.triggered.connect(self.do_DevConfig) 

271 self.ui.actionPenConfig.triggered.connect(self.do_PenConfig) 

272 self.ui.actionShortcutConfig.triggered.connect(self.do_ShortcutConfig) 

273 self.ui.actionDevStatus.triggered.connect(self.do_DevStatus) 

274 self.ui.actionReconnect.triggered.connect(self.do_Reconnect) 

275 self.ui.actionExit.triggered.connect(self.do_Exit) 

276 self.ui.actionInit.triggered.connect(self.do_Init) 

277 self.ui.actionFix.triggered.connect(self.do_Fix) 

278 self.ui.actionCopyPilimage.triggered.connect(self.do_CopyPilimage) 

279 self.ui.actionInstallCheck.triggered.connect(self.do_InstallCheck) 

280 self.ui.actionAbout.triggered.connect(self.do_About) 

281 self.ui.actionHelp.triggered.connect(self.do_Help) 

282 

283# 

284# queued signal to show a status message. This is the only update of 

285# the user interface that is issued by the thread process and must 

286# be issued as a queued connection 

287# 

288 self.sig_show_message.connect(self.show_message, QtCore.Qt.QueuedConnection) 

289 self.sig_crash.connect(self.do_crash_cleanup, QtCore.Qt.QueuedConnection) 

290 self.sig_quit.connect(self.do_Exit, QtCore.Qt.QueuedConnection) 

291 

292# 

293# Set up configuration subsystem 

294# 1. pyILPER config 

295# 

296 try: 

297 PILCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean) 

298 PILCONFIG.get(self.name,"active_tab",0) 

299 PILCONFIG.get(self.name,"tabconfigchanged",False) 

300 PILCONFIG.get(self.name,"tty","") 

301 PILCONFIG.get(self.name,"ttyspeed",0) 

302 PILCONFIG.get(self.name,"idyframe",True) 

303 PILCONFIG.get(self.name,"port",60001) 

304 PILCONFIG.get(self.name,"remotehost","localhost") 

305 PILCONFIG.get(self.name,"remoteport",60000) 

306 PILCONFIG.get(self.name,"mode",MODE_PILBOX) 

307 PILCONFIG.get(self.name,"workdir",os.path.expanduser('~')) 

308 PILCONFIG.get(self.name,"position","") 

309 PILCONFIG.get(self.name,"serverport",59999) 

310 PILCONFIG.get(self.name,"tabconfig",[[TAB_PRINTER,"Printer1"],[TAB_TERMINAL,"Terminal1"],[TAB_PLOTTER,"Plotter1"],[TAB_DRIVE,"Drive1"],[TAB_DRIVE,"Drive2"]]) 

311 PILCONFIG.get(self.name,"version","0.0.0") 

312 PILCONFIG.get(self.name,"helpposition","") 

313 PILCONFIG.get(self.name,"papersize",0) 

314 PILCONFIG.get(self.name,"lifutilspath","") 

315 PILCONFIG.get(self.name,"terminalcharsize",15) 

316 PILCONFIG.get(self.name,"directorycharsize",13) 

317 PILCONFIG.get(self.name,"hp82162a_pixelsize",1) 

318 PILCONFIG.get(self.name,"hp2225b_screenwidth",640) 

319 PILCONFIG.get(self.name,"usebom",False) 

320 

321 PILCONFIG.save() 

322 except PilConfigError as e: 

323 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

324 sys.exit(1) 

325# 

326# 2. pen configuration 

327# 

328 try: 

329 PENCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean) 

330 except PenConfigError as e: 

331 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

332 sys.exit(1) 

333# 

334# 3. terminal keyboard shortcuts 

335# 

336 try: 

337 SHORTCUTCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean) 

338 except ShortcutConfigError as e: 

339 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

340 sys.exit(1) 

341# 

342# check lifutils 

343# 

344 self.lifutils_installed= check_lifutils()[0] 

345 if self.lifutils_installed: 

346 self.ui.enableLIFControls() 

347# 

348# version check, warn user if the configuration files are of a newer 

349# version 

350# 

351 oldversion=decode_pyILPERVersion(PILCONFIG.get(self.name,"version")) 

352 thisversion=decode_pyILPERVersion(VERSION) 

353 if thisversion < oldversion: 

354 reply=QtWidgets.QMessageBox.warning(self.ui,'Warning',"Your configuration files are of pyILPER version "+PILCONFIG.get(self.name,"version")+" which is newer than the version you are running. The program might crash or mishehave. Do you want to continue?",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel) 

355 if reply== QtWidgets.QMessageBox.Cancel: 

356 sys.exit(1) 

357 PILCONFIG.put(self.name,"version",VERSION) 

358# 

359# create tab objects, scope is fixed all others are configured by tabconfig 

360# 

361 active_tab=PILCONFIG.get(self.name,"active_tab") 

362 self.registerTab(cls_tabscope,"Scope") 

363 for t in PILCONFIG.get(self.name,"tabconfig"): 

364 self.registerTab(TAB_CLASSES[t[0]],t[1]) 

365# 

366# remove config of non existing tabs 

367# 

368 if PILCONFIG.get(self.name,"tabconfigchanged"): 

369 PILCONFIG.put(self.name,"tabconfigchanged",False) 

370 PILCONFIG.put(self.name,"active_tab",0) 

371 names= [self.name] 

372 for obj in self.pilwidgets: 

373 names.append(obj.name) 

374 removekeys=[] 

375 for key in PILCONFIG.getkeys(): 

376 prefix=key.split(sep="_")[0] 

377 if not prefix in names: 

378 removekeys.append(key) 

379 for key in removekeys: 

380 PILCONFIG.remove(key) 

381 else: 

382# 

383# go to last active tab (if tabconfig did not change) 

384# 

385 self.ui.tabs.setCurrentIndex(active_tab) 

386# 

387# move window to last position 

388# 

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

390 if position !="": 

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

392 if len(position)==4: 

393 self.ui.resize(position[2],position[3]) 

394# 

395# show and raise gui 

396# 

397 self.ui.show() 

398 self.ui.raise_() 

399# 

400# start application into warm state 

401# 

402 self.enable() 

403 self.msgTimer.start(500) 

404# 

405# if we run pyILPER for the first time (oldversion =0.0.0), show startup info 

406# 

407 if PILCONFIG.get(self.name,"position")=="": 

408 self.show_StartupInfo() 

409 else: 

410# 

411# if we run a new version for the first time, show release notes 

412# 

413 if thisversion > oldversion: 

414 self.show_ReleaseInfo(VERSION) 

415# 

416# start application into warm state 

417# 

418 def enable(self): 

419 if self.status== STAT_ENABLED: 

420 return 

421# 

422# set working directory 

423# 

424 try: 

425 os.chdir(PILCONFIG.get(self.name,'workdir')) 

426 except OSError as e: 

427 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Cannot change to working directory: "+e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

428 return 

429# 

430# create and enable thread 

431# 

432 mode=PILCONFIG.get(self.name,"mode") 

433 try: 

434 commthread_class= COMMTHREAD_CLASSES[mode] 

435 self.commthread= commthread_class(self.ui) 

436 self.commthread.enable() 

437 except PilThreadError as e: 

438 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+": "+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

439 return 

440# 

441# enable all registered tab objects 

442# 

443 for obj in self.pilwidgets: 

444 obj.enable() 

445# 

446# register outbound scope 

447# 

448 self.pilwidgets[0].post_enable() 

449# 

450# start emulator thread 

451# 

452 self.commthread.start() 

453 self.status= STAT_ENABLED 

454# 

455# trigger visible virtual device widget to enable refreshs 

456# 

457 pilwidget= self.ui.tabs.pilWidget(PILCONFIG.get(self.name,"active_tab")) 

458 pilwidget.becomes_visible() 

459 

460# 

461# shut down application to cold state 

462# 

463 def disable(self): 

464 if self.status== STAT_DISABLED: 

465 return 

466# 

467# stop emulator thread 

468# 

469 if self.commthread is not None: 

470 if self.commthread.isRunning: 

471 self.commthread.finish() 

472# 

473# disable all registered tab objects 

474# 

475 for obj in self.pilwidgets: 

476 obj.disable() 

477 

478# 

479# close commobject/tcpip connection 

480# 

481 if self.commthread is not None: 

482 self.commthread.disable() 

483 self.status= STAT_DISABLED 

484 self.commthread= None 

485# 

486# clean up from thread crash 

487# 

488 def do_crash_cleanup(self): 

489 time.sleep(1) 

490 self.disable() 

491 

492# 

493# show status message 

494# 

495 def show_message(self,message): 

496 self.message=message 

497 self.ui.statusbar.showMessage(self.message) 

498 

499# 

500# refresh status message 

501# 

502 def show_refresh_message(self): 

503 self.ui.statusbar.showMessage(self.message) 

504 

505 def registerTab(self,classname,name): 

506 tab= classname(self,name) 

507 self.ui.tabs.addTab(tab,name) 

508 self.pilwidgets.append(tab) 

509# 

510# callback pyilper configuration, reset the communication only if needed 

511# 

512 def do_pyilperConfig(self): 

513 (accept, needs_reconnect, needs_reconfigure)= cls_PilConfigWindow.getPilConfig(self) 

514 if accept: 

515 if needs_reconnect: 

516 self.disable() 

517 try: 

518 PILCONFIG.save() 

519 except PilConfigError as e: 

520 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

521 return 

522 if needs_reconnect: 

523 self.enable() 

524# 

525# reconfigure the tabs while the thread is stopped 

526# 

527 if needs_reconfigure: 

528 if self.status== STAT_ENABLED: 

529 if self.commthread is not None: 

530 if self.commthread.isRunning(): 

531 self.commthread.halt() 

532 for obj in self.pilwidgets: 

533 obj.reconfigure() 

534 if self.status== STAT_ENABLED: 

535 if self.commthread is not None: 

536 self.commthread.resume() 

537# 

538# callback HP-IL device config 

539# 

540 def do_DevConfig(self): 

541 if not cls_DeviceConfigWindow.getDeviceConfig(self): 

542 return 

543 PILCONFIG.put(self.name,"tabconfigchanged",True) 

544 try: 

545 PILCONFIG.save() 

546 except PilConfigError as e: 

547 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

548 return 

549 reply=QtWidgets.QMessageBox.information(self.ui,"Restart required","HP-IL Device configuration changed. Restart Application.",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

550 

551# 

552# callback plotter pen configuration 

553# 

554 def do_PenConfig(self): 

555 if not cls_PenConfigWindow.getPenConfig(): 

556 return 

557 try: 

558 PENCONFIG.save() 

559 except PenConfigError as e: 

560 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

561 return 

562 

563# 

564# callback terminal keyboard shortcut configuration 

565# 

566 def do_ShortcutConfig(self): 

567 if not cls_ShortcutConfigWindow.getShortcutConfig(): 

568 return 

569 try: 

570 SHORTCUTCONFIG.save() 

571 except ShortcutConfigError as e: 

572 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

573 return 

574 

575# 

576# callback show hp-il device status 

577# 

578 def do_DevStatus(self): 

579 if self.devstatuswin is None: 

580 self.devstatuswin= cls_DevStatusWindow(self) 

581 self.devstatuswin.show() 

582 self.devstatuswin.raise_() 

583# 

584# callback reconnect 

585# 

586 def do_Reconnect(self): 

587 self.disable() 

588 self.enable() 

589# 

590# callback exit, store windows position and size, close floating windows 

591# 

592 def do_Exit(self): 

593 self.disable() 

594 pos_x=self.ui.pos().x() 

595 pos_y=self.ui.pos().y() 

596 if pos_x < 50: 

597 pos_x=50 

598 if pos_y < 50: 

599 pos_y=50 

600 width= self.ui.width() 

601 height= self.ui.height() 

602 

603 position=[pos_x, pos_y, width, height] 

604 

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

606 if self.helpwin is not None: 

607 helpposition=[self.helpwin.pos().x(),self.helpwin.pos().y(),self.helpwin.width(),self.helpwin.height()] 

608 PILCONFIG.put(self.name,"helpposition",helpposition) 

609 self.ui.tabs.closeFloatingWindows() 

610 try: 

611 PILCONFIG.save() 

612 except PilConfigError as e: 

613 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

614 QtWidgets.QApplication.quit() 

615# print(os.times()) 

616# 

617# callback initialize LIF medium 

618# 

619 def do_Init(self): 

620 workdir= PILCONFIG.get(self.name,"workdir") 

621 cls_lifinit.execute(workdir) 

622 

623# 

624# callback fix LIF Medium 

625# 

626 def do_Fix(self): 

627 workdir= PILCONFIG.get(self.name,"workdir") 

628 cls_liffix.execute(workdir) 

629# 

630# callback check LIFUTILS installation 

631# 

632 def do_InstallCheck(self): 

633 cls_installcheck.execute() 

634 

635# 

636# callback copy PILIMAGE.DAT to working directory 

637# 

638 def do_CopyPilimage(self): 

639 

640 srcfile=os.path.join(os.path.dirname(pyilper.__file__),"lifimage","PILIMAGE.DAT") 

641 srcfile= re.sub("//","/",srcfile,1) 

642 dstpath=PILCONFIG.get(self.name,"workdir") 

643 if os.access(os.path.join(dstpath,"PILIMAGE.DAT"),os.W_OK): 

644 reply=QtWidgets.QMessageBox.warning(self.ui,'Warning',"File PILIMAGE.DAT already exists. Do you really want to overwrite that file?",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel) 

645 if reply== QtWidgets.QMessageBox.Cancel: 

646 return 

647 try: 

648 shutil.copy(srcfile,dstpath) 

649 except OSError as e: 

650 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Cannot copy file: "+e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

651 return 

652 except shutil.SameFileError: 

653 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Source and destination file are identical",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

654 return 

655 

656# 

657# callback show about window 

658# 

659 def do_About(self): 

660 if self.aboutwin is None: 

661 self.aboutwin= cls_AboutWindow(VERSION) 

662 self.aboutwin.show() 

663 self.aboutwin.raise_() 

664# 

665# callback show help window 

666# 

667 def do_Help(self): 

668 self.show_Help("","index.html") 

669# 

670# show release information window 

671# 

672 def show_ReleaseInfo(self, version): 

673 self.show_Help("","releasenotes.html") 

674# 

675# show startup info 

676# 

677 def show_StartupInfo(self): 

678 self.show_Help("","startup.html") 

679# 

680# show help windows for a certain document 

681# 

682 def show_Help(self,path,document): 

683 if self.helpwin is None: 

684 try: 

685 self.helpwin= cls_HelpWindow() 

686 self.helpwin.loadDocument(path,document) 

687 except HelpError as e: 

688 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.value,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

689 return 

690 helpposition=PILCONFIG.get(self.name,"helpposition") 

691 if helpposition!= "": 

692 self.helpwin.move(QtCore.QPoint(helpposition[0],helpposition[1])) 

693 self.helpwin.resize(helpposition[2],helpposition[3]) 

694 else: 

695 self.helpwin.resize(720,700) 

696 self.helpwin.show() 

697 self.helpwin.raise_() 

698# 

699# copy configuration data from devel to production and vice versa 

700# - a development/beta version of pyILPER copies the files of the 

701# production version 

702# - a production version of pyILPER copies the files of the development/beta 

703# version 

704# 

705def copy_config(args): 

706 count=0 

707# 

708# get version numbers 

709# 

710 from_config=cls_pilconfig() 

711 to_config=cls_pilconfig() 

712 try: 

713 from_config.open("pyilper",CONFIG_VERSION,args.instance, not PRODUCTION,False) 

714 from_version=from_config.get("pyilper","version","0.0.0") 

715 to_config.open("pyilper",CONFIG_VERSION,args.instance, PRODUCTION,False) 

716 to_version=to_config.get("pyilper","version","0.0.0") 

717 except PilConfigError as e: 

718 print(e.msg+': '+e.add_msg) 

719 return 

720 if from_version == "0.0.0": 

721 print("There are no configuration files to copy") 

722 return 

723# 

724# ask for confirmation 

725# 

726 print("\nW A R N I N G!") 

727 print("This overwrites the configuration files") 

728 if PRODUCTION: 

729 print("of the production version: ", to_version) 

730 else: 

731 print("of the development/beta version: ", to_version) 

732 print("with the configuration files") 

733 if PRODUCTION: 

734 print("of the development/beta version: ",from_version) 

735 else: 

736 print("of the production version: ",from_version) 

737 inp= input("Continue? (enter 'YES' uppercase): ") 

738 if inp !="YES": 

739 print("cancelled") 

740 return 

741# 

742# now copy configuration files 

743# 

744 for name in ['pyilper','penconfig','shortcutconfig']: 

745 from_filename=buildconfigfilename("pyilper",name,CONFIG_VERSION,args.instance,not PRODUCTION)[0] 

746 if not os.path.isfile(from_filename): 

747 continue 

748 to_filename=buildconfigfilename("pyilper",name,CONFIG_VERSION,args.instance, PRODUCTION)[0] 

749 try: 

750 shutil.copy(from_filename,to_filename) 

751 except OSError as e: 

752 print("Error copying file "+from_filename+": "+e.strerror) 

753 return 

754 except SameFileError as e: 

755 print("Error copying file "+from_filename+" "+"source and destination file are identical") 

756 return 

757 print(from_filename) 

758 print("copied to:") 

759 print(to_filename) 

760 count+=1 

761 print(count,"files copied. Restart pyILPER without the 'cc' option now") 

762 

763# 

764# dump stack if signalled externally (for debugging) 

765# 

766def dumpstacks(signal, frame): 

767 for threadId, stack in sys._current_frames().items(): 

768 print("Thread ID %x" % threadId) 

769 traceback.print_stack(f=stack) 

770 

771def main(): 

772 parser=argparse.ArgumentParser(description='pyILPER command line invocation') 

773 parser.add_argument('--instance', '-instance', default="", help="Start a pyILPER instance INSTANCE. This instance has an own configuration file.") 

774 parser.add_argument('--clean','-clean',action='store_true',help="Start pyILPER with a config file which is reset to defaults") 

775 parser.add_argument('--cc','-cc',action='store_true',help="Copy configuration from development to production version and vice versa") 

776 args=parser.parse_args() 

777 if args.cc: 

778 copy_config(args) 

779 sys.exit(1) 

780 

781 if not isWINDOWS(): 

782 signal.signal(signal.SIGQUIT, dumpstacks) 

783 app = QtWidgets.QApplication(sys.argv) 

784 pyilper= cls_pyilper(args) 

785 sys.exit(app.exec()) 

786 

787 

788if __name__ == '__main__': 

789 

790 main()