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
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
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
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
256#
257# exec piped command, read input from file, return True if success, False otherwise
258#
259def exec_double_import(parent,cmd1,cmd2,inputfile):
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
356#
357# validator checks for valid lif label or file names, converts to capital lettes
358#
359class cls_LIF_validator(QtGui.QValidator):
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):
371 def __init__(self,parent= None):
372 super().__init__()
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):
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)
399 def setPos(self,x,y):
400 super().setPos(x,y-self.h)
402 def boundingRect(self):
403 return self.rect
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):
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)
432 def setPos(self,x,y):
433 super().setPos(x,y-self.h)
435 def boundingRect(self):
436 return self.rect
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
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):
474 def __init__(self):
475 super().__init__()
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
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()
527#
528# purge file dialog
529#
530class cls_lifpurge(QtWidgets.QDialog):
532 def __init__(self,parent= None):
533 super().__init__()
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])
542#
543# rename file dialog
544#
545class cls_lifrename (QtWidgets.QDialog):
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)
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)
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)
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
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()
590#
591# cancel do nothing
592#
593 def do_cancel(self):
594 super().reject()
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):
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)
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)
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)
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"
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)
676 self.vlayout.addWidget(self.gBox1)
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)
689 self.vlayout.addWidget(self.gBox2)
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#
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 != "":
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()
770 @staticmethod
771 def execute(lifimagefile,liffilename,liffiletype,workdir):
772 d=cls_lifexport(lifimagefile,liffilename,liffiletype,workdir)
773 result= d.exec()
775#
776# label lif image file dialog
777#
778class cls_liflabel (QtWidgets.QDialog):
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)
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)
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)
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()
811 def do_cancel(self):
812 super().reject()
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):
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=""
830 self.setWindowTitle("Import File")
831 self.vlayout= QtWidgets.QVBoxLayout()
832 self.setLayout(self.vlayout)
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)
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)
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)
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)
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()
913#
914# callbacks
915#
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()
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
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()
976#
977# cancel
978#
979 def do_cancel(self):
980 super().reject()
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)
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)
1065 except OSError as e:
1066 self.lblMessage.setText("Error while examining file")
1068 def do_ok(self):
1069 self.retval=True
1070 super().accept()
1072 def do_cancel(self):
1073 super().reject()
1075 def get_retval(self):
1076 return self.retval
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):
1089 def __init__(self,parent=None):
1090 super().__init__()
1091 self.call=[add_path("decomp41")]
1093 self.setWindowTitle("Check ROM Modules")
1094 self.vlayout= QtWidgets.QVBoxLayout()
1095 self.setLayout(self.vlayout)
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)
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()
1162 def get_call(self):
1163 return self.call
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):
1175 def __init__(self,workdir,parent= None):
1176 super().__init__()
1177 self.workdir=workdir
1178 self.outputfile=""
1180 self.setWindowTitle("View File")
1181 self.vlayout= QtWidgets.QVBoxLayout()
1182 self.setLayout(self.vlayout)
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)
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)
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")
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
1243#
1244# exit, do nothing
1245#
1246 def do_exit(self):
1247 super().accept()
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):
1288 def __init__(self,workdir,parent= None):
1289 super().__init__()
1290 self.lifimagefile=""
1291 self.workdir= workdir
1292 self.mt="hdrive1"
1294 self.setWindowTitle("Initialize LIF Image File")
1295 self.vlayout= QtWidgets.QVBoxLayout()
1296 self.setLayout(self.vlayout)
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)
1307 self.gBox0.setLayout(self.hbox)
1308 self.vlayout.addWidget(self.gBox0)
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)
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)
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)
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)
1362 self.gBox1.setLayout(self.vbox)
1363 self.gBox1.setEnabled(False)
1364 self.vlayout.addWidget(self.gBox1)
1365 self.vbox.addStretch(1)
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#
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):
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
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()
1451#
1452# cancel
1453#
1454 def do_cancel(self):
1455 super().reject()
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):
1466 def __init__(self,workdir,parent= None):
1467 super().__init__()
1468 self.lifimagefile=""
1469 self.workdir= workdir
1470 self.mt="hdrive1"
1472 self.setWindowTitle("Fix header of LIF Image File")
1473 self.vlayout= QtWidgets.QVBoxLayout()
1474 self.setLayout(self.vlayout)
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)
1485 self.gBox0.setLayout(self.hbox)
1486 self.vlayout.addWidget(self.gBox0)
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)
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)
1516 self.vbox.addStretch(1)
1517 self.vlayout.addLayout(self.vbox)
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#
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):
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
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()
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):
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()
1621 text="This version of pyILPER requires at least version "+decode_version(LIFUTILS_REQUIRED_VERSION)+" of the LIFUTILS installed."
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)
1633 def do_exit(self):
1634 super().accept()
1636 @staticmethod
1637 def execute():
1638 d=cls_installcheck()
1639 result= d.exec()