Coverage for pyilper/pyilpermain.py: 81%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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# pyILPER 1.2.4 for Linux
4#
5# An emulator for virtual HP-IL devices for the PIL-Box
6# derived from ILPER 1.4.5 for Windows
7# Copyright (c) 2008-2013 Jean-Francois Garnier
8# C++ version (c) 2013 Christoph Gießelink
9# Python Version (c) 2015 Joachim Siebold
10#
11# This program is free software; you can redistribute it and/or
12# modify it under the terms of the GNU General Public License
13# as published by the Free Software Foundation; either version 2
14# of the License, or (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24#
25# pyILPER main program -----------------------------------------------------
26#
27# Changelog
28# 05.10.2015 jsi:
29# - new style signal/slot handling for quit, crash and show_message
30# - adjust super statements to python3+ syntax
31# 11.10.2015 jsi:
32# - bump version number to 1.2.2
33# 24.10.2015 jsi
34# - raise gui (OS X issue)
35# 28.10.2015 jsi
36# - introduced separate version number for config file layout
37# 08.11.2015 jsi
38# - bump version number to 1.2.3
39# 14.11.2015 jsi
40# - bump version number to 1.3.0
41# 29.11.2015 jsi
42# - removed pause and resume pil loop
43# 30.11.2015 jsi
44# - introduced idyframe option
45# - bump version number to 1.3.1
46# 18.12.2015 jsi
47# - bump version number to 1.3.2
48# 26.12.2015 cg
49# -fixed misspelling
50# 10.1.2016 jsi
51# - store last windows position as proposed by cg
52# 11.1.2016 jsi
53# - corrected error im my implementation of determining the windows position
54# 22.1.2016 jsi
55# - corrected typo in Message box if HP-IL device config was changed, hint by cg
56# 01.02.2016 jsi
57# - added InstallCheck menu entry
58# 16.02.2016 jsi
59# - bump version number to 1.3.3 (Development)
60# 20.02.2016 jsi
61# - dump stacks introduced
62# 21.02.2016 jsi
63# - dump stacks disabled for windows
64# 02.03.2016 jsi
65# - added generic terminal configuration
66# 05.03.2016 jsi
67# - renmoved dead code
68# 22.02.2016 jsi
69# - modified call of cls_ui
70# 01.04.2016 jsi
71# - added copy function of PILIMAGE.DAT to the utility menu
72# 03.04.2016 jsi
73# - 1.3.3 productio
74# 05.04.2016 jsi
75# - bump version number to 1.3.4 (Development)
76# 07.04.2016 cg
77# - added 230400 baud to supported baudrates
78# 14.04.2016 jsi
79# - store coordinate list of position in config instead of QPoint Object
80# - set config file version to 2
81# 21.04.2016 jsi
82# - development version uses other config file than the production version. This
83# is controlled by the PRODUCTION constant
84# 26.04.2016 jsi
85# - IDY frame processing now enabled by default
86# - remove baudrate config parameter
87# - TypeError in open tty device now handled in pilbox.py
88# - show error if no serial device was configured.
89# 28.04.2016 jsi
90# - call post_enable to register outbound scope device
91# 07.05.2016 jsi
92# - introduce parameter for scroll up buffer size
93# 08.05.2016 jsi
94# - refactoring, make autobaud/baud rate setting configurable again (ttyspeed parameter)
95# 03.07.2016 jsi
96# - refactoring import reorganized
97# 08.07.2016 jsi
98# - refactoring, use constant for configuration
99# - refactoring, move constants to pilcore.py
100# - refactoring, use platform functions from pilcore.py
101# 27.08.2016 jsi
102# - tab configuration rewritten
103# 18.09.2016 jsi
104# - multiple instances capability added
105# 13.10.2016 jsi
106# - device configuration rewritten (add, remove and change position of devices)
107# 19.10.2016 jsi
108# - added pen config menu entry (merged)
109# 19.11.2016 jsi
110# - pipes thread added
111# 11.12.2016 jsi
112# - default configuration now includes the plotter
113# 07.01.2016 jsi
114# - store pyILPER version in config file
115# - show "first run" information, if started unconfigures
116# - show release information if a new pyILPER version was started the first time
117# - show tty device unconfigured message in status bar instead in pop up window
118# 19.02.2017 jsi:
119# - directorycharsize parameter introduced
120# 11.03.2017 jsi:
121# - change document names of release notes and change log
122# 16.03.2017 jsi:
123# - catch exception if neither QtWebKitWidgets or QtWebEngineWidgets are found
124# 17.03.2017 jsi:
125# - do not load initial document, if online manual is re-opened
126# 01.08.2017 jsi
127# - HP82162A added
128# 07.08.2017 jsi
129# - papersize parameter now global
130# - refactoring of tab classes
131# - minimum display position is 50,50 - otherwise some display manager hide
132# the menu bar (e.g. RASPBIAN)
133# - error in exception handling of reading the config file fixed
134# 23.08.2017 jsi:
135# - used pilsocket instead of pilpipes
136# 30.08.2017 jsi:
137# - save and restore main window size
138# 31.08.2017 jsi:
139# - config parameter terminalsize changed to terminalwidth
140# 04.09.2017 jsi:
141# - refactoring of thread classes
142# 20.09.2017 jsi:
143# - configuration of tab parameters at runtime
144# 28.10.2017 jsi:
145# - detection of lifutils now in cls_pyilper
146# 12.11.2017 jsi:
147# - removed configuration parameters: winpipename and socketname
148# - introduced configuration parameter: serverport
149# - remove MODE_PIPE communication
150# 17.11.2017 jsi:
151# - put drive tabs at the end in the default configuration
152# 05.12.2017 jsi
153# - initialized parameter hp82162a_pixelsize
154# 27.12.2017 jsi
155# - changes because of new cls_Tabs widget
156# 17.01.2018 jsi
157# - remove global parameters for terminal width and color scheme which
158# are now tab specific.
159# - remove scrollupbuffersize as global parameter
160# 05.02.2018 jsi
161# - usebom config variable introduced
162# 12.02.2018 jsi
163# - added --clean startup option
164# 01.03.2018 jsi
165# - check minimum python version
166# 10.08.2018 jsi
167# - cls_PenConfigWindow moved to penconfig.py
168# 11.08.2018 jsi
169# - terminal custom shortcut config added
170# 18.12.2018 jsi
171# - added HP2225B tab
172# 06.01.2018 jsi
173# - added HP2225B screenwidth global parameter
174# 30.11.2019 jsi
175# - improved help text for command line parameters
176# 26.11.2020 jsi
177# - fix: disable filemanagement controls, if lifutils are not installed
178# 15.11.2021 jsi
179# - raw drive tab added to TAB_CLASSES dict
180# 12.12.2021 jsi
181# - copy config runtime option added
182# - check if the configuration are from a newer pyILPER version
183# 18.12.2021 jsi
184# - copy config: display versions
185# 19.12.2021 jsi
186# - copy config: error processing added
187#
188import os
189import sys
190import signal
191import traceback
192import shutil
193import pyilper
194import re
195import argparse
196import time
197from PySide6 import QtCore, QtWidgets
198from .pilwidgets import cls_ui, cls_AboutWindow, cls_HelpWindow, HelpError, cls_DeviceConfigWindow, cls_DevStatusWindow, cls_PilConfigWindow
199from .pilcore import *
200from .pilconfig import PilConfigError, PILCONFIG, cls_pilconfig
201from .penconfig import PenConfigError, PENCONFIG, cls_PenConfigWindow
202from .shortcutconfig import ShortcutConfigError, SHORTCUTCONFIG, cls_ShortcutConfigWindow
203from .pilthreads import cls_PilBoxThread, cls_PilTcpIpThread, cls_PilSocketThread, PilThreadError
204from .lifexec import cls_lifinit, cls_liffix, cls_installcheck, check_lifutils
205from .pilhp82162a import cls_tabhp82162a
206from .pilplotter import cls_tabplotter
207from .pildrive import cls_tabdrive,cls_tabrawdrive
208from .pilscope import cls_tabscope
209from .pilprinter import cls_tabprinter
210from .pilterminal import cls_tabterminal
211from .pilhp2225b import cls_tabhp2225b
213STAT_DISABLED = 0 # Application in cold state: not running
214STAT_ENABLED = 1 # Application in warm state: running
216COMMTHREAD_CLASSES={MODE_PILBOX:cls_PilBoxThread,MODE_TCPIP:cls_PilTcpIpThread,MODE_SOCKET:cls_PilSocketThread}
218TAB_CLASSES={TAB_SCOPE:cls_tabscope,TAB_PRINTER:cls_tabprinter,TAB_DRIVE:cls_tabdrive,TAB_TERMINAL:cls_tabterminal,TAB_PLOTTER:cls_tabplotter,TAB_HP82162A:cls_tabhp82162a,TAB_HP2225B: cls_tabhp2225b, TAB_RAWDRIVE: cls_tabrawdrive}
220#
221# Main application ------------------------------------------------------
222#
224class cls_pyilper(QtCore.QObject):
226 sig_show_message=QtCore.Signal(str)
227 sig_crash=QtCore.Signal()
228 sig_quit=QtCore.Signal()
231 def __init__(self,args):
233 super().__init__()
234 self.name="pyilper"
235 self.instance=""
236 self.clean=False
237 if args.instance:
238 if args.instance.isalnum():
239 self.instance=args.instance
240 if args.clean:
241 self.clean=True
242 self.status= STAT_DISABLED
243 self.pilwidgets= [ ]
244 self.commobject=None
245 self.commthread= None
246 self.helpwin= None
247 self.aboutwin=None
248 self.devstatuswin=None
249 self.lifutils_installed= False
250 self.message=""
251 self.msgTimer=QtCore.QTimer()
252 self.msgTimer.timeout.connect(self.show_refresh_message)
253#
254# create user interface instance
255#
256 self.ui= cls_ui(self,VERSION,self.instance)
257#
258# check minimum python version
259#
260 if sys.version_info < ( PYTHON_REQUIRED_MAJOR, PYTHON_REQUIRED_MINOR):
261 required_version= str(PYTHON_REQUIRED_MAJOR)+"."+str(PYTHON_REQUIRED_MINOR)
262 found_version=str(sys.version_info.major)+"."+str(sys.version_info.minor)+"."+str(sys.version_info.micro)
263 reply=QtWidgets.QMessageBox.critical(self.ui,'Error','pyILPER requires at least Python version '+required_version+". You rund Python version "+found_version,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
264 sys.exit(1)
266#
267# Initialize Main Window, connect callbacks
268#
269 self.ui.actionConfig.triggered.connect(self.do_pyilperConfig)
270 self.ui.actionDevConfig.triggered.connect(self.do_DevConfig)
271 self.ui.actionPenConfig.triggered.connect(self.do_PenConfig)
272 self.ui.actionShortcutConfig.triggered.connect(self.do_ShortcutConfig)
273 self.ui.actionDevStatus.triggered.connect(self.do_DevStatus)
274 self.ui.actionReconnect.triggered.connect(self.do_Reconnect)
275 self.ui.actionExit.triggered.connect(self.do_Exit)
276 self.ui.actionInit.triggered.connect(self.do_Init)
277 self.ui.actionFix.triggered.connect(self.do_Fix)
278 self.ui.actionCopyPilimage.triggered.connect(self.do_CopyPilimage)
279 self.ui.actionInstallCheck.triggered.connect(self.do_InstallCheck)
280 self.ui.actionAbout.triggered.connect(self.do_About)
281 self.ui.actionHelp.triggered.connect(self.do_Help)
283#
284# queued signal to show a status message. This is the only update of
285# the user interface that is issued by the thread process and must
286# be issued as a queued connection
287#
288 self.sig_show_message.connect(self.show_message, QtCore.Qt.QueuedConnection)
289 self.sig_crash.connect(self.do_crash_cleanup, QtCore.Qt.QueuedConnection)
290 self.sig_quit.connect(self.do_Exit, QtCore.Qt.QueuedConnection)
292#
293# Set up configuration subsystem
294# 1. pyILPER config
295#
296 try:
297 PILCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean)
298 PILCONFIG.get(self.name,"active_tab",0)
299 PILCONFIG.get(self.name,"tabconfigchanged",False)
300 PILCONFIG.get(self.name,"tty","")
301 PILCONFIG.get(self.name,"ttyspeed",0)
302 PILCONFIG.get(self.name,"idyframe",True)
303 PILCONFIG.get(self.name,"port",60001)
304 PILCONFIG.get(self.name,"remotehost","localhost")
305 PILCONFIG.get(self.name,"remoteport",60000)
306 PILCONFIG.get(self.name,"mode",MODE_PILBOX)
307 PILCONFIG.get(self.name,"workdir",os.path.expanduser('~'))
308 PILCONFIG.get(self.name,"position","")
309 PILCONFIG.get(self.name,"serverport",59999)
310 PILCONFIG.get(self.name,"tabconfig",[[TAB_PRINTER,"Printer1"],[TAB_TERMINAL,"Terminal1"],[TAB_PLOTTER,"Plotter1"],[TAB_DRIVE,"Drive1"],[TAB_DRIVE,"Drive2"]])
311 PILCONFIG.get(self.name,"version","0.0.0")
312 PILCONFIG.get(self.name,"helpposition","")
313 PILCONFIG.get(self.name,"papersize",0)
314 PILCONFIG.get(self.name,"lifutilspath","")
315 PILCONFIG.get(self.name,"terminalcharsize",15)
316 PILCONFIG.get(self.name,"directorycharsize",13)
317 PILCONFIG.get(self.name,"hp82162a_pixelsize",1)
318 PILCONFIG.get(self.name,"hp2225b_screenwidth",640)
319 PILCONFIG.get(self.name,"usebom",False)
321 PILCONFIG.save()
322 except PilConfigError as e:
323 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
324 sys.exit(1)
325#
326# 2. pen configuration
327#
328 try:
329 PENCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean)
330 except PenConfigError as e:
331 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
332 sys.exit(1)
333#
334# 3. terminal keyboard shortcuts
335#
336 try:
337 SHORTCUTCONFIG.open(self.name,CONFIG_VERSION,self.instance,PRODUCTION,self.clean)
338 except ShortcutConfigError as e:
339 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
340 sys.exit(1)
341#
342# check lifutils
343#
344 self.lifutils_installed= check_lifutils()[0]
345 if self.lifutils_installed:
346 self.ui.enableLIFControls()
347#
348# version check, warn user if the configuration files are of a newer
349# version
350#
351 oldversion=decode_pyILPERVersion(PILCONFIG.get(self.name,"version"))
352 thisversion=decode_pyILPERVersion(VERSION)
353 if thisversion < oldversion:
354 reply=QtWidgets.QMessageBox.warning(self.ui,'Warning',"Your configuration files are of pyILPER version "+PILCONFIG.get(self.name,"version")+" which is newer than the version you are running. The program might crash or mishehave. Do you want to continue?",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel)
355 if reply== QtWidgets.QMessageBox.Cancel:
356 sys.exit(1)
357 PILCONFIG.put(self.name,"version",VERSION)
358#
359# create tab objects, scope is fixed all others are configured by tabconfig
360#
361 active_tab=PILCONFIG.get(self.name,"active_tab")
362 self.registerTab(cls_tabscope,"Scope")
363 for t in PILCONFIG.get(self.name,"tabconfig"):
364 self.registerTab(TAB_CLASSES[t[0]],t[1])
365#
366# remove config of non existing tabs
367#
368 if PILCONFIG.get(self.name,"tabconfigchanged"):
369 PILCONFIG.put(self.name,"tabconfigchanged",False)
370 PILCONFIG.put(self.name,"active_tab",0)
371 names= [self.name]
372 for obj in self.pilwidgets:
373 names.append(obj.name)
374 removekeys=[]
375 for key in PILCONFIG.getkeys():
376 prefix=key.split(sep="_")[0]
377 if not prefix in names:
378 removekeys.append(key)
379 for key in removekeys:
380 PILCONFIG.remove(key)
381 else:
382#
383# go to last active tab (if tabconfig did not change)
384#
385 self.ui.tabs.setCurrentIndex(active_tab)
386#
387# move window to last position
388#
389 position=PILCONFIG.get(self.name,"position")
390 if position !="":
391 self.ui.move(QtCore.QPoint(position[0],position[1]))
392 if len(position)==4:
393 self.ui.resize(position[2],position[3])
394#
395# show and raise gui
396#
397 self.ui.show()
398 self.ui.raise_()
399#
400# start application into warm state
401#
402 self.enable()
403 self.msgTimer.start(500)
404#
405# if we run pyILPER for the first time (oldversion =0.0.0), show startup info
406#
407 if PILCONFIG.get(self.name,"position")=="":
408 self.show_StartupInfo()
409 else:
410#
411# if we run a new version for the first time, show release notes
412#
413 if thisversion > oldversion:
414 self.show_ReleaseInfo(VERSION)
415#
416# start application into warm state
417#
418 def enable(self):
419 if self.status== STAT_ENABLED:
420 return
421#
422# set working directory
423#
424 try:
425 os.chdir(PILCONFIG.get(self.name,'workdir'))
426 except OSError as e:
427 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Cannot change to working directory: "+e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
428 return
429#
430# create and enable thread
431#
432 mode=PILCONFIG.get(self.name,"mode")
433 try:
434 commthread_class= COMMTHREAD_CLASSES[mode]
435 self.commthread= commthread_class(self.ui)
436 self.commthread.enable()
437 except PilThreadError as e:
438 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+": "+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
439 return
440#
441# enable all registered tab objects
442#
443 for obj in self.pilwidgets:
444 obj.enable()
445#
446# register outbound scope
447#
448 self.pilwidgets[0].post_enable()
449#
450# start emulator thread
451#
452 self.commthread.start()
453 self.status= STAT_ENABLED
454#
455# trigger visible virtual device widget to enable refreshs
456#
457 pilwidget= self.ui.tabs.pilWidget(PILCONFIG.get(self.name,"active_tab"))
458 pilwidget.becomes_visible()
460#
461# shut down application to cold state
462#
463 def disable(self):
464 if self.status== STAT_DISABLED:
465 return
466#
467# stop emulator thread
468#
469 if self.commthread is not None:
470 if self.commthread.isRunning:
471 self.commthread.finish()
472#
473# disable all registered tab objects
474#
475 for obj in self.pilwidgets:
476 obj.disable()
478#
479# close commobject/tcpip connection
480#
481 if self.commthread is not None:
482 self.commthread.disable()
483 self.status= STAT_DISABLED
484 self.commthread= None
485#
486# clean up from thread crash
487#
488 def do_crash_cleanup(self):
489 time.sleep(1)
490 self.disable()
492#
493# show status message
494#
495 def show_message(self,message):
496 self.message=message
497 self.ui.statusbar.showMessage(self.message)
499#
500# refresh status message
501#
502 def show_refresh_message(self):
503 self.ui.statusbar.showMessage(self.message)
505 def registerTab(self,classname,name):
506 tab= classname(self,name)
507 self.ui.tabs.addTab(tab,name)
508 self.pilwidgets.append(tab)
509#
510# callback pyilper configuration, reset the communication only if needed
511#
512 def do_pyilperConfig(self):
513 (accept, needs_reconnect, needs_reconfigure)= cls_PilConfigWindow.getPilConfig(self)
514 if accept:
515 if needs_reconnect:
516 self.disable()
517 try:
518 PILCONFIG.save()
519 except PilConfigError as e:
520 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
521 return
522 if needs_reconnect:
523 self.enable()
524#
525# reconfigure the tabs while the thread is stopped
526#
527 if needs_reconfigure:
528 if self.status== STAT_ENABLED:
529 if self.commthread is not None:
530 if self.commthread.isRunning():
531 self.commthread.halt()
532 for obj in self.pilwidgets:
533 obj.reconfigure()
534 if self.status== STAT_ENABLED:
535 if self.commthread is not None:
536 self.commthread.resume()
537#
538# callback HP-IL device config
539#
540 def do_DevConfig(self):
541 if not cls_DeviceConfigWindow.getDeviceConfig(self):
542 return
543 PILCONFIG.put(self.name,"tabconfigchanged",True)
544 try:
545 PILCONFIG.save()
546 except PilConfigError as e:
547 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
548 return
549 reply=QtWidgets.QMessageBox.information(self.ui,"Restart required","HP-IL Device configuration changed. Restart Application.",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
551#
552# callback plotter pen configuration
553#
554 def do_PenConfig(self):
555 if not cls_PenConfigWindow.getPenConfig():
556 return
557 try:
558 PENCONFIG.save()
559 except PenConfigError as e:
560 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
561 return
563#
564# callback terminal keyboard shortcut configuration
565#
566 def do_ShortcutConfig(self):
567 if not cls_ShortcutConfigWindow.getShortcutConfig():
568 return
569 try:
570 SHORTCUTCONFIG.save()
571 except ShortcutConfigError as e:
572 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
573 return
575#
576# callback show hp-il device status
577#
578 def do_DevStatus(self):
579 if self.devstatuswin is None:
580 self.devstatuswin= cls_DevStatusWindow(self)
581 self.devstatuswin.show()
582 self.devstatuswin.raise_()
583#
584# callback reconnect
585#
586 def do_Reconnect(self):
587 self.disable()
588 self.enable()
589#
590# callback exit, store windows position and size, close floating windows
591#
592 def do_Exit(self):
593 self.disable()
594 pos_x=self.ui.pos().x()
595 pos_y=self.ui.pos().y()
596 if pos_x < 50:
597 pos_x=50
598 if pos_y < 50:
599 pos_y=50
600 width= self.ui.width()
601 height= self.ui.height()
603 position=[pos_x, pos_y, width, height]
605 PILCONFIG.put(self.name,"position",position)
606 if self.helpwin is not None:
607 helpposition=[self.helpwin.pos().x(),self.helpwin.pos().y(),self.helpwin.width(),self.helpwin.height()]
608 PILCONFIG.put(self.name,"helpposition",helpposition)
609 self.ui.tabs.closeFloatingWindows()
610 try:
611 PILCONFIG.save()
612 except PilConfigError as e:
613 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.msg+': '+e.add_msg,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
614 QtWidgets.QApplication.quit()
615# print(os.times())
616#
617# callback initialize LIF medium
618#
619 def do_Init(self):
620 workdir= PILCONFIG.get(self.name,"workdir")
621 cls_lifinit.execute(workdir)
623#
624# callback fix LIF Medium
625#
626 def do_Fix(self):
627 workdir= PILCONFIG.get(self.name,"workdir")
628 cls_liffix.execute(workdir)
629#
630# callback check LIFUTILS installation
631#
632 def do_InstallCheck(self):
633 cls_installcheck.execute()
635#
636# callback copy PILIMAGE.DAT to working directory
637#
638 def do_CopyPilimage(self):
640 srcfile=os.path.join(os.path.dirname(pyilper.__file__),"lifimage","PILIMAGE.DAT")
641 srcfile= re.sub("//","/",srcfile,1)
642 dstpath=PILCONFIG.get(self.name,"workdir")
643 if os.access(os.path.join(dstpath,"PILIMAGE.DAT"),os.W_OK):
644 reply=QtWidgets.QMessageBox.warning(self.ui,'Warning',"File PILIMAGE.DAT already exists. Do you really want to overwrite that file?",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Cancel)
645 if reply== QtWidgets.QMessageBox.Cancel:
646 return
647 try:
648 shutil.copy(srcfile,dstpath)
649 except OSError as e:
650 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Cannot copy file: "+e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
651 return
652 except shutil.SameFileError:
653 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',"Source and destination file are identical",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
654 return
656#
657# callback show about window
658#
659 def do_About(self):
660 if self.aboutwin is None:
661 self.aboutwin= cls_AboutWindow(VERSION)
662 self.aboutwin.show()
663 self.aboutwin.raise_()
664#
665# callback show help window
666#
667 def do_Help(self):
668 self.show_Help("","index.html")
669#
670# show release information window
671#
672 def show_ReleaseInfo(self, version):
673 self.show_Help("","releasenotes.html")
674#
675# show startup info
676#
677 def show_StartupInfo(self):
678 self.show_Help("","startup.html")
679#
680# show help windows for a certain document
681#
682 def show_Help(self,path,document):
683 if self.helpwin is None:
684 try:
685 self.helpwin= cls_HelpWindow()
686 self.helpwin.loadDocument(path,document)
687 except HelpError as e:
688 reply=QtWidgets.QMessageBox.critical(self.ui,'Error',e.value,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
689 return
690 helpposition=PILCONFIG.get(self.name,"helpposition")
691 if helpposition!= "":
692 self.helpwin.move(QtCore.QPoint(helpposition[0],helpposition[1]))
693 self.helpwin.resize(helpposition[2],helpposition[3])
694 else:
695 self.helpwin.resize(720,700)
696 self.helpwin.show()
697 self.helpwin.raise_()
698#
699# copy configuration data from devel to production and vice versa
700# - a development/beta version of pyILPER copies the files of the
701# production version
702# - a production version of pyILPER copies the files of the development/beta
703# version
704#
705def copy_config(args):
706 count=0
707#
708# get version numbers
709#
710 from_config=cls_pilconfig()
711 to_config=cls_pilconfig()
712 try:
713 from_config.open("pyilper",CONFIG_VERSION,args.instance, not PRODUCTION,False)
714 from_version=from_config.get("pyilper","version","0.0.0")
715 to_config.open("pyilper",CONFIG_VERSION,args.instance, PRODUCTION,False)
716 to_version=to_config.get("pyilper","version","0.0.0")
717 except PilConfigError as e:
718 print(e.msg+': '+e.add_msg)
719 return
720 if from_version == "0.0.0":
721 print("There are no configuration files to copy")
722 return
723#
724# ask for confirmation
725#
726 print("\nW A R N I N G!")
727 print("This overwrites the configuration files")
728 if PRODUCTION:
729 print("of the production version: ", to_version)
730 else:
731 print("of the development/beta version: ", to_version)
732 print("with the configuration files")
733 if PRODUCTION:
734 print("of the development/beta version: ",from_version)
735 else:
736 print("of the production version: ",from_version)
737 inp= input("Continue? (enter 'YES' uppercase): ")
738 if inp !="YES":
739 print("cancelled")
740 return
741#
742# now copy configuration files
743#
744 for name in ['pyilper','penconfig','shortcutconfig']:
745 from_filename=buildconfigfilename("pyilper",name,CONFIG_VERSION,args.instance,not PRODUCTION)[0]
746 if not os.path.isfile(from_filename):
747 continue
748 to_filename=buildconfigfilename("pyilper",name,CONFIG_VERSION,args.instance, PRODUCTION)[0]
749 try:
750 shutil.copy(from_filename,to_filename)
751 except OSError as e:
752 print("Error copying file "+from_filename+": "+e.strerror)
753 return
754 except SameFileError as e:
755 print("Error copying file "+from_filename+" "+"source and destination file are identical")
756 return
757 print(from_filename)
758 print("copied to:")
759 print(to_filename)
760 count+=1
761 print(count,"files copied. Restart pyILPER without the 'cc' option now")
763#
764# dump stack if signalled externally (for debugging)
765#
766def dumpstacks(signal, frame):
767 for threadId, stack in sys._current_frames().items():
768 print("Thread ID %x" % threadId)
769 traceback.print_stack(f=stack)
771def main():
772 parser=argparse.ArgumentParser(description='pyILPER command line invocation')
773 parser.add_argument('--instance', '-instance', default="", help="Start a pyILPER instance INSTANCE. This instance has an own configuration file.")
774 parser.add_argument('--clean','-clean',action='store_true',help="Start pyILPER with a config file which is reset to defaults")
775 parser.add_argument('--cc','-cc',action='store_true',help="Copy configuration from development to production version and vice versa")
776 args=parser.parse_args()
777 if args.cc:
778 copy_config(args)
779 sys.exit(1)
781 if not isWINDOWS():
782 signal.signal(signal.SIGQUIT, dumpstacks)
783 app = QtWidgets.QApplication(sys.argv)
784 pyilper= cls_pyilper(args)
785 sys.exit(app.exec())
788if __name__ == '__main__':
790 main()