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
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>: {} <b>Output type</b>: {} <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 {} <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>: {} <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 += ' <b>Has filter?</b>: {}'.format(has_filter) if event.has_exclusions is not None: has_exclusions = bool_to_yes_no(event.has_exclusions) text += ' <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()