Coverage for pyilper/lifexec.py: 80%

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

1121 statements  

1#!/usr/bin/python3 

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

3# 

4# Dialogs for lif utilities operations 

5# (c) 2015 Joachim Siebold 

6# 

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

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

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

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

11# 

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

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

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

15# GNU General Public License for more details. 

16# 

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

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

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

20# 

21# lif utilities dialog classes ------------------------------------------------- 

22# 

23# Changelog 

24# 02.01.2016 - jsi 

25# - added cls_lifview, cls_chkxrom 

26# - improved error checking of piped commands 

27# - added descramble HP-41 rom postprocessing option 

28# - added scramble HP-41 rom to HEPAX sdata file preprocessing option 

29# - refactoring 

30# 05.01.2016 - jsi 

31# - replaced process pipelines with temporary files to catch error conditions  

32# more reliable 

33# - added viewing LEX file contents 

34# - decode output of textfiles in HP-ROMAN8 character set 

35# 08.01.2016 - jsi 

36# - introduced lifglobal.py, refactoring 

37# 16.01.2016 - jsi 

38# - introduced cls_chk_import 

39# 24.01.2016 - jsi 

40# - corrected regexp syntax of validators, hint by cg 

41# - new validator class automatically converts to capital letters 

42# - fixed "cancel" behaviour of cls_liflabel, refactores cls_lifrename 

43# - disable ok button if filename is empty in cls_lifrename 

44# - fixed "OK" not enabled in cls_lifimport for FOCAL raw files 

45# - check if outputfile overwrites existing file in cls_lifexport 

46# 30.01.2016 - jsi: 

47# - added unpack HEPAX ROM file postprocessiong option to cls_lifexport 

48# - added check if LIFUTILS are installed 

49# 01.02.2016 - jsi 

50# - added save button to save the contents of the viewer to a file 

51# - added LIFUTILS installation check dialog 

52# 08.02.2016 - jsi 

53# - changed os detection to platform.system() 

54# 19.02.2016 jsi 

55# - added configurable character set encoding to cls_lifview 

56# 05.03.2016 jsi 

57# - removed unneeded variables 

58# 12.03.2016 jsi 

59# - open view outputfile as unicode 

60# 13.03.2016 jsi 

61# - modified exit of modal dialogs 

62# 14.04.2016 jsi 

63# - modified filter for all files to * in file dialog 

64# 27.04.2016 jsi 

65# - do not set path for QFileDialog, it remembers the last dir automatically 

66# 11.07.2016 jsi 

67# - use functions from pilcore.py for platform detection 

68# 27.08.2016 jsi 

69# - removed duplicate dialog warning for overwriting existing file in  

70# cls_lifexport 

71# 01.10.2016 jsi 

72# - plotter rom added to xrom dialog 

73# 22.08.2017 jsi 

74# - cls_lifbarcode added 

75# - truncate error messages from external programs to 150 chars 

76# 01.09.2017 jsi 

77# - moved get_pdfFilename to cls_pdfprinter 

78# 16.08.2017 jsi 

79# - used barrconv instead of stringconv. There is no unicode exception any more. 

80# 28.10.2017 jsi 

81# - detection of lifutils extended and improved 

82# 11.11.2017 jsi: 

83# - Eramco MLDL-OS packed rom file pre- and postprocessing implemented 

84# 21.11.2017 jsi: 

85# - added -r 0 option to textlif to add HP-41 implementation specific bytes 

86# 05.02.2018 jsi: 

87# - apply BOM to saved "view" file only on Windows at the beginning of the file 

88# 10.02.2018 jsi: 

89# - fixed BOM handling 

90# 18.03.2018 jsi: 

91# - added options to import an ASCII file either for the HP-41 or the HP-71 

92# 20.03.2018 jsi: 

93# - code cleanup 

94# 12.12.2018 jsi: 

95# - changed all subprocess calls to the subprocess.run interface 

96# 31.5.2019 jsi: 

97# - added HP-75 text file import/export 

98# - do not run lifput in exec_double_import if first command failed 

99# 03.06.2019 jsi: 

100# - show HP-75 text files optional with or without line numbers 

101# 04.01.2021 jsi 

102# - exec_single did not get stderr output of executed program 

103# 

104import subprocess 

105import tempfile 

106import os 

107import pathlib 

108from PySide6 import QtCore, QtGui, QtWidgets 

109from .lifcore import * 

110from .pilcharconv import barrconv 

111from .pilcore import isWINDOWS, FONT, decode_version, PDF_ORIENTATION_PORTRAIT 

112from .pilpdf import cls_pdfprinter 

113from .pilconfig import PILCONFIG 

114if isWINDOWS(): 

115 import winreg 

116 

117PDF_MARGINS=100 

118BARCODE_HEIGHT=100 

119BARCODE_NARROW_W= 5 

120BARCODE_WIDE_W= 10 

121BARCODE_SPACING= 5 

122# 

123# get lif version if 0 is returned then lifversion was not found 

124# 

125def get_lifversion(cmd): 

126 retval=0 

127 try: 

128 ret=subprocess.run(cmd,stdout=subprocess.PIPE) 

129 retval=int(ret.stdout.decode()) 

130 finally: 

131 return retval 

132# 

133# check if lifutils are installed, return if required version found 

134# 

135def check_lifutils(): 

136 set_lifutils_path("") 

137 required_version_installed=False 

138 installed_version= 0 

139# 

140# check if we have a configured path to lifversion 

141# 

142 lifversionpath=PILCONFIG.get("pyilper","lifutilspath") 

143 if lifversionpath != "": 

144 installed_version=get_lifversion(lifversionpath) 

145# 

146# not found, check if we have lifversion in the path 

147# 

148 if installed_version == 0: 

149 installed_version=get_lifversion("lifversion") 

150# 

151# not found, use well known default locations 

152# 

153 if installed_version == 0: 

154# 

155# Windows: query Registry to get install location from uninstaller info 

156# local user installation preceeds system wide installation 

157# Note: this requires at least lifutils version 1.7.7 (nsis package build) 

158# 

159 if isWINDOWS(): 

160 path="" 

161 try: 

162 hkey=winreg.OpenKey(winreg.HKEY_CURRENT_USER,r"Software\\Microsoft\\Windows\\CurrentVersion\\uninstall\\"+LIFUTILS_UUID) 

163 path=winreg.QueryValueEx(hkey,"InstallLocation")[0] 

164 hkey.Close() 

165 except OSError as e: 

166 pass 

167 if path=="": 

168 try: 

169 hkey=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Software\\Microsoft\\Windows\\CurrentVersion\\uninstall\\"+LIFUTILS_UUID) 

170 path=winreg.QueryValueEx(hkey,"InstallLocation")[0] 

171 hkey.Close() 

172 except OSError as e: 

173 pass 

174 if path!="": 

175 p=pathlib.Path(path) 

176 p=p / "lifversion" 

177 lifversionpath=str(p) 

178 installed_version=get_lifversion(lifversionpath) 

179 else: 

180# 

181# Linux / mac OS: try /usr/ or /usr/local 

182# 

183 lifversionpath="/usr/bin/lifversion" 

184 installed_version=get_lifversion(lifversionpath) 

185 if installed_version == 0: 

186 lifversionpath="/usr/local/bin/lifversion" 

187 installed_version=get_lifversion(lifversionpath) 

188# 

189# lifutils path found, set lifutils_path as prefix for calling the  

190# executables and set LIFUTILSXROMDIR as environment variable 

191# 

192 if installed_version != 0 and lifversionpath !="" : 

193 p= pathlib.Path(lifversionpath) 

194 set_lifutils_path(str(p.parent)) 

195 if isWINDOWS(): 

196 xromdir=p.parent / "xroms" 

197 else: 

198 xromdir=p.parents[1] / "share" / "lifutils" / "xroms" 

199 if xromdir.is_dir(): 

200 os.environ["LIFUTILSXROMDIR"]=str(xromdir) 

201# 

202# check if we have the required version 

203# 

204 if installed_version >= LIFUTILS_REQUIRED_VERSION: 

205 required_version_installed=True 

206 return required_version_installed, installed_version 

207# 

208# check and display messages of lifutils 

209# 

210def check_errormessages(parent,ret): 

211 msg= ret.stderr.decode() 

212 if msg == "": 

213 return 

214# 

215# truncate message if length > 150  

216# 

217 if len(msg) >150: 

218 msg=msg[:75] + '.. (truncated)' 

219# 

220# display an error if returncode !=0 otherwise a warning 

221# 

222 if ret.returncode == 0: 

223 reply=QtWidgets.QMessageBox.warning(parent,'Warning',msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

224 else: 

225 reply=QtWidgets.QMessageBox.critical(parent,'Error',msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

226 return 

227# 

228# exec single command 

229# 

230def exec_single(parent,cmd): 

231 try: 

232 ret=subprocess.run(cmd,stderr=subprocess.PIPE) 

233 check_errormessages(parent,ret) 

234 except OSError as e: 

235 reply=QtWidgets.QMessageBox.critical(parent,'Error',e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

236 finally: 

237 return 

238# 

239# exec single command, return output 

240# 

241def exec_single_export(parent,cmd): 

242 returnvalue=None 

243 try: 

244 ret= subprocess.run(cmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

245 check_errormessages(parent,ret) 

246 if ret.returncode==0: 

247 returnvalue= ret.stdout 

248# 

249# catch errors 

250# 

251 except OSError as e: 

252 reply=QtWidgets.QMessageBox.critical(parent,'Error',e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

253 finally: 

254 return returnvalue 

255 

256# 

257# exec piped command, read input from file, return True if success, False otherwise 

258# 

259def exec_double_import(parent,cmd1,cmd2,inputfile): 

260 

261 tmpfile=None 

262 try: 

263 if isWINDOWS(): 

264 fd= os.open(inputfile, os.O_RDONLY | os.O_BINARY ) 

265 else: 

266 fd= os.open(inputfile, os.O_RDONLY) 

267# 

268# create temporary file 

269# 

270 tmpfile=tempfile.TemporaryFile() 

271# 

272# execute first command 

273# 

274 ret= subprocess.run(cmd1,stdin=fd,stdout=tmpfile,stderr=subprocess.PIPE) 

275 check_errormessages(parent,ret) 

276 os.close(fd) 

277 if ret.returncode!=0: 

278 tempfile.close() 

279 return 

280# 

281# execute second command 

282# 

283 tmpfile.seek(0) 

284 if not cls_chk_import.execute(tmpfile.fileno(), None): 

285 tmpfile.close() 

286 return 

287 tmpfile.seek(0) 

288 ret= subprocess.run(cmd2,stdin=tmpfile,stderr=subprocess.PIPE) 

289 check_errormessages(parent,ret) 

290# 

291# catch errors 

292# 

293 except OSError as e: 

294 reply=QtWidgets.QMessageBox.critical(parent,'Error',e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

295 finally: 

296 if tmpfile is not None: 

297 tmpfile.close() 

298 return 

299# 

300# exec piped command, write output to file or stdout 

301# 

302def exec_double_export(parent,cmd1,cmd2,outputfile): 

303 tmpfile=None 

304 returnvalue= None 

305 try: 

306 fd=None 

307 if outputfile != "": 

308# 

309# open output file if specified 

310# 

311 if os.access(outputfile,os.W_OK): 

312 reply=QtWidgets.QMessageBox.warning(parent,'Warning',"Do you really want to overwrite file "+outputfile,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel) 

313 if reply== QtWidgets.QMessageBox.Cancel: 

314 return 

315 if isWINDOWS(): 

316 fd= os.open(outputfile, os.O_WRONLY | os.O_BINARY | os.O_TRUNC | os.O_CREAT, 0o644) 

317 else: 

318 fd= os.open(outputfile, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0o644) 

319# 

320# create temporary file 

321# 

322 tmpfile=tempfile.TemporaryFile() 

323# 

324# execute first command 

325# 

326 ret= subprocess.run(cmd1,stdout=tmpfile,stderr=subprocess.PIPE) 

327 check_errormessages(parent,ret) 

328 if ret.returncode!=0: 

329 tmpfile.close() 

330 if fd is not None: 

331 os.close(fd) 

332 return None 

333# 

334# execute second command 

335# 

336 tmpfile.seek(0) 

337 if outputfile != "": 

338 ret= subprocess.run(cmd2,stdin=tmpfile,stdout=fd,stderr=subprocess.PIPE) 

339 else: 

340 ret= subprocess.run(cmd2,stdin=tmpfile,stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

341 if ret.returncode==0: 

342 returnvalue= ret.stdout 

343 check_errormessages(parent,ret) 

344# 

345# catch errors 

346# 

347 except OSError as e: 

348 reply=QtWidgets.QMessageBox.critical(parent,'Error',e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok) 

349 finally: 

350 if tmpfile is not None: 

351 tmpfile.close() 

352 if fd is not None: 

353 os.close(fd) 

354 return returnvalue 

355 

356# 

357# validator checks for valid lif label or file names, converts to capital lettes 

358# 

359class cls_LIF_validator(QtGui.QValidator): 

360 

361 def validate(self,string,pos): 

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

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

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

365 return result[0], result[1].upper(), result[2] 

366# 

367# pack lif image file dialog 

368# 

369class cls_lifpack(QtWidgets.QDialog): 

370 

371 def __init__(self,parent= None): 

372 super().__init__() 

373 

374 @staticmethod 

375 def execute(lifimagefile): 

376 d=cls_lifpack() 

377 reply = QtWidgets.QMessageBox.question(d, 'Message', 'Do you really want to pack the LIF image file', QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) 

378 if reply == QtWidgets.QMessageBox.Yes: 

379 exec_single(d,[add_path("lifpack"),lifimagefile]) 

380# 

381# custom class for text item 

382# 

383class cls_textItem(QtWidgets.QGraphicsItem): 

384 

385 def __init__(self,text): 

386 super().__init__() 

387 self.text=text 

388 self.font= QtGui.QFontDatabase.systemFont(QtGui.QFontDatabase.FixedFont) 

389 self.font.setStyleHint(QtGui.QFont.TypeWriter) 

390 self.font.setPointSize(2) 

391 metrics= QtGui.QFontMetrics(self.font) 

392 self.font_h= metrics.height() 

393 self.font_w= metrics.width("A") 

394 self.spacing=20 

395 self.h= self.font_h+self.spacing*2 

396 self.w= len(text)* self.font_w 

397 self.rect= QtCore.QRectF(0,0,self.w,self.h) 

398 

399 def setPos(self,x,y): 

400 super().setPos(x,y-self.h) 

401 

402 def boundingRect(self): 

403 return self.rect 

404 

405 def paint(self,painter,option,widget): 

406 posx=0 

407 posy=self.font_h 

408 painter.setFont(self.font) 

409 painter.drawText(posx,posy,self.text) 

410# 

411# custom class for barcode 

412# 

413class cls_barcodeItem(QtWidgets.QGraphicsItem): 

414 

415 def __init__(self,barcode_title,barcode_row, barcode_height, barcode_narrow_w, barcode_wide_w, barcode_spacing): 

416 super().__init__() 

417 self.font=QtGui.QFont() 

418 self.font.setPointSize(2) 

419 metrics= QtGui.QFontMetrics(self.font) 

420 self.font_h= metrics.height() 

421 self.spacing=20 

422 self.h= self.font_h+barcode_height+self.spacing*3 

423 self.w= len(barcode_row)*(barcode_wide_w+barcode_spacing)*8+(barcode_wide_w+barcode_spacing)*4 

424 self.barcode_title= barcode_title 

425 self.barcode_row= barcode_row 

426 self.barcode_height= barcode_height 

427 self.barcode_narrow_w= barcode_narrow_w 

428 self.barcode_wide_w= barcode_wide_w 

429 self.barcode_spacing= barcode_spacing 

430 self.rect= QtCore.QRectF(0,0,self.w,self.h) 

431 

432 def setPos(self,x,y): 

433 super().setPos(x,y-self.h) 

434 

435 def boundingRect(self): 

436 return self.rect 

437 

438 def paint(self,painter,option,widget): 

439 posx=0 

440 posy=self.font_h 

441# 

442# header text 

443# 

444 painter.setFont(self.font) 

445 painter.drawText(posx,posy,self.barcode_title) 

446 posy+=self.spacing 

447# 

448# barcodes 

449# 

450 painter.fillRect(posx,posy,self.barcode_narrow_w,self.barcode_height,QtCore.Qt.black) 

451 posx+= self.barcode_narrow_w+self.barcode_spacing 

452 painter.fillRect(posx,posy,self.barcode_narrow_w,self.barcode_height,QtCore.Qt.black) 

453 posx+= self.barcode_narrow_w+self.barcode_spacing 

454 

455 for i in range(len(self.barcode_row)): 

456 for k in reversed(range(8)): 

457 if self.barcode_row[i] & (1 << k): 

458 painter.fillRect(posx,posy,self.barcode_wide_w,self.barcode_height,QtCore.Qt.black) 

459 posx+= self.barcode_wide_w+self.barcode_spacing 

460 else: 

461 painter.fillRect(posx,posy,self.barcode_narrow_w,self.barcode_height,QtCore.Qt.black) 

462 posx+= self.barcode_narrow_w+self.barcode_spacing 

463 painter.fillRect(posx,posy,self.barcode_wide_w,self.barcode_height,QtCore.Qt.black) 

464 posx+= self.barcode_wide_w+self.barcode_spacing 

465 painter.fillRect(posx,posy,self.barcode_narrow_w,self.barcode_height,QtCore.Qt.black) 

466 posx+= self.barcode_narrow_w+self.barcode_spacing 

467 return 

468# 

469# output barcode dialog 

470# 

471class cls_lifbarcode(QtWidgets.QDialog): 

472 

473 

474 def __init__(self): 

475 super().__init__() 

476 

477 @staticmethod 

478 def execute (lifimagefile, liffilename, ft,papersize): 

479 d= cls_lifbarcode() 

480# 

481# get output file name 

482# 

483 flist= cls_pdfprinter.get_pdfFilename() 

484 if flist is None: 

485 return 

486 output_filename= flist[0] 

487# 

488# generate binary barcode data from lifutils prog41bar or sdatabar 

489# 

490 if ft== 0xE080: 

491 output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],[add_path("prog41bar")],"") 

492 title="Barcodes for HP-41 program file: "+liffilename 

493 else: 

494 output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],[add_path("sdatabar")],"") 

495 title="Barcodes for HP-41 data file: "+liffilename 

496 if output is None: 

497 return 

498 

499# 

500# initialize pdf printer 

501# 

502 pdfprinter=cls_pdfprinter(papersize,PDF_ORIENTATION_PORTRAIT, output_filename,title,True,1) 

503 pdfprinter.begin() 

504# 

505# process binary barcode data and generate PDF file 

506# 

507 i=0 

508 row=0 

509 while i < len(output): 

510# Process barcode, print title 

511# 

512 row+=1 

513 length=(output[i] &0xF)+1 

514 barcode_row= [] 

515 i+=1 

516 for k in range(length): 

517 if i== len(output): 

518 return 

519 barcode_row.append(output[i]) 

520 i+=1 

521 barcode_header="Row: "+str(row) 

522 barcode_item= cls_barcodeItem(barcode_header,barcode_row,BARCODE_HEIGHT, BARCODE_NARROW_W, BARCODE_WIDE_W, BARCODE_SPACING) 

523 pdfprinter.print_item(barcode_item) 

524# 

525 pdfprinter.end() 

526 

527# 

528# purge file dialog 

529# 

530class cls_lifpurge(QtWidgets.QDialog): 

531 

532 def __init__(self,parent= None): 

533 super().__init__() 

534 

535 @staticmethod 

536 def execute(lifimagefile,liffile): 

537 d=cls_lifpurge() 

538 reply = QtWidgets.QMessageBox.question(d, 'Message', 'Do you really want to purge '+liffile, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) 

539 if reply == QtWidgets.QMessageBox.Yes: 

540 exec_single(d,[add_path("lifpurge"),lifimagefile, liffile]) 

541 

542# 

543# rename file dialog 

544# 

545class cls_lifrename (QtWidgets.QDialog): 

546 

547 def __init__(self,lifimagefile,filename,parent= None): 

548 super().__init__() 

549 self.lifimagefile=lifimagefile 

550 self.oldfilename=filename 

551 self.setWindowTitle("Rename File") 

552 self.vlayout= QtWidgets.QVBoxLayout() 

553 self.setLayout(self.vlayout) 

554 

555 self.lbl=QtWidgets.QLabel("Rename file:") 

556 self.vlayout.addWidget(self.lbl) 

557 self.leditFileName=QtWidgets.QLineEdit(self) 

558 self.leditFileName.setText(filename) 

559 self.leditFileName.setMaxLength(10) 

560 self.leditFileName.textChanged.connect(self.do_checkenable) 

561 self.validator = cls_LIF_validator() 

562 self.leditFileName.setValidator(self.validator) 

563 self.vlayout.addWidget(self.leditFileName) 

564 

565 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

567 self.buttonBox.setCenterButtons(True) 

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

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

570 self.vlayout.addWidget(self.buttonBox) 

571 

572# 

573# check, if the OK button can be enabled 

574# 

575 def do_checkenable(self): 

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

577 if (self.leditFileName.text() != ""): 

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

579 return 

580 

581#  

582# ok rename file 

583# 

584 def do_ok(self): 

585 newfilename=self.leditFileName.text() 

586 if newfilename != "": 

587 exec_single(self,[add_path("lifrename"),self.lifimagefile,self.oldfilename,newfilename]) 

588 super().accept() 

589 

590# 

591# cancel do nothing 

592# 

593 def do_cancel(self): 

594 super().reject() 

595 

596 @staticmethod 

597 def execute(lifimagefile,liffile): 

598 d=cls_lifrename(lifimagefile,liffile) 

599 result= d.exec() 

600# 

601# export file dialog 

602# 

603class cls_lifexport (QtWidgets.QDialog): 

604 

605 def __init__(self,lifimagefile,liffilename,liffiletype,workdir,parent= None): 

606 super().__init__() 

607 self.lifimagefile= lifimagefile 

608 self.liffiletype= liffiletype 

609 self.liffilename= liffilename 

610 self.workdir=workdir 

611 self.setWindowTitle("Export File") 

612 self.vlayout= QtWidgets.QVBoxLayout() 

613 self.setLayout(self.vlayout) 

614 

615 self.gBox0=QtWidgets.QGroupBox("File to be exported to file system") 

616 self.lblLifFilename=QtWidgets.QLabel(self.liffilename+" (type: "+self.liffiletype+")") 

617 self.vbox0=QtWidgets.QVBoxLayout() 

618 self.vbox0.addWidget(self.lblLifFilename) 

619 self.vbox0.addStretch(1) 

620 self.gBox0.setLayout(self.vbox0) 

621 self.vlayout.addWidget(self.gBox0) 

622 

623 self.gBox1=QtWidgets.QGroupBox("Postprocessing options") 

624 self.radioLifAscii= QtWidgets.QRadioButton("convert LIF-Text to ASCII") 

625 self.radioLifAscii.clicked.connect(self.do_radio) 

626 self.radioLifAscii.setEnabled(False) 

627 self.radioTxt75Ascii= QtWidgets.QRadioButton("convert HP-75 text file to ASCII, omit line numbers") 

628 self.radioTxt75Ascii.clicked.connect(self.do_radio) 

629 self.radioTxt75Ascii.setEnabled(False) 

630 self.radioTxt75AsciiNumbers= QtWidgets.QRadioButton("convert HP-75 text file to ASCII, retain line numbers") 

631 self.radioTxt75AsciiNumbers.clicked.connect(self.do_radio) 

632 self.radioTxt75AsciiNumbers.setEnabled(False) 

633 self.radioEramco= QtWidgets.QRadioButton("unpack Eramco MLDL-OS ROM file") 

634 self.radioEramco.clicked.connect(self.do_radio) 

635 self.radioEramco.setEnabled(False) 

636 self.radioHepax= QtWidgets.QRadioButton("unpack HEPAX HP41 SDATA ROM file") 

637 self.radioHepax.setEnabled(False) 

638 self.radioHepax.clicked.connect(self.do_radio) 

639 self.radioRaw= QtWidgets.QRadioButton("remove LIF header, create RAW file") 

640 self.radioRaw.clicked.connect(self.do_radio) 

641 self.radioNone= QtWidgets.QRadioButton("None") 

642 self.radioNone.clicked.connect(self.do_radio) 

643 

644 if self.liffiletype== "TEXT": 

645 self.radioLifAscii.setEnabled(True) 

646 self.radioLifAscii.setChecked(True) 

647 self.outputextension=".txt" 

648 elif self.liffiletype== "TXT75": 

649 self.radioTxt75Ascii.setEnabled(True) 

650 self.radioTxt75Ascii.setChecked(True) 

651 self.radioTxt75AsciiNumbers.setEnabled(True) 

652 self.outputextension=".txt" 

653 elif self.liffiletype== "X-M41": 

654 self.radioEramco.setEnabled(True) 

655 self.radioEramco.setChecked(True) 

656 self.outputextension=".rom" 

657 elif self.liffiletype== "SDATA": 

658 self.radioNone.setChecked(True) 

659 self.radioHepax.setEnabled(True) 

660 self.outputextension=".lif" 

661 else: 

662 self.radioNone.setChecked(True) 

663 self.outputextension=".lif" 

664 

665 self.vbox=QtWidgets.QVBoxLayout() 

666 self.vbox.addWidget(self.radioLifAscii) 

667 self.vbox.addWidget(self.radioTxt75Ascii) 

668 self.vbox.addWidget(self.radioTxt75AsciiNumbers) 

669 self.vbox.addWidget(self.radioEramco) 

670 self.vbox.addWidget(self.radioHepax) 

671 self.vbox.addWidget(self.radioRaw) 

672 self.vbox.addWidget(self.radioNone) 

673 self.vbox.addStretch(1) 

674 self.gBox1.setLayout(self.vbox) 

675 

676 self.vlayout.addWidget(self.gBox1) 

677 

678 self.gBox2=QtWidgets.QGroupBox("Output file") 

679 self.hbox=QtWidgets.QHBoxLayout() 

680 self.outputfile=os.path.join(self.workdir, self.liffilename.lower()+self.outputextension) 

681 self.lblFilename=QtWidgets.QLabel(self.outputfile) 

682 self.hbox.addWidget(self.lblFilename) 

683 self.hbox.addStretch(1) 

684 self.butChange= QtWidgets.QPushButton("Change") 

685 self.butChange.clicked.connect(self.do_filenameChanged) 

686 self.hbox.addWidget(self.butChange) 

687 self.gBox2.setLayout(self.hbox) 

688 

689 self.vlayout.addWidget(self.gBox2) 

690 

691 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

693 self.buttonBox.setCenterButtons(True) 

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

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

696 self.vlayout.addWidget(self.buttonBox) 

697# 

698# Radio button clicked, adjust file type 

699# 

700 def do_radio(self): 

701 if self.radioLifAscii.isChecked(): 

702 self.outputextension=".txt" 

703 elif self.radioTxt75Ascii.isChecked(): 

704 self.outputextension=".txt" 

705 elif self.radioTxt75AsciiNumbers.isChecked(): 

706 self.outputextension=".txt" 

707 elif self.radioHepax.isChecked(): 

708 self.outputextension=".rom" 

709 elif self.radioEramco.isChecked(): 

710 self.outputextension=".rom" 

711 elif self.radioRaw.isChecked(): 

712 self.outputextension=".raw" 

713 else: 

714 self.outputextension=".lif" 

715 self.outputfile=os.path.join(self.workdir, self.liffilename.lower()+self.outputextension) 

716 self.lblFilename.setText(self.outputfile) 

717# 

718# enter output file name dialog 

719# 

720 def get_outputFilename(self): 

721 dialog=QtWidgets.QFileDialog() 

722 dialog.setWindowTitle("Select Output File") 

723 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

724 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 

725 dialog.setNameFilters( ["All Files (*)"] ) 

726 dialog.selectFile(self.liffilename.lower()+self.outputextension) 

727 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

728 if dialog.exec(): 

729 return dialog.selectedFiles() 

730# 

731# callback 

732# 

733# 

734# change output file name 

735# 

736 

737 def do_filenameChanged(self): 

738 flist= self.get_outputFilename() 

739 if flist is None: 

740 return 

741 self.outputfile=flist[0] 

742 self.lblFilename.setText(self.outputfile) 

743# 

744# export 

745# 

746 def do_ok(self): 

747 if self.outputfile != "": 

748 

749 if self.radioLifAscii.isChecked(): 

750 exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("liftext"),self.outputfile) 

751 elif self.radioTxt75Ascii.isChecked(): 

752 exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("liftext75"),self.outputfile) 

753 elif self.radioTxt75AsciiNumbers.isChecked(): 

754 exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename], [add_path("liftext75"),"-n"],self.outputfile) 

755 elif self.radioEramco.isChecked(): 

756 exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("er41rom"),self.outputfile) 

757 elif self.radioHepax.isChecked(): 

758 exec_double_export(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename],add_path("hx41rom"),self.outputfile) 

759 elif self.radioRaw.isChecked(): 

760 exec_single(self,[add_path("lifget"),"-r",self.lifimagefile,self.liffilename,self.outputfile]) 

761 elif self.radioNone.isChecked(): 

762 exec_single(self,[add_path("lifget"),self.lifimagefile,self.liffilename,self.outputfile]) 

763 super().accept() 

764# 

765# cancel: do nothing 

766# 

767 def do_cancel(self): 

768 super().reject() 

769 

770 @staticmethod 

771 def execute(lifimagefile,liffilename,liffiletype,workdir): 

772 d=cls_lifexport(lifimagefile,liffilename,liffiletype,workdir) 

773 result= d.exec() 

774 

775# 

776# label lif image file dialog 

777# 

778class cls_liflabel (QtWidgets.QDialog): 

779 

780 def __init__(self,lifimagefile,oldlabel,parent= None): 

781 super().__init__() 

782 self.lifimagefile=lifimagefile 

783 self.setWindowTitle("Label LIF image file") 

784 self.vlayout= QtWidgets.QVBoxLayout() 

785 self.setLayout(self.vlayout) 

786 

787 self.lbl=QtWidgets.QLabel("Label:") 

788 self.vlayout.addWidget(self.lbl) 

789 self.leditLabel=QtWidgets.QLineEdit(self) 

790 self.leditLabel.setText(oldlabel) 

791 self.leditLabel.setMaxLength(6) 

792 self.validator = cls_LIF_validator() 

793 self.leditLabel.setValidator(self.validator) 

794 self.vlayout.addWidget(self.leditLabel) 

795 

796 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

798 self.buttonBox.setCenterButtons(True) 

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

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

801 self.vlayout.addWidget(self.buttonBox) 

802 

803 def do_ok(self): 

804 newlabel=self.leditLabel.text() 

805 if newlabel != "": 

806 exec_single(self,[add_path("liflabel"),self.lifimagefile, newlabel]) 

807 else: 

808 exec_single(self,[add_path("liflabel"),"-c",self.lifimagefile]) 

809 super().accept() 

810 

811 def do_cancel(self): 

812 super().reject() 

813 

814 @staticmethod 

815 def execute(lifimagefile,oldlabel): 

816 d=cls_liflabel(lifimagefile,oldlabel) 

817 result= d.exec() 

818# 

819# import file dialog 

820# 

821class cls_lifimport (QtWidgets.QDialog): 

822 

823 def __init__(self,lifimagefile,workdir,parent= None): 

824 super().__init__() 

825 self.inputfile="" 

826 self.lifimagefile=lifimagefile 

827 self.workdir= workdir 

828 self.liffilename="" 

829 

830 self.setWindowTitle("Import File") 

831 self.vlayout= QtWidgets.QVBoxLayout() 

832 self.setLayout(self.vlayout) 

833 

834 self.gBox0=QtWidgets.QGroupBox("Input file") 

835 self.hbox=QtWidgets.QHBoxLayout() 

836 self.lblFilename=QtWidgets.QLabel(self.inputfile) 

837 self.hbox.addWidget(self.lblFilename) 

838 self.hbox.addStretch(1) 

839 self.butChange= QtWidgets.QPushButton("Change") 

840 self.butChange.clicked.connect(self.do_filenameChanged) 

841 self.hbox.addWidget(self.butChange) 

842 self.gBox0.setLayout(self.hbox) 

843 self.vlayout.addWidget(self.gBox0) 

844 

845 self.gBox1=QtWidgets.QGroupBox("Preprocessing options") 

846 self.bGroup=QtWidgets.QButtonGroup() 

847 self.radioLif41= QtWidgets.QRadioButton("convert from ASCII to LIF-Text (HP-41)") 

848 self.radioLif71= QtWidgets.QRadioButton("convert from ASCII to LIF-Text (HP-71)") 

849 self.radioTxt75= QtWidgets.QRadioButton("convert from ASCII to HP-75 text, create new line numbers") 

850 self.radioTxt75Numbers= QtWidgets.QRadioButton("convert from ASCII to HP-75 text, take existing line numbers") 

851 self.radioHepax= QtWidgets.QRadioButton("convert HP-41 rom file to SDATA file (HEPAX)") 

852 self.radioEramco= QtWidgets.QRadioButton("convert HP-41 rom file to XM-41 file (Eramco MLDL-OS)") 

853 self.radioFocal= QtWidgets.QRadioButton("add LIF header to HP41 FOCAL raw file") 

854 self.radioNone= QtWidgets.QRadioButton("None") 

855 self.radioNone.setChecked(True) 

856 self.bGroup.addButton(self.radioLif41) 

857 self.bGroup.addButton(self.radioLif71) 

858 self.bGroup.addButton(self.radioTxt75) 

859 self.bGroup.addButton(self.radioTxt75Numbers) 

860 self.bGroup.addButton(self.radioHepax) 

861 self.bGroup.addButton(self.radioEramco) 

862 self.bGroup.addButton(self.radioFocal) 

863 self.bGroup.addButton(self.radioNone) 

864 self.bGroup.buttonClicked.connect(self.do_butclicked) 

865 

866 self.vbox=QtWidgets.QVBoxLayout() 

867 self.vbox.addWidget(self.radioLif41) 

868 self.vbox.addWidget(self.radioLif71) 

869 self.vbox.addWidget(self.radioTxt75) 

870 self.vbox.addWidget(self.radioTxt75Numbers) 

871 self.vbox.addWidget(self.radioHepax) 

872 self.vbox.addWidget(self.radioEramco) 

873 self.vbox.addWidget(self.radioFocal) 

874 

875 self.hbox2=QtWidgets.QHBoxLayout() 

876 self.lbl=QtWidgets.QLabel("LIF Filename:") 

877 self.hbox2.addWidget(self.lbl) 

878 self.leditFileName=QtWidgets.QLineEdit(self) 

879 self.leditFileName.setText(self.liffilename) 

880 self.leditFileName.setMaxLength(10) 

881 self.validator = cls_LIF_validator() 

882 self.leditFileName.setValidator(self.validator) 

883 self.leditFileName.setEnabled(False) 

884 self.leditFileName.textChanged.connect(self.do_checkenable) 

885 self.hbox2.addWidget(self.leditFileName) 

886 self.vbox.addLayout(self.hbox2) 

887 self.gBox1.setLayout(self.vbox) 

888 self.gBox1.setEnabled(False) 

889 self.vlayout.addWidget(self.gBox1) 

890 self.vbox.addWidget(self.radioNone) 

891 self.vbox.addStretch(1) 

892 

893 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

895 self.buttonBox.setCenterButtons(True) 

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

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

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

899 self.vlayout.addWidget(self.buttonBox) 

900# 

901# dialog to enter input file name 

902# 

903 def get_inputFilename(self): 

904 dialog=QtWidgets.QFileDialog() 

905 dialog.setWindowTitle("Select Input File") 

906 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

907 dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) 

908 dialog.setNameFilters( ["All Files (*)"] ) 

909 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

910 if dialog.exec(): 

911 return dialog.selectedFiles() 

912 

913# 

914# callbacks 

915# 

916 

917# 

918# change filename button 

919# 

920 def do_filenameChanged(self): 

921 flist= self.get_inputFilename() 

922 if flist is None: 

923 return 

924 self.inputfile=flist[0] 

925 self.lblFilename.setText(self.inputfile) 

926 self.gBox1.setEnabled(True) 

927 self.do_checkenable() 

928# 

929# any radio button clicked, enable/disable lif filename entry, check ok button 

930# 

931 def do_butclicked(self,id): 

932 if id== self.radioNone: 

933 self.leditFileName.setEnabled(False) 

934 else: 

935 self.leditFileName.setEnabled(True) 

936 self.do_checkenable() 

937 

938# 

939# check, if the OK button can be enabled 

940# 

941 def do_checkenable(self): 

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

943 if self.radioNone.isChecked(): 

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

945 else: 

946 if self.leditFileName.text() != "": 

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

948 return 

949 

950# 

951# OK button callback import file 

952# 

953 def do_ok(self): 

954 if self.inputfile != "": 

955 if self.radioNone.isChecked(): 

956 if cls_chk_import.execute(None, self.inputfile): 

957 exec_single(self,[add_path("lifput"),self.lifimagefile,self.inputfile]) 

958 else: 

959 self.liffilename=self.leditFileName.text() 

960 if self.radioLif41.isChecked(): 

961 exec_double_import(self,[add_path("textlif"),"-r 0",self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

962 elif self.radioLif71.isChecked(): 

963 exec_double_import(self,[add_path("textlif"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

964 elif self.radioTxt75.isChecked(): 

965 exec_double_import(self,[add_path("textlif75"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

966 elif self.radioTxt75Numbers.isChecked(): 

967 exec_double_import(self,[add_path("textlif75"),"-n",self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

968 elif self.radioHepax.isChecked(): 

969 exec_double_import(self,[add_path("rom41hx"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

970 elif self.radioEramco.isChecked(): 

971 exec_double_import(self,[add_path("rom41er"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

972 elif self.radioFocal.isChecked(): 

973 exec_double_import(self,[add_path("raw41lif"),self.liffilename],[add_path("lifput"),self.lifimagefile],self.inputfile) 

974 super().accept() 

975 

976# 

977# cancel 

978# 

979 def do_cancel(self): 

980 super().reject() 

981 

982 @staticmethod 

983 def execute(lifimagefile,workdir): 

984 d=cls_lifimport(lifimagefile,workdir) 

985 result= d.exec() 

986# 

987# check import dialog, ensure that we import a valid LIF transport file 

988# 

989class cls_chk_import(QtWidgets.QDialog): 

990 def __init__(self,fd,inputfile,parent=None): 

991 super().__init__() 

992 self.filename="" 

993 self.ftype_num=0 

994 self.blocks=0 

995 self.retval=False 

996 self.setWindowTitle("Import File to LIF Image file") 

997 self.vlayout= QtWidgets.QVBoxLayout() 

998 self.setLayout(self.vlayout) 

999 self.vlayout.addWidget(QtWidgets.QLabel("Import this file?")) 

1000 self.grid=QtWidgets.QGridLayout() 

1001 self.grid.addWidget(QtWidgets.QLabel("Filename:"),0,0) 

1002 self.grid.addWidget(QtWidgets.QLabel("Filetype:"),1,0) 

1003 self.grid.addWidget(QtWidgets.QLabel("Filesize (Blocks):"),2,0) 

1004 self.lblFilename=QtWidgets.QLabel("") 

1005 self.grid.addWidget(self.lblFilename,0,1) 

1006 self.lblFiletype=QtWidgets.QLabel("") 

1007 self.grid.addWidget(self.lblFiletype,1,1) 

1008 self.lblFilesize=QtWidgets.QLabel("") 

1009 self.grid.addWidget(self.lblFilesize,2,1) 

1010 

1011 self.vlayout.addLayout(self.grid) 

1012 self.lblMessage=QtWidgets.QLabel("") 

1013 self.vlayout.addWidget(self.lblMessage) 

1014 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

1016 self.buttonBox.setCenterButtons(True) 

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

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

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

1020 self.vlayout.addWidget(self.buttonBox) 

1021# 

1022# examine header information, if inputfile is None then we have a file descriptor 

1023# 

1024 try: 

1025 if inputfile is not None: 

1026 if isWINDOWS(): 

1027 fd= os.open(inputfile, os.O_RDONLY | os.O_BINARY ) 

1028 else: 

1029 fd= os.open(inputfile, os.O_RDONLY) 

1030 b=os.read(fd,32) 

1031 if len(b) <32: 

1032 self.lblMessage.setText("File is too short") 

1033 else: 

1034 self.filename=getLifString(b,0,10) 

1035 self.ftype_num=getLifInt(b,10,2) 

1036 self.blocks=getLifInt(b,16,4) 

1037 self.filetype=get_finfo_type(self.ftype_num) 

1038 if self.filetype is None: 

1039 self.filetype="Unknown" 

1040 else: 

1041 self.filetype= self.filetype[0] 

1042 self.lblFilename.setText(self.filename) 

1043 self.lblFiletype.setText(self.filetype) 

1044 self.lblFilesize.setText(str(self.blocks)) 

1045 if inputfile is not None: 

1046 os.close(fd) 

1047# 

1048# Check valid header 

1049# 

1050 if self.blocks < 1: 

1051 self.lblMessage.setText("Illegal file length") 

1052 return 

1053 if self.filetype=="Unknown": 

1054 self.lblMessage.setText("Unknown file type") 

1055 return 

1056 self.regexp = QtCore.QRegularExpression('[A-Z][A-Z0-9]*') 

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

1058 result=self.validator.validate(self.filename,0)[0] 

1059 if result != QtGui.QValidator.Acceptable: 

1060 self.lblMessage.setText("Illegal file name") 

1061 return 

1062 self.lblMessage.setText("Ready to import") 

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

1064 

1065 except OSError as e: 

1066 self.lblMessage.setText("Error while examining file") 

1067 

1068 def do_ok(self): 

1069 self.retval=True 

1070 super().accept() 

1071 

1072 def do_cancel(self): 

1073 super().reject() 

1074 

1075 def get_retval(self): 

1076 return self.retval 

1077 

1078 

1079 @staticmethod 

1080 def execute(fd,inputfile): 

1081 d=cls_chk_import(fd,inputfile) 

1082 result= d.exec() 

1083 return d.get_retval() 

1084# 

1085# check xroms dialog 

1086# 

1087class cls_chkxrom(QtWidgets.QDialog): 

1088 

1089 def __init__(self,parent=None): 

1090 super().__init__() 

1091 self.call=[add_path("decomp41")] 

1092 

1093 self.setWindowTitle("Check ROM Modules") 

1094 self.vlayout= QtWidgets.QVBoxLayout() 

1095 self.setLayout(self.vlayout) 

1096 

1097 self.cardrdr= QtWidgets.QCheckBox("Card Reader") 

1098 self.vlayout.addWidget(self.cardrdr) 

1099 self.printer= QtWidgets.QCheckBox("Printer") 

1100 self.vlayout.addWidget(self.printer) 

1101 self.wand= QtWidgets.QCheckBox("Wand") 

1102 self.vlayout.addWidget(self.wand) 

1103 self.hpil= QtWidgets.QCheckBox("HP-IL") 

1104 self.vlayout.addWidget(self.hpil) 

1105 self.hpil.setChecked(True) 

1106 self.xfunc= QtWidgets.QCheckBox("X-Function") 

1107 self.vlayout.addWidget(self.xfunc) 

1108 self.xfunc.setChecked(True) 

1109 self.time= QtWidgets.QCheckBox("Time") 

1110 self.vlayout.addWidget(self.time) 

1111 self.hepax= QtWidgets.QCheckBox("HEPAX") 

1112 self.vlayout.addWidget(self.hepax) 

1113 self.xio= QtWidgets.QCheckBox("Extended IO") 

1114 self.vlayout.addWidget(self.xio) 

1115 self.devil= QtWidgets.QCheckBox("HP-IL Devel") 

1116 self.vlayout.addWidget(self.devil) 

1117 self.plotter= QtWidgets.QCheckBox("Plotter") 

1118 self.vlayout.addWidget(self.plotter) 

1119 

1120 self.exitButton= QtWidgets.QPushButton("Exit") 

1121 self.exitButton.setFixedWidth(60) 

1122 self.exitButton.clicked.connect(self.do_exit) 

1123 self.hlayout= QtWidgets.QHBoxLayout() 

1124 self.hlayout.addWidget(self.exitButton) 

1125 self.vlayout.addLayout(self.hlayout) 

1126# 

1127# exit, return the parameters of the selected modules 

1128# 

1129 def do_exit(self): 

1130 if self.cardrdr.isChecked(): 

1131 self.call.append("-x") 

1132 self.call.append("cardrdr") 

1133 if self.printer.isChecked(): 

1134 self.call.append("-x") 

1135 self.call.append("printer") 

1136 if self.wand.isChecked(): 

1137 self.call.append("-x") 

1138 self.call.append("wand") 

1139 if self.hpil.isChecked(): 

1140 self.call.append("-x") 

1141 self.call.append("hpil") 

1142 if self.xfunc.isChecked(): 

1143 self.call.append("-x") 

1144 self.call.append("xfn") 

1145 if self.time.isChecked(): 

1146 self.call.append("-x") 

1147 self.call.append("time") 

1148 if self.hepax.isChecked(): 

1149 self.call.append("-x") 

1150 self.call.append("hepax") 

1151 if self.xio.isChecked(): 

1152 self.call.append("-x") 

1153 self.call.append("xio") 

1154 if self.devil.isChecked(): 

1155 self.call.append("-x") 

1156 self.call.append("devil") 

1157 if self.plotter.isChecked(): 

1158 self.call.append("-x") 

1159 self.call.append("plotter") 

1160 super().accept() 

1161 

1162 def get_call(self): 

1163 return self.call 

1164 

1165 @staticmethod 

1166 def execute(): 

1167 d=cls_chkxrom() 

1168 result= d.exec() 

1169 return d.get_call() 

1170# 

1171# view file dialog 

1172# 

1173class cls_lifview(QtWidgets.QDialog): 

1174 

1175 def __init__(self,workdir,parent= None): 

1176 super().__init__() 

1177 self.workdir=workdir 

1178 self.outputfile="" 

1179 

1180 self.setWindowTitle("View File") 

1181 self.vlayout= QtWidgets.QVBoxLayout() 

1182 self.setLayout(self.vlayout) 

1183 

1184 self.viewer=QtWidgets.QTextEdit() 

1185 self.viewer.setMinimumWidth(600) 

1186 self.viewer.setMinimumHeight(600) 

1187 self.viewer.setReadOnly(True) 

1188 self.font=QtGui.QFont(FONT) 

1189 self.font.setPixelSize(12) 

1190 self.viewer.setFont(self.font) 

1191 

1192 self.vlayout.addWidget(self.viewer) 

1193 self.saveButton= QtWidgets.QPushButton("Save") 

1194 self.saveButton.setFixedWidth(60) 

1195 self.saveButton.clicked.connect(self.do_save) 

1196 self.exitButton= QtWidgets.QPushButton("Exit") 

1197 self.exitButton.setFixedWidth(60) 

1198 self.exitButton.clicked.connect(self.do_exit) 

1199 self.hlayout= QtWidgets.QHBoxLayout() 

1200 self.hlayout.addWidget(self.saveButton) 

1201 self.hlayout.addWidget(self.exitButton) 

1202 self.vlayout.addLayout(self.hlayout) 

1203 

1204 def set_text(self,output): 

1205 self.viewer.setText(output) 

1206 return 

1207# 

1208# enter output file name dialog 

1209# 

1210 def get_outputFilename(self): 

1211 dialog=QtWidgets.QFileDialog() 

1212 dialog.setWindowTitle("Select Output File") 

1213 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

1214 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 

1215 dialog.setNameFilters( ["All Files (*)"] ) 

1216 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

1217 if dialog.exec(): 

1218 return dialog.selectedFiles() 

1219# 

1220# save content to file 

1221# 

1222 def do_save(self): 

1223 flist= self.get_outputFilename() 

1224 if flist is None: 

1225 return 

1226 outputfile=flist[0] 

1227 if os.access(self.outputfile,os.W_OK): 

1228 reply=QtWidgets.QMessageBox.warning(self,'Warning',"Do you really want to overwrite file "+self.outputfile,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel) 

1229 if reply== QtWidgets.QMessageBox.Cancel: 

1230 return 

1231 try: 

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

1233 outfile=open(outputfile,"a",encoding="UTF-8-SIG") 

1234 else: 

1235 outfile=open(outputfile,"a",encoding="UTF-8") 

1236 

1237 outfile.write(str(self.viewer.toPlainText())) 

1238 outfile.close() 

1239 except OSError as e: 

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

1241 return 

1242 

1243# 

1244# exit, do nothing 

1245# 

1246 def do_exit(self): 

1247 super().accept() 

1248 

1249# 

1250# get file and pipe it to filter program, show output in editor window 

1251# 

1252 @staticmethod 

1253 def execute(lifimagefile, liffilename, liffiletype,workdir,charset): 

1254 d=cls_lifview(workdir) 

1255 ft=get_finfo_name(liffiletype) 

1256 call= get_finfo_type(ft)[1] 

1257# 

1258# decomp41 needs additional parameters (xmoms) 

1259# 

1260 if call == "decomp41": 

1261 call= cls_chkxrom.execute() 

1262# 

1263# liftext75 has the option to show line numbers 

1264# 

1265 elif call == "liftext75": 

1266 call= add_path(call) 

1267 reply=QtWidgets.QMessageBox.question(None,'',"Show line numbers?",QtWidgets.QMessageBox.Yes,QtWidgets.QMessageBox.No) 

1268 if reply== QtWidgets.QMessageBox.Yes: 

1269 call= [ add_path(call), "-n"] 

1270# 

1271# all other lifutil progs 

1272# 

1273 else: 

1274 call= add_path(call) 

1275 output=exec_double_export(d,[add_path("lifget"),"-r",lifimagefile,liffilename],call,"") 

1276# 

1277# convert and show the file content 

1278# 

1279 if output is None: 

1280 return 

1281 d.set_text(barrconv(output,charset)) 

1282 result= d.exec() 

1283# 

1284# Init LIF image file dialog 

1285#  

1286class cls_lifinit (QtWidgets.QDialog): 

1287 

1288 def __init__(self,workdir,parent= None): 

1289 super().__init__() 

1290 self.lifimagefile="" 

1291 self.workdir= workdir 

1292 self.mt="hdrive1" 

1293 

1294 self.setWindowTitle("Initialize LIF Image File") 

1295 self.vlayout= QtWidgets.QVBoxLayout() 

1296 self.setLayout(self.vlayout) 

1297 

1298 self.gBox0=QtWidgets.QGroupBox("LIF image file") 

1299 self.hbox=QtWidgets.QHBoxLayout() 

1300 self.lblFilename=QtWidgets.QLabel(self.lifimagefile) 

1301 self.hbox.addWidget(self.lblFilename) 

1302 self.hbox.addStretch(1) 

1303 self.butChange= QtWidgets.QPushButton("Change") 

1304 self.butChange.clicked.connect(self.do_filenameChanged) 

1305 self.hbox.addWidget(self.butChange) 

1306 

1307 self.gBox0.setLayout(self.hbox) 

1308 self.vlayout.addWidget(self.gBox0) 

1309 

1310 self.gBox1=QtWidgets.QGroupBox("Medium type") 

1311 self.bGroup=QtWidgets.QButtonGroup() 

1312 self.radio1= QtWidgets.QRadioButton("HP 82161A cassette") 

1313 self.radio2= QtWidgets.QRadioButton("HP 9114B double sided disk ") 

1314 self.radio3= QtWidgets.QRadioButton("HDRIVE1 640 KB") 

1315 self.radio4= QtWidgets.QRadioButton("HDRIVE1 2 MB") 

1316 self.radio5= QtWidgets.QRadioButton("HDRIVE1 4 MB") 

1317 self.radio6= QtWidgets.QRadioButton("HDRIVE1 8 MB") 

1318 self.radio7= QtWidgets.QRadioButton("HDRIVE1 16 MB") 

1319 self.radio3.setChecked(True) 

1320 self.bGroup.addButton(self.radio1) 

1321 self.bGroup.addButton(self.radio2) 

1322 self.bGroup.addButton(self.radio3) 

1323 self.bGroup.addButton(self.radio4) 

1324 self.bGroup.addButton(self.radio5) 

1325 self.bGroup.addButton(self.radio6) 

1326 self.bGroup.addButton(self.radio7) 

1327 self.bGroup.buttonClicked.connect(self.do_butclicked) 

1328 

1329 self.vbox=QtWidgets.QVBoxLayout() 

1330 self.vbox.addWidget(self.radio1) 

1331 self.vbox.addWidget(self.radio2) 

1332 self.vbox.addWidget(self.radio3) 

1333 self.vbox.addWidget(self.radio4) 

1334 self.vbox.addWidget(self.radio5) 

1335 self.vbox.addWidget(self.radio6) 

1336 self.vbox.addWidget(self.radio7) 

1337 

1338 self.hbox1=QtWidgets.QHBoxLayout() 

1339 self.lbl1=QtWidgets.QLabel("Directory size:") 

1340 self.hbox1.addWidget(self.lbl1) 

1341 self.leditDirSize=QtWidgets.QLineEdit(self) 

1342 self.leditDirSize.setText("500") 

1343 self.leditDirSize.setMaxLength(4) 

1344 self.regexpDirSize = QtCore.QRegularExpression('[1-9][0-9]*') 

1345 self.validatorDirSize = QtGui.QRegularExpressionValidator(self.regexpDirSize) 

1346 self.leditDirSize.setValidator(self.validatorDirSize) 

1347 self.leditDirSize.textChanged.connect(self.do_checkenable) 

1348 self.hbox1.addWidget(self.leditDirSize) 

1349 self.vbox.addLayout(self.hbox1) 

1350 

1351 self.hbox2=QtWidgets.QHBoxLayout() 

1352 self.lbl2=QtWidgets.QLabel("LIF Label:") 

1353 self.hbox2.addWidget(self.lbl2) 

1354 self.leditLabel=QtWidgets.QLineEdit(self) 

1355 self.leditLabel.setText("") 

1356 self.leditLabel.setMaxLength(6) 

1357 self.validatorLabel = cls_LIF_validator() 

1358 self.leditLabel.setValidator(self.validatorLabel) 

1359 self.hbox2.addWidget(self.leditLabel) 

1360 self.vbox.addLayout(self.hbox2) 

1361 

1362 self.gBox1.setLayout(self.vbox) 

1363 self.gBox1.setEnabled(False) 

1364 self.vlayout.addWidget(self.gBox1) 

1365 self.vbox.addStretch(1) 

1366 

1367 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

1369 self.buttonBox.setCenterButtons(True) 

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

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

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

1373 self.vlayout.addWidget(self.buttonBox) 

1374# 

1375# dialog to enter input file name 

1376# 

1377 def get_inputFilename(self): 

1378 dialog=QtWidgets.QFileDialog() 

1379 dialog.setWindowTitle("Select LIF Image File") 

1380 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

1381 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile) 

1382 dialog.setNameFilters( ["All Files (*)"] ) 

1383 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

1384 if dialog.exec(): 

1385 return dialog.selectedFiles() 

1386# 

1387# callbacks 

1388# 

1389 

1390# 

1391# change filename button 

1392# 

1393 def do_filenameChanged(self): 

1394 flist= self.get_inputFilename() 

1395 if flist is None: 

1396 return 

1397 self.lifimagefile=flist[0] 

1398 self.lblFilename.setText(self.lifimagefile) 

1399 self.gBox1.setEnabled(True) 

1400 self.do_checkenable() 

1401# 

1402# radio button clicked, set default directory size 

1403# 

1404 def do_butclicked(self): 

1405 

1406 if self.radio1.isChecked(): 

1407 self.leditDirSize.setText("150") 

1408 self.mt="cass" 

1409 if self.radio2.isChecked(): 

1410 self.leditDirSize.setText("500") 

1411 self.mt="disk" 

1412 if self.radio3.isChecked(): 

1413 self.leditDirSize.setText("500") 

1414 self.mt="hdrive1" 

1415 if self.radio4.isChecked(): 

1416 self.leditDirSize.setText("1000") 

1417 self.mt="hdrive2" 

1418 if self.radio5.isChecked(): 

1419 self.leditDirSize.setText("2000") 

1420 self.mt="hdrive4" 

1421 if self.radio6.isChecked(): 

1422 self.leditDirSize.setText("2000") 

1423 self.mt="hdrive8" 

1424 if self.radio7.isChecked(): 

1425 self.leditDirSize.setText("2000") 

1426 self.mt="hdrive16" 

1427 self.do_checkenable() 

1428# 

1429# check, if the OK button can be enabled 

1430# 

1431 def do_checkenable(self): 

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

1433 if (self.leditDirSize.text() != "" and self.lifimagefile != ""): 

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

1435 return 

1436 

1437# 

1438# OK button, initialize file 

1439# 

1440 def do_ok(self): 

1441 if self.lifimagefile != "": 

1442 if os.access(self.lifimagefile,os.W_OK): 

1443 reply=QtWidgets.QMessageBox.warning(self,'Warning',"Do you really want to overwrite file "+self.lifimagefile,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel) 

1444 if reply== QtWidgets.QMessageBox.Cancel: 

1445 return 

1446 exec_single(self,[add_path("lifinit"),"-m",self.mt,self.lifimagefile,self.leditDirSize.text()]) 

1447 if self.leditLabel.text() != "": 

1448 exec_single(self,[add_path("liflabel"),self.lifimagefile,self.leditLabel.text()]) 

1449 super().accept() 

1450 

1451# 

1452# cancel 

1453# 

1454 def do_cancel(self): 

1455 super().reject() 

1456 

1457 @staticmethod 

1458 def execute(workdir): 

1459 d=cls_lifinit(workdir) 

1460 result= d.exec() 

1461# 

1462# fix LIF header dialog 

1463# 

1464class cls_liffix (QtWidgets.QDialog): 

1465 

1466 def __init__(self,workdir,parent= None): 

1467 super().__init__() 

1468 self.lifimagefile="" 

1469 self.workdir= workdir 

1470 self.mt="hdrive1" 

1471 

1472 self.setWindowTitle("Fix header of LIF Image File") 

1473 self.vlayout= QtWidgets.QVBoxLayout() 

1474 self.setLayout(self.vlayout) 

1475 

1476 self.gBox0=QtWidgets.QGroupBox("LIF image file") 

1477 self.hbox=QtWidgets.QHBoxLayout() 

1478 self.lblFilename=QtWidgets.QLabel(self.lifimagefile) 

1479 self.hbox.addWidget(self.lblFilename) 

1480 self.hbox.addStretch(1) 

1481 self.butChange= QtWidgets.QPushButton("Change") 

1482 self.butChange.clicked.connect(self.do_filenameChanged) 

1483 self.hbox.addWidget(self.butChange) 

1484 

1485 self.gBox0.setLayout(self.hbox) 

1486 self.vlayout.addWidget(self.gBox0) 

1487 

1488 self.gBox1=QtWidgets.QGroupBox("Medium type") 

1489 self.bGroup=QtWidgets.QButtonGroup() 

1490 self.radio1= QtWidgets.QRadioButton("HP 82161A cassette") 

1491 self.radio2= QtWidgets.QRadioButton("HP 9114B double sided disk ") 

1492 self.radio3= QtWidgets.QRadioButton("HDRIVE1 640 KB") 

1493 self.radio4= QtWidgets.QRadioButton("HDRIVE1 2 MB") 

1494 self.radio5= QtWidgets.QRadioButton("HDRIVE1 4 MB") 

1495 self.radio6= QtWidgets.QRadioButton("HDRIVE1 8 MB") 

1496 self.radio7= QtWidgets.QRadioButton("HDRIVE1 16 MB") 

1497 self.radio3.setChecked(True) 

1498 self.bGroup.addButton(self.radio1) 

1499 self.bGroup.addButton(self.radio2) 

1500 self.bGroup.addButton(self.radio3) 

1501 self.bGroup.addButton(self.radio4) 

1502 self.bGroup.addButton(self.radio5) 

1503 self.bGroup.addButton(self.radio6) 

1504 self.bGroup.addButton(self.radio7) 

1505 self.bGroup.buttonClicked.connect(self.do_butclicked) 

1506 

1507 self.vbox=QtWidgets.QVBoxLayout() 

1508 self.vbox.addWidget(self.radio1) 

1509 self.vbox.addWidget(self.radio2) 

1510 self.vbox.addWidget(self.radio3) 

1511 self.vbox.addWidget(self.radio4) 

1512 self.vbox.addWidget(self.radio5) 

1513 self.vbox.addWidget(self.radio6) 

1514 self.vbox.addWidget(self.radio7) 

1515 

1516 self.vbox.addStretch(1) 

1517 self.vlayout.addLayout(self.vbox) 

1518 

1519 self.buttonBox = QtWidgets.QDialogButtonBox(self) 

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

1521 self.buttonBox.setCenterButtons(True) 

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

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

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

1525 self.vlayout.addWidget(self.buttonBox) 

1526# 

1527# dialog to enter input file name 

1528# 

1529 def get_inputFilename(self): 

1530 dialog=QtWidgets.QFileDialog() 

1531 dialog.setWindowTitle("Select LIF Image File") 

1532 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) 

1533 dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) 

1534 dialog.setNameFilters( ["All Files (*)"] ) 

1535 dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog) 

1536 if dialog.exec(): 

1537 return dialog.selectedFiles() 

1538# 

1539# callbacks 

1540# 

1541 

1542# 

1543# change filename button 

1544# 

1545 def do_filenameChanged(self): 

1546 flist= self.get_inputFilename() 

1547 if flist is None: 

1548 return 

1549 self.lifimagefile=flist[0] 

1550 self.lblFilename.setText(self.lifimagefile) 

1551 self.gBox1.setEnabled(True) 

1552 self.do_checkenable() 

1553# 

1554# radio button clicked, set default directory size 

1555# 

1556 def do_butclicked(self): 

1557 

1558 if self.radio1.isChecked(): 

1559 self.mt="cass" 

1560 if self.radio2.isChecked(): 

1561 self.mt="disk" 

1562 if self.radio3.isChecked(): 

1563 self.mt="hdrive1" 

1564 if self.radio4.isChecked(): 

1565 self.mt="hdrive2" 

1566 if self.radio5.isChecked(): 

1567 self.mt="hdrive4" 

1568 if self.radio6.isChecked(): 

1569 self.mt="hdrive8" 

1570 if self.radio7.isChecked(): 

1571 self.mt="hdrive16" 

1572 self.do_checkenable() 

1573# 

1574# check, if the OK button can be enabled 

1575# 

1576 def do_checkenable(self): 

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

1578 if self.lifimagefile != "": 

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

1580 return 

1581 

1582# 

1583# OK button, initialize file 

1584# 

1585 def do_ok(self): 

1586 if self.lifimagefile != "": 

1587 exec_single(self,[add_path("liffix"),"-m",self.mt,self.lifimagefile]) 

1588 super().accept() 

1589# 

1590# cancel 

1591# 

1592 def do_cancel(self): 

1593 super().reject() 

1594 

1595 @staticmethod 

1596 def execute(workdir): 

1597 d=cls_liffix(workdir) 

1598 result= d.exec() 

1599# 

1600# Check installation of LIFUTILS dialog 

1601# 

1602class cls_installcheck(QtWidgets.QDialog): 

1603 

1604 def __init__(self): 

1605 super().__init__() 

1606 self.setWindowTitle('Status of LIFUTILS installation') 

1607 self.vlayout = QtWidgets.QVBoxLayout() 

1608 self.setLayout(self.vlayout) 

1609 self.view = QtWidgets.QLabel() 

1610 self.view.setFixedWidth(500) 

1611 self.view.setWordWrap(True) 

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

1613 self.button.setFixedWidth(60) 

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

1615 self.vlayout.addWidget(self.view) 

1616 self.hlayout = QtWidgets.QHBoxLayout() 

1617 self.hlayout.addWidget(self.button) 

1618 self.vlayout.addLayout(self.hlayout) 

1619 required_version_installed, installed_version= check_lifutils() 

1620 

1621 text="This version of pyILPER requires at least version "+decode_version(LIFUTILS_REQUIRED_VERSION)+" of the LIFUTILS installed." 

1622 

1623 if required_version_installed: 

1624 text+=" Version "+decode_version(installed_version)+" was found on this system. File management controls are enabled." 

1625 else: 

1626 if installed_version !=0: 

1627 text+=" Version "+decode_version(installed_version)+" was found of this system. Please upgrade to the latest version of LIFUTILS and restart pyILPER." 

1628 else: 

1629 text+=" No version of LIFUTILS was found on this system or the installed version is too old to report a version number. Please install the latest version of LIFUTILS and restart pyILPER." 

1630 text+=" File management controls are disabled." 

1631 self.view.setText(text) 

1632 

1633 def do_exit(self): 

1634 super().accept() 

1635 

1636 @staticmethod 

1637 def execute(): 

1638 d=cls_installcheck() 

1639 result= d.exec()