UPDATE_TIMER=25                 # Poll timer (ms) for terminal output queue PDF_ORIENTATION_PORTRAIT=0 PILCONFIG=  cls_pilconfig() def charconv(s, charset):
   return convert_to_unicode[charset][ord(s)] CHARSET_HP41=1 CHARSET_ROMAN8=3 class cls_pildevbase:

   def __init__(self):

      self.__aid__ = 0x00          # accessory id 
      self.__defaddr__ = 0         # default address alter AAU
      self.__did__ = ""            # device id
      self.__status__ = 0          # HP-IL status
      self.__addr__ = 0            # HP-IL primary address (by TAD,LAD)
                                   # bits 0-5=AAD or AEP, bit 7=1 means
                                   # auto address taken
      self.__addr2nd__ = 0         # HP-IL secondary address (by SAD)
                                   # bits 0-5=AES, bit 7 means auto addr taken
      self.__ilstate__ =0          # state machine flag
                                   # bit 7, bit 6, bit5, bit 4
                                   # 0      0      0     0 idle
                                   # 0      0      1     0 addressed listener in
                                   #                       second. add. mode
                                   # 0      0      0     1 addressed talker in
                                   #                       second. add. mode
                                   # 1      0      0     0 addressed listener
                                   # 0      1      0     0 addressed talker 
                                   # bit 0 or bit 1 set    active takler
                                   # bit 1: SDA, SDI
                                   # bit 0: SST, SDI, SAI, NRD
      self.__talker_frame__=0      # frame sent as talker
      self.__ptsdi__ = 0           # output pointer for device id
      self.__status_len__=1        # length of device status in bytes
      self.__ptssi__ = 0           # output pointer for hp-il status
      self.__isactive__= False     # device active in loop
      self.__addr_framecounter__=0 # framecounter when device got an address
      self.__threadobject__=None   # reference to the thread object
      self.__status_lock__= threading.Lock()
      self.__islocked__= False
      self.__access_lock__= threading.Lock()
#
# --- public functions ---
#
#  set device active/inactive. If the device becomes active check if an AAU,
#  AAD, AEP or AES happened in the mean time. In this case reset the device
#  address
#
   def setactive(self, active):
      if not self.__isactive__ and active:
         if self.__addr_framecounter__ != self.__threadobject__.get_addr_framecounter():
            self.__addr__=0
            self.__addr2nd__=0
      self.__isactive__= active
#
#  set object reference to thread object
#
   def setThreadObject(self,obj):
      self.__threadobject__= obj
#
#  set local and update globel addr_framecounter
#
   def update_addr_framecounter(self):
      self.__addr_framecounter__= self.__threadobject__.get_framecounter()
      self.__threadobject__.update_addr_framecounter(self.__addr_framecounter__)
#
#  return device status
#
   def getstatus(self):
      self.__access_lock__.acquire()
      status="idle"
      if self.__ilstate__ & 0x03:
         status="act. talker"
      else:
         if self.__ilstate__ & 0xA0:
            status="addr. listener"
         elif self.__ilstate__ & 0x50:
            status="addr. talker"

      ret= [self.__isactive__, self.__did__, self.__aid__, self.__addr__, self.__addr2nd__, status]
      self.__access_lock__.release()
      return ret

#
#  lock device, all output is disabled
#
   def setlocked(self,locked):
      self.__access_lock__.acquire()
      self.__islocked__= locked
      self.__access_lock__.release()

#
#  Process device
#
   def process(self,frame):

#
#     if device is not active, return
#
      if not self.__isactive__:
         return(frame)
#
#     process frames
#
      if (frame & 0x400) == 0:
         frame= self.__do_doe__(frame)
      elif (frame & 0x700) == 0x400:
         frame= self.__do_cmd__(frame)
      elif (frame & 0x700) == 0x500:
         frame= self.__do_rdy__(frame)
#
#     set service request bit if data available status bit set
#
      if self.__getstatus__() & 0x40:
         if (frame & 0x700) == 0x000:  # data 00x xxxx xxxx -> 001 xxxx xxxx
            frame= frame | 0x100
         if (frame & 0x700) == 0x200:  # end  01x xxxx xxxx -> 011 xxxx xxxx
            frame= frame | 0x100
         if (frame & 0x700) == 0x600:  # idy  11x xxxx xxxx -> 111 xxxx xxxx
            frame= frame | 0x100
      return(frame)

#
#  --- private ---
#
#
#  get status byte from device thread safe
#
   def __getstatus__(self):
      self.__status_lock__.acquire()
      status= self.__status__
      self.__status_lock__.release()
      return(status)
#
# set status byte of device thread safe
#
   def __setstatus__(self,status):
      self.__status_lock__.acquire()
      self.__status__= status
      self.__status_lock__.release()
#
#  output data stub
#
   def __outdata__(self,frame):
      return frame

#
#  input data stub
#
   def __indata__(self,frame):
      return

#
#  device clear stub
#
   def __clear_device__(self):
      # reset service request bit
      s= self.__getstatus__()
      s &= ~ 0x40
      self.__setstatus__(s)
      return
#
#  stub for extended sad commands
#
   def __cmd_sad_ext__(self,frame):
      return frame

#
# stub for extended commands
#
   def __cmd_ext__(self,frame):
      return frame

#
#  manage HPIL data frames, returns the returned frame
#
   def __do_doe__(self,frame):
      talker_error= False

      if (self.__ilstate__ & 0xC0) == 0x40: # addressed talker?

         if (self.__ilstate__ & 0x03) != 0: # active talker?

#           compare last talker frame with actual frame without SRQ bit
            talker_error = ((frame & 0x6FF) != (self.__talker_frame__ &0x6FF))

#           data (SDA) status (SST) or accessory ID (SDI)
            if (not talker_error) and (self.__ilstate__ & 0x02 !=0): # talker

#              save current SRQ bit
               SrqBit= frame & 0x100 

#              status (SST) or accessory ID (SDI)
               if (self.__ilstate__ & 0x01) != 0: 

#                 0x43: active talker (multibyte status)
                  if self.__ptssi__ > 0:   # SST
                     self.__ptssi__= self.__ptssi__-1
                     if self.__ptssi__ > 0:
                        frame= (self.__getstatus__() >> (( self.__status_len__-self.__ptssi__ ) * 8)) & 0xFF
                  if self.__ptsdi__ > 0:   # SDI
                     if self.__ptsdi__ == len(self.__did__):
                        frame=0
                        self.__ptsdi__=0
                     else: # SDI
                        frame= ord(self.__did__ [self.__ptsdi__])
                        self.__ptsdi__= self.__ptsdi__+1
                  if self.__ptssi__ == 0 and self.__ptsdi__ == 0 : 
#                    EOT for SST and SDI
                     frame= 0x540
               else: # 0x42 active talker (data)
                  frame=self.__outdata__(frame) # SDA
#              a set SRQ bit doesn't matter on ready class frames
               frame |= SrqBit
            else: # 0x41 active talker (single byte status)
               frame= 0x540 # end of SAI or NRD or talker error

         if frame == 0x540: # EOT
#           check for error and set ETE frame
            if talker_error:
               frame= 0x541 # ETE
            self.__ilstate__ &= ~ 0x03 # delete active talker
         self.__talker_frame__= frame

      if (self.__ilstate__ & 0xC0)==0x80:  # listener
            self.__indata__(frame)
      return(frame)
#
# manage HP-IL command frames
#
   def __do_cmd__(self,frame):

      n= frame & 0xff
      t= n >> 5
      if t == 0:
         if n == 4:   # SDC
            if (self.__ilstate__ & 0x80) != 0: # listener
               self.__clear_device__()
         elif n == 20: # DCL
             self.__clear_device__()
      elif t == 1:  # LAD
         n= n & 31
         if n == 31: # UNL, if not talker then go to idle state
             if (self.__ilstate__ & 0xA0) != 0: # not talker ?
                self.__ilstate__ &= 0x50 # idle
         else:       # if MLA go to listen state
             if (self.__ilstate__ &0x80) ==0 and  n == (self.__addr__ & 31):
                if (self.__addr2nd__ & 0x80) == 0:
                   self.__ilstate__ = 0x80 # set addressed listener
                else:
                   self.__ilstate__ = 0x20 # set addressed listener second. add mode
      elif t == 2:  # TAD  
         n= n & 31
         if n == (self.__addr__ & 31):
            if (self.__addr2nd__ & 0x80) == 0: # if MTA go to talker state
               self.__ilstate__= 0x40 # set addressed talker
            else:
               self.__ilstate__= 0x10 # set addressed talker, second. add. mode
         else:
            if ( self.__ilstate__ & 0x50) != 0: # addressed talker?
               self.__ilstate__ &= 0xA0 # idle
      elif t == 3: # SAD
         if (self.__ilstate__ & 0x30) !=0: # addressed talker or listener 2nd addr mode?
            n = n & 31
            if n == (self.__addr2nd__ & 31):
               self.__ilstate__<<=2  # switch to addressed listener/talker
            else:
               self.__ilstate__=0 # idle
         else:
            frame= self.__cmd_sad_ext__(frame)
      elif t == 4: 
         n= n & 31
         if n == 16: # IFC
            self.__ilstate__= 0x0 # idle
         elif n == 26: # AAU
            self.update_addr_framecounter()
            self.__addr__=  self.__defaddr__
            self.__addr2nd__= 0
      else:
         frame= self.__cmd_ext__(frame)
      return(frame)
            
#
# HP-IL ready frames
#
   def __do_rdy__(self,frame):

      n= frame & 0xFF
      if n <= 127:
         if ( self.__ilstate__ & 0x40) !=0:  # SOT, addressed talker?
            if  n == 66:  #NRD
               self.__ptsdi__ = 0
               self.__ptssi__ = 0
               self.__ilstate__ = 0x41 # abort transfer, clear SDA/SDI

            elif n == 96: # SDA
               frame=self.__outdata__(frame)
               if frame != 0x560: # not sda received data
                  self.__ilstate__= 0x42 # active talker, SDA/SDI
                  self.__talker_frame__= frame # last talker frame
            elif n == 97: # SST
                # reset service request bit
                s= self.__getstatus__()
                s &= ~ 0x40
                self.__setstatus__(s)
                # update IL status and return no. of status bytes
                self.__ptssi__ = self.__status_len__
                if self.__ptssi__ > 0: # response to status request
                   frame = (self.__getstatus__() >> ((self.__status_len__-self.__ptssi__) * 8)) & 0xFF
                   self.__ilstate__= 0x43 # active talker
                   self.__talker_frame__= frame # last talker frame
            elif n == 98:  # SDI
               if self.__did__ != "":
                  frame= ord(self.__did__[0])
                  self.__ptsdi__ = 1 # other 2
                  self.__ilstate__= 0x43 # active talker, SDA/SDI, SST/SDI/SAI
                  self.__talker_frame__= frame
            elif n == 99: # SAI
                  frame= self.__aid__ & 0xFF
                  self.__ilstate__= 0x41 # active talker, SST/SDI/SAI
                  self.__talker_frame__= frame
      else:
         if n < 0x80 +31: # AAD
            if ((self.__addr__ & 0x80) == 0 and self.__addr2nd__ ==0):
               # AAD, if not already an assigned address, take it
               self.update_addr_framecounter()
               self.__addr__ = n
               frame=frame+1
         elif  (n >= 0xA0 and n < 0xA0 + 31): # AEP
            # AEP, if not already an assigned address and got an AES frame,
            if ((self.__addr__ & 0x80) == 0 and (self.__addr2nd__ & 0x80) != 0):
               # take it
               self.update_addr_framecounter()
               self.__addr__= n & 0x9F
         elif (n >= 0xC0 and n < 0xC0 +31): # AES
            if (self.__addr__ & 0x80) == 0:
               self.update_addr_framecounter()
               self.__addr2nd__= n & 0x9F
               frame=frame + 1
      return (frame) class cls_tabgeneric(QtWidgets.QWidget):

   def __init__(self,parent,name):
      super().__init__()
      self.name= name
      self.active= PILCONFIG.get(self.name,"active",False)
      self.parent=parent
      self.font_name=FONT
      self.font_size=0
      self.font_width=0
      self.font_height=0
      self.width= 0
      self.height= 0
      self.guiobject= None
      self.cbLogging= None
      self.logging= False
      self.pildevice=None
      self.widget_index=1
      self.cBut= None
#
#     Build basic layout
#
      self.vbox= QtWidgets.QVBoxLayout()
      self.setLayout(self.vbox)
#
#     hbox1: container of GUI Object
#
      self.hbox1=QtWidgets.QHBoxLayout()
      self.hbox1.setContentsMargins(10,10,10,10)
#
#     hbox2: container of control widgets
# 
      self.hbox2= QtWidgets.QHBoxLayout()
      self.hbox2.setContentsMargins(10,3,10,3)

      self.cbActive= QtWidgets.QCheckBox('Device enabled')
      self.cbActive.setChecked(self.active)
      self.cbActive.setEnabled(False)
      self.cbActive.stateChanged.connect(self.do_cbActive)

      self.hbox2.addWidget(self.cbActive)
      self.hbox2.addStretch(1)

      self.vbox.addLayout(self.hbox1)
      self.vbox.addLayout(self.hbox2)
#
#  add the gui object to hbox1
#
   def add_guiobject(self,guiobject):
      self.guiobject= guiobject
      self.hbox1.addWidget(self.guiobject)
#
#  insert a control widget
#
   def add_controlwidget(self,widget):
      self.hbox2.insertWidget(self.widget_index,widget)
      self.widget_index+=1
#
#  add a config cascading menu: always at the end of the control box
#
   def add_configwidget(self):
      self.cBut= cls_config_tool_button(self.name,"Configuration")
      self.hbox2.addWidget(self.cBut)
#
#  insert the cbLogging widget in hbox2 after the cbActive Widget
#
   def add_logging(self):
      self.cbLogging= LogCheckboxWidget(self.name)
      self.hbox2.insertWidget(1,self.cbLogging)
      self.logging= PILCONFIG.get(self.name,"logging",False)
      self.cbLogging.setChecked(self.logging)
      self.cbLogging.setEnabled(False)
      self.cbLogging.stateChanged.connect(self.do_cbLogging)
      self.widget_index+=1
#
#  if config button present, then add log buffer option
#
      if self.cBut is not None:
         self.cBut.add_option("Log buffering","buffer_log",T_BOOLEAN,[True,False])
#
#  insert a status widget
#
   def add_statuswidget(self,statuswidget):
      self.hbox2.insertWidget(self.widget_index,statuswidget)
      self.hbox2.insertStretch(self.widget_index,1)
#
#  tab config changed, handle buffer_log change
#
   def do_tabconfig_changed(self):
     param= self.cBut.get_changed_option_name()
     if param== "buffer_log":
         self.cbLogging.set_buffering(PILCONFIG.get(self.name,"buffer_log"))
#
#  reconfigure, nothing to do here
#
   def reconfigure(self):
      return
# 
#  disable tab
#
   def disable(self):
      if self.cbLogging is not None:
         if self.logging:
            self.cbLogging.logClose()
         self.cbLogging.setEnabled(False)
      self.cbActive.setEnabled(False)
#
#  enable tab
#
   def enable(self):
      self.cbActive.setEnabled(True)
      if self.cbLogging is not None:
         if self.logging:
            self.cbLogging.logOpen()
         self.cbLogging.setEnabled(True)
#
#  toggle active/inactive
#
   def toggle_active(self):
      return
#
#  action: toogle active checkbox
#
   def do_cbActive(self):
      self.active= self.cbActive.isChecked()
      PILCONFIG.put(self.name,"active",self.active)
      self.pildevice.setactive(self.active)
      self.toggle_active()
#
#  action: toggle log checkbox
#
   def do_cbLogging(self):
      self.cbLogging.setEnabled(False)
      self.logging= self.cbLogging.isChecked()
      self.pildevice.setlocked(True)
      if self.logging:
         self.cbLogging.logOpen()
      else:
         self.cbLogging.logClose()
      PILCONFIG.put(self.name,"logging",self.logging)
      self.pildevice.setlocked(False)
      self.cbLogging.setEnabled(True) class LogCheckboxWidget(QtWidgets.QCheckBox):
   def __init__(self,name):
      super().__init__("Log "+name)
      self.name=name
      self.filename=self.name+".log"
      self.log= None
      self.buffer_log=PILCONFIG.get(self.name,"buffer_log",True)
#
#   configure log buffering
#
   def set_buffering(self,buffer_log):
      self.buffer_log= buffer_log
#
#   open log file, output BOM only on Windows at the beginning of the file
#
   def logOpen(self):
      try:
         if isWINDOWS() and PILCONFIG.get("pyilper","usebom"):
            self.log=open(self.filename,"a",encoding="UTF-8-SIG")
         else:
            self.log=open(self.filename,"a",encoding="UTF-8")
         self.log.write("\nBegin log "+self.filename+" at ")
         self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
         self.log.write("\n")
      except OSError as e:
         reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot open log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)

   def logClose(self):
      try:
         self.log.write("\nEnd log "+self.filename+" at ")
         self.log.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
         self.log.write("\n")
         self.log.close()
         self.log= None
      except OSError as e:
         reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot close log file: "+ e.strerror,QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)

   def logWrite(self,line):
      if self.log is None:
         return
      try:
         self.log.write(line)
      except OSError as e:
         reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot write to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
         try:
            self.log.close()
         except OSError:
            pass
         self.log = None

   def logFlush(self):
      if self.log is None:
         return
      if self.buffer_log:
         return
      try:
         self.log.flush()
      except OSError as e:
         reply=QtWidgets.QMessageBox.critical(self,'Error',"Cannot flush to log file: "+ e.strerror+". Logging disabled",QtWidgets.QMessageBox.Ok,QtWidgets.QMessageBox.Ok)
         try:
            self.log.close()
         except OSError:
            pass
         self.log = None T_INTEGER=3 O_DEFAULT= -1 class cls_pdfprinter(QtCore.QObject):

   def __init__(self,papersize, orientation, outputfilename, title, pagenumbering, columns):
      super().__init__()
      self.papersize= papersize
      self.orientation= orientation
      self.outputfilename= outputfilename
      self.title= title
      self.pagenumbering= pagenumbering
      self.columns= columns
#
#     initialize printer and printer scene
#
      self.printer = QtPrintSupport.QPrinter (QtPrintSupport.QPrinter.HighResolution)
      if self.orientation== PDF_ORIENTATION_PORTRAIT:
         self.printer.setOrientation(QtPrintSupport.QPrinter.Portrait)
      else:
         self.printer.setOrientation(QtPrintSupport.QPrinter.Landscape)
      self.printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat)
      self.pdfscene=QtWidgets.QGraphicsScene()
#
#     page set up, we use 1/10 mm as scene units
#
      if self.papersize== PDF_FORMAT_A4:
         self.printer.setPageSize(QtPrintSupport.QPrinter.A4)
         self.scene_w= 2100
         self.scene_h=2970
         self.pdfscene.setSceneRect(0,0,self.scene_w,self.scene_h)
      else:
         self.printer.setPageSize(QtPrintSupport.QPrinter.Letter)
         self.scene_w= 2160
         self.scene_h= 2790
         self.pdfscene.setSceneRect(0,0,self.scene_w,self.scene_h)

      self.pdfpainter = QtGui.QPainter()
      self.font=QtGui.QFont()
      self.font.setPointSize(20)
#
#     set output filename
#
      self.printer.setOutputFileName(self.outputfilename)
#
#     variables
#
      self.pdfitems=[]
      self.anzitems=0
      self.row=0
      self.column=0
      self.topy=0
      self.x=0
      self.y=0
      self.max_y= self.scene_h-PDF_MARGINS
      self.columnshift=(self.scene_w- 2* PDF_MARGINS)/ self.columns
#
#     start printing
#
   def begin(self):
      self.pdfpainter.begin(self.printer)
      self.titleitem=QtWidgets.QGraphicsSimpleTextItem(self.title)
      self.titleitem.setFont(self.font)
      self.pdfscene.addItem(self.titleitem)
      self.titleitem.setPos(PDF_MARGINS,PDF_MARGINS)
      self.pageno=1
      if self.pagenumbering:
         self.pagenumberitem=QtWidgets.QGraphicsSimpleTextItem("Page "+str(self.pageno))
         self.pagenumberitem.setFont(self.font)
         self.pdfscene.addItem(self.pagenumberitem)
         self.pagenumberitem.setPos(self.scene_w-self.pagenumberitem.boundingRect().width(),PDF_MARGINS)
#
#    init variables
#
      self.pdfitems=[]
      self.anzitems=0
      self.row=0
      self.column=0
      self.top_y=PDF_MARGINS+self.titleitem.boundingRect().height()+40
      self.x= PDF_MARGINS
      self.y= self.top_y

   def print_item(self,item):
#
#     no more rows on page
#
      if self.y > self.max_y:
#
#        no more columns, issue page break
#
         if self.column == self.columns-1:
            self.pdfscene.render(self.pdfpainter)
            self.printer.newPage()
            self.x=PDF_MARGINS
            self.y= self.top_y

            for l in reversed(range(self.anzitems)):
               self.pdfscene.removeItem(self.pdfitems[l])
               del self.pdfitems[-1]
            self.pageno+=1
            if self.pagenumbering:
               self.pagenumberitem.setText("Page "+str(self.pageno))
            self.anzitems=0
            self.column=0
            self.row=0
#
#        next column
#
         else:
            self.x+= self.columnshift
            self.y= self.top_y
            self.column+=1
            self.row=0
#
#     add item to scene
#
      self.row+=1
      self.pdfscene.addItem(item)
      self.y+= item.boundingRect().height()
      item.setPos(self.x,self.y)
      self.anzitems+=1
      self.pdfitems.append(item)
#
#  output remaining data and terminate printing
#
   def end(self):
      if self.anzitems > 0:
         self.pdfscene.render(self.pdfpainter)
         for l in reversed(range(self.anzitems)):
            self.pdfscene.removeItem(self.pdfitems[l])
            del self.pdfitems[-1]
#
#     clean up
#
      self.pdfscene.removeItem(self.titleitem)
      if self.pagenumbering:
         self.pdfscene.removeItem(self.pagenumberitem)
      self.pdfpainter.end()
 

#     file name input dialogue for pdf print file
#
   @staticmethod
   def get_pdfFilename():
      dialog=QtWidgets.QFileDialog()
      dialog.setWindowTitle("Enter PDF file name")
      dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
      dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
      dialog.setDefaultSuffix("pdf")
      dialog.setNameFilters( ["PDF (*.pdf )", "All Files (*)"] )
      dialog.setOptions(QtWidgets.QFileDialog.DontUseNativeDialog)
      if dialog.exec():
         return dialog.selectedFiles() HP82162A_LINEBUFFERSIZE=2000
import copy
import queue
import threading
import re
from PyQt5 import QtCore, QtWidgets
from .pilcore import UPDATE_TIMER, PDF_ORIENTATION_PORTRAIT
from .pilconfig import PILCONFIG
from .pilcharconv import charconv, CHARSET_HP41, CHARSET_ROMAN8
from .pildevbase import cls_pildevbase
from .pilwidgets import cls_tabgeneric, LogCheckboxWidget, T_INTEGER, O_DEFAULT
from .pilpdf import cls_pdfprinter
from .pilcore import HP82162A_LINEBUFFERSIZE
   def reconfigure(self):
      tmp=PILCONFIG.get_dual(self.name,"hp82162a_pixelsize")
#
#     re/configure the printview widget
#
      if tmp != self.pixelsize:
         self.pixelsize=tmp
         self.w=PRINTER_WIDTH_CHARS*PRINTER_CHARACTER_WIDTH_PIXELS*self.pixelsize
         self.h=PRINTER_CHARACTER_HEIGHT_PIXELS*self.pixelsize
#
#        set fixed width
#
         self.setFixedWidth(self.w+2*self.pixelsize)
#
#        initialize scene if not existing
#
         if  self.printscene is None:
            self.printscene= cls_hp82162a_scene(self,self.pixelsize)
            self.setScene(self.printscene)
            self.reset()
#
#        otherwise reconfigure scene and its content
#
         else:
            self.printscene.reconfigure(self.pixelsize)
            for i in range(0,self.linebuffersize):
               if self.lb[i] is not None:
                  self.lb[i].reconfigure(self.pixelsize)
            self.do_resize()
#
#       now shrink all parent windows to minimum size
#
#        w=self.parentWidget()
#        while w is not None:
#           w.adjustSize()
#           w=w.parentWidget()
      return
#
#      reset output window
#
   def reset(self):
      for i in range(0,self.linebuffersize):
         if self.lb[i] is not None:
            self.lb[i]= None
      self.lb_current= -1
      self.lb_anz=0
      self.lb_position=0
      self.printscene.reset()
#
#  overwrite standard events
#
#  resize event, adjust the scene size, reposition everything and redraw
#
   def resizeEvent(self,event):
      self.do_resize()

   def do_resize(self):
      h=self.height()
      self.rows=h // (PRINTER_CHARACTER_HEIGHT_PIXELS*self.pixelsize) 
      self.printscene.set_scenesize(self.rows)
      scroll_max=self.lb_current- self.rows+1
      if scroll_max < 0:
         scroll_max=0
      self.parent.scrollbar.setMaximum(scroll_max)
      self.parent.scrollbar.setPageStep(self.rows)
      self.printscene.update_scene()
      return
#
#  Mouse wheel event
#
   def wheelEvent(self,event):
       numDegrees= event.angleDelta()/8
       delta=0
       if numDegrees.y() is not None:
          if numDegrees.y() < 0:
             delta=1 
          if numDegrees.y() > 0:
             delta=-1
       event.accept()
       if self.lb_current < self.rows:
          return
       if self.lb_position+delta <0 or self.lb_position+delta+self.rows-1 > self.lb_current:
          return
       self.lb_position+=delta
       self.parent.scrollbar.setValue(self.lb_position)
       self.printscene.update_scene()
       return
#
#  external methods
#
   def add_line(self,line,col_idx):
#
#     add line bitmap to line bitmap buffer
#
      if self.lb_anz < self.linebuffersize:
         self.lb_anz+=1
         self.lb_current+=1
         self.lb[self.lb_current]=cls_hp82162a_line(line,col_idx,self.pixelsize,)
      else:
         self.lb= self.lb[1:] + self.lb[:1]
         self.lb[self.lb_current]=cls_hp82162a_line(line,col_idx,self.pixelsize,)
      self.lb_position=self.lb_current- self.rows+1
      if self.lb_position < 0:
         self.lb_position=0
      self.parent.scrollbar.setMaximum(self.lb_position)
      self.parent.scrollbar.setValue(self.lb_position)

      self.printscene.update_scene()
#
#     scroll bar action
#
   def do_scroll(self,value):
      self.lb_position=value
      self.printscene.update_scene()
#
#     PDF output of lb buffer content, prints with page number and optional
#     header. Output is formatted in 1-3 columns
#
   def pdf(self,filename,columns,title):
      

      self.pdfprinter= cls_pdfprinter(self.papersize,PDF_ORIENTATION_PORTRAIT,filename, title, True, columns)