Example #1
0
class QLttngcMainWindow(Qt.QMainWindow, utils.QtUiLoad):
    _UI_NAME = 'main'
    selected_session_changed = QtCore.pyqtSignal()
    session_button_middle_clicked = QtCore.pyqtSignal(object)

    def __init__(self, features):
        super().__init__()
        self._features = features
        self._load_icons()
        self._setup_model()
        self._setup_ui()
        self._setup_list_models()
        self._setup_dialogs()
        self._setup_actions()
        self._setup_signals()
        self.clear_lists()

    def center_screen(self):
        desktop_widget = Qt.QDesktopWidget()
        primary_screen = desktop_widget.primaryScreen()
        screen_w = desktop_widget.screen(primary_screen).width()
        screen_h = desktop_widget.screen(primary_screen).height()
        self.setGeometry(screen_w / 2 - self.width() / 2,
                         screen_h / 2 - self.height() / 2,
                         self.width(), self.height())

    @staticmethod
    def _create_pseudo_channel(name, domain):
        channel = model.Channel(name, domain, True, None, [])
        channel.is_pseudo = True

        return channel

    def _setup_model(self):
        self._session_buttons = {}
        self._selected_session_name = None
        self._channels_list_model = QLttngcChannelsListModel(self._active_icon,
                                                             self._inactive_icon)
        self._events_list_model = QLttngcEventsListModel(self._active_icon,
                                                         self._inactive_icon)
        self._pseudo_channels = {}
        self._pseudo_channel_jul = self._create_pseudo_channel('java.util.logging pseudo-channel',
                                                               model.Domain.JUL)
        self._pseudo_channel_log4j = self._create_pseudo_channel('log4j 1.2 pseudo-channel',
                                                                 model.Domain.LOG4J)
        self._pseudo_channels[model.Domain.JUL] = self._pseudo_channel_jul
        self._pseudo_channels[model.Domain.LOG4J] = self._pseudo_channel_log4j

        if features.Feature.PYTHON_DOMAIN in self._features:
            self._pseudo_channel_python = self._create_pseudo_channel('Python pseudo-channel',
                                                                      model.Domain.PYTHON)
            self._pseudo_channels[model.Domain.PYTHON] = self._pseudo_channel_python

        self._fully_disabled = False

    def _load_icons(self):
        self._play_icon = utils.get_qicon('play')
        self._stop_icon = utils.get_qicon('stop')
        self._active_icon = utils.get_qicon('active')
        self._inactive_icon = utils.get_qicon('inactive')

    def _setup_icons(self):
        actions_icons = [
            (self._action_load, 'load'),
            (self._action_save, 'save'),
            (self._action_quit, 'quit'),
            (self._action_add_session, 'add'),
            (self._action_remove_session, 'remove'),
            (self._action_remove_all_sessions, 'explosion'),
            (self._action_toggle_tracing, 'play'),
            (self._action_add_channel, 'add'),
            (self._action_attach_context, 'attach'),
            (self._action_enable_channels, 'enable'),
            (self._action_disable_channels, 'disable'),
            (self._action_add_events, 'add'),
            (self._action_enable_events, 'enable'),
            (self._action_disable_events, 'disable'),
            (self._action_show_sessions, 'sessions'),
            (self._action_show_channels, 'channels'),
            (self._action_show_events, 'events'),
            (self._action_show_object_infos, 'infos'),
            (self._action_show_last_operation, 'terminal'),
            (self._action_show_filters, 'filter'),
            (self._action_auto_refresh, 'auto-refresh'),
            (self._action_refresh, 'refresh'),
            (self._action_about, 'about'),
            (self._action_goto_lttng_org, 'lttng'),
        ]

        for pair in actions_icons:
            icon = utils.get_qicon(pair[1])
            pair[0].setIcon(icon)

        buttons_icons = [
            (self._add_session_btn, 'add'),
            (self._add_channel_btn, 'add'),
            (self._attach_context_btn, 'attach'),
            (self._enable_channels_btn, 'enable'),
            (self._disable_channels_btn, 'disable'),
            (self._add_events_btn, 'add'),
            (self._enable_events_btn, 'enable'),
            (self._disable_events_btn, 'disable'),
        ]

        for pair in buttons_icons:
            icon = utils.get_qicon(pair[1])
            pair[0].setIcon(icon)
            pair[0].setIconSize(Qt.QSize(16, 16))

        self.setWindowIcon(utils.get_qicon('lttng'))

    def _setup_dialogs(self):
        self._about_dialog = QLttngcAboutDialog()

    def _setup_filter_btns(self):
        # channels
        domains = [
            model.Domain.KERNEL,
            model.Domain.USER,
            model.Domain.JUL,
            model.Domain.LOG4J,
        ]

        if features.Feature.PYTHON_DOMAIN in self._features:
            domains.append(model.Domain.PYTHON)

        self._channel_filter_btns_layout = QLttngcFilterButtonsLayout(domains, 'channels')
        self._channel_filter_btns_layout.button_clicked.connect(self._channel_filter_btn_clicked)
        self._channels_box.layout().insertLayout(0, self._channel_filter_btns_layout)
        self._channel_filter_btns_layout.set_buttons_visible(False)

        # events
        self._event_filter_btns_layout = QLttngcFilterButtonsLayout(domains, 'events')
        self._event_filter_btns_layout.button_clicked.connect(self._event_filter_btn_clicked)
        self._events_box.layout().insertLayout(0, self._event_filter_btns_layout)
        self._event_filter_btns_layout.set_buttons_visible(False)

    def _setup_ui(self):
        self._load_ui()
        self._setup_icons()
        self._setup_filter_btns()
        self._base_title = 'lttngc'
        self._object_infos_box.hide()
        self._last_operation_dock.hide()

    def _create_proxy_model(self, src_model, list_view):
        proxy_model = QLttngcDomainSortFilterProxyModel()
        proxy_model.setSourceModel(src_model)
        proxy_model.enable_domain(model.Domain.KERNEL, True)
        proxy_model.enable_domain(model.Domain.USER, True)
        proxy_model.enable_domain(model.Domain.JUL, True)
        proxy_model.enable_domain(model.Domain.LOG4J, True)

        if features.Feature.PYTHON_DOMAIN in self._features:
            proxy_model.enable_domain(model.Domain.PYTHON, True)

        list_view.setModel(proxy_model)

        return proxy_model

    def _setup_list_models(self):
        # channels
        proxy_model = self._create_proxy_model(self._channels_list_model,
                                               self._channels_list)
        self._channels_proxy_model = proxy_model

        # events
        proxy_model = self._create_proxy_model(self._events_list_model,
                                               self._events_list)
        self._events_proxy_model = proxy_model

    def _goto_lttng_org(self):
        utils.goto_url('http://lttng.org/')

    def _show_about_dialog(self):
        pos = self.pos()
        pos.setX(pos.x() + 40)
        pos.setY(pos.y() + 40)
        self._about_dialog.show_move(pos)

    def _setup_actions(self):
        self._action_goto_lttng_org.triggered.connect(self._goto_lttng_org)
        self._action_about.triggered.connect(self._show_about_dialog)

    def _channel_filter_btn_clicked(self, btn, checked):
        domain = btn.domain
        self._channels_proxy_model.enable_domain(domain, checked)
        self._update_view_from_model()

    def _event_filter_btn_clicked(self, btn, checked):
        domain = btn.domain
        self._events_proxy_model.enable_domain(domain, checked)
        self._update_view_from_model()

    def _setup_signals(self):
        # selection changed
        channels_selection_model = self._channels_list.selectionModel()
        channels_selection_model.selectionChanged.connect(self._channels_selection_changed)
        events_selection_model = self._events_list.selectionModel()
        events_selection_model.selectionChanged.connect(self._events_selection_changed)

        # click (lists)
        self._channels_list.clicked.connect(self._channels_list_clicked)
        self._events_list.clicked.connect(self._events_list_clicked)

        # show filter buttons
        self._action_show_filters.triggered.connect(self._show_channel_filters_triggered)
        self._action_show_filters.triggered.connect(self._show_event_filters_triggered)

        # support closing a dock from the dock itself
        self._last_operation_dock.visibilityChanged.connect(self._last_op_visibility_changed)

    def _last_op_visibility_changed(self, is_visible):
        self._action_show_last_operation.setChecked(is_visible)

    def _show_channel_filters_triggered(self, checked):
        self._channel_filter_btns_layout.set_buttons_visible(checked)

    def _show_event_filters_triggered(self, checked):
        self._event_filter_btns_layout.set_buttons_visible(checked)

    def get_channel_at_row(self, row):
        # get channel object
        index = self._channels_proxy_model.index(row, 0)
        src_index = self._channels_proxy_model.mapToSource(index)
        item = self._channels_list_model.get_item_at_row(src_index.row())

        return item.obj

    def get_event_at_row(self, row):
        # get event object
        index = self._events_proxy_model.index(row, 0)
        src_index = self._events_proxy_model.mapToSource(index)
        item = self._events_list_model.get_item_at_row(src_index.row())

        return item.obj

    def _event_clicked(self, index):
        # get event object
        event = self.get_event_at_row(index.row())
        self._set_event_infos(event)

    def _set_channel_infos(self, channel):
        # update object infos
        obj_name = 'Channel'

        if channel.is_pseudo:
            obj_name = 'Pseudo-channel'

        domain_name = model.DOMAIN_NAMES[channel.domain]
        text = '<b>{}</b>: <i>{}</i> ({} domain)'.format(obj_name,
                                                         channel.name,
                                                         domain_name)

        if channel.is_pseudo:
            self._object_infos_lbl.setText(text)
            return

        text += '<br>'
        attr = channel.attributes
        mode = model.CHANNEL_MODE_NAMES[attr.mode]
        output_type = model.CHANNEL_OUTPUT_TYPE_NAMES[attr.output_type]
        buffer_scheme = model.CHANNEL_BUFFER_SCHEME_NAMES[attr.buffer_scheme]
        fmt = '<b>Mode</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;<b>Output type</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;<b>Buffering</b>: {}<br>'
        text += fmt.format(mode, output_type, buffer_scheme)
        subbuf_size = utils.bytes_to_human(attr.subbuf_size)

        if attr.tracefile_size <= 0:
            tracefile = 'unlimited size'
        else:
            tracefile_size = utils.bytes_to_human(attr.tracefile_size)
            tracefile = '{} x {}'.format(attr.tracefile_count, tracefile_size)

        fmt = '<b>Sub-buffers</b>: {} x {}&nbsp;&nbsp;&nbsp;&nbsp;<b>Trace files</b>: {}<br>'
        text += fmt.format(attr.subbuf_count, subbuf_size, tracefile)
        switch_timer = attr.switch_timer_interval

        if switch_timer == 0:
            switch = 'stopped'
        else:
            switch = '{:.6f} s'.format(switch_timer)

        read_timer = attr.read_timer_interval

        if read_timer == 0:
            read = 'stopped'
        else:
            read = '{:.6f} s'.format(read_timer)

        fmt = '<b>Switch timer</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;<b>Read timer</b>: {}'
        text += fmt.format(switch, read)
        self._object_infos_lbl.setText(text)

    def _set_event_infos(self, event):
        def bool_to_yes_no(b):
            return 'yes' if b else 'no'

        # update object infos
        channel_name = event.channel.name
        domain_name = model.DOMAIN_NAMES[event.channel.domain]
        fmt = '<b>Event</b>: <i>{}</i> ({}, {} domain, channel <i>{}</i>)<br>'
        event_type_name = model.EVENT_TYPE_NAMES[event.source.type]
        text = fmt.format(event.source.name, event_type_name, domain_name,
                          event.channel.name)
        ll = 'none'

        if event.log_level is not None and event.log_level_function != model.LogLevelFunction.ANY:
            if event.log_level_function == model.LogLevelFunction.LT_EQ:
                func = 'lesser than or equal to'
            else:
                func = 'equal to'

            ll = '{} {}'.format(func, event.log_level)

        text += '<b>Log level</b>: {}'.format(ll)
        has_filter = bool_to_yes_no(event.has_filter)
        text += '&nbsp;&nbsp;&nbsp;&nbsp;<b>Has filter?</b>: {}'.format(has_filter)

        if event.has_exclusions is not None:
            has_exclusions = bool_to_yes_no(event.has_exclusions)
            text += '&nbsp;&nbsp;&nbsp;&nbsp;<b>Has exclusions?</b>: {}'.format(has_exclusions)

        self._object_infos_lbl.setText(text)

    def _session_button_clicked(self, checked):
        session_button = self.sender()
        session_summary = session_button.session_summary

        if self._selected_session_name == session_summary.name and not checked:
            # cancel toggling
            session_button.setChecked(True)
            return

        # uncheck current one
        self.set_selected_session_name(session_summary.name)

        # notify
        self.selected_session_changed.emit()

    def _update_toggle_tracing_btn(self):
        selected_session_btn = self._get_selected_session_button()

        if not selected_session_btn:
            self._action_toggle_tracing.setEnabled(False)
            return

        if selected_session_btn.session_summary.is_active:
            self._action_toggle_tracing.setText('&Stop tracing')
            self._action_toggle_tracing.setToolTip('Stop tracing')
            self._action_toggle_tracing.setStatusTip('Stop tracing')
            self._action_toggle_tracing.setIcon(self._stop_icon)
        else:
            self._action_toggle_tracing.setText('&Start tracing')
            self._action_toggle_tracing.setToolTip('Start tracing')
            self._action_toggle_tracing.setStatusTip('Start tracing')
            self._action_toggle_tracing.setIcon(self._play_icon)

        self._action_toggle_tracing.setEnabled(True)

    def _update_selected_session_button(self):
        for session_button in self._session_buttons.values():
            session_summary = session_button.session_summary

            if session_summary.name == self._selected_session_name:
                if not session_button.isChecked():
                    session_button.setChecked(True)
            else:
                if session_button.isChecked():
                    session_button.setChecked(False)

    def _update_remove_session_btn(self):
        selected_session_btn = self._get_selected_session_button()
        is_enabled = bool(selected_session_btn)
        self._action_remove_session.setEnabled(is_enabled)
        self._action_remove_all_sessions.setEnabled(is_enabled)

    def _sort_lists(self):
        self._channels_proxy_model.sort(0)
        self._events_proxy_model.sort(0)

    def _channels_list_clicked(self, index):
        channel = self.get_channel_at_row(index.row())
        self._set_channel_infos(channel)

    def _events_list_clicked(self, index):
        event = self.get_event_at_row(index.row())
        self._set_event_infos(event)

    def _events_selection_changed(self, selected, deselected):
        events = self.get_selected_events()

        if len(events) == 1:
            self._set_event_infos(events[0])

        self._update_view_from_model()

    def _channels_selection_changed(self, selected, deselected):
        channels = self.get_selected_channels()

        if len(channels) == 1:
            self._set_channel_infos(channels[0])

        self._clear_events_list()
        self._update_events_list_from_selected_channels()
        self._update_view_from_model()

    def _update_widgets_enabled(self):
        # one session?
        one_session = bool(self._session_buttons)
        one_session = one_session and not self._fully_disabled

        # one channel selected?
        channels = self.get_selected_channels()
        one_channel = bool(channels)
        one_channel = one_channel and not self._fully_disabled

        # selected channels are all pseudo-channels?
        all_pseudo = True

        for channel in channels:
            all_pseudo = all_pseudo and channel.is_pseudo

        # selected channels have the same domain?
        same_domain = False
        selected_domains = set()

        for channel in channels:
            selected_domains.add(channel.domain)

        if len(selected_domains) == 1:
            same_domain = True

        same_domain = same_domain and not self._fully_disabled

        # one event selected?
        one_event = bool(self.get_selected_events())
        one_event = one_event and not self._fully_disabled

        # enable/disable buttons and actions
        if not one_session:
            self._clear_lists_no_update()

        self._add_session_btn.setEnabled(True)
        self._action_load.setEnabled(not self._fully_disabled)
        self._action_add_session.setEnabled(True)
        self._action_remove_session.setEnabled(one_session)
        self._action_remove_all_sessions.setEnabled(one_session)
        self._add_channel_btn.setEnabled(one_session)
        self._action_add_channel.setEnabled(one_session)
        self._action_save.setEnabled(one_session)
        self._menu_session.setEnabled(not self._fully_disabled)
        self._menu_channel.setEnabled(one_session)
        self._channels_box.setEnabled(one_session)
        en_dis_channels = one_channel and not all_pseudo
        self._enable_channels_btn.setEnabled(en_dis_channels)
        self._disable_channels_btn.setEnabled(en_dis_channels)
        self._attach_context_btn.setEnabled(en_dis_channels)
        self._add_events_btn.setEnabled(same_domain)
        self._action_enable_channels.setEnabled(en_dis_channels)
        self._action_disable_channels.setEnabled(en_dis_channels)
        self._action_attach_context.setEnabled(en_dis_channels)
        self._action_add_events.setEnabled(same_domain)
        self._menu_event.setEnabled(one_channel)
        self._events_box.setEnabled(one_channel)
        self._enable_events_btn.setEnabled(one_event)
        self._disable_events_btn.setEnabled(one_event)
        self._action_enable_events.setEnabled(one_event)
        self._action_disable_events.setEnabled(one_event)

    def _update_title(self):
        sel_session_summary = self.get_selected_session_summary()

        if sel_session_summary is None:
            self.setWindowTitle(self._base_title)
            return

        title = '{} [{}]'.format(self._base_title, sel_session_summary.name)
        self.setWindowTitle(title)

    def _update_view_from_model(self):
        self._update_selected_session_button()
        self._update_toggle_tracing_btn()
        self._update_remove_session_btn()
        self._sort_lists()
        self._update_widgets_enabled()
        self._update_title()

    def _add_session_button(self, session_summary):
        btn = QLttngcSessionButton(session_summary, self._active_icon,
                             self._inactive_icon)
        insert_pos = self._session_layout.count() - 2
        self._session_layout.insertWidget(insert_pos, btn)
        self._session_buttons[session_summary.name] = btn
        btn.clicked.connect(self._session_button_clicked)
        btn.middle_clicked.connect(self.session_button_middle_clicked)

        return btn

    def _get_session_button(self, sname):
        if sname in self._session_buttons:
            return self._session_buttons[sname]

    def _remove_session_button(self, sname):
        self._session_buttons[sname].setParent(None)
        del self._session_buttons[sname]

    def update_session_buttons_from_summaries(self, session_summaries):
        known_snames = []

        # update/add
        for summary in session_summaries.values():
            sname = summary.name

            # does not exist?
            btn = self._get_session_button(sname)

            if btn is None:
                # create it
                btn = self._add_session_button(summary)

            # update session summary
            btn.session_summary = summary

            # mark it as known
            known_snames.append(sname)

        # now remove unknown ones
        to_remove = []

        for sname, session_btn in self._session_buttons.items():
            if sname not in known_snames:
                to_remove.append(sname)

        for sname in to_remove:
            self._remove_session_button(sname)

        # remove current selected session if it was removed
        if self._selected_session_name not in self._session_buttons:
            self._selected_session_name = None

        self._update_view_from_model()

    def _get_selected_session_button(self):
        if self._selected_session_name is None:
            return

        return self._session_buttons[self._selected_session_name]

    def set_selected_session_name(self, sname):
        if sname not in self._session_buttons:
            return

        if self._selected_session_name == sname:
            return

        self._selected_session_name = sname
        self._channels_list_model.set_channels(list(self._pseudo_channels.values()))
        self._update_view_from_model()

    def get_session_names(self):
        return list(self._session_buttons.keys())

    def get_selected_session_summary(self):
        selected_button = self._get_selected_session_button()

        if not selected_button:
            return

        return selected_button.session_summary

    def _update_events_list_from_selected_channels(self):
        channels = self.get_selected_channels()
        events = []

        for channel in channels:
            for event in channel.events:
                events.append(event)

        self._events_list_model.set_events(events)

    def update_lists_from_channels(self, channels):
        # add empty pseudo-channels if they're missing
        domains = set([
            model.Domain.JUL,
            model.Domain.LOG4J,
        ])

        if features.Feature.PYTHON_DOMAIN in self._features:
            domains.add(model.Domain.PYTHON)

        for channel in channels:
            if channel.domain in domains:
                domains.remove(channel.domain)

        for domain in domains:
            pseudo_channel = self._pseudo_channels[domain]
            channels.append(pseudo_channel)

        self._channels_list_model.set_channels(channels)
        self._update_events_list_from_selected_channels()
        self._update_view_from_model()

    def get_current_channels(self):
        return self._channels_list_model.objects

    def get_current_events(self):
        return self._events_list_model.objects

    def _clear_events_list(self):
        self._events_list.selectionModel().reset()
        self._events_list_model.set_events([])

    def _clear_channels_list(self):
        self._channels_list.selectionModel().reset()
        self._channels_list_model.set_channels(list(self._pseudo_channels.values()))

    def _clear_lists_no_update(self):
        self._clear_events_list()
        self._clear_channels_list()

    def clear_lists(self):
        self._clear_lists_no_update()
        self._update_view_from_model()

    @staticmethod
    def _get_selected_objects(object_list, get_at_row):
        model_index_list = object_list.selectedIndexes()
        objects = []

        for index in model_index_list:
            obj = get_at_row(index.row())
            objects.append(obj)

        return objects

    def get_selected_channels(self):
        return self._get_selected_objects(self._channels_list,
                                          self.get_channel_at_row)

    def get_selected_events(self):
        return self._get_selected_objects(self._events_list,
                                          self.get_event_at_row)

    def full_disable(self):
        to_remove = list(self._session_buttons.keys())

        for sname in to_remove:
            self._remove_session_button(sname)

        self._selected_session_name = None
        self._fully_disabled = True
        self._update_view_from_model()

    def full_enable(self):
        self._fully_disabled = False
        self._update_view_from_model()
Example #2
0
 def _setup_dialogs(self):
     self._about_dialog = QLttngcAboutDialog()