Coverage for pyilper/pilwidgets.py: 87%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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.1 for Linux
4#
5# An emulator for virtual HP-IL devices for the PIL-Box
6# derived from ILPER 1.4.5 for Windows
7# Copyright (c) 2008-2013 Jean-Francois Garnier
8# C++ version (c) 2013 Christoph Gießelink
9# Python Version (c) 2015 Joachim Siebold
10#
11# This program is free software; you can redistribute it and/or
12# modify it under the terms of the GNU General Public License
13# as published by the Free Software Foundation; either version 2
14# of the License, or (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24#
25# pyILPER widget classes ----------------------------------------------------
26#
27# Changelog
28# 24.09.2015 cg
29# - fixed loading file in cls_HelpWindow() when file path contain special characters
30# - fixed name in cls_AboutWindow() text
31# - fixed some errors at error condition
32# 05.10.2015 jsi:
33# - new style signal/slot handling for quit, crash and show_message
34# - new style signal/slot handling for bottonBox widget
35# - adjust super statements to python3+ syntax
36# - removed unusable code to resize main window
37# 20.10.2015 jsi
38# - fix leading // for docpath (OSX)
39# 24.10.2015 jsi
40# - use non native menus only (OSX issues)
41# - removed ) from date of medium formatted
42# 30.11.2015 jsi
43# - introduced idy frame option
44# 22.12.2015 jsi
45# - added navigation buttons to the help window
46# - make help window resizeable
47# 28.12.2015 jsi
48# - do_cbActive: check for method toggle_active fixed
49# 06.01.2016 jsi
50# - initialize charset properly at program start
51# - use utf-8-sig as charset for logging
52# 29.01.2016 jsi
53# - improve os detection
54# 30.01.2016 jsi
55# - use font metrics to determine terminal window size
56# - removed experimental mark from TCP/IP configuration
57# 01.02.2016 jsi
58# - added InstallCheck menu callback
59# 26.02.2016 jsi:
60# - do not update terminal, if not visible
61# 28.02.2016 jsi:
62# - removed insert/replace indicator for the terminal widget
63# 02.03.2016 jsi:
64# - removed the terminal size combobox for the terminal widget
65# - added the following configuration options to the pyilper configuration for
66# the generic terminal: terminal size, color scheme, character size
67# 05.03.2016 jsi:
68# - improved HP-IL device status window
69# 12.03.2016 jsi:
70# - set terminal output queue poll timer to 25ms
71# 13.03.2016 jsi:
72# - modified exit of modal dialogs
73# 21.03.2016 jsi:
74# - refactored interface to HPTerm
75# - modified parameters of cls_ui
76# 27.03.2016 jsi:
77# - moved virtual HP-IL device status to utilities menu
78# 01.04..2016 jsi:
79# - added copy function for PILIMAGE.DAT to the utlity menu
80# 07.04.2016 cg
81# added 230400 baud to baudrate combo box
82# 12.04.2016 cg
83# get available Windows COM ports from registry
84# 14.04.2016 jsi
85# - corrected all files filter to * in the file dialog
86# 17.04.2016 jsi
87# - catch winreg access error if no COM port is available
88# 19.04.2016 jsi
89# - modified serial device filtering for MAC OS X
90# 25.04.2016 jsi
91# - remove baudrate configuration
92# 27.04.2016 jsi
93# - do not set path for QFileDialog, it remembers the last directory automatically
94# 28.04.2016 jsi
95# - enable tabscope to log inbound, outbound and both traffic
96# 29.04.2016 jsi
97# - log scope unbuffered
98# 07.05.2016 jsi
99# - make scroll up buffer size configurable
100# 08.05.2016 jsi
101# - refactoring of PilConfigWindow, make autobaud/baudrate setting configurable again
102# 17.06.2016 jsi
103# - refactoring, use constant to access configuration
104# - refactoring, move constants to pilcore.py
105# - refactoring, use platform functions from pilcore.py
106# 27.08.2016 jsi
107# - tab configuration rewritten
108# 18.09.2016 jsi
109# - configurable device sequence added
110# 04.10.2016 jsi
111# - renamed sequence to position in the menu entry
112# 05.10.2016 jsi
113# - redraw terminal widgets if parent window was resized
114# 13.10.2016 jsi
115# - device configuration rewritten, configure devices and their position in one dialogue
116# now.
117# 19.10.2016 jsi
118# - plotter tab widget added (merged)
119# - pen definition dialog added (merged)
120# - webkit/webengine handling added (experimental)
121# 24.10.2016 jsi
122# - show python and qt version in the About window
123# 04.12.2016 jsi
124# - allow LIF directories not starting at record 2
125# 11.12.2016 jsi
126# - extend configuration regarding pipes (Linux and Mac OS only)
127# 07.01.2016 jsi
128# - extended cls_HelpWindow to load arbitrary html files
129# 16.03.2017 jsi
130# - catch exception if neither QtWebKitWidgets or QtWebEngineWidgets are found
131# 01.08.2017 jsi
132# - add HP82162A tab
133# - refactoring: tab classes moved to pilxxxx.py
134# 23.08.2017 jsi
135# - socket config replaced by unix domain socket config
136# 31.08.2017 jsi
137# - changed config param terminalsize to terminalwidth
138# 03.09.2017 jsi
139# - getDevices is now method of commthread
140# 07.09.2017 jsi
141# - bugfixes: moved pen config classes to pilplotter, remove double addLayout in
142# config window
143# 08.09.2017 jsi
144# - renamed paper format from US to Letter
145# 14.04.2017 jsi
146# - refactoring of cls_tabtermgeneric
147# 20.09.2017 jsi
148# - changes of the pyILPER configuration dialog. Determine which changes need a
149# restart of the communication and wich need a restart of the application.
150# Issue appropriate messages.
151# 30.10.2017 jsi
152# - added configuration of lifutils path
153# - removed deprecated constant QFileDialog.DirectoryOnly
154# 12.11.2017 jsi
155# - removed editing of parameters winpipe and socketname
156# - added editing of parameter serverport
157# - two column layout of config window
158# 17.11.2017 jsi
159# - added missing reconfigure method to cls_tabtermgeneric
160# 22.11.2017 jsi
161# - renamed header of socket server configuration
162# 01.12.2017 jsi
163# - added HP82162A thermal printer display pixelsize configuration
164# 27.12.2017 jsi
165# - removed central widget
166# - introduced floating virtual devices
167# 04.01.2018 jsi
168# - make buffer_log configurable
169# - do a log buffer flush only if buffer_log is True (Log Checkbox Widget)
170# - reconfigure log checkbox object (cls_tabtermgeneric)
171# 16.01.2018 jsi
172# - added class for cascading configuration menus
173# - cls_tabgeneric and cls_tabtermgeneric rewritten, implemented new
174# device tab/window configuration
175# - removed configuration of terminal width and terminal color scheme
176# from cls_PilConfigWindow
177# 20.01.2018 jsi:
178# - scrollupbuffersize is not a local parameter
179# - terminalcharsize is now dual config parameter
180# - cls_config_tool_button can now handle the "Default" option (only for
181# T_INTEGER)
182# 05.02.2018 jsi:
183# - apply BOM to log file on Windows only at the beginning of a new file
184# if usebom config variable is tru
185# - make usebom variable configurable
186# - allow smaller font sizes for terminal window
187# 10.02.2018 jsi:
188# - fixed bom handling
189# 19.02.2018 jsi:
190# - introduced "paper" color scheme
191# 21.02.2018 jsi:
192# - fixed crash in cls_DevStatusWindo.de_refresh() if pyILPER status is
193# disabled
194# 08.07.2018:
195# - fixed closeEvent of cls_ui
196# 11.08.2018 jsi:
197# - terminal custom shortcut configuration added in main menu
198# 06.01.2018 jsi
199# - added global configuration for HP2225B screenwidth
200# 13.02.2020 cg
201# - fixed wrong address view for addresses > 15 and changed device
202# address view to HP71 style in cls_DevStatusWindow()
203# 19.12.2021 jsi
204# - tab title text color fixed (macOS problem)
205#
206import os
207import glob
208import datetime
209import re
210import sys
211import functools
212import pyilper
213from PySide6 import QtCore, QtGui, QtWidgets,QtWebEngineWidgets
214from .pilqterm import QScrolledTerminalWidget
215from .pilcharconv import CHARSET_HP71, charsets
216from .pilconfig import PILCONFIG
217from .pilcore import *
218if isWINDOWS():
219 import winreg
220#
221# constants for color schemes
222#
223COLOR_SCHEME_WHITE=0
224COLOR_SCHEME_GREEN=1
225COLOR_SCHEME_AMBER=2
226COLOR_SCHEME_PAPER=3
227color_scheme_names= ["white","amber","green","paper"]
228#
229# constants for cls_config_tool_button
230#
231T_BOOLEAN=1
232T_STRING=2
233T_INTEGER=3
234O_DEFAULT= -1
236#
237# class for cascading config menus
238#
239class cls_config_tool_button(QtWidgets.QToolButton):
241 config_changed_signal= QtCore.Signal()
243 def __init__(self,name,text):
244 super().__init__()
245 self.name= name
246 self.menu= QtWidgets.QMenu()
247 self.setText(text)
248 self.setMenu(self.menu)
249 self.setPopupMode(QtWidgets.QToolButton.InstantPopup)
250 self.options= []
251 self.menu.aboutToShow.connect(self.do_before_show_menu)
252 self.config_changed=None
255 def add_option(self,option_text, option_name, option_type, option_choices):
256 n=len(self.options)
257 submenu=QtWidgets.QMenu(option_text,parent=self.menu)
258 self.menu.addMenu(submenu)
259 submenu.aboutToShow.connect(functools.partial(self.do_before_show_submenu,n))
260 k=0
261 option_actions=[]
262 for choice in option_choices:
263 if option_type== T_BOOLEAN:
264 if choice== True:
265 choice="On"
266 else:
267 choice="Off"
268 if option_type== T_INTEGER:
269 if choice== -1:
270 choice="Default"
271 else:
272 choice= str(choice)
273 action= submenu.addAction(choice)
274 option_actions.append(action)
275 action.triggered.connect(functools.partial(self. do_choice_submenu,n,k))
276 k+=1
277 self.options.append([option_name, option_type, option_choices, option_actions])
278#
279# clears the variable which contains the changed option
280#
281 def do_before_show_menu(self):
282 self.config_changed=None
283#
284# returns the name of the changed option or None
285#
286 def get_changed_option_name(self):
287 if self.config_changed==None:
288 return None
289 else:
290 return self.options[self.config_changed][0]
291#
292# Action: get the value of the selected choice
293#
294 def do_choice_submenu(self,n,k):
295 v=self.options[n][2][k]
296 option_type=self.options[n][1]
298 self.config_changed=n
299# store parameter value
300 if option_type== T_INTEGER:
301 option_value= int(v)
302 elif option_type== T_BOOLEAN:
303 option_value= v
304 elif option_type== T_STRING:
305 option_value=k
306 PILCONFIG.put(self.name, self.options[n][0],option_value)
307 self.config_changed_signal.emit()
308#
309# Action: disable the current option value in the choices
310#
311 def do_before_show_submenu(self,n):
312 option_type=self.options[n][1]
314# get parameter value
315 option_value=PILCONFIG.get(self.name,self.options[n][0])
316 if option_type== T_STRING:
317 option_value= self.options[n][2][option_value]
318 self.do_enable_disable(n,option_value)
321 def do_enable_disable(self,n,option_value):
322 k=0
323 for choice in self.options[n][2]:
324 if choice== option_value:
325 self.options[n][3][k].setEnabled(False)
326 else:
327 self.options[n][3][k].setEnabled(True)
328 k+=1
330#
331# custom class float button for tab corner -----------------------------------
332#
333class cls_FloatButton(QtWidgets.QPushButton):
335 def __init__(self,parent=None):
336 super().__init__(parent)
337 self.button_size=20
338 self.icon=self.style().standardIcon(QtWidgets.QStyle.SP_TitleBarNormalButton)
339 self.pixmap= self.icon.pixmap(self.button_size, self.button_size)
340 self.setFixedSize(self.button_size, self.button_size)
342 def paintEvent(self, ev):
343 QtWidgets.QPushButton.paintEvent(self, ev)
344 p = QtGui.QPainter(self)
345 p.drawPixmap(self.button_size-self.pixmap.width(),0,self.pixmap)
346#
347# custom class for floating dialog widget ------------------------------------
348#
349class cls_FloatWindow(QtWidgets.QDialog):
351 def __init__(self,parent,position,pilwidget,name):
352 super().__init__()
353 self.parent=parent
354 self.name= name
355 wlayout= QtWidgets.QVBoxLayout()
356 wlayout.addWidget(pilwidget)
357 self.setLayout(wlayout)
358 self.setWindowTitle(name)
359 if position !="":
360 self.move(QtCore.QPoint(position[0],position[1]))
361 if len(position)==4:
362 self.resize(position[2],position[3])
363#
364# catch close event, dock widget and save position and size
365#
366 def closeEvent(self,e):
367 pos_y=self.pos().y()
368 pos_x=self.pos().x()
369 if pos_x < 50:
370 pos_x=50
371 if pos_y < 50:
372 pos_y=50
373 width= self.width()
374 height= self.height()
375 self.parent.setPosition([pos_x, pos_y, width, height])
376 self.parent.dock()
377 e.ignore()
378 return
379#
380# custom class dockable tab, handels all docking/undocking stuff -------------
381#
382class cls_DockableTab(QtWidgets.QWidget):
384 def __init__(self,tabs,index,pilwidget,name):
385 super().__init__()
386 self.pilwidget=pilwidget
387 self.tabs=tabs
388 self.index=index
389 self.name=name
390 self.vLayout=QtWidgets.QVBoxLayout()
391 self.vLayout.addWidget(self.pilwidget)
392 self.setLayout(self.vLayout)
393 self.window= None
394 self.position= PILCONFIG.get(self.name,"position","")
395 self.is_visible= False
396 self.is_floating= PILCONFIG.get(self.name,"floating",False)
397 if self.is_floating:
398 self.undock()
399#
400# undock virtual device widget. A floating widget is always "visible"
401#
402 def undock(self):
403 if self.window is None:
404 self.vLayout.removeWidget(self.pilwidget)
405 self.window=cls_FloatWindow(self,self.position,self.pilwidget,self.name)
406 if not self.is_visible:
407 self.pilwidget.becomes_visible()
408 self.window.show()
409 self.window.raise_()
410 self.is_floating= True
411 self.tabs.setTabEnabled(self.index,False)
412 return
413#
414# dock virtual device widget
415#
416 def dock(self):
417 if not (self.window is None):
418 self.window.layout().removeWidget(self.pilwidget)
419 self.vLayout.addWidget(self.pilwidget)
420 self.window= None
421 self.tabs.setTabEnabled(self.index,True)
422 if not self.is_visible:
423 self.pilwidget.becomes_invisible()
424 self.is_floating= False
425#
426# check disable dock if widget is floating: setting tab disabled does
427# not work from the __init__ method
428#
429 def checkDisabled(self):
430 if self.is_floating:
431 self.tabs.setTabEnabled(self.index,False)
432#
433# tab becomes visible, do not trigger visibility if tab is floating
434#
435 def becomes_visible(self):
436 self.is_visible= True
437 if self.window is None:
438 self.pilwidget.becomes_visible()
439#
440# tab becomes invisible, do not trigger invisibility if tab is floating
441#
442 def becomes_invisible(self):
443 self.is_visible= False
444 if self.window is None:
445 self.pilwidget.becomes_invisible()
446#
447# set position and size information, called by floating window on close
448#
449 def setPosition(self,position):
450 self.position=position
451#
452# force close of floating tab on program exit, preserve the is_floating flag
453#
454 def closeFloatWindow(self):
455 if self.window is not None:
456 self.window.close()
457 self.is_floating= True
458 PILCONFIG.put(self.name,"position",self.position)
459 PILCONFIG.put(self.name,"floating",self.is_floating)
460#
461# return positon
462#
463 def getPosition(self):
464 return self.position
465#
466# return if tab is floating
467#
468 def isFloating(self):
469 return self.is_floating
470#
471# custom tab class, hides the dockable tab widget ----------------------------
472#
473class cls_Tabs(QtWidgets.QTabWidget):
475 def __init__(self):
476 super().__init__()
477 self.old_index=-1
478 self.floatbutton=cls_FloatButton(self)
479 self.setCornerWidget(self.floatbutton,QtCore.Qt.TopRightCorner)
480 self.floatbutton.clicked.connect(self.do_undock)
481 self.currentChanged[int].connect(self.tab_current_changed)
482#
483# add virtual device via a dockable tab widget
484#
485 def addTab(self,pilwidget,name):
486 n=self.count()
487 dockable=cls_DockableTab(self,n,pilwidget,name)
488 super().addTab(dockable,name)
489 dockable.checkDisabled()
490#
491# force close of floating Windows (on program exit)
492#
493 def closeFloatingWindows(self):
494 for j in range(self.count()):
495 dockable=self.widget(j)
496 dockable.closeFloatWindow()
497#
498# undock virtual device widget triggerd by the undock corner button
499#
500 def do_undock(self):
501 i=self.currentIndex()
502 t=self.widget(i)
503 t.undock()
504 self.setTabEnabled(i,False)
505#
506# get virtual device widget of tab index i
507#
508 def pilWidget(self,i):
509 t=self.widget(i)
510 return t.pilwidget
511#
512# signal handler if tab index changes, handle visible/invisible stuff
513#
514 def tab_current_changed(self,index):
515#
516# on macOS bigSur the text of a selected tab is unreadable, so
517# we need to force the text color here
518#
519 self.tabBar().setTabTextColor(self.old_index,QtGui.QColor("#000"))
520 self.tabBar().setTabTextColor(index,QtGui.QColor("#aaa"))
521 if self.old_index >=0:
522 self.widget(self.old_index).becomes_invisible()
523 self.old_index=index
524 self.widget(self.old_index).becomes_visible()
525 PILCONFIG.put("pyilper","active_tab",index)
527#
528# Logging check box class --------------------------------------------------
529#
530class LogCheckboxWidget(QtWidgets.QCheckBox):
531 def __init__(self,name):
532 super().__init__("Log "+name)
533 self.name=name
534 self.filename=self.name+".log"
535 self.log= None
536 self.buffer_log=PILCONFIG.get(self.name,"buffer_log",True)
537#
538# configure log buffering
539#
540 def set_buffering(self,buffer_log):
541 self.buffer_log= buffer_log
542#
543# open log file, output BOM only on Windows at the beginning of the file
544#
545 def logOpen(self):
546 try:
547 if isWINDOWS() and PILCONFIG.get("pyilper","usebom"):
548 self.log=open(self.filename,"a",encoding="UTF-8-SIG")
549 else:
550 self.log=open(self.filename,"a",encoding="UTF-8")
551 self.log.write("\nBegin log "+self.filename+" at ")
552 self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
553 self.log.write("\n")
554 except OSError as e:
555 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot open log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
557 def logClose(self):
558 try:
559 self.log.write("\nEnd log "+self.filename+" at ")
560 self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
561 self.log.write("\n")
562 self.log.close()
563 self.log= None
564 except OSError as e:
565 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot close log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
567 def logWrite(self,line):
568 if self.log is None:
569 return
570 try:
571 self.log.write(line)
572 except OSError as e:
573 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot write to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
574 try:
575 self.log.close()
576 except OSError:
577 pass
578 self.log = None
580 def logFlush(self):
581 if self.log is None:
582 return
583 if self.buffer_log:
584 return
585 try:
586 self.log.flush()
587 except OSError as e:
588 reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot flush to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
589 try:
590 self.log.close()
591 except OSError:
592 pass
593 self.log = None
594#
595# abstract generic tab class -------------------------------------------------
596#
597class cls_tabgeneric(QtWidgets.QWidget):
599 def __init__(self,parent,name):
600 super().__init__()
601 self.name= name
602 self.active= PILCONFIG.get(self.name,"active",False)
603 self.parent=parent
604 self.font_name=FONT
605 self.font_size=0
606 self.font_width=0
607 self.font_height=0
608 self.width= 0
609 self.height= 0
610 self.guiobject= None
611 self.cbLogging= None
612 self.logging= False
613 self.pildevice=None
614 self.widget_index=1
615 self.cBut= None
616#
617# Build basic layout
618#
619 self.vbox= QtWidgets.QVBoxLayout()
620 self.setLayout(self.vbox)
621#
622# hbox1: container of GUI Object
623#
624 self.hbox1=QtWidgets.QHBoxLayout()
625 self.hbox1.setContentsMargins(10,10,10,10)
626#
627# hbox2: container of control widgets
628#
629 self.hbox2= QtWidgets.QHBoxLayout()
630 self.hbox2.setContentsMargins(10,3,10,3)
632 self.cbActive= QtWidgets.QCheckBox('Device enabled')
633 self.cbActive.setChecked(self.active)
634 self.cbActive.setEnabled(False)
635 self.cbActive.stateChanged.connect(self.do_cbActive)
637 self.hbox2.addWidget(self.cbActive)
638 self.hbox2.addStretch(1)
640 self.vbox.addLayout(self.hbox1)
641 self.vbox.addLayout(self.hbox2)
642#
643# add the gui object to hbox1
644#
645 def add_guiobject(self,guiobject):
646 self.guiobject= guiobject
647 self.hbox1.addWidget(self.guiobject)
648#
649# insert a control widget
650#
651 def add_controlwidget(self,widget):
652 self.hbox2.insertWidget(self.widget_index,widget)
653 self.widget_index+=1
654#
655# add a config cascading menu: always at the end of the control box
656#
657 def add_configwidget(self):
658 self.cBut= cls_config_tool_button(self.name,"Configuration")
659 self.hbox2.addWidget(self.cBut)
660#
661# insert the cbLogging widget in hbox2 after the cbActive Widget
662#
663 def add_logging(self):
664 self.cbLogging= LogCheckboxWidget(self.name)
665 self.hbox2.insertWidget(1,self.cbLogging)
666 self.logging= PILCONFIG.get(self.name,"logging",False)
667 self.cbLogging.setChecked(self.logging)
668 self.cbLogging.setEnabled(False)
669 self.cbLogging.stateChanged.connect(self.do_cbLogging)
670 self.widget_index+=1
671#
672# if config button present, then add log buffer option
673#
674 if self.cBut is not None:
675 self.cBut.add_option("Log buffering","buffer_log",T_BOOLEAN,[True,False])
676#
677# insert a status widget
678#
679 def add_statuswidget(self,statuswidget):
680 self.hbox2.insertWidget(self.widget_index,statuswidget)
681 self.hbox2.insertStretch(self.widget_index,1)
682#
683# tab config changed, handle buffer_log change
684#
685 def do_tabconfig_changed(self):
686 param= self.cBut.get_changed_option_name()
687 if param== "buffer_log":
688 self.cbLogging.set_buffering(PILCONFIG.get(self.name,"buffer_log"))
689#
690# reconfigure, nothing to do here
691#
692 def reconfigure(self):
693 return
694#
695# disable tab
696#
697 def disable(self):
698 if self.cbLogging is not None:
699 if self.logging:
700 self.cbLogging.logClose()
701 self.cbLogging.setEnabled(False)
702 self.cbActive.setEnabled(False)
703#
704# enable tab
705#
706 def enable(self):
707 self.cbActive.setEnabled(True)
708 if self.cbLogging is not None:
709 if self.logging:
710 self.cbLogging.logOpen()
711 self.cbLogging.setEnabled(True)
712#
713# toggle active/inactive
714#
715 def toggle_active(self):
716 return
717#
718# action: toogle active checkbox
719#
720 def do_cbActive(self):
721 self.active= self.cbActive.isChecked()
722 PILCONFIG.put(self.name,"active",self.active)
723 self.pildevice.setactive(self.active)
724 self.toggle_active()
725#
726# action: toggle log checkbox
727#
728 def do_cbLogging(self):
729 self.cbLogging.setEnabled(False)
730 self.logging= self.cbLogging.isChecked()
731 self.pildevice.setlocked(True)
732 if self.logging:
733 self.cbLogging.logOpen()
734 else:
735 self.cbLogging.logClose()
736 PILCONFIG.put(self.name,"logging",self.logging)
737 self.pildevice.setlocked(False)
738 self.cbLogging.setEnabled(True)
739#
740# generic terminal tab widget ------------------------------------------------
741#
742class cls_tabtermgeneric(cls_tabgeneric):
744 def __init__(self,parent,name):
745 super().__init__(parent,name)
746 self.kbd_delay=False
747#
748# init local configuration parameters
749#
750 self.terminalwidth= PILCONFIG.get(self.name,"terminalwidth",80)
751 self.colorscheme= PILCONFIG.get(self.name,"colorscheme",COLOR_SCHEME_WHITE)
752 self.scrollupbuffersize=PILCONFIG.get(self.name,"scrollupbuffersize",1000)
753 self.termcharsize=PILCONFIG.get(self.name,"terminalcharsize",-1)
754#
755# Build GUI
756#
757 self.guiobject=QScrolledTerminalWidget(self,self.name)
758#
759# add guiobject to tab
760#
761 self.add_guiobject(self.guiobject)
762#
763# add tab config widget
764#
765 self.add_configwidget()
766#
767# add basic terminal config options to cascading menu
768#
769 self.cBut.add_option("Terminal width","terminalwidth",T_INTEGER,[80,120])
770 self.cBut.add_option("Color scheme","colorscheme",T_STRING,color_scheme_names)
771 self.cBut.add_option("Font size","terminalcharsize",T_INTEGER,[O_DEFAULT,13,14,15,16,17,18,19,20])
772 self.cBut.add_option("Scrollup buffer","scrollupbuffersize",T_INTEGER,[1000,2000,5000,10000])
774 self.statuswidget=QtWidgets.QLabel("")
775 self.statuswidget.setText("Display size :")
776 self.add_statuswidget(self.statuswidget)
777#
778# handle changes of tab configuration
779#
780 def do_tabconfig_changed(self):
781 param= self.cBut.get_changed_option_name()
782 if param=="terminalwidth":
783 self.guiobject.reconfigure()
784 elif param=="colorscheme":
785 self.guiobject.reconfigure()
786 elif param=="scrollupbuffersize":
787 self.guiobject.reconfigure()
788 elif param=="terminalcharsize":
789 self.guiobject.reconfigure()
790 super().do_tabconfig_changed()
791#
792# reconfigure tab: reconfigure gui object
793#
794 def reconfigure(self):
795 self.guiobject.reconfigure()
796 super().reconfigure()
797#
798# catch resize event to redraw the terminal window
799#
800 def resizeEvent(self,event):
801 self.guiobject.redraw()
802#
803# update status widget
804#
805 def update_status(self,rows,cols):
806 self.statuswidget.setText("Display size: {:d}x{:d}".format(rows,cols))
807#
808# enable/disable
809#
810 def enable(self):
811 super().enable()
812 self.guiobject.enable()
814 def disable(self):
815 super().disable()
816 self.guiobject.disable()
817#
818# becomes visible, refresh content, activate update and blink
819#
820 def becomes_visible(self):
821 self.guiobject.becomes_visible()
822 return
823#
824# becomes invisible, deactivate update and blink
825#
826 def becomes_invisible(self):
827 self.guiobject.becomes_invisible()
828 return
829#
830# Help Dialog class ----------------------------------------------------------
831#
832class HelpError(Exception):
833 def __init__(self,value):
834 self.value=value
836 def __str__(self):
837 return repr(self.value)
840class cls_HelpWindow(QtWidgets.QDialog):
842 def __init__(self,parent=None):
843#
844 super().__init__()
845 self.setWindowTitle('pyILPER Manual')
847 self.vlayout = QtWidgets.QVBoxLayout()
848 self.setLayout(self.vlayout)
849 self.view = QtWebEngineWidgets.QWebEngineView()
850 self.view.setMinimumWidth(600)
851 self.vlayout.addWidget(self.view)
852 self.buttonExit = QtWidgets.QPushButton('Exit')
853 self.buttonExit.setFixedWidth(60)
854 self.buttonExit.clicked.connect(self.do_exit)
855 self.buttonBack = QtWidgets.QPushButton('<')
856 self.buttonBack.setFixedWidth(60)
857 self.buttonForward = QtWidgets.QPushButton('>')
858 self.buttonForward.setFixedWidth(60)
859 self.hlayout = QtWidgets.QHBoxLayout()
860 self.hlayout.addWidget(self.buttonBack)
861 self.hlayout.addWidget(self.buttonExit)
862 self.hlayout.addWidget(self.buttonForward)
863 self.vlayout.addLayout(self.hlayout)
864 self.buttonBack.clicked.connect(self.do_back)
865 self.buttonForward.clicked.connect(self.do_forward)
867 def do_exit(self):
868 self.hide()
870 def do_back(self):
871 self.view.back()
873 def do_forward(self):
874 self.view.forward()
876 def loadDocument(self,subdir,document):
877 if subdir=="":
878 docpath=os.path.join(os.path.dirname(pyilper.__file__),"Manual",document)
879 else:
880 docpath=os.path.join(os.path.dirname(pyilper.__file__),"Manual",subdir,document)
881 docpath=re.sub("//","/",docpath,1)
882 self.view.load(QtCore.QUrl.fromLocalFile(docpath))
883#
884# Release Info Dialog class --------------------------------------------------
885#
886class cls_ReleaseWindow(QtWidgets.QDialog):
888 def __init__(self,version):
889 super().__init__()
890 self.setWindowTitle('Release Information for pyILPER '+version)
891 self.vlayout = QtWidgets.QVBoxLayout()
892 self.setLayout(self.vlayout)
893 self.view = QtWidgets.QLabel()
894 self.view.setFixedWidth(500)
895 self.view.setWordWrap(True)
896 self.view.setText("Release Info Text")
897 self.button = QtWidgets.QPushButton('OK')
898 self.button.setFixedWidth(60)
899 self.button.clicked.connect(self.do_exit)
900 self.vlayout.addWidget(self.view)
901 self.hlayout = QtWidgets.QHBoxLayout()
902 self.hlayout.addWidget(self.button)
903 self.vlayout.addLayout(self.hlayout)
905 def do_exit(self):
906 self.hide()
908#
909#
910# About Dialog class --------------------------------------------------------
911#
912class cls_AboutWindow(QtWidgets.QDialog):
914 def __init__(self,version):
915 super().__init__()
916 self.qtversion=QtCore.__version__
917 self.pyversion=str(sys.version_info.major)+"."+str(sys.version_info.minor)+"."+str(sys.version_info.micro)
918 self.setWindowTitle('pyILPER About ...')
919 self.vlayout = QtWidgets.QVBoxLayout()
920 self.setLayout(self.vlayout)
921 self.view = QtWidgets.QLabel()
922 self.view.setFixedWidth(300)
923 self.view.setWordWrap(True)
924 self.view.setText("pyILPER "+version+ "\n\nAn emulator for virtual HP-IL devices for the PIL-Box derived from ILPER 1.4.5 for Windows\n\nCopyright (c) 2008-2013 Jean-Francois Garnier\nC++ version (c) 2017 Christoph Gießelink\nTerminal emulator code Henning Schröder\nPython Version (c) 2015-2020 Joachim Siebold\n\nGNU General Public License Version 2\n\nYou run Python "+self.pyversion+" and Qt "+self.qtversion+"\n")
927 self.button = QtWidgets.QPushButton('OK')
928 self.button.setFixedWidth(60)
929 self.button.clicked.connect(self.do_exit)
930 self.vlayout.addWidget(self.view)
931 self.hlayout = QtWidgets.QHBoxLayout()
932 self.hlayout.addWidget(self.button)
933 self.vlayout.addLayout(self.hlayout)
935 def do_exit(self):
936 self.hide()
937#
938# Get TTy Dialog class ------------------------------------------------------
939#
941class cls_TtyWindow(QtWidgets.QDialog):
943 def __init__(self, parent=None):
944 super().__init__()
946 self.setWindowTitle("Select serial device")
947 self.vlayout= QtWidgets.QVBoxLayout()
948 self.setLayout(self.vlayout)
950 self.label= QtWidgets.QLabel()
951 self.label.setText("Select or enter serial port")
952# self.label.setAlignment(QtCore.Qt.AlignCenter)
954 self.__ComboBox__ = QtWidgets.QComboBox()
955 self.__ComboBox__.setEditable(True)
957 if isWINDOWS():
958#
959# Windows COM ports from registry
960#
961 try:
962 with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Hardware\DeviceMap\SerialComm",0,winreg.KEY_QUERY_VALUE|winreg.KEY_ENUMERATE_SUB_KEYS) as key:
963 for i in range (0, winreg.QueryInfoKey(key)[1]):
964 port = winreg.EnumValue(key, i)[1]
965 self.__ComboBox__.addItem( port, port )
966 except FileNotFoundError:
967 pass
968 elif isLINUX():
969#
970# Linux /dev/ttyUSB?
971#
972 devlist=glob.glob("/dev/ttyUSB*")
973 for port in devlist:
974 self.__ComboBox__.addItem( port, port )
975#
976# Mac OS X /dev/tty.usbserial-*
977#
978 elif isMACOS():
979 devlist=glob.glob("/dev/tty.usbserial-*")
980 for port in devlist:
981 self.__ComboBox__.addItem( port, port )
983 else:
984#
985# Other
986#
987 devlist=glob.glob("/dev/tty*")
988 for port in devlist:
989 self.__ComboBox__.addItem( port, port )
991# self.__ComboBox__.activated['QString'].connect(self.combobox_choosen)
992 self.__ComboBox__.editTextChanged.connect(self.combobox_textchanged)
993 self.buttonBox = QtWidgets.QDialogButtonBox()
994 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
995 self.buttonBox.setCenterButtons(True)
996 self.buttonBox.accepted.connect(self.do_ok)
997 self.buttonBox.rejected.connect(self.do_cancel)
998 self.vlayout.addWidget(self.label)
999 self.vlayout.addWidget(self.__ComboBox__)
1000 self.hlayout = QtWidgets.QHBoxLayout()
1001 self.hlayout.addWidget(self.buttonBox)
1002 self.vlayout.addWidget(self.buttonBox)
1003 self.__device__= ""
1005 def do_ok(self):
1006 if self.__device__=="":
1007 self.__device__= self.__ComboBox__.currentText()
1008 if self.__device__=="":
1009 return
1010 super().accept()
1012 def do_cancel(self):
1013 super().reject()
1016 def combobox_textchanged(self, device):
1017 self.__device__= device
1019 def combobox_choosen(self, device):
1020 self.__device__= device
1022 def getDevice(self):
1023 return self.__device__
1025 @staticmethod
1026 def getTtyDevice(parent=None):
1027 dialog= cls_TtyWindow(parent)
1028 dialog.resize(200,100)
1029 result= dialog.exec()
1030 if result== QtWidgets.QDialog.Accepted:
1031 return dialog.getDevice()
1032 else:
1033 return ""
1034#
1035# Main pyILPER configuration dialog ------------------------------------------
1036#
1037class cls_PilConfigWindow(QtWidgets.QDialog):
1039 def __init__(self,parent):
1040 super().__init__()
1041 self.__needs_reconnect__= False
1042 self.__needs_reconfigure__= False
1043 self.__needs_restart__= False
1044 self.__name__=parent.name
1045 self.__parent__= parent
1046 self.__mode__= PILCONFIG.get(self.__name__,"mode")
1047 self.__tty__= PILCONFIG.get(self.__name__,"tty")
1048 self.__ttyspeed__= PILCONFIG.get(self.__name__,"ttyspeed")
1049 self.__port__= PILCONFIG.get(self.__name__,"port")
1050 self.__idyframe__= PILCONFIG.get(self.__name__,"idyframe")
1051 self.__remotehost__= PILCONFIG.get(self.__name__,"remotehost")
1052 self.__remoteport__= PILCONFIG.get(self.__name__,"remoteport")
1053 self.__serverport__= PILCONFIG.get(self.__name__,"serverport")
1054 self.__workdir__= PILCONFIG.get(self.__name__,"workdir")
1055 self.__termcharsize__=PILCONFIG.get(self.__name__,"terminalcharsize")
1056 self.__dircharsize__=PILCONFIG.get(self.__name__,"directorycharsize")
1057 self.__papersize__=PILCONFIG.get(self.__name__,"papersize")
1058 self.__lifutilspath__=PILCONFIG.get(self.__name__,"lifutilspath")
1059 self.__hp82162a_pixelsize__=PILCONFIG.get(self.__name__,"hp82162a_pixelsize")
1060 self.__hp2225b_screenwidth__=PILCONFIG.get(self.__name__,"hp2225b_screenwidth")
1061 self.__usebom__= PILCONFIG.get(self.__name__,"usebom")
1063 self.setWindowTitle("pyILPER configuration")
1064 self.vbox0= QtWidgets.QVBoxLayout()
1065 self.setLayout(self.vbox0)
1066 self.hbox0= QtWidgets.QHBoxLayout()
1067 self.vbox0.addLayout(self.hbox0)
1068 self.vbox1= QtWidgets.QVBoxLayout()
1069 self.vbox2= QtWidgets.QVBoxLayout()
1070 self.hbox0.addLayout(self.vbox1)
1071 self.hbox0.addLayout(self.vbox2)
1073#
1074# Group box with radio buttons for communication typ
1075#
1077 self.gbox = QtWidgets.QGroupBox()
1078 self.gbox.setFlat(True)
1079 self.gbox.setTitle("Communication configuration")
1080 self.vboxgbox= QtWidgets.QVBoxLayout()
1081 self.gbox.setLayout(self.vboxgbox)
1082#
1083# Section PIL-Box
1084#
1085 self.radbutPIL = QtWidgets.QRadioButton(self.gbox)
1086 self.radbutPIL.setText("PIL-Box")
1087 self.radbutPIL.clicked.connect(self.setCheckBoxes)
1088 self.vboxgbox.addWidget(self.radbutPIL)
1089#
1090# serial device
1091#
1092 self.hboxtty= QtWidgets.QHBoxLayout()
1093 self.lbltxt1=QtWidgets.QLabel("Serial Device: ")
1094 self.hboxtty.addWidget(self.lbltxt1)
1095 self.lblTty=QtWidgets.QLabel()
1096 self.lblTty.setText(self.__tty__)
1097 self.hboxtty.addWidget(self.lblTty)
1098 self.hboxtty.addStretch(1)
1099 self.butTty=QtWidgets.QPushButton()
1100 self.butTty.setText("change")
1101 self.butTty.pressed.connect(self.do_config_Interface)
1102 self.hboxtty.addWidget(self.butTty)
1103 self.vboxgbox.addLayout(self.hboxtty)
1104#
1105# tty speed combo box
1106#
1107 self.hboxbaud= QtWidgets.QHBoxLayout()
1108 self.lbltxt2=QtWidgets.QLabel("Baud rate ")
1109 self.hboxbaud.addWidget(self.lbltxt2)
1110 self.comboBaud=QtWidgets.QComboBox()
1111 i=0
1112 for baud in BAUDRATES:
1113 self.comboBaud.addItem(baud[0])
1114 if self.__ttyspeed__== baud[1]:
1115 self.comboBaud.setCurrentIndex(i)
1116 i+=1
1118 self.hboxbaud.addWidget(self.comboBaud)
1119 self.hboxbaud.addStretch(1)
1120 self.vboxgbox.addLayout(self.hboxbaud)
1122#
1123# idy frames
1124#
1125 self.cbIdyFrame= QtWidgets.QCheckBox('Enable IDY frames')
1126 self.cbIdyFrame.setChecked(self.__idyframe__)
1127 self.cbIdyFrame.setEnabled(True)
1128 self.cbIdyFrame.stateChanged.connect(self.do_cbIdyFrame)
1129 self.vboxgbox.addWidget(self.cbIdyFrame)
1130#
1131# section TCP/IP communication
1132#
1133 self.radbutTCPIP = QtWidgets.QRadioButton(self.gbox)
1134 self.radbutTCPIP.setText("HP-IL over TCP/IP")
1135 self.radbutTCPIP.clicked.connect(self.setCheckBoxes)
1136 self.vboxgbox.addWidget(self.radbutTCPIP)
1137#
1138# TCP/IP Parameter input (port, remote host, remote port)
1139#
1140 self.intvalidator= QtGui.QIntValidator()
1141 self.glayout=QtWidgets.QGridLayout()
1142 self.lbltxt3=QtWidgets.QLabel("Port:")
1143 self.glayout.addWidget(self.lbltxt3,0,0)
1144 self.lbltxt4=QtWidgets.QLabel("Remote host:")
1145 self.glayout.addWidget(self.lbltxt4,1,0)
1146 self.lbltxt5=QtWidgets.QLabel("Remote port:")
1147 self.glayout.addWidget(self.lbltxt5,2,0)
1148 self.edtPort= QtWidgets.QLineEdit()
1149 self.glayout.addWidget(self.edtPort,0,1)
1150 self.edtPort.setText(str(self.__port__))
1151 self.edtPort.setValidator(self.intvalidator)
1152 self.edtRemoteHost= QtWidgets.QLineEdit()
1153 self.glayout.addWidget(self.edtRemoteHost,1,1)
1154 self.edtRemoteHost.setText(self.__remotehost__)
1155 self.edtRemotePort= QtWidgets.QLineEdit()
1156 self.glayout.addWidget(self.edtRemotePort,2,1)
1157 self.edtRemotePort.setText(str(self.__remoteport__))
1158 self.edtRemotePort.setValidator(self.intvalidator)
1159 self.vboxgbox.addLayout(self.glayout)
1160 self.vbox1.addWidget(self.gbox)
1161#
1162# Section TCP/IP server port
1163#
1164 self.radbutServerport = QtWidgets.QRadioButton(self.gbox)
1165 self.radbutServerport.setText("TCP/IP socket Server (PIL-Box emulator)")
1166 self.radbutServerport.clicked.connect(self.setCheckBoxes)
1167 self.vboxgbox.addWidget(self.radbutServerport)
1168 self.splayout=QtWidgets.QGridLayout()
1169 self.splayout.addWidget(QtWidgets.QLabel("Server port:"),0,0)
1170 self.edtServerport=QtWidgets.QLineEdit()
1171 self.edtServerport.setValidator(self.intvalidator)
1172 self.splayout.addWidget(self.edtServerport,0,1)
1173 self.edtServerport.setText(str(self.__serverport__))
1174 self.vboxgbox.addLayout(self.splayout)
1176#
1177# Init radio buttons
1178#
1179 if self.__mode__==0:
1180 self.radbutPIL.setChecked(True)
1181 elif self.__mode__==1:
1182 self.radbutTCPIP.setChecked(True)
1183 else:
1184 self.radbutServerport.setChecked(True)
1185 self.setCheckBoxes()
1186#
1187# Section Working Directory
1188#
1189 self.gboxw = QtWidgets.QGroupBox()
1190 self.gboxw.setFlat(True)
1191 self.gboxw.setTitle("Working directory")
1192 self.vboxgboxw= QtWidgets.QVBoxLayout()
1193 self.gboxw.setLayout(self.vboxgboxw)
1194 self.hboxwdir= QtWidgets.QHBoxLayout()
1195 self.lbltxt6=QtWidgets.QLabel("Directory: ")
1196 self.hboxwdir.addWidget(self.lbltxt6)
1197 self.lblwdir=QtWidgets.QLabel()
1198 self.lblwdir.setText(self.__workdir__)
1199 self.hboxwdir.addWidget(self.lblwdir)
1200 self.hboxwdir.addStretch(1)
1201 self.butwdir=QtWidgets.QPushButton()
1202 self.butwdir.setText("change")
1203 self.butwdir.pressed.connect(self.do_config_Workdir)
1204 self.hboxwdir.addWidget(self.butwdir)
1205 self.vboxgboxw.addLayout(self.hboxwdir)
1206 self.vbox1.addWidget(self.gboxw)
1208 self.vbox1.addStretch(1)
1209#
1210# section lifutils path
1211#
1212 self.gboxlifpath = QtWidgets.QGroupBox()
1213 self.gboxlifpath.setFlat(True)
1214 self.gboxlifpath.setTitle("Custom LIFUTILS location")
1215 self.vboxgboxlifpath= QtWidgets.QVBoxLayout()
1216 self.gboxlifpath.setLayout(self.vboxgboxlifpath)
1217 self.hboxlifpath= QtWidgets.QHBoxLayout()
1218 self.lbltxt7=QtWidgets.QLabel("Path to lifversion program: ")
1219 self.hboxlifpath.addWidget(self.lbltxt7)
1220 self.lbllifpath=QtWidgets.QLabel()
1221 self.lbllifpath.setText(self.__lifutilspath__)
1222 self.hboxlifpath.addWidget(self.lbllifpath)
1223 self.hboxlifpath.addStretch(1)
1225 self.vboxlifbut= QtWidgets.QVBoxLayout()
1226 self.butlifpathchange=QtWidgets.QPushButton()
1227 self.butlifpathchange.setText("change")
1228 self.butlifpathchange.pressed.connect(self.do_config_lifutilspath_change)
1229 self.butlifpathclear=QtWidgets.QPushButton()
1230 self.butlifpathclear.setText("clear")
1231 self.butlifpathclear.pressed.connect(self.do_config_lifutilspath_clear)
1232 self.vboxlifbut.addWidget(self.butlifpathchange)
1233 self.vboxlifbut.addWidget(self.butlifpathclear)
1235 self.hboxlifpath.addLayout(self.vboxlifbut)
1236 self.vboxgboxlifpath.addLayout(self.hboxlifpath)
1237 self.vbox2.addWidget(self.gboxlifpath)
1238#
1239# Section Terminal configuration: scroll up buffer, font size
1240#
1241 self.gboxt= QtWidgets.QGroupBox()
1242 self.gboxt.setFlat(True)
1243 self.gboxt.setTitle("Terminal Settings")
1244 self.gridt= QtWidgets.QGridLayout()
1245 self.gridt.setSpacing(3)
1246 self.gridt.addWidget(QtWidgets.QLabel("Font Size"),0,0)
1248 self.spinTermCharsize=QtWidgets.QSpinBox()
1249 self.spinTermCharsize.setMinimum(13)
1250 self.spinTermCharsize.setMaximum(20)
1251 self.spinTermCharsize.setValue(self.__termcharsize__)
1252 self.gridt.addWidget(self.spinTermCharsize,0,1)
1254 self.gboxt.setLayout(self.gridt)
1255 self.vbox2.addWidget(self.gboxt)
1256#
1257# HP82162A thermal printer settings
1258#
1259 self.gbox82162a= QtWidgets.QGroupBox()
1260 self.gbox82162a.setFlat(True)
1261 self.gbox82162a.setTitle("HP82162A Settings")
1262 self.grid82162a= QtWidgets.QGridLayout()
1263 self.grid82162a.setSpacing(3)
1264 self.grid82162a.addWidget(QtWidgets.QLabel("Pixel size"),0,0)
1266 self.spinHP82162APixelsize=QtWidgets.QSpinBox()
1267 self.spinHP82162APixelsize.setMinimum(1)
1268 self.spinHP82162APixelsize.setMaximum(2)
1269 self.spinHP82162APixelsize.setValue(self.__hp82162a_pixelsize__)
1270 self.grid82162a.addWidget(self.spinHP82162APixelsize,0,1)
1272 self.gbox82162a.setLayout(self.grid82162a)
1273 self.vbox2.addWidget(self.gbox82162a)
1274#
1275# HP2225B thermal printer settings
1276#
1277 self.gbox2225B= QtWidgets.QGroupBox()
1278 self.gbox2225B.setFlat(True)
1279 self.gbox2225B.setTitle("HP2225B Settings")
1280 self.grid2225B= QtWidgets.QGridLayout()
1281 self.grid2225B.setSpacing(3)
1282 self.grid2225B.addWidget(QtWidgets.QLabel("Screen width size"),0,0)
1284 self.spinHP2225Bscreenwidth=QtWidgets.QSpinBox()
1285 self.spinHP2225Bscreenwidth.setMinimum(640)
1286 self.spinHP2225Bscreenwidth.setMaximum(1280)
1287 self.spinHP2225Bscreenwidth.setSingleStep(320)
1288 self.spinHP2225Bscreenwidth.setValue(self.__hp2225b_screenwidth__)
1289 self.grid2225B.addWidget(self.spinHP2225Bscreenwidth,0,1)
1291 self.gbox2225B.setLayout(self.grid2225B)
1292 self.vbox2.addWidget(self.gbox2225B)
1293#
1294# Section Directory listing configuration: font size
1295#
1296 self.gboxd= QtWidgets.QGroupBox()
1297 self.gboxd.setFlat(True)
1298 self.gboxd.setTitle("Directory Listing Settings")
1299 self.gridd= QtWidgets.QGridLayout()
1300 self.gridd.setSpacing(3)
1301 self.gridd.addWidget(QtWidgets.QLabel("Font Size"),0,0)
1302 self.spinDirCharsize=QtWidgets.QSpinBox()
1303 self.spinDirCharsize.setMinimum(11)
1304 self.spinDirCharsize.setMaximum(18)
1305 self.spinDirCharsize.setValue(self.__dircharsize__)
1306 self.gridd.addWidget(self.spinDirCharsize,0,1)
1308 self.gboxd.setLayout(self.gridd)
1309 self.vbox2.addWidget(self.gboxd)
1310#
1311# Section Papersize
1312#
1313 self.gboxps= QtWidgets.QGroupBox()
1314 self.gboxps.setFlat(True)
1315 self.gboxps.setTitle("Papersize (Plotter and PDF output)")
1316 self.gridps=QtWidgets.QGridLayout()
1317 self.gridps.setSpacing(3)
1319 self.gridps.addWidget(QtWidgets.QLabel("Papersize:"),0,0)
1320 self.combops=QtWidgets.QComboBox()
1321 self.combops.addItem("A4")
1322 self.combops.addItem("Letter")
1323 self.combops.setCurrentIndex(self.__papersize__)
1324 self.gridps.addWidget(self.combops,0,1)
1325 self.gboxps.setLayout(self.gridps)
1326 self.vbox2.addWidget(self.gboxps)
1327#
1328# section log file options (Windows only)
1329#
1330 self.gboxbom= QtWidgets.QGroupBox()
1331 self.gboxbom.setFlat(True)
1332 self.gboxbom.setTitle("UTF-8 encoding")
1333 self.vboxbom= QtWidgets.QVBoxLayout()
1334 self.cbUseBom= QtWidgets.QCheckBox('Use BOM (Windows only)')
1335 self.cbUseBom.setChecked(self.__usebom__)
1336 self.cbUseBom.stateChanged.connect(self.do_cbUseBom)
1337 self.vboxbom.addWidget(self.cbUseBom)
1338 self.gboxbom.setLayout(self.vboxbom)
1339 if isWINDOWS():
1340 self.vbox2.addWidget(self.gboxbom)
1342 self.vbox2.addStretch(1)
1343#
1344# add ok/cancel buttons
1345#
1346 self.gbox_buttonlist=[self.radbutPIL, self.radbutTCPIP]
1348 self.buttonBox = QtWidgets.QDialogButtonBox()
1349 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
1350 self.buttonBox.setCenterButtons(True)
1351 self.buttonBox.accepted.connect(self.do_ok)
1352 self.buttonBox.rejected.connect(self.do_cancel)
1353 self.hlayout = QtWidgets.QHBoxLayout()
1354 self.hlayout.addWidget(self.buttonBox)
1355 self.vbox0.addLayout(self.hlayout)
1357 def setCheckBoxes(self):
1358 if self.radbutPIL.isChecked():
1359 self.__mode__=0
1360 self.butTty.setEnabled(True)
1361 self.edtPort.setEnabled(False)
1362 self.edtRemoteHost.setEnabled(False)
1363 self.edtRemotePort.setEnabled(False)
1364 self.cbIdyFrame.setEnabled(True)
1365 self.edtServerport.setEnabled(False)
1366 self.comboBaud.setEnabled(True)
1367 elif self.radbutTCPIP.isChecked():
1368 self.__mode__=1
1369 self.butTty.setEnabled(False)
1370 self.edtPort.setEnabled(True)
1371 self.edtRemoteHost.setEnabled(True)
1372 self.edtRemotePort.setEnabled(True)
1373 self.cbIdyFrame.setEnabled(True)
1374 self.edtServerport.setEnabled(False)
1375 self.comboBaud.setEnabled(False)
1376 elif self.radbutServerport.isChecked():
1377 self.__mode__=2
1378 self.butTty.setEnabled(False)
1379 self.edtPort.setEnabled(False)
1380 self.edtRemoteHost.setEnabled(False)
1381 self.edtRemotePort.setEnabled(False)
1382 self.cbIdyFrame.setEnabled(False)
1383 self.edtServerport.setEnabled(True)
1384 self.comboBaud.setEnabled(False)
1386 def do_config_Interface(self):
1387 interface= cls_TtyWindow.getTtyDevice()
1388 if interface == "" :
1389 return
1390 self.__tty__= interface
1391 self.lblTty.setText(self.__tty__)
1393 def getWorkDirName(self):
1394 dialog=QtWidgets.QFileDialog()
1395 dialog.setWindowTitle("Select pyILPER working directory")
1396 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
1397 dialog.setFileMode(QtWidgets.QFileDialog.Directory)
1398 dialog.setOption(QtWidgets.QFileDialog.ShowDirsOnly,True)
1399 if dialog.exec():
1400 return dialog.selectedFiles()
1402 def do_cbIdyFrame(self):
1403 self.__idyframe__= self.cbIdyFrame.isChecked()
1405 def do_cbUseBom(self):
1406 self.__usebom__= self.cbUseBom.isChecked()
1408 def do_config_Workdir(self):
1409 flist=self.getWorkDirName()
1410 if flist is None:
1411 return
1412 self.__workdir__= flist[0]
1413 self.lblwdir.setText(self.__workdir__)
1415 def getLifutilsDirName(self):
1416 dialog=QtWidgets.QFileDialog()
1417 dialog.setWindowTitle("Select Path to lifversion executable")
1418 dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
1419 dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
1420 if dialog.exec():
1421 return dialog.selectedFiles()
1423 def do_config_lifutilspath_clear(self):
1424 self.__lifutilspath__= ""
1425 self.lbllifpath.setText(self.__lifutilspath__)
1427 def do_config_lifutilspath_change(self):
1428 flist=self.getLifutilsDirName()
1429 if flist is None:
1430 return
1431 self.__lifutilspath__= flist[0]
1432 self.lbllifpath.setText(self.__lifutilspath__)
1433#
1434# check if configuration parameter was changed
1435#
1436 def check_param(self,param,value):
1437 oldvalue= PILCONFIG.get(self.__name__,param,value)
1438 return (value!= oldvalue)
1440#
1441# OK button pressed
1442#
1443 def do_ok(self):
1444#
1445# check if we need to restart the pyILPER communication
1446#
1447 self.__needs_reconnect__= False
1448 self.__needs_reconnect__ |= self.check_param("mode",self.__mode__)
1449 self.__needs_reconnect__ |= self.check_param("tty", self.lblTty.text())
1450 self.__needs_reconnect__ |= self.check_param("ttyspeed", BAUDRATES[self.comboBaud.currentIndex()][1])
1451 self.__needs_reconnect__ |= self.check_param("idyframe",self.__idyframe__)
1452 self.__needs_reconnect__ |= self.check_param("port", int(self.edtPort.text()))
1453 self.__needs_reconnect__ |= self.check_param("remotehost", self.edtRemoteHost.text())
1454 self.__needs_reconnect__ |= self.check_param("serverport", int(self.edtServerport.text()))
1455 self.__needs_reconnect__ |= self.check_param("workdir", self.lblwdir.text())
1456#
1457# we need to reconnect, so get confirmation
1458#
1459 if self.__needs_reconnect__ and PILCONFIG.get("pyilper","show_msg_commparams_changed",True):
1460 msgbox= QtWidgets.QMessageBox()
1461 msgbox.setText("The changes of communication parameters or the working directory require a disconnect and reconnect of the pyILPER communication. Continue?")
1462 msgbox.setIcon(QtWidgets.QMessageBox.Warning)
1463 msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
1464 msgbox.setDefaultButton(QtWidgets.QMessageBox.Cancel)
1465 cb=QtWidgets.QCheckBox("Do not show this message again")
1466 msgbox.setCheckBox(cb)
1467 msgbox.setWindowTitle("Warning")
1468 reply=msgbox.exec()
1469 PILCONFIG.put("pyilper","show_msg_commparams_changed",cb.checkState()!=QtCore.Qt.Checked)
1470#
1471# not confirmed, cancel everything
1472#
1473 if reply == QtWidgets.QMessageBox.Cancel:
1474 super().reject
1475#
1476# store parameters
1477#
1478 PILCONFIG.put(self.__name__,"mode",self.__mode__)
1479 PILCONFIG.put(self.__name__,"tty", self.lblTty.text())
1480 PILCONFIG.put(self.__name__,"ttyspeed", BAUDRATES[self.comboBaud.currentIndex()][1])
1481 PILCONFIG.put(self.__name__,"idyframe",self.__idyframe__)
1482 PILCONFIG.put(self.__name__,"port", int(self.edtPort.text()))
1483 PILCONFIG.put(self.__name__,"remotehost", self.edtRemoteHost.text())
1484 PILCONFIG.put(self.__name__,"remoteport", int(self.edtRemotePort.text()))
1485 PILCONFIG.put(self.__name__,"serverport", int(self.edtServerport.text()))
1486 PILCONFIG.put(self.__name__,"workdir", self.lblwdir.text())
1487#
1488# these parameters require a reconfiguration
1489#
1490 self.__needs_reconfigure__= False
1491 self.__needs_reconfigure__ |= self.check_param("terminalcharsize",self.spinTermCharsize.value())
1492 self.__needs_reconfigure__ |= self.check_param("directorycharsize",self.spinDirCharsize.value())
1493 self.__needs_reconfigure__ |= self.check_param("hp82162a_pixelsize",self.spinHP82162APixelsize.value())
1494 self.__needs_reconfigure__ |= self.check_param("hp2225b_screenwidth",self.spinHP2225Bscreenwidth.value())
1495#
1496# These parameters need a restart, display message
1497#
1498 self.__needs_restart__= False
1499 self.__needs_restart__ |= self.check_param("papersize",self.combops.currentIndex())
1500 self.__needs_restart__ |= self.check_param("lifutilspath",self.lbllifpath.text())
1501#
1502# some parameters need a restart of the application, inform user
1503#
1504 if self.__needs_restart__ and PILCONFIG.get("pyilper","show_msg_restartparams_changed",True):
1505 msgbox= QtWidgets.QMessageBox()
1506 msgbox.setText("Changes of the papersize, the scrollup buffer size or the lifutils path require a restart of pyILPER for take the changes to effect.")
1507 msgbox.setIcon(QtWidgets.QMessageBox.Information)
1508 msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok)
1509 msgbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
1510 cb=QtWidgets.QCheckBox("Do not show this message again")
1511 msgbox.setCheckBox(cb)
1512 msgbox.setWindowTitle("Information")
1513 reply=msgbox.exec()
1514 PILCONFIG.put("pyilper","show_msg_restartparams_changed",cb.checkState()!=QtCore.Qt.Checked)
1515#
1516# store parameters
1517#
1518 PILCONFIG.put(self.__name__,"terminalcharsize",self.spinTermCharsize.value())
1519 PILCONFIG.put(self.__name__,"directorycharsize",self.spinDirCharsize.value())
1520 PILCONFIG.put(self.__name__,"papersize",self.combops.currentIndex())
1521 PILCONFIG.put(self.__name__,"lifutilspath",self.lbllifpath.text())
1522 PILCONFIG.put(self.__name__,"hp82162a_pixelsize",self.spinHP82162APixelsize.value())
1523 PILCONFIG.put(self.__name__,"hp2225b_screenwidth",self.spinHP2225Bscreenwidth.value())
1524 PILCONFIG.put(self.__name__,"usebom",self.__usebom__)
1525 super().accept()
1527 def do_cancel(self):
1528 super().reject()
1530 def get_status(self):
1531 return (self.__needs_reconnect__, self.__needs_reconfigure__)
1534 @staticmethod
1535 def getPilConfig(parent):
1536 dialog= cls_PilConfigWindow(parent)
1537 result= dialog.exec()
1538 (reconnect,reconfigure)= dialog.get_status()
1539 if result== QtWidgets.QDialog.Accepted:
1540 return True, reconnect, reconfigure
1541 else:
1542 return False, False, False
1543#
1544# HP-IL virtual device configuration class ----------------------------------
1545#
1547class cls_DeviceConfigWindow(QtWidgets.QDialog):
1549 def __init__(self,parent):
1550 super().__init__()
1551 self.parent=parent
1552 self.setWindowTitle('Virtual HP-IL device config')
1553 self.vlayout = QtWidgets.QVBoxLayout()
1554#
1555# item list and up/down buttons
1556#
1557 self.hlayout = QtWidgets.QHBoxLayout()
1558 self.devList = QtWidgets.QListWidget()
1559 self.hlayout.addWidget(self.devList)
1560 self.vlayout2= QtWidgets.QVBoxLayout()
1561 self.buttonUp= QtWidgets.QPushButton("^")
1562 self.vlayout2.addWidget(self.buttonUp)
1563 self.buttonDown= QtWidgets.QPushButton("v")
1564 self.vlayout2.addWidget(self.buttonDown)
1565 self.buttonAdd= QtWidgets.QPushButton("Add")
1566 self.vlayout2.addWidget(self.buttonAdd)
1567 self.buttonRemove= QtWidgets.QPushButton("Remove")
1568 self.vlayout2.addWidget(self.buttonRemove)
1569 self.hlayout.addLayout(self.vlayout2)
1570 self.vlayout.addLayout(self.hlayout)
1571 self.buttonUp.clicked.connect(self.do_itemUp)
1572 self.buttonDown.clicked.connect(self.do_itemDown)
1573 self.buttonAdd.clicked.connect(self.do_itemAdd)
1574 self.buttonRemove.clicked.connect(self.do_itemRemove)
1575#
1576# ok/cancel button box
1577#
1578 self.buttonBox = QtWidgets.QDialogButtonBox()
1579 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
1580 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
1581 self.buttonBox.setCenterButtons(True)
1582 self.buttonBox.accepted.connect(self.do_ok)
1583 self.buttonBox.rejected.connect(self.do_cancel)
1584 self.vlayout.addWidget(self.buttonBox)
1585 self.setLayout(self.vlayout)
1586#
1587# fill list widget
1588#
1589 self.tabList=PILCONFIG.get(self.parent.name,"tabconfig")
1590 for tab in self.tabList:
1591 typ= tab[0]
1592 name=tab[1]
1593 self.devList.addItem(name+" ("+ TAB_NAMES[typ]+ ")")
1594 self.devList.setCurrentRow(0)
1596 def do_ok(self):
1597 PILCONFIG.put(self.parent.name,"tabconfig",self.tabList)
1598 super().accept()
1600 def do_cancel(self):
1601 super().reject()
1603 def do_itemUp(self):
1604 num_rows=self.devList.count()
1605 if num_rows == 0:
1606 return
1607 row=self.devList.currentRow()
1608 if row == 0:
1609 return
1610 item= self.devList.takeItem(row)
1611 self.devList.insertItem(row-1,item)
1612 item= None
1613 self.devList.setCurrentRow(row-1)
1614 temp=self.tabList[row]
1615 self.tabList[row]= self.tabList[row-1]
1616 self.tabList[row-1]=temp
1617 return
1620 def do_itemDown(self):
1621 num_rows=self.devList.count()
1622 if num_rows == 0:
1623 return
1624 row=self.devList.currentRow()
1625 if row+1 == num_rows:
1626 return
1627 item= self.devList.takeItem(row)
1628 self.devList.insertItem(row+1,item)
1629 item= None
1630 self.devList.setCurrentRow(row+1)
1631 temp=self.tabList[row]
1632 self.tabList[row]= self.tabList[row+1]
1633 self.tabList[row+1]=temp
1634 return
1636 def do_itemRemove(self):
1637 row=self.devList.currentRow()
1638 del(self.tabList[row])
1639 item=self.devList.takeItem(row)
1640 item= None
1642 def do_itemAdd(self):
1643 retval=cls_AddDeviceWindow.getAddDevice(self)
1644 if retval== "":
1645 return
1646 typ=retval[0]
1647 name=retval[1]
1648 self.devList.addItem(name+" ("+ TAB_NAMES[typ]+ ")")
1649 self.tabList.append([typ,name])
1651 @staticmethod
1652 def getDeviceConfig(parent):
1653 dialog= cls_DeviceConfigWindow(parent)
1654 dialog.resize(350,100)
1655 result= dialog.exec()
1656 if result== QtWidgets.QDialog.Accepted:
1657 return True
1658 else:
1659 return False
1660#
1661# validator checks for valid device name -------------------------------------
1662#
1663class cls_Device_validator(QtGui.QValidator):
1665 def validate(self,string,pos):
1666 self.regexp = QtCore.QRegularExpression('[A-Za-z][A-Za-z0-9]*')
1667 self.validator = QtGui.QRegularExpressionValidator(self.regexp)
1668 result=self.validator.validate(string,pos)
1669 return result[0], result[1], result[2]
1670#
1671# Add virtual device dialog class --------------------------------------------
1672#
1673class cls_AddDeviceWindow(QtWidgets.QDialog):
1675 def __init__(self,parent):
1676 super().__init__()
1677 self.typ= None
1678 self.name=None
1679 self.tabList=parent.tabList
1680 self.setWindowTitle('New Virtual HP-IL device')
1681#
1682# Device name, allow only letter followed by letters or digits
1683#
1684 self.vlayout = QtWidgets.QVBoxLayout()
1685 self.leditName= QtWidgets.QLineEdit(self)
1686 self.leditName.setText("")
1687 self.leditName.setMaxLength(10)
1688 self.leditName.textChanged.connect(self.do_checkdup)
1689 self.validator=cls_Device_validator()
1690 self.leditName.setValidator(self.validator)
1691 self.vlayout.addWidget(self.leditName)
1692#
1693# Combobox, omit the scope!
1694#
1695 self.comboTyp=QtWidgets.QComboBox()
1696 for i in range(1,len(TAB_NAMES)):
1697 self.comboTyp.addItem(TAB_NAMES[i])
1698 self.vlayout.addWidget(self.comboTyp)
1700 self.buttonBox = QtWidgets.QDialogButtonBox()
1701 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
1702 self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
1703 self.buttonBox.setCenterButtons(True)
1704 self.buttonBox.accepted.connect(self.do_ok)
1705 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
1706 self.buttonBox.rejected.connect(self.do_cancel)
1707 self.vlayout.addWidget(self.buttonBox)
1708 self.setLayout(self.vlayout)
1709#
1710# validate if name is not empty and unique
1711#
1712 def do_checkdup(self):
1713 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
1714 tst=self.leditName.text()
1715 if tst=="":
1716 return
1717 for tab in self.tabList:
1718 if tst== tab[1]:
1719 return
1720 self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
1721#
1722# return results
1723#
1724 def getResult(self):
1725 return([self.typ,self.name])
1727#
1728# only enabled if name is not empty and unique
1729#
1730 def do_ok(self):
1731 self.name= self.leditName.text()
1732 self.typ= self.comboTyp.currentIndex()+1
1733 super().accept()
1735 def do_cancel(self):
1736 super().reject()
1738 @staticmethod
1739 def getAddDevice(parent):
1740 dialog= cls_AddDeviceWindow(parent)
1741 dialog.resize(250,100)
1742 result= dialog.exec()
1743 if result== QtWidgets.QDialog.Accepted:
1744 return dialog.getResult()
1745 else:
1746 return ""
1747#
1748# HP-IL device Status Dialog class -------------------------------------------
1749#
1750class cls_DevStatusWindow(QtWidgets.QDialog):
1752 def __init__(self,parent):
1753 super().__init__()
1754 self.parent=parent
1755 self.setWindowTitle('Virtual HP-IL device status')
1756 self.vlayout = QtWidgets.QVBoxLayout()
1757 self.setLayout(self.vlayout)
1758 self.__timer__=QtCore.QTimer()
1759 self.__timer__.timeout.connect(self.do_refresh)
1760 self.rows=len(parent.pilwidgets)-1
1761 self.cols=5
1762 self.__table__ = QtWidgets.QTableWidget(self.rows,self.cols) # Table view for dir
1763 self.__table__.setSortingEnabled(False) # no sorting
1764#
1765# switch off grid, no focus, no row selection
1766#
1767 self.__table__.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
1768 self.__table__.setFocusPolicy(QtCore.Qt.NoFocus)
1769 self.__table__.setShowGrid(False)
1770 h1= QtWidgets.QTableWidgetItem()
1771 h1.setText("Device")
1772 self.__table__.setHorizontalHeaderItem(0,h1)
1773 h2= QtWidgets.QTableWidgetItem()
1774 h2.setText("DID")
1775 self.__table__.setHorizontalHeaderItem(1,h2)
1776 h3= QtWidgets.QTableWidgetItem()
1777 h3.setText("AID")
1778 self.__table__.setHorizontalHeaderItem(2,h3)
1779 h4= QtWidgets.QTableWidgetItem()
1780 h4.setText("Addr.")
1781 self.__table__.setHorizontalHeaderItem(3,h4)
1782 h5= QtWidgets.QTableWidgetItem()
1783 h5.setText("HP-IL Status")
1784 self.__table__.setHorizontalHeaderItem(4,h5)
1785 self.__table__.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
1786 self.__table__.resizeColumnsToContents()
1787#
1788# no vertical header
1789#
1790 self.__table__.verticalHeader().setVisible(False)
1791 self.__table__.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
1792 self.__table__.verticalHeader().setDefaultSectionSize(16)
1793#
1794# populate
1795#
1796 self.__items__= { }
1797 for row in range(self.rows):
1798 for col in range(self.cols):
1799 self.__items__[row,col]= QtWidgets.QTableWidgetItem()
1800 if col > 1:
1801 self.__items__[row,col].setTextAlignment(QtCore.Qt.AlignHCenter)
1802 self.__items__[row,col].setText(" ")
1803 self.__table__.setItem(row,col,self.__items__[row,col])
1805 self.__table__.resizeRowsToContents()
1806 self.vlayout.addWidget(self.__table__)
1807 self.button = QtWidgets.QPushButton('OK')
1808 self.button.setFixedWidth(60)
1809 self.button.clicked.connect(self.do_exit)
1810 self.hlayout = QtWidgets.QHBoxLayout()
1811 self.hlayout.addWidget(self.button)
1812 self.vlayout.addLayout(self.hlayout)
1813 self.resize(600,self.sizeHint().height())
1814 self.do_refresh()
1816 def hideEvent(self,event):
1817 self.__timer__.stop()
1819 def showEvent(self,event):
1820 self.__timer__.start(500)
1822 def do_exit(self):
1823 super().accept()
1825 def do_refresh(self):
1826 devices=self.parent.commthread.getDevices()
1827 if not devices:
1828 return
1829 i=1
1830 for row in range(self.rows):
1831 pildevice= devices[i][0]
1832 name=devices[i][1]
1833 i+=1
1834 self.__items__[row,0].setText(name)
1835 for col in range (1,self.cols):
1836 self.__items__[row,col].setText("")
1837 if pildevice is None:
1838 continue
1839 (active, did, aid, addr, addr2nd, hpilstatus)= pildevice.getstatus()
1840 if not active:
1841 continue
1842 devaddr = ''
1843 if (addr & 0x80) != 0:
1844 devaddr = str(addr & 0x1F)
1845 if (addr2nd & 0x80) != 0:
1846 devextaddr = ".{0:02d}".format((addr2nd & 0x1F) + 1)
1847 devaddr += devextaddr
1848 self.__items__[row,1].setText(did)
1849 self.__items__[row,2].setText("{0:x}".format(aid))
1850 self.__items__[row,3].setText(devaddr)
1851 self.__items__[row,4].setText("{0:s}".format(hpilstatus))
1852#
1853# Main Window user interface class -------------------------------------------
1854#
1855class cls_ui(QtWidgets.QMainWindow):
1857 def __init__(self,parent,version,instance):
1858 super().__init__()
1859 if instance == "":
1860 self.setWindowTitle("pyILPER "+version)
1861 else:
1862 self.setWindowTitle("pyILPER "+version+" Instance: "+instance)
1863#
1864# signals
1865#
1866 self.sig_crash= parent.sig_crash
1867 self.sig_quit= parent.sig_quit
1868 self.sig_show_message= parent.sig_show_message
1870#
1871# Menu
1872#
1873 self.menubar = self.menuBar()
1874 self.menubar.setNativeMenuBar(False)
1875 self.menuFile= self.menubar.addMenu('File')
1876 self.menuUtil= self.menubar.addMenu('Utilities')
1877 self.menuHelp= self.menubar.addMenu('Help')
1879 self.actionConfig=self.menuFile.addAction("pyILPER configuration")
1880 self.actionDevConfig=self.menuFile.addAction("Virtual HP-IL device configuration")
1881 self.actionPenConfig=self.menuFile.addAction("Plotter pen configuration")
1882 self.actionShortcutConfig=self.menuFile.addAction("Terminal keyboard shortcut configuration")
1883 self.actionReconnect=self.menuFile.addAction("Reconnect")
1884 self.actionExit=self.menuFile.addAction("Quit")
1886 self.actionInit=self.menuUtil.addAction("Initialize LIF image file")
1887 self.actionFix=self.menuUtil.addAction("Fix Header of LIF image file")
1888 self.actionDevStatus=self.menuUtil.addAction("Virtual HP-IL device status")
1889 self.actionCopyPilimage=self.menuUtil.addAction("Copy PILIMAGE.DAT to workdir")
1890 self.actionInstallCheck=self.menuUtil.addAction("Check LIFUTILS installation")
1891 self.actionInit.setEnabled(False)
1892 self.actionFix.setEnabled(False)
1894 self.actionAbout=self.menuHelp.addAction("About")
1895 self.actionHelp=self.menuHelp.addAction("Manual")
1896#
1897 self.tabs=cls_Tabs()
1898 self.setCentralWidget(self.tabs)
1899#
1900# Status bar
1901#
1902 self.statusbar=self.statusBar()
1903#
1904# Size policy
1905#
1906 self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
1908#
1909# queued emit of the signal to update the message text
1910#
1911 def emit_message(self,s):
1912 self.sig_show_message.emit(s)
1913#
1914# queued emit of the signal to indicate crash
1915#
1916 def emit_crash(self):
1917 self.sig_crash.emit()
1918#
1919# catch close event
1920#
1921 def closeEvent(self,event):
1922 event.accept()
1923 self.sig_quit.emit()
1924#
1925# enable controls that require lifutils
1926#
1927 def enableLIFControls(self):
1928 self.actionInit.setEnabled(True)
1929 self.actionFix.setEnabled(True)