示例#1
0
    def setup_plot_widgets(self):
        self.peakmap_plotter = PeakMapPlottingWidget()
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.eic_plotter = EicPlottingWidget(with_range=False)
        self.mz_plotter = MzPlottingWidget()

        self.peakmap_plotter.set_logarithmic_scale(1)
        self.peakmap_plotter.set_gamma(self.gamma)

        self.eic_plotter.set_overall_range(self.rtmin, self.rtmax)
        self.mz_plotter.set_overall_range(self.mzmin, self.mzmax)
示例#2
0
    def setup_plot_widgets(self):
        self.peakmap_plotter = PeakMapPlottingWidget()
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.eic_plotter = EicPlottingWidget(with_range=False)
        self.mz_plotter = MzPlottingWidget()

        self.peakmap_plotter.set_logarithmic_scale(1)
        self.peakmap_plotter.set_gamma(self.gamma)

        self.eic_plotter.set_overall_range(self.rtmin, self.rtmax)
        self.mz_plotter.set_overall_range(self.mzmin, self.mzmax)
示例#3
0
class PeakMapExplorer(EmzedDialog):
    def __init__(self, ok_rows_container=[], parent=None):
        super(PeakMapExplorer, self).__init__(parent)
        self.setWindowFlags(Qt.Window)
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.ok_rows = ok_rows_container

        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowFlags(Qt.Window)

        self.gamma = 3.0

        self.last_used_directory_for_load = None
        self.last_used_directory_for_save = None

        self.history = History()

    def keyPressEvent(self, e):
        # avoid closing of dialog when Esc key pressed:
        if e.key() != Qt.Key_Escape:
            return super(PeakMapExplorer, self).keyPressEvent(e)

    def setWindowTitle(self):
        if self.peakmap2 is None:
            title = os.path.basename(self.peakmap.meta.get("source", ""))
        else:
            p1 = os.path.basename(self.peakmap.meta.get("source", ""))
            p2 = os.path.basename(self.peakmap2.meta.get("source", ""))
            title = "yellow=%s, blue=%s" % (p1, p2)
        super(PeakMapExplorer, self).setWindowTitle(title)

    def setup(self, peakmap, peakmap2=None, table=None):
        self.table = table

        def collect_precursor_mz(pm):
            for s in pm:
                if s.precursors:
                    if s.msLevel > 1:
                        yield s.precursors[0][0]

        self.ms_levels = set(peakmap.getMsLevels())
        self.precursor_mz = set(collect_precursor_mz(peakmap))
        if peakmap2 is not None:
            self.ms_levels &= set(peakmap2.getMsLevels())
            self.precursor_mz &= set(collect_precursor_mz(peakmap2))

        self.ms_levels = sorted(self.ms_levels)
        self.precursor_mz = sorted(self.precursor_mz)

        self.setup_table_widgets()
        self.setup_input_widgets()
        self.history_list = QComboBox(self)

        self.setup_ms2_widgets()
        self.full_pm = peakmap
        self.full_pm2 = peakmap2
        self.dual_mode = self.full_pm2 is not None

        self.current_ms_level = self.ms_levels[0]
        self.process_peakmap(self.current_ms_level)
        self.rtmin, self.rtmax, self.mzmin, self.mzmax = get_range(
            self.peakmap, self.peakmap2)

        self.setup_plot_widgets()
        self.setup_menu_bar()
        self.setup_layout()
        self.connect_signals_and_slots()
        self.setup_initial_values()
        self.plot_peakmap()

    def setup_ms2_widgets(self):
        self.spectra_selector_widget.set_data(self.ms_levels,
                                              self.precursor_mz)

    def setup_table_widgets(self):
        if self.table is not None:
            self.table_widget = create_table_widget(self.table, self)
            self.select_all_peaks = QPushButton("Select all peaks", self)
            self.unselect_all_peaks = QPushButton("Unselect all peaks", self)
            self.done_button = QPushButton("Done", self)

    def setup_menu_bar(self):
        self.menu_bar = QMenuBar(self)
        menu = QMenu("Peakmap Explorer", self.menu_bar)
        self.menu_bar.addMenu(menu)
        if not self.dual_mode:
            self.load_action = QAction("Load Peakmap", self)
            self.load_action.setShortcut(QKeySequence("Ctrl+L"))
            self.load_action2 = None
            menu.addAction(self.load_action)
        else:
            self.load_action = QAction("Load Yellow Peakmap", self)
            self.load_action2 = QAction("Load Blue Peakmap", self)
            menu.addAction(self.load_action)
            menu.addAction(self.load_action2)

        self.save_action = QAction("Save selected range as image", self)
        self.save_action.setShortcut(QKeySequence("Ctrl+S"))
        menu.addAction(self.save_action)

        menu = QMenu("Help", self.menu_bar)
        self.help_action = QAction("Help", self)
        self.help_action.setShortcut(QKeySequence("F1"))
        menu.addAction(self.help_action)
        self.menu_bar.addMenu(menu)

    def process_peakmap(self, ms_level, pre_mz_min=None, pre_mz_max=None):

        peakmap = self.full_pm.filter(lambda s: s.msLevel == ms_level)
        if ms_level > 1 and pre_mz_min is not None:
            peakmap = peakmap.filter(
                lambda s: s.precursors[0][0] >= pre_mz_min)
        if ms_level > 1 and pre_mz_max is not None:
            peakmap = peakmap.filter(
                lambda s: s.precursors[0][0] <= pre_mz_max)

        if self.full_pm2 is not None:
            peakmap2 = self.full_pm2.filter(lambda s: s.msLevel == ms_level)

        self.peakmap = peakmap
        if self.dual_mode:
            self.peakmap2 = peakmap2
        else:
            self.peakmap2 = None

        for i, msl in enumerate(self.ms_levels):
            if msl == ms_level:
                pass  # TODO self.ms_level.setCurrentIndex(i)

        self.setWindowTitle()

    def setup_initial_values(self):

        imax = self.peakmap_plotter.get_total_imax()
        self.image_scaling_widget.set_max_intensity(imax)
        self.image_scaling_widget.set_gamma(self.gamma)

        self.view_range_widget.set_view_range(self.rtmin, self.rtmax,
                                              self.mzmin, self.mzmax)

    def setup_input_widgets(self):
        self.image_scaling_widget = ImageScalingWidget(self)
        self.spectra_selector_widget = SpectraSelectorWidget(self)
        self.view_range_widget = ViewRangeWidget(self)

    def setup_plot_widgets(self):
        self.peakmap_plotter = PeakMapPlottingWidget()
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.eic_plotter = EicPlottingWidget(with_range=False)
        self.mz_plotter = MzPlottingWidget()

        self.peakmap_plotter.set_logarithmic_scale(1)
        self.peakmap_plotter.set_gamma(self.gamma)

        self.eic_plotter.set_overall_range(self.rtmin, self.rtmax)
        self.mz_plotter.set_overall_range(self.mzmin, self.mzmax)

    def setup_layout(self):
        outer_layout = QVBoxLayout()
        outer_layout.addWidget(self.menu_bar)
        outer_layout.setStretch(0, 1)

        h_splitter = QSplitter(self)
        h_splitter.setOrientation(Qt.Horizontal)

        # FIRST COLUMN of h_splitter is chromatogram + peakmap:  ############################

        v_splitter1 = QSplitter(self)
        v_splitter1.setOrientation(Qt.Vertical)
        v_splitter1.addWidget(self.eic_plotter)
        v_splitter1.addWidget(self.peakmap_plotter)
        self.peakmap_plotter.setMinimumSize(250, 200)
        v_splitter1.setStretchFactor(0, 1)
        v_splitter1.setStretchFactor(1, 3)

        h_splitter.addWidget(v_splitter1)
        h_splitter.setStretchFactor(0, 2)

        # SECOND COLUMN of h_splittier holds controlx boxes + mz plot #######################

        v_splitter2 = QSplitter(self)
        v_splitter2.setOrientation(Qt.Vertical)

        v_splitter2.addWidget(self.image_scaling_widget)
        v_splitter2.addWidget(self.spectra_selector_widget)
        v_splitter2.addWidget(self.view_range_widget)
        v_splitter2.addWidget(self.history_list)
        v_splitter2.addWidget(self.mz_plotter)

        v_splitter2.setStretchFactor(0, 0)
        v_splitter2.setStretchFactor(1, 0)
        v_splitter2.setStretchFactor(2, 0)
        v_splitter2.setStretchFactor(3, 0)
        v_splitter2.setStretchFactor(4, 1)

        h_splitter.addWidget(v_splitter2)
        h_splitter.setStretchFactor(1, 1)

        # THIRD COLUMN of h_splittier holds control table + buttons ##########################
        if self.table:
            frame = QFrame(self)
            layout = QVBoxLayout(frame)
            frame.setLayout(layout)
            layout.addWidget(self.table_widget)

            button_row_layout = QHBoxLayout(frame)
            button_row_layout.addWidget(self.select_all_peaks)
            button_row_layout.addWidget(self.unselect_all_peaks)
            button_row_layout.addWidget(self.done_button)

            layout.addLayout(button_row_layout)
            h_splitter.addWidget(frame)
            h_splitter.setStretchFactor(2, 2)

        outer_layout.addWidget(h_splitter)
        self.setLayout(outer_layout)
        outer_layout.setStretch(1, 99)

    def connect_signals_and_slots(self):
        self.image_scaling_widget.USE_LOG_SCALE.connect(self.use_logscale)
        self.image_scaling_widget.GAMMA_CHANGED.connect(self.gamma_changed)

        self.image_scaling_widget.IMIN_CHANGED.connect(self.set_image_min)
        self.image_scaling_widget.IMAX_CHANGED.connect(self.set_image_max)

        self.spectra_selector_widget.MS_LEVEL_CHOSEN.connect(
            self.ms_level_chosen)
        self.spectra_selector_widget.PRECURSOR_RANGE_CHANGED.connect(
            self.set_precursor_range)

        self.view_range_widget.RANGE_CHANGED.connect(self.update_image_range)

        self.connect(self.history_list, SIGNAL("activated(int)"),
                     self.history_item_selected)

        if self.dual_mode:
            self.connect(self.load_action, SIGNAL("triggered()"),
                         self.do_load_yellow)
            self.connect(self.load_action2, SIGNAL("triggered()"),
                         self.do_load_blue)
        else:
            self.connect(self.load_action, SIGNAL("triggered()"), self.do_load)
        self.connect(self.save_action, SIGNAL("triggered()"), self.do_save)
        self.connect(self.help_action, SIGNAL("triggered()"), self.show_help)

        self.peakmap_plotter.NEW_IMAGE_LIMITS.connect(
            self.image_limits_upated_by_user)

        self.peakmap_plotter.KEY_LEFT.connect(
            self.user_pressed_left_key_in_plot)
        self.peakmap_plotter.KEY_RIGHT.connect(
            self.user_pressed_right_key_in_plot)
        self.peakmap_plotter.KEY_BACKSPACE.connect(
            self.user_pressed_backspace_key_in_plot)
        self.peakmap_plotter.KEY_END.connect(self.user_pressed_end_key_in_plot)
        self.peakmap_plotter.CURSOR_MOVED.connect(self.cursor_moved_in_plot)
        self.eic_plotter.CURSOR_MOVED.connect(self.eic_cursor_moved)
        self.eic_plotter.VIEW_RANGE_CHANGED.connect(
            self.eic_view_range_changed)
        self.mz_plotter.CURSOR_MOVED.connect(self.mz_cursor_moved)
        self.mz_plotter.VIEW_RANGE_CHANGED.connect(self.mz_view_range_changed)

        if self.table is not None:
            self.connect(self.table_widget.verticalHeader(),
                         SIGNAL("sectionClicked(int)"), self.row_selected)
            self.connect(self.table_widget,
                         SIGNAL("itemClicked(QTableWidgetItem*)"),
                         self.cell_clicked)
            self.connect(self.select_all_peaks, SIGNAL("pressed()"),
                         self.select_all_peaks_button_pressed)
            self.connect(self.unselect_all_peaks, SIGNAL("pressed()"),
                         self.unselect_all_peaks_button_pressed)
            self.connect(self.done_button, SIGNAL("pressed()"),
                         self.done_button_pressed)

            def key_release_handler(evt):
                tw = self.table_widget
                active_rows = set(
                    ix.row()
                    for ix in tw.selectionModel().selection().indexes())
                if active_rows:
                    row = active_rows.pop()
                    if evt.key() in (Qt.Key_Up, Qt.Key_Down):
                        tw.selectRow(row)
                        tw.verticalHeader().emit(SIGNAL("sectionClicked(int)"),
                                                 row)
                        return
                return QTableWidget.keyPressEvent(tw, evt)

            self.table_widget.keyReleaseEvent = key_release_handler

    def cursor_moved_in_plot(self, rt, mz):
        self.eic_plotter.set_cursor_pos(rt)
        self.mz_plotter.set_cursor_pos(mz)

    def eic_cursor_moved(self, rt):
        self.peakmap_plotter.set_cursor_rt(rt)

    def eic_view_range_changed(self, rtmin, rtmax):
        """
        we want to avoid the loop   EIC_RANGE_CHANGED -> VIEW_RANGE_CHANGED -> EIC_RANGE_CHANGED
        and we do not want to fully block emitting of VIEW_RANGE_CHANGED.
        so self.peakmap_plotter.blockSignals() does not work here, instead we "cut" the last
        connection here:
        """
        self.eic_plotter.VIEW_RANGE_CHANGED.disconnect()
        self.peakmap_plotter.blockSignals(True)
        self.peakmap_plotter.set_rt_limits(rtmin, rtmax)
        self.peakmap_plotter.blockSignals(False)
        self.peakmap_plotter.replot()
        self.eic_plotter.VIEW_RANGE_CHANGED.connect(
            self.eic_view_range_changed)

    def mz_view_range_changed(self, mzmin, mzmax):
        """
        we want to avoid the loop  MZ_RANGE_CHANGED -> VIEW_RANGE_CHANGED -> MZ_RANGE_CHANGED
        and we do not want to fully block emitting of VIEW_RANGE_CHANGED.
        so self.peakmap_plotter.blockSignals() does not work here, instead we "cut" the last
        connection here:
        """
        self.mz_plotter.VIEW_RANGE_CHANGED.disconnect()
        self.peakmap_plotter.blockSignals(True)
        self.peakmap_plotter.set_mz_limits(mzmin, mzmax)
        self.peakmap_plotter.blockSignals(False)
        self.peakmap_plotter.replot()
        self.mz_plotter.VIEW_RANGE_CHANGED.connect(self.mz_view_range_changed)

    def mz_cursor_moved(self, mz):
        self.peakmap_plotter.set_cursor_mz(mz)

    def image_limits_upated_by_user(self, rtmin, rtmax, mzmin, mzmax):
        self.update_peakmap_projection_views(rtmin, rtmax, mzmin, mzmax)
        self.history.new_head((rtmin, rtmax, mzmin, mzmax))
        self.update_history_entries()

    def set_image_min(self, value):
        self.peakmap_plotter.set_imin(value)
        self.peakmap_plotter.replot()

    def set_image_max(self, value):
        self.peakmap_plotter.set_imax(value)
        self.peakmap_plotter.replot()

    def update_peakmap_projection_views(self, rtmin, rtmax, mzmin, mzmax):

        rts, chroma = self.peakmap.chromatogram(mzmin, mzmax)
        self.eic_plotter.del_all_items()
        if self.dual_mode:
            rts2, chroma2 = self.peakmap2.chromatogram(mzmin, mzmax, rtmin,
                                                       rtmax)
            self.eic_plotter.add_eics([(rts, chroma), (rts2, chroma2)],
                                      configs=[blue_line, yellow_line])
        else:
            self.eic_plotter.add_eics([(rts, chroma)], configs=[grey_line])

        self.eic_plotter.shrink_and_replot(rtmin, rtmax)

        if self.dual_mode:
            data = [(self.peakmap, rtmin, rtmax, mzmin, mzmax, 3000),
                    (self.peakmap2, rtmin, rtmax, mzmin, mzmax, 3000)]
            configs = [dict(color="#aaaa00"), dict(color="#0000aa")]
            self.mz_plotter.plot_peakmaps(data, configs)
        else:
            self.mz_plotter.plot_peakmaps([(self.peakmap, rtmin, rtmax, mzmin,
                                            mzmax, 3000)])

        self.mz_plotter.shrink_and_replot(mzmin, mzmax)
        self.view_range_widget.set_view_range(rtmin, rtmax, mzmin, mzmax)

    def _handle_history_action(self, action):
        item = action()
        if item is not None:
            self.peakmap_plotter.set_limits_no_sig(*item)
            self.update_peakmap_projection_views(*item)
            self.update_history_entries()

    def user_pressed_left_key_in_plot(self):
        self._handle_history_action(self.history.go_back)

    def user_pressed_right_key_in_plot(self):
        self._handle_history_action(self.history.go_forward)

    def user_pressed_backspace_key_in_plot(self):
        self._handle_history_action(self.history.go_to_beginning)

    def user_pressed_end_key_in_plot(self):
        self._handle_history_action(self.history.go_to_end)

    def history_item_selected(self, index):
        self._handle_history_action(
            lambda index=index: self.history.set_position(index))

    @protect_signal_handler
    def do_save(self):
        pix = self.peakmap_plotter.paint_pixmap()
        while True:
            path = askForSave(self.last_used_directory_for_save,
                              caption="Save Image",
                              extensions=("png", "PNG"))
            if path is None:
                break
            __, ext = os.path.splitext(path)
            if ext not in (".png", ".PNG"):
                QMessageBox.warning(self, "Warning",
                                    "wrong/missing extension '.png'")
            else:
                self.last_used_directory_for_save = os.path.dirname(path)
                pix.save(path)
                break
        return

    def _do_load(self, title, attribute):
        path = askForSingleFile(self.last_used_directory_for_load,
                                caption=title,
                                extensions=("mzML", "mzData", "mzXML"))
        if path is not None:
            setattr(self, attribute, loadPeakMap(path))
            self.process_peakmap()
            self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
            self.setup_initial_values()
            self.setWindowTitle()
            self.peakmap_plotter.replot()
            self.plot_peakmap()
            self.last_used_directory_for_load = os.path.dirname(path)

    @protect_signal_handler
    def do_load(self):
        self._do_load("Load Peakmap", "peakmap")

    @protect_signal_handler
    def do_load_yellow(self):
        self._do_load("Load Yellow Peakmap", "peakmap")

    @protect_signal_handler
    def do_load_blue(self):
        self._do_load("Load Blue Peakmap", "peakmap2")

    @protect_signal_handler
    def select_all_peaks_button_pressed(self):
        for row in range(self.table_widget.rowCount()):
            item = self.table_widget.item(row, 0)
            item.setCheckState(Qt.Checked)

    @protect_signal_handler
    def unselect_all_peaks_button_pressed(self):
        for row in range(self.table_widget.rowCount()):
            item = self.table_widget.item(row, 0)
            item.setCheckState(Qt.Unchecked)

    @protect_signal_handler
    def done_button_pressed(self):
        self.ok_rows[:] = [
            i for i in range(len(self.table))
            if self.table_widget.item(i, 0).checkState() == Qt.Checked
        ]
        self.accept()

    @protect_signal_handler
    def row_selected(self, row_idx):
        row = self.table.getValues(self.table.rows[row_idx])
        needed = ["rtmin", "rtmax", "mzmin", "mzmax"]
        if all(n in row for n in needed):
            rtmin, rtmax, mzmin, mzmax = [row.get(ni) for ni in needed]
            self.peakmap_plotter.set_limits(rtmin, rtmax, mzmin, mzmax)
        else:
            needed = ["mzmin", "mzmax"]
            if all(n in row for n in needed):
                mzmin, mzmax = [row.get(ni) for ni in needed]
                self.peakmap_plotter.set_limits(self.rtmin, self.rtmax, mzmin,
                                                mzmax)

    @protect_signal_handler
    def cell_clicked(self, item):
        row = item.row()
        self.table_widget.selectRow(row)
        self.table_widget.verticalHeader().emit(SIGNAL("sectionClicked(int)"),
                                                row)

    @protect_signal_handler
    def show_help(self):
        html = resource_string("emzed.core.explorers",
                               "help_peakmapexplorer.html")
        QWebSettings.globalSettings().setFontFamily(QWebSettings.StandardFont,
                                                    'Courier')
        QWebSettings.globalSettings().setFontSize(QWebSettings.DefaultFontSize,
                                                  12)
        v = QWebView(self)
        v.setHtml(html)
        dlg = QDialog(self, Qt.Window)
        dlg.setMinimumSize(300, 300)
        l = QVBoxLayout(dlg)
        l.addWidget(v)
        dlg.setLayout(l)
        dlg.show()

    def update_history_entries(self):
        self.history_list.clear()
        for item in self.history.items:
            rtmin, rtmax, mzmin, mzmax = item
            str_item = "%10.5f .. %10.5f %6.2fm...%6.2fm " % (
                mzmin, mzmax, rtmin / 60.0, rtmax / 60.0)
            self.history_list.addItem(str_item)

        self.history_list.setCurrentIndex(self.history.position)

    @protect_signal_handler
    def use_logscale(self, is_log):
        self.peakmap_plotter.set_logarithmic_scale(is_log)
        self.peakmap_plotter.replot()

    @protect_signal_handler
    def ms_level_chosen(self, ms_level):
        if ms_level != self.current_ms_level:
            self.current_ms_level = ms_level
            self.process_peakmap(ms_level)
            self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
            self.peakmap_plotter.replot()
            self.plot_peakmap()

    @protect_signal_handler
    def set_precursor_range(self, pre_mz_min, pre_mz_max):
        self.process_peakmap(self.current_ms_level, pre_mz_min, pre_mz_max)
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.peakmap_plotter.replot()
        self.plot_peakmap()

    @protect_signal_handler
    def gamma_changed(self, value):
        self.peakmap_plotter.set_gamma(value)
        self.peakmap_plotter.replot()

    @protect_signal_handler
    def update_image_range(self, rtmin, rtmax, mzmin, mzmax):

        rtmin *= 60.0
        rtmax *= 60.0

        if rtmin < self.rtmin:
            rtmin = self.rtmin
        if rtmax > self.rtmax:
            rtmax = self.rtmax
        if mzmin < self.mzmin:
            mzmin = self.mzmin
        if mzmax > self.mzmax:
            mzmax = self.mzmax
        rtmin, rtmax = sorted((rtmin, rtmax))
        mzmin, mzmax = sorted((mzmin, mzmax))

        self.peakmap_plotter.set_limits(rtmin, rtmax, mzmin, mzmax)

    def plot_peakmap(self):
        self.peakmap_plotter.set_limits(self.rtmin, self.rtmax, self.mzmin,
                                        self.mzmax)
示例#4
0
class PeakMapExplorer(EmzedDialog):

    def __init__(self, ok_rows_container=[], parent=None):
        super(PeakMapExplorer, self).__init__(parent)
        self.setWindowFlags(Qt.Window)
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.ok_rows = ok_rows_container

        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowFlags(Qt.Window)

        self.gamma = 3.0

        self.last_used_directory_for_load = None
        self.last_used_directory_for_save = None

        self.history = History()

    def keyPressEvent(self, e):
        # avoid closing of dialog when Esc key pressed:
        if e.key() != Qt.Key_Escape:
            return super(PeakMapExplorer, self).keyPressEvent(e)

    def setWindowTitle(self):
        if self.peakmap2 is None:
            title = os.path.basename(self.peakmap.meta.get("source", ""))
        else:
            p1 = os.path.basename(self.peakmap.meta.get("source", ""))
            p2 = os.path.basename(self.peakmap2.meta.get("source", ""))
            title = "yellow=%s, blue=%s" % (p1, p2)
        super(PeakMapExplorer, self).setWindowTitle(title)

    def setup(self, peakmap, peakmap2=None, table=None):
        self.table = table

        def collect_precursor_mz(pm):
            for s in pm:
                if s.precursors:
                    if s.msLevel > 1:
                        yield s.precursors[0][0]

        self.ms_levels = set(peakmap.getMsLevels())
        self.precursor_mz = set(collect_precursor_mz(peakmap))
        if peakmap2 is not None:
            self.ms_levels &= set(peakmap2.getMsLevels())
            self.precursor_mz &= set(collect_precursor_mz(peakmap2))

        self.ms_levels = sorted(self.ms_levels)
        self.precursor_mz = sorted(self.precursor_mz)

        self.setup_table_widgets()
        self.setup_input_widgets()
        self.history_list = QComboBox(self)

        self.setup_ms2_widgets()
        self.full_pm = peakmap
        self.full_pm2 = peakmap2
        self.dual_mode = self.full_pm2 is not None

        self.current_ms_level = self.ms_levels[0]
        self.process_peakmap(self.current_ms_level)
        self.rtmin, self.rtmax, self.mzmin, self.mzmax = get_range(self.peakmap, self.peakmap2)

        self.setup_plot_widgets()
        self.setup_menu_bar()
        self.setup_layout()
        self.connect_signals_and_slots()
        self.setup_initial_values()
        self.plot_peakmap()

    def setup_ms2_widgets(self):
        self.spectra_selector_widget.set_data(self.ms_levels, self.precursor_mz)

    def setup_table_widgets(self):
        if self.table is not None:
            self.table_widget = create_table_widget(self.table, self)
            self.select_all_peaks = QPushButton("Select all peaks", self)
            self.unselect_all_peaks = QPushButton("Unselect all peaks", self)
            self.done_button = QPushButton("Done", self)

    def setup_menu_bar(self):
        self.menu_bar = QMenuBar(self)
        menu = QMenu("Peakmap Explorer", self.menu_bar)
        self.menu_bar.addMenu(menu)
        if not self.dual_mode:
            self.load_action = QAction("Load Peakmap", self)
            self.load_action.setShortcut(QKeySequence("Ctrl+L"))
            self.load_action2 = None
            menu.addAction(self.load_action)
        else:
            self.load_action = QAction("Load Yellow Peakmap", self)
            self.load_action2 = QAction("Load Blue Peakmap", self)
            menu.addAction(self.load_action)
            menu.addAction(self.load_action2)

        self.save_action = QAction("Save selected range as image", self)
        self.save_action.setShortcut(QKeySequence("Ctrl+S"))
        menu.addAction(self.save_action)

        menu = QMenu("Help", self.menu_bar)
        self.help_action = QAction("Help", self)
        self.help_action.setShortcut(QKeySequence("F1"))
        menu.addAction(self.help_action)
        self.menu_bar.addMenu(menu)

    def process_peakmap(self, ms_level, pre_mz_min=None, pre_mz_max=None):

        peakmap = self.full_pm.filter(lambda s: s.msLevel == ms_level)
        if ms_level > 1 and pre_mz_min is not None:
            peakmap = peakmap.filter(lambda s: s.precursors[0][0] >= pre_mz_min)
        if ms_level > 1 and pre_mz_max is not None:
            peakmap = peakmap.filter(lambda s: s.precursors[0][0] <= pre_mz_max)

        if self.full_pm2 is not None:
            peakmap2 = self.full_pm2.filter(lambda s: s.msLevel == ms_level)

        self.peakmap = peakmap
        if self.dual_mode:
            self.peakmap2 = peakmap2
        else:
            self.peakmap2 = None

        for i, msl in enumerate(self.ms_levels):
            if msl == ms_level:
                pass # TODO self.ms_level.setCurrentIndex(i)

        self.setWindowTitle()

    def setup_initial_values(self):

        imax = self.peakmap_plotter.get_total_imax()
        self.image_scaling_widget.set_max_intensity(imax)
        self.image_scaling_widget.set_gamma(self.gamma)

        self.view_range_widget.set_view_range(self.rtmin, self.rtmax, self.mzmin, self.mzmax)

    def setup_input_widgets(self):
        self.image_scaling_widget = ImageScalingWidget(self)
        self.spectra_selector_widget = SpectraSelectorWidget(self)
        self.view_range_widget = ViewRangeWidget(self)

    def setup_plot_widgets(self):
        self.peakmap_plotter = PeakMapPlottingWidget()
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.eic_plotter = EicPlottingWidget(with_range=False)
        self.mz_plotter = MzPlottingWidget()

        self.peakmap_plotter.set_logarithmic_scale(1)
        self.peakmap_plotter.set_gamma(self.gamma)

        self.eic_plotter.set_overall_range(self.rtmin, self.rtmax)
        self.mz_plotter.set_overall_range(self.mzmin, self.mzmax)


    def setup_layout(self):
        outer_layout = QVBoxLayout()
        outer_layout.addWidget(self.menu_bar)
        outer_layout.setStretch(0, 1)

        h_splitter = QSplitter(self)
        h_splitter.setOrientation(Qt.Horizontal)

        # FIRST COLUMN of h_splitter is chromatogram + peakmap:  ############################

        v_splitter1 = QSplitter(self)
        v_splitter1.setOrientation(Qt.Vertical)
        v_splitter1.addWidget(self.eic_plotter)
        v_splitter1.addWidget(self.peakmap_plotter)
        self.peakmap_plotter.setMinimumSize(250, 200)
        v_splitter1.setStretchFactor(0, 1)
        v_splitter1.setStretchFactor(1, 3)

        h_splitter.addWidget(v_splitter1)
        h_splitter.setStretchFactor(0, 2)

        # SECOND COLUMN of h_splittier holds controlx boxes + mz plot #######################

        v_splitter2 = QSplitter(self)
        v_splitter2.setOrientation(Qt.Vertical)

        v_splitter2.addWidget(self.image_scaling_widget)
        v_splitter2.addWidget(self.spectra_selector_widget)
        v_splitter2.addWidget(self.view_range_widget)
        v_splitter2.addWidget(self.history_list)
        v_splitter2.addWidget(self.mz_plotter)

        v_splitter2.setStretchFactor(0, 0)
        v_splitter2.setStretchFactor(1, 0)
        v_splitter2.setStretchFactor(2, 0)
        v_splitter2.setStretchFactor(3, 0)
        v_splitter2.setStretchFactor(4, 1)

        h_splitter.addWidget(v_splitter2)
        h_splitter.setStretchFactor(1, 1)

        # THIRD COLUMN of h_splittier holds control table + buttons ##########################
        if self.table:
            frame = QFrame(self)
            layout = QVBoxLayout(frame)
            frame.setLayout(layout)
            layout.addWidget(self.table_widget)

            button_row_layout = QHBoxLayout(frame)
            button_row_layout.addWidget(self.select_all_peaks)
            button_row_layout.addWidget(self.unselect_all_peaks)
            button_row_layout.addWidget(self.done_button)

            layout.addLayout(button_row_layout)
            h_splitter.addWidget(frame)
            h_splitter.setStretchFactor(2, 2)

        outer_layout.addWidget(h_splitter)
        self.setLayout(outer_layout)
        outer_layout.setStretch(1, 99)

    def connect_signals_and_slots(self):
        self.image_scaling_widget.USE_LOG_SCALE.connect(self.use_logscale)
        self.image_scaling_widget.GAMMA_CHANGED.connect(self.gamma_changed)

        self.image_scaling_widget.IMIN_CHANGED.connect(self.set_image_min)
        self.image_scaling_widget.IMAX_CHANGED.connect(self.set_image_max)

        self.spectra_selector_widget.MS_LEVEL_CHOSEN.connect(self.ms_level_chosen)
        self.spectra_selector_widget.PRECURSOR_RANGE_CHANGED.connect(self.set_precursor_range)

        self.view_range_widget.RANGE_CHANGED.connect(self.update_image_range)

        self.connect(self.history_list, SIGNAL("activated(int)"), self.history_item_selected)

        if self.dual_mode:
            self.connect(self.load_action, SIGNAL("triggered()"), self.do_load_yellow)
            self.connect(self.load_action2, SIGNAL("triggered()"), self.do_load_blue)
        else:
            self.connect(self.load_action, SIGNAL("triggered()"), self.do_load)
        self.connect(self.save_action, SIGNAL("triggered()"), self.do_save)
        self.connect(self.help_action, SIGNAL("triggered()"), self.show_help)

        self.peakmap_plotter.NEW_IMAGE_LIMITS.connect(self.image_limits_upated_by_user)

        self.peakmap_plotter.KEY_LEFT.connect(self.user_pressed_left_key_in_plot)
        self.peakmap_plotter.KEY_RIGHT.connect(self.user_pressed_right_key_in_plot)
        self.peakmap_plotter.KEY_BACKSPACE.connect(self.user_pressed_backspace_key_in_plot)
        self.peakmap_plotter.KEY_END.connect(self.user_pressed_end_key_in_plot)
        self.peakmap_plotter.CURSOR_MOVED.connect(self.cursor_moved_in_plot)
        self.eic_plotter.CURSOR_MOVED.connect(self.eic_cursor_moved)
        self.eic_plotter.VIEW_RANGE_CHANGED.connect(self.eic_view_range_changed)
        self.mz_plotter.CURSOR_MOVED.connect(self.mz_cursor_moved)
        self.mz_plotter.VIEW_RANGE_CHANGED.connect(self.mz_view_range_changed)

        if self.table is not None:
            self.connect(self.table_widget.verticalHeader(), SIGNAL("sectionClicked(int)"),
                         self.row_selected)
            self.connect(self.table_widget, SIGNAL("itemClicked(QTableWidgetItem*)"),
                         self.cell_clicked)
            self.connect(self.select_all_peaks, SIGNAL("pressed()"),
                         self.select_all_peaks_button_pressed)
            self.connect(self.unselect_all_peaks, SIGNAL("pressed()"),
                         self.unselect_all_peaks_button_pressed)
            self.connect(self.done_button, SIGNAL("pressed()"),
                         self.done_button_pressed)

            def key_release_handler(evt):
                tw = self.table_widget
                active_rows = set(ix.row() for ix in tw.selectionModel().selection().indexes())
                if active_rows:
                    row = active_rows.pop()
                    if evt.key() in (Qt.Key_Up, Qt.Key_Down):
                        tw.selectRow(row)
                        tw.verticalHeader().emit(SIGNAL("sectionClicked(int)"), row)
                        return
                return QTableWidget.keyPressEvent(tw, evt)

            self.table_widget.keyReleaseEvent = key_release_handler

    def cursor_moved_in_plot(self, rt, mz):
        self.eic_plotter.set_cursor_pos(rt)
        self.mz_plotter.set_cursor_pos(mz)

    def eic_cursor_moved(self, rt):
        self.peakmap_plotter.set_cursor_rt(rt)

    def eic_view_range_changed(self, rtmin, rtmax):
        """
        we want to avoid the loop   EIC_RANGE_CHANGED -> VIEW_RANGE_CHANGED -> EIC_RANGE_CHANGED
        and we do not want to fully block emitting of VIEW_RANGE_CHANGED.
        so self.peakmap_plotter.blockSignals() does not work here, instead we "cut" the last
        connection here:
        """
        self.eic_plotter.VIEW_RANGE_CHANGED.disconnect()
        self.peakmap_plotter.blockSignals(True)
        self.peakmap_plotter.set_rt_limits(rtmin, rtmax)
        self.peakmap_plotter.blockSignals(False)
        self.peakmap_plotter.replot()
        self.eic_plotter.VIEW_RANGE_CHANGED.connect(self.eic_view_range_changed)

    def mz_view_range_changed(self, mzmin, mzmax):
        """
        we want to avoid the loop  MZ_RANGE_CHANGED -> VIEW_RANGE_CHANGED -> MZ_RANGE_CHANGED
        and we do not want to fully block emitting of VIEW_RANGE_CHANGED.
        so self.peakmap_plotter.blockSignals() does not work here, instead we "cut" the last
        connection here:
        """
        self.mz_plotter.VIEW_RANGE_CHANGED.disconnect()
        self.peakmap_plotter.blockSignals(True)
        self.peakmap_plotter.set_mz_limits(mzmin, mzmax)
        self.peakmap_plotter.blockSignals(False)
        self.peakmap_plotter.replot()
        self.mz_plotter.VIEW_RANGE_CHANGED.connect(self.mz_view_range_changed)

    def mz_cursor_moved(self, mz):
        self.peakmap_plotter.set_cursor_mz(mz)

    def image_limits_upated_by_user(self, rtmin, rtmax, mzmin, mzmax):
        self.update_peakmap_projection_views(rtmin, rtmax, mzmin, mzmax)
        self.history.new_head((rtmin, rtmax, mzmin, mzmax))
        self.update_history_entries()

    def set_image_min(self, value):
        self.peakmap_plotter.set_imin(value)
        self.peakmap_plotter.replot()

    def set_image_max(self, value):
        self.peakmap_plotter.set_imax(value)
        self.peakmap_plotter.replot()

    def update_peakmap_projection_views(self, rtmin, rtmax, mzmin, mzmax):

        rts, chroma = self.peakmap.chromatogram(mzmin, mzmax)
        self.eic_plotter.del_all_items()
        if self.dual_mode:
            rts2, chroma2 = self.peakmap2.chromatogram(mzmin, mzmax, rtmin, rtmax)
            self.eic_plotter.add_eics([(rts, chroma), (rts2, chroma2)], configs=[blue_line, yellow_line])
        else:
            self.eic_plotter.add_eics([(rts, chroma)], configs=[grey_line])

        self.eic_plotter.shrink_and_replot(rtmin, rtmax)

        if self.dual_mode:
            data = [(self.peakmap, rtmin, rtmax, mzmin, mzmax, 3000),
                    (self.peakmap2, rtmin, rtmax, mzmin, mzmax, 3000)]
            configs = [dict(color="#aaaa00"), dict(color="#0000aa")]
            self.mz_plotter.plot_peakmaps(data, configs)
        else:
            self.mz_plotter.plot_peakmaps([(self.peakmap, rtmin, rtmax, mzmin, mzmax, 3000)])

        self.mz_plotter.shrink_and_replot(mzmin, mzmax)
        self.view_range_widget.set_view_range(rtmin, rtmax, mzmin, mzmax)

    def _handle_history_action(self, action):
        item = action()
        if item is not None:
            self.peakmap_plotter.set_limits_no_sig(*item)
            self.update_peakmap_projection_views(*item)
            self.update_history_entries()

    def user_pressed_left_key_in_plot(self):
        self._handle_history_action(self.history.go_back)

    def user_pressed_right_key_in_plot(self):
        self._handle_history_action(self.history.go_forward)

    def user_pressed_backspace_key_in_plot(self):
        self._handle_history_action(self.history.go_to_beginning)

    def user_pressed_end_key_in_plot(self):
        self._handle_history_action(self.history.go_to_end)

    def history_item_selected(self, index):
        self._handle_history_action(lambda index=index: self.history.set_position(index))

    @protect_signal_handler
    def do_save(self):
        pix = self.peakmap_plotter.paint_pixmap()
        while True:
            path = askForSave(self.last_used_directory_for_save,
                              caption="Save Image",
                              extensions=("png", "PNG")
                              )
            if path is None:
                break
            __, ext = os.path.splitext(path)
            if ext not in (".png", ".PNG"):
                QMessageBox.warning(self, "Warning", "wrong/missing extension '.png'")
            else:
                self.last_used_directory_for_save = os.path.dirname(path)
                pix.save(path)
                break
        return

    def _do_load(self, title, attribute):
        path = askForSingleFile(self.last_used_directory_for_load,
                                caption=title,
                                extensions=("mzML", "mzData", "mzXML")
                                )
        if path is not None:
            setattr(self, attribute, loadPeakMap(path))
            self.process_peakmap()
            self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
            self.setup_initial_values()
            self.setWindowTitle()
            self.peakmap_plotter.replot()
            self.plot_peakmap()
            self.last_used_directory_for_load = os.path.dirname(path)

    @protect_signal_handler
    def do_load(self):
        self._do_load("Load Peakmap", "peakmap")

    @protect_signal_handler
    def do_load_yellow(self):
        self._do_load("Load Yellow Peakmap", "peakmap")

    @protect_signal_handler
    def do_load_blue(self):
        self._do_load("Load Blue Peakmap", "peakmap2")

    @protect_signal_handler
    def select_all_peaks_button_pressed(self):
        for row in range(self.table_widget.rowCount()):
            item = self.table_widget.item(row, 0)
            item.setCheckState(Qt.Checked)

    @protect_signal_handler
    def unselect_all_peaks_button_pressed(self):
        for row in range(self.table_widget.rowCount()):
            item = self.table_widget.item(row, 0)
            item.setCheckState(Qt.Unchecked)

    @protect_signal_handler
    def done_button_pressed(self):
        self.ok_rows[:] = [i for i in range(len(self.table))
                           if self.table_widget.item(i, 0).checkState() == Qt.Checked]
        self.accept()

    @protect_signal_handler
    def row_selected(self, row_idx):
        row = self.table.getValues(self.table.rows[row_idx])
        needed = ["rtmin", "rtmax", "mzmin", "mzmax"]
        if all(n in row for n in needed):
            rtmin, rtmax, mzmin, mzmax = [row.get(ni) for ni in needed]
            self.peakmap_plotter.set_limits(rtmin, rtmax, mzmin, mzmax)
        else:
            needed = ["mzmin", "mzmax"]
            if all(n in row for n in needed):
                mzmin, mzmax = [row.get(ni) for ni in needed]
                self.peakmap_plotter.set_limits(self.rtmin, self.rtmax, mzmin, mzmax)

    @protect_signal_handler
    def cell_clicked(self, item):
        row = item.row()
        self.table_widget.selectRow(row)
        self.table_widget.verticalHeader().emit(SIGNAL("sectionClicked(int)"), row)

    @protect_signal_handler
    def show_help(self):
        html = resource_string("emzed.core.explorers", "help_peakmapexplorer.html")
        QWebSettings.globalSettings().setFontFamily(QWebSettings.StandardFont, 'Courier')
        QWebSettings.globalSettings().setFontSize(QWebSettings.DefaultFontSize, 12)
        v = QWebView(self)
        v.setHtml(html)
        dlg = QDialog(self, Qt.Window)
        dlg.setMinimumSize(300, 300)
        l = QVBoxLayout(dlg)
        l.addWidget(v)
        dlg.setLayout(l)
        dlg.show()

    def update_history_entries(self):
        self.history_list.clear()
        for item in self.history.items:
            rtmin, rtmax, mzmin, mzmax = item
            str_item = "%10.5f .. %10.5f %6.2fm...%6.2fm " % (mzmin, mzmax, rtmin / 60.0,
                                                              rtmax / 60.0)
            self.history_list.addItem(str_item)

        self.history_list.setCurrentIndex(self.history.position)

    @protect_signal_handler
    def use_logscale(self, is_log):
        self.peakmap_plotter.set_logarithmic_scale(is_log)
        self.peakmap_plotter.replot()

    @protect_signal_handler
    def ms_level_chosen(self, ms_level):
        if ms_level != self.current_ms_level:
            self.current_ms_level = ms_level
            self.process_peakmap(ms_level)
            self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
            self.peakmap_plotter.replot()
            self.plot_peakmap()

    @protect_signal_handler
    def set_precursor_range(self, pre_mz_min, pre_mz_max):
        self.process_peakmap(self.current_ms_level, pre_mz_min, pre_mz_max)
        self.peakmap_plotter.set_peakmaps(self.peakmap, self.peakmap2)
        self.peakmap_plotter.replot()
        self.plot_peakmap()

    @protect_signal_handler
    def gamma_changed(self, value):
        self.peakmap_plotter.set_gamma(value)
        self.peakmap_plotter.replot()

    @protect_signal_handler
    def update_image_range(self, rtmin, rtmax, mzmin, mzmax):

        rtmin *= 60.0
        rtmax *= 60.0

        if rtmin < self.rtmin:
            rtmin = self.rtmin
        if rtmax > self.rtmax:
            rtmax = self.rtmax
        if mzmin < self.mzmin:
            mzmin = self.mzmin
        if mzmax > self.mzmax:
            mzmax = self.mzmax
        rtmin, rtmax = sorted((rtmin, rtmax))
        mzmin, mzmax = sorted((mzmin, mzmax))

        self.peakmap_plotter.set_limits(rtmin, rtmax, mzmin, mzmax)

    def plot_peakmap(self):
        self.peakmap_plotter.set_limits(self.rtmin, self.rtmax, self.mzmin, self.mzmax)