예제 #1
0
def addMenuLabel(menu, text):
    """Adds a QLabel contaning text to the given menu"""
    qaw = QWidgetAction(menu)
    lab = QLabel(text, menu)
    qaw.setDefaultWidget(lab)
    lab.setAlignment(Qt.AlignCenter)
    lab.setFrameShape(QFrame.StyledPanel)
    lab.setFrameShadow(QFrame.Sunken)
    menu.addAction(qaw)
    return lab
예제 #2
0
def addMenuLabel(menu, text):
    """Adds a QLabel contaning text to the given menu"""
    qaw = QWidgetAction(menu)
    lab = QLabel(text, menu)
    qaw.setDefaultWidget(lab)
    lab.setAlignment(Qt.AlignCenter)
    lab.setFrameShape(QFrame.StyledPanel)
    lab.setFrameShadow(QFrame.Sunken)
    menu.addAction(qaw)
    return lab
예제 #3
0
    def __init__(self, db, book_id_map, parent=None):
        from calibre.ebooks.oeb.polish.main import HELP
        QDialog.__init__(self, parent)
        self.db, self.book_id_map = weakref.ref(db), book_id_map
        self.setWindowIcon(QIcon(I('polish.png')))
        title = _('Polish book')
        if len(book_id_map) > 1:
            title = _('Polish %d books')%len(book_id_map)
        self.setWindowTitle(title)

        self.help_text = {
            'polish': _('<h3>About Polishing books</h3>%s')%HELP['about'].format(
                _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book,
                  then polishing will run on ORIGINAL_EPUB (the same for other
                  ORIGINAL_* formats).  So if you
                  want Polishing to not run on the ORIGINAL_* format, delete the
                  ORIGINAL_* format before running it.</p>''')
            ),

            'embed':_('<h3>Embed referenced fonts</h3>%s')%HELP['embed'],
            'subset':_('<h3>Subsetting fonts</h3>%s')%HELP['subset'],

            'smarten_punctuation':
            _('<h3>Smarten punctuation</h3>%s')%HELP['smarten_punctuation'],

            'metadata':_('<h3>Updating metadata</h3>'
                         '<p>This will update all metadata <i>except</i> the cover in the'
                         ' ebook files to match the current metadata in the'
                         ' calibre library.</p>'
                         ' <p>Note that most ebook'
                         ' formats are not capable of supporting all the'
                         ' metadata in calibre.</p><p>There is a separate option to'
                         ' update the cover.</p>'),
            'do_cover': _('<p>Update the covers in the ebook files to match the'
                        ' current cover in the calibre library.</p>'
                        '<p>If the ebook file does not have'
                        ' an identifiable cover, a new cover is inserted.</p>'
                        ),
            'jacket':_('<h3>Book Jacket</h3>%s')%HELP['jacket'],
            'remove_jacket':_('<h3>Remove Book Jacket</h3>%s')%HELP['remove_jacket'],
        }

        self.l = l = QGridLayout()
        self.setLayout(l)

        self.la = la = QLabel('<b>'+_('Select actions to perform:'))
        l.addWidget(la, 0, 0, 1, 2)

        count = 0
        self.all_actions = OrderedDict([
            ('embed', _('&Embed all referenced fonts')),
            ('subset', _('&Subset all embedded fonts')),
            ('smarten_punctuation', _('Smarten &punctuation')),
            ('metadata', _('Update &metadata in the book files')),
            ('do_cover', _('Update the &cover in the book files')),
            ('jacket', _('Add metadata as a "book &jacket" page')),
            ('remove_jacket', _('&Remove a previously inserted book jacket')),
        ])
        prefs = gprefs.get('polishing_settings', {})
        for name, text in self.all_actions.iteritems():
            count += 1
            x = QCheckBox(text, self)
            x.setChecked(prefs.get(name, False))
            x.stateChanged.connect(partial(self.option_toggled, name))
            l.addWidget(x, count, 0, 1, 1)
            setattr(self, 'opt_'+name, x)
            la = QLabel(' <a href="#%s">%s</a>'%(name, _('About')))
            setattr(self, 'label_'+name, x)
            la.linkActivated.connect(self.help_link_activated)
            l.addWidget(la, count, 1, 1, 1)

        count += 1
        l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Expanding), count, 1, 1, 2)

        la = self.help_label = QLabel('')
        self.help_link_activated('#polish')
        la.setWordWrap(True)
        la.setTextFormat(Qt.RichText)
        la.setFrameShape(QFrame.StyledPanel)
        la.setAlignment(Qt.AlignLeft|Qt.AlignTop)
        la.setLineWidth(2)
        la.setStyleSheet('QLabel { margin-left: 75px }')
        l.addWidget(la, 0, 2, count+1, 1)
        l.setColumnStretch(2, 1)

        self.show_reports = sr = QCheckBox(_('Show &report'), self)
        sr.setChecked(gprefs.get('polish_show_reports', True))
        sr.setToolTip(textwrap.fill(_('Show a report of all the actions performed'
                        ' after polishing is completed')))
        l.addWidget(sr, count+1, 0, 1, 1)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.save_button = sb = bb.addButton(_('&Save Settings'), bb.ActionRole)
        sb.clicked.connect(self.save_settings)
        self.load_button = lb = bb.addButton(_('&Load Settings'), bb.ActionRole)
        self.load_menu = QMenu(lb)
        lb.setMenu(self.load_menu)
        self.all_button = b = bb.addButton(_('Select &all'), bb.ActionRole)
        b.clicked.connect(partial(self.select_all, True))
        self.none_button = b = bb.addButton(_('Select &none'), bb.ActionRole)
        b.clicked.connect(partial(self.select_all, False))
        l.addWidget(bb, count+1, 1, 1, -1)
        self.setup_load_button()

        self.resize(QSize(950, 600))
예제 #4
0
파일: polish.py 프로젝트: mrmac123/calibre
    def __init__(self, db, book_id_map, parent=None):
        from calibre.ebooks.oeb.polish.main import HELP
        QDialog.__init__(self, parent)
        self.db, self.book_id_map = weakref.ref(db), book_id_map
        self.setWindowIcon(QIcon(I('polish.png')))
        title = _('Polish book')
        if len(book_id_map) > 1:
            title = _('Polish %d books')%len(book_id_map)
        self.setWindowTitle(title)

        self.help_text = {
            'polish': _('<h3>About Polishing books</h3>%s')%HELP['about'].format(
                _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book,
                  then polishing will run on ORIGINAL_EPUB (the same for other
                  ORIGINAL_* formats).  So if you
                  want Polishing to not run on the ORIGINAL_* format, delete the
                  ORIGINAL_* format before running it.</p>''')
            ),

            'embed':_('<h3>Embed referenced fonts</h3>%s')%HELP['embed'],
            'subset':_('<h3>Subsetting fonts</h3>%s')%HELP['subset'],

            'smarten_punctuation':
            _('<h3>Smarten punctuation</h3>%s')%HELP['smarten_punctuation'],

            'metadata':_('<h3>Updating metadata</h3>'
                         '<p>This will update all metadata <i>except</i> the cover in the'
                         ' ebook files to match the current metadata in the'
                         ' calibre library.</p>'
                         ' <p>Note that most ebook'
                         ' formats are not capable of supporting all the'
                         ' metadata in calibre.</p><p>There is a separate option to'
                         ' update the cover.</p>'),
            'do_cover': _('<h3>Update cover</h3><p>Update the covers in the ebook files to match the'
                        ' current cover in the calibre library.</p>'
                        '<p>If the ebook file does not have'
                        ' an identifiable cover, a new cover is inserted.</p>'
                        ),
            'jacket':_('<h3>Book Jacket</h3>%s')%HELP['jacket'],
            'remove_jacket':_('<h3>Remove Book Jacket</h3>%s')%HELP['remove_jacket'],
            'remove_unused_css':_('<h3>Remove unused CSS rules</h3>%s')%HELP['remove_unused_css'],
        }

        self.l = l = QGridLayout()
        self.setLayout(l)

        self.la = la = QLabel('<b>'+_('Select actions to perform:'))
        l.addWidget(la, 0, 0, 1, 2)

        count = 0
        self.all_actions = OrderedDict([
            ('embed', _('&Embed all referenced fonts')),
            ('subset', _('&Subset all embedded fonts')),
            ('smarten_punctuation', _('Smarten &punctuation')),
            ('metadata', _('Update &metadata in the book files')),
            ('do_cover', _('Update the &cover in the book files')),
            ('jacket', _('Add metadata as a "book &jacket" page')),
            ('remove_jacket', _('&Remove a previously inserted book jacket')),
            ('remove_unused_css', _('Remove &unused CSS rules from the book')),
        ])
        prefs = gprefs.get('polishing_settings', {})
        for name, text in self.all_actions.iteritems():
            count += 1
            x = QCheckBox(text, self)
            x.setChecked(prefs.get(name, False))
            x.stateChanged.connect(partial(self.option_toggled, name))
            l.addWidget(x, count, 0, 1, 1)
            setattr(self, 'opt_'+name, x)
            la = QLabel(' <a href="#%s">%s</a>'%(name, _('About')))
            setattr(self, 'label_'+name, x)
            la.linkActivated.connect(self.help_link_activated)
            l.addWidget(la, count, 1, 1, 1)

        count += 1
        l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Expanding), count, 1, 1, 2)

        la = self.help_label = QLabel('')
        self.help_link_activated('#polish')
        la.setWordWrap(True)
        la.setTextFormat(Qt.RichText)
        la.setFrameShape(QFrame.StyledPanel)
        la.setAlignment(Qt.AlignLeft|Qt.AlignTop)
        la.setLineWidth(2)
        la.setStyleSheet('QLabel { margin-left: 75px }')
        l.addWidget(la, 0, 2, count+1, 1)
        l.setColumnStretch(2, 1)

        self.show_reports = sr = QCheckBox(_('Show &report'), self)
        sr.setChecked(gprefs.get('polish_show_reports', True))
        sr.setToolTip(textwrap.fill(_('Show a report of all the actions performed'
                        ' after polishing is completed')))
        l.addWidget(sr, count+1, 0, 1, 1)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.save_button = sb = bb.addButton(_('&Save Settings'), bb.ActionRole)
        sb.clicked.connect(self.save_settings)
        self.load_button = lb = bb.addButton(_('&Load Settings'), bb.ActionRole)
        self.load_menu = QMenu(lb)
        lb.setMenu(self.load_menu)
        self.all_button = b = bb.addButton(_('Select &all'), bb.ActionRole)
        b.clicked.connect(partial(self.select_all, True))
        self.none_button = b = bb.addButton(_('Select &none'), bb.ActionRole)
        b.clicked.connect(partial(self.select_all, False))
        l.addWidget(bb, count+1, 1, 1, -1)
        self.setup_load_button()

        self.resize(QSize(950, 600))
예제 #5
0
class AppForm(QMainWindow):
  'QT form for application'
  def __init__(self, splash,args,parent=None):
    self.starttime=None # start time of data
    self.endtime=None # end time of data
    self.bbox=args.b # geographical bounding box [w,e,s,n]
    self.preferredOrigin='p' # prefferred should be 'p': preferred origin ; 'f': first origin ; 'l': last origin
    self.includeunassoc=False # should we look also at unassociated origins (default - False)
    self.minmag=0 # minimum magnitude
    self.maxmag=10.0 # maximum magnitude
    self.deltaT=60.0 # absolute time difference in seconds to associate events
    self.deltaR=100.0 # absolute distance difference in km to associate events
    self._datadict = {} # dictionary of input events
    self._refdict = {} # dictionary of reference events
    self.data2reflut = {} # input to ref look up table
    self.ref2datalut = {} # ref to input look up table
    self.Bbox = bboxForm()
    self.Bbox.setLims(*self.bbox)
    splash.showMessage('Initializing...',Qt.AlignCenter)
    QApplication.processEvents()
    QMainWindow.__init__(self, parent)
    self.setWindowTitle('SC3 XML Diff')
    self.args = args # save command line arguments
    self.meter = mpl.lines.Line2D([],[],color='r') # line for measuring distances along canvas
    splash.showMessage('Creating application...',Qt.AlignCenter)
    QApplication.processEvents()
    self.create_menu() # create the app top menu
    self.create_status_bar() # add a status bar
    self.create_main_frame() # create the main frame.
    self.create_toolbox() # create a toolbox
    if args.i:
      splash.showMessage('Reading input data...',Qt.AlignCenter)
      QApplication.processEvents()
      self.get_input_file(args.i)
    if args.r:
      self.get_ref_file(args.r)
      splash.showMessage('Reading reference data...',Qt.AlignCenter)
      QApplication.processEvents()
    self.init_connections() # initialize signal connections
    splash.showMessage('Populating tables...',Qt.AlignCenter)
    QApplication.processEvents()
    self.updatetables()

  def init_connections(self):
    '''Connect signals to functions.
       Using signals to run functions from subprocesses.'''
    self.Bbox.accepted.connect(self.onBboxAccepted) # connect bbox to dialog ok button
    self.connect(self.minmagLine, SIGNAL('ok'),self.updateMagLims)
    self.connect(self.maxmagLine, SIGNAL('ok'),self.updateMagLims)
    self.connect(self.deltaTLine, SIGNAL('ok'),self.updateDeltaT)
    self.connect(self.deltaRLine, SIGNAL('ok'),self.updateDeltaR)
    self.starttimeLine.dateTimeChanged.connect(self.updatestarttime)
    self.endtimeLine.dateTimeChanged.connect(self.updateendtime)
    self.includeunassocLine.stateChanged.connect(self.updateUnassoc)
    self.preferredOriginLine.currentIndexChanged[str].connect(self.updatePreferredOrigin)
#    self.canvas.mpl_connect('button_press_event',self.on_click) # connect click on canvas
#    self.canvas.mpl_connect('button_release_event',self.on_unclick) # connect mouse button release on canvas
#    self.canvas.mpl_connect('motion_notify_event',self.on_move) # connect mouse motion on canvas

  def create_main_frame(self):
    'Create the main frame of application'
    # create widget
    self.main_frame = QWidget()
    # create a general layout
    vbox = QVBoxLayout()
    # create a side by side layout for tables
    self.tb = self.addToolBar('Tools')
    # tool bar can be moved around
    self.tb.setMovable(True)
    self.tb.setFloatable(True)
    hsplit = QSplitter(Qt.Horizontal)
    self.headerlabels = HEADERLABELS
    ######################################
    # create a layout for reference data #
    ######################################
    vboxref = QVBoxLayout()
    # add a title
    label = QLabel('Reference Data')
    # create the reference table
    self.reftable = QTableWidget(0,len(self.headerlabels))
    # Add header to table
    self.reftable.setHorizontalHeaderLabels(self.headerlabels)
    #self.reftable.setSortingEnabled(True)
    # settings for drag&drop
    self.reftable.setDragEnabled(True)
    self.reftable.setDragDropOverwriteMode(False)
    self.reftable.setDragDropMode(3) # can drag and drop
    # connect click events to row selection
    self.reftable.cellClicked.connect(self.reftable_cellClicked)
    # can select only rows
    self.reftable.setSelectionBehavior(QAbstractItemView.SelectRows)
    # no edits please
    #self.reftable.setEditTriggers(QAbstractItemView.NoEditTriggers)
    # No auto scrolling
    self.reftable.setAutoScroll(False)
    # connect drop events to adding events
    self.reftable.dropEvent = self.reftable_dropEvent
    # add label to ref layout
    vboxref.addWidget(label)
    # add table to ref layout
    vboxref.addWidget(self.reftable)
    ##################################
    # create a layout for input data #
    ##################################
    vboxdata = QVBoxLayout()
    # add a title
    label = QLabel('Input data')
    # create the input table
    self.datatable = QTableWidget(0,len(self.headerlabels))
    # Add header to table
    self.datatable.setHorizontalHeaderLabels(self.headerlabels)
    #self.datatable.setSortingEnabled(True)
    # settings for drag&drop
    self.datatable.setDragEnabled(True)
    self.datatable.setDragDropOverwriteMode(False)
    self.datatable.setDragDropMode(3) # can drag and drop
    # connect click events to row selection
    self.datatable.cellClicked.connect(self.datatable_cellClicked)
    # can select only rows
    self.datatable.setSelectionBehavior(QAbstractItemView.SelectRows)
    # no edits please
    #self.datatable.setEditTriggers(QAbstractItemView.NoEditTriggers)
    # No auto scrolling
    self.datatable.setAutoScroll(False)
    # connect drop events to ID association
    self.datatable.dropEvent = self.datatable_dropEvent
    # add label to SC3 layout
    vboxdata.addWidget(label)
    # add table to SC3 layout
    vboxdata.addWidget(self.datatable)
    # Add layouts to side by side layout
    w = QWidget()
    w.setLayout(vboxref)
    hsplit.addWidget(w)
    w = QWidget()
    w.setLayout(vboxdata)
    hsplit.addWidget(w)
    # add side by side to general layout
    vbox.insertWidget(1,hsplit)
    # add layout to widget
    self.main_frame.setLayout(vbox)
    # set widget to be central widget
    self.setCentralWidget(self.main_frame)

  def reftable_cellClicked(self,row,col):
    'any click on table will select the whole row'
    # select all row
    self.reftable.selectRow(row)
    # clear any selection in data table
    self.datatable.clearSelection()

  def datatable_cellClicked(self,row,col):
    'any click on table will select the whole row'
    # select all row
    self.datatable.selectRow(row)
    # clear any selection in ref table
    self.reftable.clearSelection()

  def datatable_dropEvent(self,event):
    'Associate ref event with input event if ref row is dragged and dropped on input row'
    if not event.source()==self.reftable: return # make sure row is from reference table
    if not self.datatable.itemAt(event.pos()): return # make sure we dropped reference row on an input row
    # get the referrence ID
    refID = str(event.source().selectedItems()[0].text())
    # get the ID item on input table
    item = self.datatable.item(self.datatable.itemAt(event.pos()).row(),0)
    inputID = str(item.text())
    # confirm with user to associate
    if refID in self.data2reflut or inputID in self.data2reflut:
      self.statusBar().showMessage('%s and/or %s are already associated. Aborting.'%(refID,inputID) , 2000)
      return
    ok = QMessageBox.question(self,'Associate Event',
                           'Are you sure you want to associate\n%s to %s?'%(refID,inputID),
                           'No','Yes')
    if ok:
      # update the lut
      self.ref2datalut[refID]=inputID
      self.data2reflut[inputID]=refID
      self.updateStatus()
      # report to status bar
      self.statusBar().showMessage('Associated %s with %s'%(refID,inputID) , 2000)

  def reftable_dropEvent(self,event):
    'Add new events to reference table if input row is dragged and dropped on ref table'
    if not event.source()==self.datatable: return # make sure its an input row
    # get row items
    items = event.source().selectedItems()
    # get input ID
    ID = str(items[0].text())
    # make sure ID is associated already
    if ID in self.data2reflut:
      self.statusBar().showMessage('Event %s already associated with %s'%(ID,self.data2reflut[ID]) , 2000)
      return
    # disable sorting
    self.datatable.setSortingEnabled(False)
    self.reftable.setSortingEnabled(False)
    #update lut
    self.data2reflut[ID]=ID
    self.ref2datalut[ID]=ID
    # add a new row to reference table
    i = self.reftable.rowCount()
    self.reftable.insertRow(i)
    # copy line from input table
    for j,item in enumerate(items):
      newitem = QTableWidgetItem(item.text())
      self.reftable.setItem(i,j,newitem)
    # update status
    self.updateStatus()
    # sort ref table
    self.datatable.setSortingEnabled(True)
    self.reftable.setSortingEnabled(True)
    # report to statusbar
    self.statusBar().showMessage('Added Event %s'%(ID) , 2000)

  def delete_row(self):
    'Delete a selected row from table'
    # get selected row(s) on SC3 table
    items = [i for i in self.datatable.selectedItems()+self.reftable.selectedItems() if i.column()==0]
    for item in items:
      # get selected row number
      row = item.row()
      # get event ID
      ID = str(item.text())
      # remove the row from table
      item.tableWidget().removeRow(row)
      # remove lut records
      [self.data2reflut.pop(k) for k,v in self.data2reflut.items() if k==ID or v==ID]
      [self.ref2datalut.pop(k) for k,v in self.ref2datalut.items() if k==ID or v==ID]
      self.updateStatus()
      # report to statusbar
      self.statusBar().showMessage('Removed Event %s from table'%ID , 2000)
    if len(items)>1: self.statusBar().showMessage('%d Events removed from table'%len(items) , 2000)

  def clear_table(self,table):
    'Clear a table'
    for i in range(table.rowCount())[::-1]:
      table.removeRow(i)

  def clear_tables(self):
    [self.clear_table(t) for t in [self.datatable,self.reftable]]

  def create_tooltip_widget(self):
    'creates tooltip of figure elements'
    self.TTWidget = QLabel() # take a QLabel
    self.TTWidget.setFrameShape(QFrame.StyledPanel) # add a frame
    self.TTWidget.setWindowFlags(Qt.ToolTip) # make window look like a tooltip
    self.TTWidget.setAttribute(Qt.WA_TransparentForMouseEvents) # mouse events can't affet it.
    self.TTWidget.hide() # hide for now. see self.on_move function on how to use.

  def get_input_file(self,filesurl=None):
    'open a dialog to get a file name'
    if not filesurl: filesurl = QFileDialog.getOpenFileNames(self, 'Open Input data file(s)',filter='*.xml') # get the file name
    self.ref2datalut={} # init look up table
    self.data2reflut={} # init look up table
    if len(filesurl):
      xmls = [str(f) for f in filesurl]
      Dataep = concatenateSC3XML(xmls)
      if not Dataep:
        self.statusBar().showMessage('No Events in file.', 2000)
        return
      self._datadict = eparams2seisEvents(Dataep,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._datadict), 2000)
    self.updatetables()

  def get_ref_file(self,filesurl=None,refFileType=None):
    'open a dialog to get a file name'
    if not filesurl:
      filesurl,refFileType = QFileDialog.getOpenFileNamesAndFilter(self, 'Open Reference data file(s)',filter='XML Files (*.xml);;CSV Files (*.csv)') # get the file name
    else:
      refFileType =  os.path.splitext(filesurl[0])[1].upper()
    self.ref2datalut={}# init look up table
    self.data2reflut={}# init look up table
    if 'XML' in refFileType:
      xmls = [str(f) for f in filesurl]
      Refep = concatenateSC3XML(xmls)
      if not Refep:
        self.statusBar().showMessage('No Events in file.', 2000)
        return
      self._refdict = eparams2seisEvents(Refep,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._refdict), 2000)
      return self.updatetables()
    if 'CSV' in refFileType:
      CSVs = [str(f) for f in filesurl]
      Refdb = concatenateCSV(CSVs)
      if not len(Refdb):
        self.statusBar().showMessage('No Events in file.', 2000)
        return self.updatetables()
      self._refdict = db2seisEvents(Refdb,starttime=self.starttime,endtime=self.endtime,minmag=self.minmag,maxmag=self.maxmag,bbox=self.bbox,preferred=self.preferredOrigin,includeunassoc=self.includeunassoc)
      self.statusBar().showMessage('Loaded %d events'%len(self._refdict), 2000)
      return self.updatetables()
    self.statusBar().showMessage("Can't load reference file(s)", 3000)

  def filterEvents(self,D):
    'filter events by time,location and magnitude limits'
    [D.pop(i) for i in [k for k in D if D[k].mag>self.maxmag or D[k].mag<self.minmag]]
    if self.starttime: [D.pop(i) for i in [k for k in D if D[k].ot<self.starttime]]
    if self.endtime: [D.pop(i) for i in [k for k in D if D[k].ot>self.endtime]]
    [D.pop(i) for i in [k for k in D if D[k].lon>self.bbox[1] or D[k].lon<self.bbox[0]]]
    [D.pop(i) for i in [k for k in D if D[k].lat>self.bbox[3] or D[k].lat<self.bbox[2]]]
    return D

  def associate(self):
    'associate events'
    self.ref2datalut = {}
    self.data2reflut = {}
    # filter unwanted events
    self.refdict = {}
    self.datadict = {}
    self.refdict.update(self._refdict)
    self.datadict.update(self._datadict)
    self.refdict = self.filterEvents(self.refdict)
    self.datadict= self.filterEvents(self.datadict)
    for ref in self.refdict.keys(): # get potentials associated events for reference. (time diff,seisevent obj) list
      potentials = [(abs(self.datadict[k].ot-self.refdict[ref].ot).total_seconds(),k) for k in self.datadict if
                     abs(self.datadict[k].ot-self.refdict[ref].ot).total_seconds()<=self.deltaT
                     and
                     (self.datadict[k]-self.refdict[ref])[-1]/1000.0<=self.deltaR]

      potentials.sort() # sort according to time difference
      if potentials:
        self.ref2datalut[ref] = potentials[0][1] # get first one.
        self.data2reflut[potentials[0][1]] = ref

  def recolor_table(self,table):
    ' add colors to lines. Green - True event, Red - false event, orange - missed event'
    # mark missed events on table
    stat = HEADERLABELS.index('Status')
    for i in xrange(table.rowCount()):
      table.selectRow(i)
      for item in table.selectedItems():
        f = item.foreground()
        if str(table.item(i,stat).text())=='M':
          f.setColor(QColor('#FFA500'))
        elif str(table.item(i,stat).text())=='F':
          f.setColor(QColor('red'))
        elif str(table.item(i,stat).text())=='T':
          f.setColor(QColor('k'))
        item.setForeground(f)
    table.clearSelection()


  def recolor_tables(self):
    [self.recolor_table(table) for table in [self.datatable,self.reftable]]

  def build_tables(self):
    'populate the gui tables from dictionaries'
    # populate ref table
    self.reftable.setSortingEnabled(False)
    for i,e in enumerate(self.refdict):
      # add a row
      self.reftable.insertRow(i)
      # create an ID item
      item = QTableWidgetItem(e)
      # add ID to row
      self.reftable.setItem(i,0,item)
      # insert event date to row
      for j,v in enumerate([self.refdict[e].__dict__[k] for k in ['ot','mag','lat','lon','status','refid','SCevaluationStatus']]):
        item = QTableWidgetItem(str(v))
        self.reftable.setItem(i,j+1,item)
    # sort the table
    self.reftable.setSortingEnabled(True)
    # adjust columns size
    self.reftable.resizeColumnsToContents()
    # populate data table
    self.datatable.setSortingEnabled(False)
    for i,e in enumerate(self.datadict):
      # add a row
      self.datatable.insertRow(i)
      # create an ID cell
      item = QTableWidgetItem(e)
      # add ID to row
      self.datatable.setItem(i,0,item)
      # insert event date to row
      for j,v in enumerate([self.datadict[e].__dict__[k] for k in ['ot','mag','lat','lon','status','refid','SCevaluationStatus']]):
        item = QTableWidgetItem(str(v))
        self.datatable.setItem(i,j+1,item)
    # sort the table
    self.datatable.setSortingEnabled(True)
    # adjust columns size
    self.datatable.resizeColumnsToContents()
    # update status
    self.updateStatus()

  def updateStatus(self):
    'update status of event (T,M,F)'
    # referrece table
    stat = HEADERLABELS.index('Status')
    refID = HEADERLABELS.index('refID')
    self.reftable.setSortingEnabled(False)
    for i in xrange(self.reftable.rowCount()):
      ID = str(self.reftable.item(i,0).text())
      if ID in self.ref2datalut:
        self.reftable.item(i,stat).setText('T')
        self.reftable.item(i,refID).setText(self.ref2datalut[ID])
      else:
        self.reftable.item(i,stat).setText('M')
        self.reftable.item(i,refID).setText('')
    self.reftable.setSortingEnabled(True)
    self.datatable.setSortingEnabled(False)
    for i in xrange(self.datatable.rowCount()):
      ID = str(self.datatable.item(i,0).text())
      if ID in self.data2reflut:
        self.datatable.item(i,stat).setText('T')
        self.datatable.item(i,refID).setText(self.data2reflut[ID])
      else:
        self.datatable.item(i,stat).setText('F')
        self.datatable.item(i,refID).setText('')
    self.datatable.setSortingEnabled(True)
    # repaint missed False and true events
    self.recolor_tables()

  def updatetables(self):
    self.associate()
    self.clear_tables()
    self.build_tables()
    self.statusBar().showMessage('Tables updated',2000)

  def updateMagLims(self):
    self.minmag = float(self.minmagLine.text())
    self.maxmag = float(self.maxmagLine.text())
    self.statusBar().showMessage('Magnitude limits updated',2000)

  def updateDeltaT(self):
    self.deltaT = float(self.deltaTLine.text())
    self.statusBar().showMessage('Association time limit updated',2000)

  def updateDeltaR(self):
    self.deltaR = float(self.deltaRLine.text())
    self.statusBar().showMessage('Association distance limit updated',2000)

  def updatestarttime(self,starttime):
    self.starttime = starttime.toPyDateTime()
    self.statusBar().showMessage('Start time limit updated',2000)

  def updateendtime(self,endtime):
    self.endtime = endtime.toPyDateTime()
    self.statusBar().showMessage('End time limit updated',2000)

  def updateUnassoc(self,stat):
    self.includeunassoc = bool(stat)
    self.statusBar().showMessage('Including unassociated origins set to %s'%self.includeunassoc,2000)

  def updatePreferredOrigin(self,pref):
    self.preferredOrigin = pref[0]
    self.statusBar().showMessage('Preferred Origins set to %s'%pref,2000)

  def evaluate(self):
    i = self.headerlabels.index('Status')
    table = self.datatable
    T = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='T'])
    F = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='F'])
    table = self.reftable
    M = len([table.item(j,i) for j in xrange(table.rowCount()) if str(table.item(j,i).text())=='M'])
    self.statusBar().showMessage('T: %d ; M: %d ; F: %d ; Score: %.1f%% for %d events'%(T,M,F,100.0*T/sum([T,F,M]),sum([T,F,M])), 5000)
    return T,F,M,100.0*T/sum([T,F,M])

  def saveAs_figure(self):
    pass

  def create_menu(self):
    'Creates main menu'
    # Populate the menubar:
    # Add File submenu
    self.file_menu = self.menuBar().addMenu("&File")
    # load input data
    load_input_action = self.create_action("Load &Input",
            shortcut="Ctrl+I", slot=self.get_input_file,
            icon='document-open',tip="Load input data from a file(s)")
    # load reference data
    load_ref_action = self.create_action("Load &Reference",
            shortcut="Ctrl+R", slot=self.get_ref_file,
            icon='document-open',tip="Load reference data from a file(s)")
    # Save As...
    saveAs_action = self.create_action("S&ave As...",
            shortcut="Shift+S", slot=self.saveAs_figure,
            icon='filesaveas',tip="Save the figure")
    # Quit
    quit_action = self.create_action("&Quit", slot=self.close,
            icon='system-shutdown',shortcut="Ctrl+Q", tip="Close the application")
    # populate the file submenu
    self.add_actions(self.file_menu,
            (load_input_action,load_ref_action, saveAs_action, None, quit_action))
    # Add Edit submenu
    self.Edit_menu = self.menuBar().addMenu("&Edit")
        # delete an event
    delete_action   = self.create_action("&Delete",
            slot=self.delete_row,shortcut="Del",
            tip="Delete selected row from table")
    # populate tools submenu
    self.add_actions(self.Edit_menu,[delete_action])
    # Add help submenu
    self.help_menu = self.menuBar().addMenu("&Help")
    # Help
    help_action = self.create_action("&Help",
            shortcut='F1', slot=self.on_help,
            icon='Help',tip='help')
    # About
    about_action = self.create_action("&About",
            shortcut='F2', slot=self.on_about,
            tip='About This Application')
    # About QT
    aboutQt_action = self.create_action("&About QT",
            shortcut='F3', slot=self.on_aboutQt,
            tip='About QT')
    # License
    license_action = self.create_action("&License",
            shortcut='F4', slot=self.on_license,
            tip='Application License')
    # Populate help submenu
    self.add_actions(self.help_menu, (help_action,None,about_action,aboutQt_action,license_action))

  def create_toolbox(self):
    w = QWidget()
    l = QGridLayout(w)
    self.refreshbutton = self.create_pushButton('Refresh', slot=self.updatetables, shortcut=None, icon=None, tip='Refresh Tables')
    self.scorebutton = self.create_pushButton('Score', slot=self.evaluate, shortcut=None, icon=None, tip='Evaluate Score')
    l.addWidget(self.refreshbutton,0,0)
    l.addWidget(self.scorebutton,1,0)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.minmagLine = ConnectedLineEdit(str(self.minmag),0,self.maxmag)
    self.maxmagLine = ConnectedLineEdit(str(self.maxmag),self.minmag,10)
    self.minmagLine.setConnected(self.maxmagLine, 'bottom')
    self.maxmagLine.setConnected(self.minmagLine, 'top')
    l.addWidget(QLabel('Magnitude Limits'),0,1)
    l.addWidget(QLabel('Min'),1,0)
    l.addWidget(QLabel('Max'),2,0)
    l.addWidget(self.minmagLine,1,1)
    l.addWidget(self.maxmagLine,2,1)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.deltaTLine = ConnectedLineEdit(str(self.deltaT),0,18000,3)
    self.deltaRLine = ConnectedLineEdit(str(self.deltaR),0,1000,3)
    l.addWidget(QLabel('Association Limits'),0,1)
    l.addWidget(QLabel('Time (sec)'),1,0)
    l.addWidget(QLabel('Range (km)'),2,0)
    l.addWidget(self.deltaTLine,1,1)
    l.addWidget(self.deltaRLine,2,1)
    self.tb.addWidget(w)
    w = QWidget()
    v = QVBoxLayout(w)
    ww = QWidget()
    v.addWidget(ww)
    l = QGridLayout(ww)
    W,E,S,N = self.bbox
    self.EAST = QLabel(str(E))
    self.WEST = QLabel(str(W))
    self.NORTH = QLabel(str(N))
    self.SOUTH = QLabel(str(S))
    self.bboxbutton = self.create_pushButton('Region', slot=self.Bbox.show, shortcut=None, icon=None, tip='Define a geographic region')
    l.addWidget(self.EAST,1,2,1,1,Qt.Alignment(Qt.AlignLeft))
    l.addWidget(self.WEST,1,0,1,1,Qt.Alignment(Qt.AlignRight))
    l.addWidget(self.NORTH,0,1,1,1,Qt.Alignment(Qt.AlignHCenter))
    l.addWidget(self.SOUTH,2,1,1,1,Qt.Alignment(Qt.AlignHCenter))
    v.addWidget(self.bboxbutton)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.starttimeLine = QDateTimeEdit()
    self.starttimeLine.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
    self.starttimeLine.setCalendarPopup(True)
    self.endtimeLine = QDateTimeEdit()
    self.endtimeLine.setDateTime(QDateTime.currentDateTimeUtc())
    self.endtimeLine.setDisplayFormat('yyyy-MM-dd HH:mm:ss')
    self.endtimeLine.setCalendarPopup(True)
    l.addWidget(QLabel('Start:'),0,0)
    l.addWidget(self.starttimeLine,0,1)
    l.addWidget(QLabel('End:'),1,0)
    l.addWidget(self.endtimeLine,1,1)
    self.tb.addWidget(w)
    w = QWidget()
    l = QGridLayout(w)
    self.preferredOriginLine = QComboBox()
    self.preferredOriginLine.addItems(['preferred','first','last'])
    self.includeunassocLine = QCheckBox('Unassociated')
    self.includeunassocLine.setCheckState(self.includeunassoc)
    l.addWidget(QLabel('Origins:'),0,0)
    l.addWidget(self.preferredOriginLine,1,0)
    l.addWidget(self.includeunassocLine,2,0)
    self.tb.addWidget(w)

  def onBboxAccepted(self):
    '''get region limits from BboxForm.
       fires when Bbox is accepted'''
    if self.Bbox.validate(): # make sure limits are acceptable
      self.bbox = self.Bbox.getLims() # get limits
      w,e,s,n = self.bbox
      self.WEST.setText(str(w))
      self.EAST.setText(str(e))
      self.NORTH.setText(str(n))
      self.SOUTH.setText(str(s))

  def add_actions(self, target, actions):
    'Utility function for menu creation'
    for action in actions:
      if action is None:
        target.addSeparator()
      else:
        target.addAction(action)

  def create_action(  self, text, slot=None, shortcut=None,
                      icon=None, tip=None, checkable=False,
                      signal="triggered()"):
    'Utility function for menu actions creation'
    action = QAction(text, self)
    action.setIconVisibleInMenu(True)
    if icon is not None:
      i = QIcon.fromTheme(icon,QIcon(":/%s.png" % icon))
      action.setIcon(i)
    if shortcut is not None:
      action.setShortcut(shortcut)
    if tip is not None:
      action.setToolTip(tip)
      action.setStatusTip(tip)
    if slot is not None:
      self.connect(action, SIGNAL(signal), slot)
    if checkable:
      action.setCheckable(True)
    return action

  def create_pushButton(self,text,toolbar=None, slot=None, shortcut=None, icon=None, tip=None):
    'Utility function for button creation'
    # create the button
    button = QPushButton(text,self)
    # populate properties
    if slot:
      # connect a function
      button.clicked.connect(slot)
    if icon:
      # add icon
      i = QIcon.fromTheme(icon,QIcon(":/%s.png" % icon))
      button.setIcon(i)
      button.setIconSize(QSize(24,24))
    if shortcut:
      # set the shortcut
      button.setShortcut(shortcut)
    if tip:
      # add tooltip and status tip
      button.setToolTip(tip)
      button.setStatusTip(tip)
    if toolbar:
      # add the button to a toolbar (or any widget)
      toolbar.addWidget(button)
    return button

  def create_status_bar(self):
    'Add a status bar'
    # set default message
    self.status_text = QLabel("Ready")
    self.connstatLabel = QLabel()
    self.statusBar().addWidget(self.status_text, 1)
    self.statusBar().addPermanentWidget(self.connstatLabel)


  def on_about(self):
    'show a messagebox about the application'
    msg = "<p align='center'><big>SCxmlDiff</big><br><br> \
    Compare two Seiscomp3 XML files<br><br> \
    <small>Created<br> \
    by<br> \
    Ran Novitsky Nof @ BSL, 2015</small><br><br>\
    <a href='http://ran.rnof.info/'>http://ran.rnof.info</a><p>"
    QMessageBox.about(self,"About", msg.strip())

  def on_aboutQt(self):
    'show a messagebox about QT'
    QMessageBox.aboutQt(self,'')

  def on_license(self):
    'GPL licanse message'
    msg = "<p><b>This</b> is a free software; you can redistribute it and/or modify it under the \
terms of the GNU General Public License as published by the Free Software \
Foundation; either version 3 of the License, or (at your option) any later \
version.</p>\
<p><b>This application</b> is distributed in the hope that it will be useful, but WITHOUT ANY \
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR \
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.</p> \
<p>You should have received a copy of the GNU General Public License along with \
this application; if not, see <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.</p>"
    QMessageBox.about(self,"Application Licanse", msg.strip())

  def on_help(self):
    'Show help on a message window. Uses the argparse help'
    msg = '<pre>'+parser.format_help()#.replace('\n','</p><p>')
    msg = msg.replace('*****@*****.**',"<a href='mailto:[email protected]'>[email protected]</a>").strip()+'<\pre>'
    QMessageBox.about(self,"Help", msg)

  def message(self,msg,title='Error'):
    'a simple message window'
    QMessageBox.about(self,title,msg)