class ObjectBrowser(QtGui.QMainWindow): """ Object browser main application window. """ _n_instances = 0 def __init__(self, obj, name = '', attribute_columns = DEFAULT_ATTR_COLS, attribute_details = DEFAULT_ATTR_DETAILS, show_routine_attributes = None, show_special_attributes = None, reset = False): """ Constructor :param obj: any Python object or variable :param name: name of the object as it will appear in the root node :param attribute_columns: list of AttributeColumn objects that define which columns are present in the table and their defaults :param attribute_details: list of AttributeDetails objects that define which attributes can be selected in the details pane. :param show_routine_attributes: if True rows where the 'is attribute' and 'is routine' columns are both True, are displayed. Otherwise they are hidden. :param show_special_attributes: if True rows where the 'is attribute' is True and the object name starts and ends with two underscores, are displayed. Otherwise they are hidden. :param reset: If true the persistent settings, such as column widths, are reset. """ super(ObjectBrowser, self).__init__() ObjectBrowser._n_instances += 1 self._instance_nr = self._n_instances # Model self._attr_cols = attribute_columns self._attr_details = attribute_details (show_routine_attributes, show_special_attributes) = self._readModelSettings(reset = reset, show_routine_attributes = show_routine_attributes, show_special_attributes = show_special_attributes) self._tree_model = TreeModel(obj, root_obj_name = name, attr_cols = self._attr_cols, show_routine_attributes = show_routine_attributes, show_special_attributes = show_special_attributes) # Views self._setup_actions() self._setup_menu() self._setup_views() self.setWindowTitle("{} - {}".format(PROGRAM_NAME, name)) app = QtGui.QApplication.instance() app.lastWindowClosed.connect(app.quit) self._readViewSettings(reset = reset) # Update views with model self.toggle_special_attribute_action.setChecked(show_special_attributes) self.toggle_callable_action.setChecked(show_routine_attributes) # Select first row so that a hidden root node will not be selected. first_row = self._tree_model.first_item_index() self.obj_tree.setCurrentIndex(first_row) def _make_show_column_function(self, column_idx): """ Creates a function that shows or hides a column.""" show_column = lambda checked: self.obj_tree.setColumnHidden(column_idx, not checked) return show_column def _setup_actions(self): """ Creates the main window actions. """ # Show/hide callable objects self.toggle_callable_action = \ QtGui.QAction("Show routine attributes", self, checkable=True, statusTip = "Shows/hides attributes that are routings (functions, methods, etc)") assert self.toggle_callable_action.toggled.connect(self.toggle_callables) # Show/hide special attributes self.toggle_special_attribute_action = \ QtGui.QAction("Show __special__ attributes", self, checkable=True, statusTip = "Shows or hides __special__ attributes") assert self.toggle_special_attribute_action.toggled.connect(self.toggle_special_attributes) def _setup_menu(self): """ Sets up the main menu. """ file_menu = self.menuBar().addMenu("&File") file_menu.addAction("C&lose", self.close_window, "Ctrl+W") file_menu.addAction("E&xit", self.quit_application, "Ctrl+Q") if DEBUGGING is True: file_menu.addSeparator() file_menu.addAction("&Test", self.my_test, "Ctrl+T") view_menu = self.menuBar().addMenu("&View") self.show_cols_submenu = view_menu.addMenu("Table columns") view_menu.addSeparator() view_menu.addAction(self.toggle_callable_action) view_menu.addAction(self.toggle_special_attribute_action) self.menuBar().addSeparator() help_menu = self.menuBar().addMenu("&Help") help_menu.addAction('&About', self.about) def _setup_views(self): """ Creates the UI widgets. """ self.central_splitter = QtGui.QSplitter(self, orientation = QtCore.Qt.Vertical) self.setCentralWidget(self.central_splitter) central_layout = QtGui.QVBoxLayout() self.central_splitter.setLayout(central_layout) # Tree widget self.obj_tree = ToggleColumnTreeView() self.obj_tree.setAlternatingRowColors(True) self.obj_tree.setModel(self._tree_model) self.obj_tree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.obj_tree.setUniformRowHeights(True) self.obj_tree.setAnimated(True) self.obj_tree.add_header_context_menu() # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. obj_tree_header = self.obj_tree.header() obj_tree_header.setMovable(True) obj_tree_header.setStretchLastSection(False) for action in self.obj_tree.toggle_column_actions_group.actions(): self.show_cols_submenu.addAction(action) central_layout.addWidget(self.obj_tree) # Bottom pane bottom_pane_widget = QtGui.QWidget() bottom_layout = QtGui.QHBoxLayout() bottom_layout.setSpacing(0) bottom_layout.setContentsMargins(5, 5, 5, 5) # left top right bottom bottom_pane_widget.setLayout(bottom_layout) central_layout.addWidget(bottom_pane_widget) group_box = QtGui.QGroupBox("Details") bottom_layout.addWidget(group_box) group_layout = QtGui.QHBoxLayout() group_layout.setContentsMargins(2, 2, 2, 2) # left top right bottom group_box.setLayout(group_layout) # Radio buttons radio_widget = QtGui.QWidget() radio_layout = QtGui.QVBoxLayout() radio_layout.setContentsMargins(0, 0, 0, 0) # left top right bottom radio_widget.setLayout(radio_layout) self.button_group = QtGui.QButtonGroup(self) for button_id, attr_detail in enumerate(self._attr_details): radio_button = QtGui.QRadioButton(attr_detail.name) radio_layout.addWidget(radio_button) self.button_group.addButton(radio_button, button_id) self.button_group.buttonClicked[int].connect(self._change_details_field) self.button_group.button(0).setChecked(True) radio_layout.addStretch(1) group_layout.addWidget(radio_widget) # Editor widget font = QtGui.QFont() font.setFamily('Courier') font.setFixedPitch(True) #font.setPointSize(14) self.editor = QtGui.QPlainTextEdit() self.editor.setReadOnly(True) self.editor.setFont(font) group_layout.addWidget(self.editor) # Splitter parameters self.central_splitter.setCollapsible(0, False) self.central_splitter.setCollapsible(1, True) self.central_splitter.setSizes([400, 200]) self.central_splitter.setStretchFactor(0, 10) self.central_splitter.setStretchFactor(1, 0) # Connect signals selection_model = self.obj_tree.selectionModel() assert selection_model.currentChanged.connect(self._update_details) # End of setup_methods def _settings_group_name(self, prefix): """ The persistent settings are stored per combination of column names and windows instance number. """ column_names = ",".join([col.name for col in self._attr_cols]) settings_str = column_names + str(self._instance_nr) settings_grp = "{}_{}".format(prefix, hex(hash(settings_str))) logger.debug(" settings group is: {!r}".format(settings_grp)) return settings_grp def _readModelSettings(self, reset=False, show_routine_attributes = None, show_special_attributes = None): """ Reads the persistent model settings . The persistent settings (show_routine_attributes, show_special_attributes) can be \ overridden by giving it a True or False value. If reset is True and the setting is None, True is used as default. """ default_sra = True default_ssa = True if reset: logger.debug("Resetting persistent model settings") if show_routine_attributes is None: show_routine_attributes = default_sra if show_special_attributes is None: show_special_attributes = default_ssa else: logger.debug("Reading model settings for window: {:d}".format(self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('model')) if show_routine_attributes is None: show_routine_attributes = setting_str_to_bool( settings.value("show_routine_attributes", default_sra)) if show_special_attributes is None: show_special_attributes = setting_str_to_bool( settings.value("show_special_attributes", default_ssa)) settings.endGroup() logger.debug("read show_routine_attributes: {!r}".format(show_routine_attributes)) logger.debug("read show_special_attributes: {!r}".format(show_special_attributes)) return (show_routine_attributes, show_special_attributes) def _writeModelSettings(self): """ Writes the model settings to the persistent store """ logger.debug("Writing model settings for window: {:d}".format(self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('model')) logger.debug("writing show_routine_attributes: {!r}".format(self._tree_model.getShowCallables())) logger.debug("wrting show_special_attributes: {!r}".format(self._tree_model.getShowSpecialAttributes())) settings.setValue("show_routine_attributes", self._tree_model.getShowCallables()) settings.setValue("show_special_attributes", self._tree_model.getShowSpecialAttributes()) settings.endGroup() def _readViewSettings(self, reset=False): """ Reads the persistent program settings :param reset: If True, the program resets to its default settings """ pos = QtCore.QPoint(20 * self._instance_nr, 20 * self._instance_nr) window_size = QtCore.QSize(1024, 700) details_button_idx = 0 header = self.obj_tree.header() header_restored = False if reset: logger.debug("Resetting persistent view settings") else: logger.debug("Reading view settings for window: {:d}".format(self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('view')) pos = settings.value("main_window/pos", pos) window_size = settings.value("main_window/size", window_size) details_button_idx = int(settings.value("details_button_idx", details_button_idx)) self.central_splitter.restoreState(settings.value("central_splitter/state")) header_restored = self.obj_tree.read_view_settings('table/header_state', settings, reset) settings.endGroup() if not header_restored: column_sizes = [col.width for col in self._attr_cols] column_visible = [col.col_visible for col in self._attr_cols] for idx, size in enumerate(column_sizes): if size > 0: # Just in case header.resizeSection(idx, size) for idx, visible in enumerate(column_visible): self.obj_tree.toggle_column_actions_group.actions()[idx].setChecked(visible) self.resize(window_size) self.move(pos) button = self.button_group.button(details_button_idx) if button is not None: button.setChecked(True) def _writeViewSettings(self): """ Writes the view settings to the persistent store """ logger.debug("Writing view settings for window: {:d}".format(self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('view')) self.obj_tree.write_view_settings("table/header_state", settings) settings.setValue("central_splitter/state", self.central_splitter.saveState()) settings.setValue("details_button_idx", self.button_group.checkedId()) settings.setValue("main_window/pos", self.pos()) settings.setValue("main_window/size", self.size()) settings.endGroup() @QtCore.Slot(QtCore.QModelIndex, QtCore.QModelIndex) def _update_details(self, current_index, _previous_index): """ Shows the object details in the editor given an index. """ tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _change_details_field(self, _button_id=None): """ Changes the field that is displayed in the details pane """ #logger.debug("_change_details_field: {}".format(_button_id)) current_index = self.obj_tree.selectionModel().currentIndex() tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _update_details_for_item(self, tree_item): """ Shows the object details in the editor given an tree_item """ self.editor.setStyleSheet("color: black;") try: #obj = tree_item.obj button_id = self.button_group.checkedId() assert button_id >= 0, "No radio button selected. Please report this bug." attr_details = self._attr_details[button_id] data = attr_details.data_fn(tree_item) self.editor.setPlainText(data) self.editor.setWordWrapMode(attr_details.line_wrap) except StandardError, ex: self.editor.setStyleSheet("color: red;") stack_trace = traceback.format_exc() self.editor.setPlainText("{}\n\n{}".format(ex, stack_trace)) self.editor.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) if DEBUGGING is True: raise
class ObjectBrowser(FMainWindow): """ Object browser main application window. """ _n_instances = 0 def __init__(self, obj, name='', attribute_columns=DEFAULT_ATTR_COLS, attribute_details=DEFAULT_ATTR_DETAILS, show_routine_attributes=None, show_special_attributes=None, reset=False): """ Constructor :param obj: any Python object or variable :param name: name of the object as it will appear in the root node :param attribute_columns: list of AttributeColumn objects that define which columns are present in the table and their defaults :param attribute_details: list of AttributeDetails objects that define which attributes can be selected in the details pane. :param show_routine_attributes: if True rows where the 'is attribute' and 'is routine' columns are both True, are displayed. Otherwise they are hidden. :param show_special_attributes: if True rows where the 'is attribute' is True and the object name starts and ends with two underscores, are displayed. Otherwise they are hidden. :param reset: If true the persistent settings, such as column widths, are reset. """ super(ObjectBrowser, self).__init__() ObjectBrowser._n_instances += 1 self._instance_nr = self._n_instances # Model self._attr_cols = attribute_columns self._attr_details = attribute_details (show_routine_attributes, show_special_attributes) = self._readModelSettings( reset=reset, show_routine_attributes=show_routine_attributes, show_special_attributes=show_special_attributes) self.show_routine_attributes = show_routine_attributes self.show_special_attributes = show_special_attributes # Views self._setup_views() self.setModel(obj) self._setup_actions() self._setup_menu() self.setWindowTitle("{} - {}".format(PROGRAM_NAME, name)) app = QtGui.QApplication.instance() app.lastWindowClosed.connect(app.quit) self.initSize() self.titleBar().skinButton.hide() self.titleBar().closeButton.clicked.connect(self.close) self.setWindowIcon(":/icons/dark/appbar.tree.png") self.titleBar().modeButton.click() self.toggle_callable_action.toggled.emit(False) self.toggle_special_attribute_action.toggled.emit(False) self.setskin() def setskin(self, skinID="BB"): path = os.path.dirname(__file__) # setSkinForApp( os.path.join(path, '%s.qss' % skinID)) # 设置主窗口样式 fd = open(os.path.join(path, '%s.qss' % skinID), "r") style = fd.read() fd.close() self.setStyleSheet(style) def initSize(self): desktopWidth = QtGui.QDesktopWidget().availableGeometry().width() desktopHeight = QtGui.QDesktopWidget().availableGeometry().height() self.resize(desktopWidth * 0.6, desktopHeight * 0.8) self.moveCenter() def setModel(self, obj): self._tree_model = TreeModel( obj, root_obj_name='', attr_cols=self._attr_cols, show_routine_attributes=self.show_routine_attributes, show_special_attributes=self.show_special_attributes) self.obj_tree.setModel(self._tree_model) first_row = self._tree_model.first_item_index() self.obj_tree.setCurrentIndex(first_row) # Connect signals selection_model = self.obj_tree.selectionModel() selection_model.currentChanged.connect(self._update_details) def _make_show_column_function(self, column_idx): """ Creates a function that shows or hides a column.""" show_column = lambda checked: self.obj_tree.setColumnHidden( column_idx, not checked) return show_column def _setup_actions(self): """ Creates the main window actions. """ # Show/hide callable objects self.toggle_callable_action = \ QtGui.QAction("Show routine attributes", self, checkable=True, statusTip = "Shows/hides attributes that are routings (functions, methods, etc)") self.toggle_callable_action.toggled.connect(self.toggle_callables) # Show/hide special attributes self.toggle_special_attribute_action = \ QtGui.QAction("Show __special__ attributes", self, checkable=True, statusTip = "Shows or hides __special__ attributes") self.toggle_special_attribute_action.toggled.connect( self.toggle_special_attributes) def _setup_menu(self): """ Sets up the main menu. """ # file_menu = self.menuBar().addMenu("&File") self.menu = QtGui.QMenu(self) self.menu.addAction("C&lose", self.close_window, "Ctrl+W") self.menu.addAction("E&xit", self.quit_application, "Ctrl+Q") if DEBUGGING is True: self.menu.addSeparator() self.menu.addAction("&Test", self.my_test, "Ctrl+T") self.show_cols_submenu = self.menu.addMenu("Table columns") self.menu.addAction(self.toggle_callable_action) self.menu.addAction(self.toggle_special_attribute_action) self.titleBar().settingDownButton.setMenu(self.menu) self.titleBar().settingMenuShowed.connect( self.titleBar().settingDownButton.showMenu) def _setup_views(self): """ Creates the UI widgets. """ self.central_splitter = QtGui.QSplitter(self, orientation=QtCore.Qt.Vertical) self.setCentralWidget(self.central_splitter) # central_layout = QtGui.QVBoxLayout() # self.central_splitter.setLayout(central_layout) # Tree widget self.obj_tree = ToggleColumnTreeView() self.obj_tree.setAlternatingRowColors(True) self.obj_tree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.obj_tree.setUniformRowHeights(True) self.obj_tree.setAnimated(True) self.obj_tree.add_header_context_menu() # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. obj_tree_header = self.obj_tree.header() # obj_tree_header.setMovable(True) obj_tree_header.setStretchLastSection(False) for action in self.obj_tree.toggle_column_actions_group.actions(): self.show_cols_submenu.addAction(action) self.central_splitter.addWidget(self.obj_tree) # Bottom pane bottom_pane_widget = QtGui.QWidget() bottom_layout = QtGui.QHBoxLayout() bottom_layout.setSpacing(0) bottom_layout.setContentsMargins(5, 5, 5, 5) # left top right bottom bottom_pane_widget.setLayout(bottom_layout) self.central_splitter.addWidget(bottom_pane_widget) group_box = QtGui.QGroupBox("Details") bottom_layout.addWidget(group_box) group_layout = QtGui.QHBoxLayout() group_layout.setContentsMargins(2, 2, 2, 2) # left top right bottom group_box.setLayout(group_layout) # Radio buttons radio_widget = QtGui.QWidget() radio_layout = QtGui.QVBoxLayout() radio_layout.setContentsMargins(0, 0, 0, 0) # left top right bottom radio_widget.setLayout(radio_layout) self.button_group = QtGui.QButtonGroup(self) for button_id, attr_detail in enumerate(self._attr_details): radio_button = QtGui.QRadioButton(attr_detail.name) radio_layout.addWidget(radio_button) self.button_group.addButton(radio_button, button_id) self.button_group.buttonClicked[int].connect( self._change_details_field) self.button_group.button(0).setChecked(True) radio_layout.addStretch(1) group_layout.addWidget(radio_widget) # Editor widget font = QtGui.QFont() font.setFamily('Courier') font.setFixedPitch(True) #font.setPointSize(14) self.editor = QtGui.QPlainTextEdit() self.editor.setReadOnly(True) self.editor.setFont(font) group_layout.addWidget(self.editor) # Splitter parameters self.central_splitter.setCollapsible(0, False) self.central_splitter.setCollapsible(1, True) self.central_splitter.setSizes([400, 200]) self.central_splitter.setStretchFactor(0, 10) self.central_splitter.setStretchFactor(1, 0) # End of setup_methods def _settings_group_name(self, prefix): """ The persistent settings are stored per combination of column names and windows instance number. """ column_names = ",".join([col.name for col in self._attr_cols]) settings_str = column_names + str(self._instance_nr) settings_grp = "{}_{}".format(prefix, hex(hash(settings_str))) logger.debug(" settings group is: {!r}".format(settings_grp)) return settings_grp def _readModelSettings(self, reset=False, show_routine_attributes=None, show_special_attributes=None): """ Reads the persistent model settings . The persistent settings (show_routine_attributes, show_special_attributes) can be \ overridden by giving it a True or False value. If reset is True and the setting is None, True is used as default. """ default_sra = True default_ssa = True if reset: logger.debug("Resetting persistent model settings") if show_routine_attributes is None: show_routine_attributes = default_sra if show_special_attributes is None: show_special_attributes = default_ssa else: logger.debug("Reading model settings for window: {:d}".format( self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('model')) if show_routine_attributes is None: show_routine_attributes = setting_str_to_bool( settings.value("show_routine_attributes", default_sra)) if show_special_attributes is None: show_special_attributes = setting_str_to_bool( settings.value("show_special_attributes", default_ssa)) settings.endGroup() logger.debug("read show_routine_attributes: {!r}".format( show_routine_attributes)) logger.debug("read show_special_attributes: {!r}".format( show_special_attributes)) return (show_routine_attributes, show_special_attributes) def _writeModelSettings(self): """ Writes the model settings to the persistent store """ logger.debug("Writing model settings for window: {:d}".format( self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('model')) logger.debug("writing show_routine_attributes: {!r}".format( self._tree_model.getShowCallables())) logger.debug("wrting show_special_attributes: {!r}".format( self._tree_model.getShowSpecialAttributes())) settings.setValue("show_routine_attributes", self._tree_model.getShowCallables()) settings.setValue("show_special_attributes", self._tree_model.getShowSpecialAttributes()) settings.endGroup() def _readViewSettings(self, reset=False): """ Reads the persistent program settings :param reset: If True, the program resets to its default settings """ pos = QtCore.QPoint(20 * self._instance_nr, 20 * self._instance_nr) window_size = QtCore.QSize(1024, 700) details_button_idx = 0 header = self.obj_tree.header() header_restored = False if reset: logger.debug("Resetting persistent view settings") else: logger.debug("Reading view settings for window: {:d}".format( self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('view')) pos = settings.value("main_window/pos", pos) window_size = settings.value("main_window/size", window_size) details_button_idx = int( settings.value("details_button_idx", details_button_idx)) self.central_splitter.restoreState( settings.value("central_splitter/state")) header_restored = self.obj_tree.read_view_settings( 'table/header_state', settings, reset) settings.endGroup() if not header_restored: column_sizes = [col.width for col in self._attr_cols] column_visible = [col.col_visible for col in self._attr_cols] for idx, size in enumerate(column_sizes): if size > 0: # Just in case header.resizeSection(idx, size) for idx, visible in enumerate(column_visible): self.obj_tree.toggle_column_actions_group.actions( )[idx].setChecked(visible) self.resize(window_size) self.move(pos) button = self.button_group.button(details_button_idx) if button is not None: button.setChecked(True) def _writeViewSettings(self): """ Writes the view settings to the persistent store """ logger.debug("Writing view settings for window: {:d}".format( self._instance_nr)) settings = QtCore.QSettings() settings.beginGroup(self._settings_group_name('view')) self.obj_tree.write_view_settings("table/header_state", settings) settings.setValue("central_splitter/state", self.central_splitter.saveState()) settings.setValue("details_button_idx", self.button_group.checkedId()) settings.setValue("main_window/pos", self.pos()) settings.setValue("main_window/size", self.size()) settings.endGroup() @QtCore.Slot(QtCore.QModelIndex, QtCore.QModelIndex) def _update_details(self, current_index, _previous_index): """ Shows the object details in the editor given an index. """ tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _change_details_field(self, _button_id=None): """ Changes the field that is displayed in the details pane """ #logger.debug("_change_details_field: {}".format(_button_id)) current_index = self.obj_tree.selectionModel().currentIndex() tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _update_details_for_item(self, tree_item): """ Shows the object details in the editor given an tree_item """ self.editor.setStyleSheet("color: black;") try: #obj = tree_item.obj button_id = self.button_group.checkedId() assert button_id >= 0, "No radio button selected. Please report this bug." attr_details = self._attr_details[button_id] data = attr_details.data_fn(tree_item) self.editor.setPlainText(data) self.editor.setWordWrapMode(attr_details.line_wrap) except StandardError, ex: self.editor.setStyleSheet("color: red;") stack_trace = traceback.format_exc() self.editor.setPlainText("{}{}".format(ex, stack_trace)) self.editor.setWordWrapMode( QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere) if DEBUGGING is True: raise
class ObjectBrowser(QtGui.QMainWindow): """ Object browser main application window. """ def __init__(self, obj = None, obj_name = '', show_special_methods = True, show_root_node = False, width = 1200, height = 800): """ Constructor :param obj: any Python object or variable :param obj_name: name of the object as it will appear in the root node :param show_special_methods: if True the objects special methods, i.e. methods with a name that starts and ends with two underscores, will be displayed (in grey). If False they are hidden. :param width: if width and height are set, the main windows is resized. :param height: if width and height are set, the main windows is resized. """ super(ObjectBrowser, self).__init__() # Model self._tree_model = TreeModel(obj, obj_name = obj_name, show_root_node = show_root_node, show_special_methods = show_special_methods) # Table columns defw = 200 self.col_settings = [None] * TreeModel.N_COLS self.col_settings[TreeModel.COL_PATH] = ColumnSettings(visible=True, width=350) self.col_settings[TreeModel.COL_NAME] = ColumnSettings(visible=True, width=defw) self.col_settings[TreeModel.COL_VALUE] = ColumnSettings(visible=True, width=defw) self.col_settings[TreeModel.COL_TYPE] = ColumnSettings(visible=False, width=defw) self.col_settings[TreeModel.COL_CLASS] = ColumnSettings(visible=True, width=defw) self.col_settings[TreeModel.COL_ID] = ColumnSettings(visible=False, width=120) self.col_settings[TreeModel.COL_PREDICATE] = ColumnSettings(visible=False, width=defw) for idx, header in enumerate(TreeModel.HEADERS): self.col_settings[idx].name = header # Views self._setup_actions() self._setup_menu() self._setup_views() self.setWindowTitle("{} - {}".format(PROGRAM_NAME, obj_name)) app = QtGui.QApplication.instance() app.lastWindowClosed.connect(app.quit) # Update views with model for settings in self.col_settings: settings.toggle_action.setChecked(settings.visible) self.radio_str.setChecked(True) if show_root_node is True: self.obj_tree.expandToDepth(0) # Select first row so that a hidden root node will not be selected. first_row = self._tree_model.first_item_index() self.obj_tree.setCurrentIndex(first_row) if width and height: self.resize(width, height) def _setup_actions(self): """ Creates the MainWindow actions. """ # Create actions for the table columns from its settings. for col_idx, settings in enumerate(self.col_settings): settings.toggle_action = \ QtGui.QAction("Show {} Column".format(settings.name), self, checkable=True, checked=True, statusTip = "Shows or hides the {} column".format(settings.name)) if col_idx >= 0 and col_idx <= 9: settings.toggle_action.setShortcut("Ctrl+{:d}".format(col_idx)) settings.toggle_function = self._make_show_column_function(col_idx) # keep reference assert settings.toggle_action.toggled.connect(settings.toggle_function) self.toggle_special_method_action = \ QtGui.QAction("Show __special_methods__", self, checkable=True, checked=True, statusTip = "Shows or hides __special_methods__") assert self.toggle_special_method_action.toggled.connect(self.toggle_special_methods) def _setup_menu(self): """ Sets up the main menu. """ file_menu = self.menuBar().addMenu("&File") file_menu.addAction("C&lose", self.close_window, "Ctrl+W") file_menu.addAction("E&xit", self.quit_application, "Ctrl+Q") if DEBUGGING is True: file_menu.addSeparator() file_menu.addAction("&Test", self.my_test, "Ctrl+T") view_menu = self.menuBar().addMenu("&View") for _idx, settings in enumerate(self.col_settings): view_menu.addAction(settings.toggle_action) view_menu.addSeparator() view_menu.addAction(self.toggle_special_method_action) self.menuBar().addSeparator() help_menu = self.menuBar().addMenu("&Help") help_menu.addAction('&About', self.about) def _setup_views(self): """ Creates the UI widgets. """ central_splitter = QtGui.QSplitter(self, orientation = QtCore.Qt.Vertical) self.setCentralWidget(central_splitter) central_layout = QtGui.QVBoxLayout() central_splitter.setLayout(central_layout) # Tree widget self.obj_tree = QtGui.QTreeView() self.obj_tree.setModel(self._tree_model) self.obj_tree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.obj_tree.setUniformRowHeights(True) for idx, settings in enumerate(self.col_settings): #logger.debug("resizing {}: {:d}".format(settings.name, settings.width)) self.obj_tree.header().resizeSection(idx, settings.width) # Stretch last column? # It doesn't play nice when columns are hidden and then shown again. self.obj_tree.header().setStretchLastSection(False) central_layout.addWidget(self.obj_tree) # Bottom pane pane_widget = QtGui.QWidget() central_layout.addWidget(pane_widget) pane_layout = QtGui.QHBoxLayout() pane_widget.setLayout(pane_layout) # Radio buttons group_box = QtGui.QGroupBox("Details") radio_layout = QtGui.QVBoxLayout() def create_radio(description): "Adds a new radio button to the radio_layout" radio_button = QtGui.QRadioButton(description) radio_button.toggled.connect(self._change_details_field) radio_layout.addWidget(radio_button) return radio_button self.radio_str = create_radio("str") self.radio_repr = create_radio("repr") self.radio_pretty = create_radio("pretty print") self.radio_doc = create_radio("doc string") self.radio_getdoc = create_radio("inspect.getdoc") self.radio_getcomments = create_radio("inspect.getcomments") self.radio_getfile = create_radio("inspect.getfile") self.radio_getmodule = create_radio("inspect.getmodule") self.radio_getsourcefile = create_radio("inspect.getsourcefile") self.radio_getsourcelines = create_radio("inspect.getsourcelines") self.radio_getsource = create_radio("inspect.getsource") radio_layout.addStretch(1) group_box.setLayout(radio_layout) pane_layout.addWidget(group_box) # Editor widget font = QtGui.QFont() font.setFamily('Courier') font.setFixedPitch(True) font.setPointSize(14) self.editor = QtGui.QPlainTextEdit() self.editor.setReadOnly(True) self.editor.setFont(font) pane_layout.addWidget(self.editor) # Splitter parameters central_splitter.setCollapsible(0, False) central_splitter.setCollapsible(1, True) central_splitter.setSizes([400, 200]) central_splitter.setStretchFactor(0, 10) central_splitter.setStretchFactor(1, 0) # Connect signals selection_model = self.obj_tree.selectionModel() assert selection_model.currentChanged.connect(self._update_details) # End of setup_methods def my_test(self): """ Function for testing """ logger.debug("my_test") @QtCore.Slot(QtCore.QModelIndex, QtCore.QModelIndex) def _update_details(self, current_index, _previous_index): """ Shows the object details in the editor given an index. """ tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _change_details_field(self): """ Changes the field that is displayed in the details pane """ current_index = self.obj_tree.selectionModel().currentIndex() tree_item = self._tree_model.treeItem(current_index) self._update_details_for_item(tree_item) def _update_details_for_item(self, tree_item): """ Shows the object details in the editor given an tree_item """ self.editor.setStyleSheet("color: black;") try: obj = tree_item.obj if self.radio_str.isChecked(): data = str(obj) elif self.radio_repr.isChecked(): data = repr(obj) elif self.radio_doc.isChecked(): try: data = obj.__doc__ except AttributeError: data = '<no doc string found>' elif self.radio_pretty.isChecked(): pretty_printer = pprint.PrettyPrinter(indent=4) data = pretty_printer.pformat(obj) elif self.radio_getdoc.isChecked(): data = inspect.getdoc(obj) elif self.radio_getcomments.isChecked(): data = inspect.getcomments(obj) elif self.radio_getfile.isChecked(): try: data = inspect.getfile(obj) except TypeError: data = '' elif self.radio_getmodule.isChecked(): module = inspect.getmodule(obj) if module: data = module.__name__ else: data = '' elif self.radio_getsourcefile.isChecked(): try: data = inspect.getsourcefile(obj) except TypeError: data = '' elif self.radio_getsourcelines.isChecked(): try: data = repr(inspect.getsourcelines(obj)) except TypeError: data = '' elif self.radio_getsource.isChecked(): try: data = inspect.getsource(obj) except TypeError: data = '' else: assert False, "No radio button checked." self.editor.setPlainText(data) except StandardError, ex: self.editor.setStyleSheet("color: red;") stack_trace = traceback.format_exc() self.editor.setPlainText("{}\n\n{}".format(ex, stack_trace)) if DEBUGGING is True: raise