def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction ) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) self.stack = central self.setCentralWidget(central) # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox( self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr("Enable shadow and ping animations for nodes " "in the workflow.") ) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox( self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr("Show source and sink channel names " "over the links.") ) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug")]) self.bind(combo, "currentIndex", "logging/level") layout.addWidget(combo) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") self.bind(cb1, "checked", "help/open-in-external-browser") layout.addWidget(cb1) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize()
def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction ) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) # Add a close button to the bottom of the dialog # (to satisfy GNOME 3 which shows the dialog without a title bar). container = container_widget_helper() container.layout().addWidget(central) buttonbox = QDialogButtonBox(QDialogButtonBox.Close) buttonbox.rejected.connect(self.close) container.layout().addWidget(buttonbox) self.setCentralWidget(container) self.stack = central # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox( self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr("Enable shadow and ping animations for node " "items in the scheme.") ) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox( self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr("Show source and sink channel names " "over the links.") ) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget(self, objectName="streams") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Standard output")) cb2 = QCheckBox(self.tr("Standard error")) self.bind(cb1, "checked", "output/redirect-stdout") self.bind(cb2, "checked", "output/redirect-stderr") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Redirect output"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug")]) cb = QCheckBox(self.tr("Show output on 'Error'"), objectName="focus-on-error") self.bind(combo, "currentIndex", "logging/level") self.bind(cb, "checked", "output/show-on-error") layout.addWidget(combo) layout.addWidget(cb) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Stay on top"), objectName="stay-on-top") cb2 = QCheckBox(self.tr("Dockable"), objectName="output-dockable") self.bind(cb1, "checked", "output/stay-on-top") self.bind(cb2, "checked", "output/dockable") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Output window"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") cb2 = QCheckBox(self.tr("Stay on top"), objectName="help-stay-on-top") cb3 = QCheckBox(self.tr("Dockable"), objectName="help-dockable") self.bind(cb1, "checked", "help/open-in-external-browser") self.bind(cb2, "checked", "help/stay-on-top") self.bind(cb3, "checked", "help/dockable") layout.addWidget(cb1) layout.addWidget(cb2) layout.addWidget(cb3) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) # Categories Tab tab = QWidget() layout = QVBoxLayout() view = QListView() from .. import registry reg = registry.global_registry() model = QStandardItemModel() settings = QSettings() for cat in reg.categories(): item = QStandardItem() item.setText(cat.name) item.setCheckable(True) visible, _ = category_state(cat, settings) item.setCheckState(Qt.Checked if visible else Qt.Unchecked) model.appendRow([item]) view.setModel(model) layout.addWidget(view) tab.setLayout(layout) model.itemChanged.connect( lambda item: save_category_state( reg.category(str(item.text())), _State(item.checkState() == Qt.Checked, -1), settings ) ) self.addTab(tab, "Categories") if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize()
class PlotScalesWidget(QWidget): plotScaleChanged = pyqtSignal() def __init__(self, type_key, title, select_min_time_value=False): QWidget.__init__(self) self.__type_key = type_key self.__type = None self.__double_spinner = self.createDoubleSpinner(minimum=-999999999.0, maximum=999999999.0) self.__integer_spinner = self.createIntegerSpinner(minimum=0, maximum=999999999) self.__time_map = ReportStepsModel().getList() self.__time_index_map = {} for index in range(len(self.__time_map)): time = self.__time_map[index] self.__time_index_map[time] = index self.__time_spinner = self.createTimeSpinner(select_minimum_value=select_min_time_value) layout = QVBoxLayout() self.setLayout(layout) self.__label = QLabel(title) self.__label.setAlignment(Qt.AlignHCenter) self.__stack = QStackedWidget() self.__stack.setSizePolicy(QSizePolicy(QSizePolicy.Preferred)) self.__stack.addWidget(self.__integer_spinner) self.__stack.addWidget(self.__double_spinner) self.__stack.addWidget(self.__time_spinner) layout.addWidget(self.__stack) layout.addWidget(self.__label) self.setLayout(layout) def createDoubleSpinner(self, minimum, maximum): spinner = QDoubleSpinBox() spinner.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) spinner.setMinimumWidth(105) spinner.setRange(minimum, maximum) spinner.setKeyboardTracking(False) spinner.setDecimals(8) spinner.editingFinished.connect(self.plotScaleChanged) spinner.valueChanged.connect(self.plotScaleChanged) return spinner def createIntegerSpinner(self, minimum, maximum): spinner = QSpinBox() spinner.setMinimumWidth(75) spinner.setRange(minimum, maximum) spinner.setKeyboardTracking(False) spinner.editingFinished.connect(self.plotScaleChanged) spinner.valueChanged.connect(self.plotScaleChanged) return spinner def createTimeSpinner(self, select_minimum_value): def converter(item): return "%s" % (str(item.date())) spinner = ListSpinBox(self.__time_map) spinner.setMinimumWidth(75) if select_minimum_value: spinner.setValue(0) spinner.valueChanged[int].connect(self.plotScaleChanged) spinner.editingFinished.connect(self.plotScaleChanged) spinner.setStringConverter(converter) return spinner def getValue(self): if self.__type is int: return self.__integer_spinner.value() elif self.__type is float: return self.__double_spinner.value() elif self.__type is CTime: index = self.__time_spinner.value() return self.__time_map[index] else: raise TypeError("Unsupported spinner type: %s" % self.__type) def setValue(self, value): if value is not None: if self.__type is int: self.__integer_spinner.setValue(int(value)) elif self.__type is float: self.__double_spinner.setValue(value) elif self.__type is CTime: index = self.__time_index_map[value] self.__time_spinner.setValue(index) else: raise TypeError("Unsupported spinner type: %s" % self.__type) def setFontSize(self, size): font = self.__double_spinner.font() font.setPointSize(size) self.__double_spinner.setFont(font) font = self.__integer_spinner.font() font.setPointSize(size) self.__integer_spinner.setFont(font) font = self.__time_spinner.font() font.setPointSize(size) self.__time_spinner.setFont(font) font = self.__label.font() font.setPointSize(size) self.__label.setFont(font) def setType(self, spinner_type): self.__type = spinner_type if spinner_type is int: self.__stack.setCurrentWidget(self.__integer_spinner) elif spinner_type is float: self.__stack.setCurrentWidget(self.__double_spinner) elif spinner_type is CTime: self.__stack.setCurrentWidget(self.__time_spinner) else: raise TypeError("Unsupported spinner type: %s" % spinner_type) def getType(self): return self.__type
class PlotScalesWidget(QWidget): plotScaleChanged = pyqtSignal() def __init__(self, type_key, title, select_min_time_value=False): QWidget.__init__(self) self.__type_key = type_key self.__type = None self.__double_spinner = self.createDoubleSpinner(minimum=-999999999.0, maximum=999999999.0) self.__integer_spinner = self.createIntegerSpinner(minimum=0, maximum=999999999) self.__time_map = ReportStepsModel().getList() self.__time_index_map = {} for index in range(len(self.__time_map)): time = self.__time_map[index] self.__time_index_map[time] = index self.__time_spinner = self.createTimeSpinner( select_minimum_value=select_min_time_value) layout = QVBoxLayout() self.setLayout(layout) self.__label = QLabel(title) self.__label.setAlignment(Qt.AlignHCenter) self.__stack = QStackedWidget() self.__stack.setSizePolicy(QSizePolicy(QSizePolicy.Preferred)) self.__stack.addWidget(self.__integer_spinner) self.__stack.addWidget(self.__double_spinner) self.__stack.addWidget(self.__time_spinner) layout.addWidget(self.__stack) layout.addWidget(self.__label) self.setLayout(layout) def createDoubleSpinner(self, minimum, maximum): spinner = QDoubleSpinBox() spinner.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) spinner.setMinimumWidth(105) spinner.setRange(minimum, maximum) spinner.setKeyboardTracking(False) spinner.setDecimals(8) spinner.editingFinished.connect(self.plotScaleChanged) spinner.valueChanged.connect(self.plotScaleChanged) return spinner def createIntegerSpinner(self, minimum, maximum): spinner = QSpinBox() spinner.setMinimumWidth(75) spinner.setRange(minimum, maximum) spinner.setKeyboardTracking(False) spinner.editingFinished.connect(self.plotScaleChanged) spinner.valueChanged.connect(self.plotScaleChanged) return spinner def createTimeSpinner(self, select_minimum_value): def converter(item): return "%s" % (str(item.date())) spinner = ListSpinBox(self.__time_map) spinner.setMinimumWidth(75) if select_minimum_value: spinner.setValue(0) spinner.valueChanged[int].connect(self.plotScaleChanged) spinner.editingFinished.connect(self.plotScaleChanged) spinner.setStringConverter(converter) return spinner def getValue(self): if self.__type is int: return self.__integer_spinner.value() elif self.__type is float: return self.__double_spinner.value() elif self.__type is CTime: index = self.__time_spinner.value() return self.__time_map[index] else: raise TypeError("Unsupported spinner type: %s" % self.__type) def setValue(self, value): if value is not None: if self.__type is int: self.__integer_spinner.setValue(int(value)) elif self.__type is float: self.__double_spinner.setValue(value) elif self.__type is CTime: index = self.__time_index_map[value] self.__time_spinner.setValue(index) else: raise TypeError("Unsupported spinner type: %s" % self.__type) def setFontSize(self, size): font = self.__double_spinner.font() font.setPointSize(size) self.__double_spinner.setFont(font) font = self.__integer_spinner.font() font.setPointSize(size) self.__integer_spinner.setFont(font) font = self.__time_spinner.font() font.setPointSize(size) self.__time_spinner.setFont(font) font = self.__label.font() font.setPointSize(size) self.__label.setFont(font) def setType(self, spinner_type): self.__type = spinner_type if spinner_type is int: self.__stack.setCurrentWidget(self.__integer_spinner) elif spinner_type is float: self.__stack.setCurrentWidget(self.__double_spinner) elif spinner_type is CTime: self.__stack.setCurrentWidget(self.__time_spinner) else: raise TypeError("Unsupported spinner type: %s" % spinner_type) def getType(self): return self.__type
def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) self.stack = central self.setCentralWidget(central) # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox(self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr( "Enable shadow and ping animations for nodes " "in the workflow.")) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox(self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr( "Show source and sink channel names " "over the links.")) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget(self, objectName="streams") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Standard output")) cb2 = QCheckBox(self.tr("Standard error")) self.bind(cb1, "checked", "output/redirect-stdout") self.bind(cb2, "checked", "output/redirect-stderr") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Redirect output"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([ self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug") ]) cb = QCheckBox(self.tr("Show output on 'Error'"), objectName="focus-on-error") self.bind(combo, "currentIndex", "logging/level") self.bind(cb, "checked", "output/show-on-error") layout.addWidget(combo) layout.addWidget(cb) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Stay on top"), objectName="stay-on-top") cb2 = QCheckBox(self.tr("Dockable"), objectName="output-dockable") self.bind(cb1, "checked", "output/stay-on-top") self.bind(cb2, "checked", "output/dockable") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Output window"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") cb2 = QCheckBox(self.tr("Stay on top"), objectName="help-stay-on-top") cb3 = QCheckBox(self.tr("Dockable"), objectName="help-dockable") self.bind(cb1, "checked", "help/open-in-external-browser") self.bind(cb2, "checked", "help/stay-on-top") self.bind(cb3, "checked", "help/dockable") layout.addWidget(cb1) layout.addWidget(cb2) layout.addWidget(cb3) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize()
class EntryView(BaseView): def _setup(self): self._setupUi() self.etable = EntryTable(self.model.etable, view=self.tableView) self.efbar = EntryFilterBar(model=self.model.filter_bar, view=self.filterBar) self.bgraph = Chart(self.model.bargraph, view=self.barGraphView) self.lgraph = Chart(self.model.balgraph, view=self.lineGraphView) self._setupColumns() # Can only be done after the model has been connected self.reconciliationButton.clicked.connect(self.model.toggle_reconciliation_mode) def _setupUi(self): self.resize(483, 423) self.verticalLayout = QVBoxLayout(self) self.verticalLayout.setSpacing(0) self.verticalLayout.setMargin(0) self.horizontalLayout = QHBoxLayout() self.horizontalLayout.setSpacing(0) self.filterBar = RadioBox(self) sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.filterBar.sizePolicy().hasHeightForWidth()) self.filterBar.setSizePolicy(sizePolicy) self.horizontalLayout.addWidget(self.filterBar) self.horizontalLayout.addItem(horizontalSpacer()) self.reconciliationButton = QPushButton(tr("Reconciliation")) self.reconciliationButton.setCheckable(True) self.horizontalLayout.addWidget(self.reconciliationButton) self.verticalLayout.addLayout(self.horizontalLayout) self.splitterView = QSplitter() self.splitterView.setOrientation(Qt.Vertical) self.splitterView.setChildrenCollapsible(False) self.tableView = TableView(self) self.tableView.setAcceptDrops(True) self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed) self.tableView.setDragEnabled(True) self.tableView.setDragDropMode(QAbstractItemView.InternalMove) self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) self.tableView.setSortingEnabled(True) self.tableView.horizontalHeader().setHighlightSections(False) self.tableView.horizontalHeader().setMinimumSectionSize(18) self.tableView.verticalHeader().setVisible(False) self.tableView.verticalHeader().setDefaultSectionSize(18) self.splitterView.addWidget(self.tableView) self.graphView = QStackedWidget(self) sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.graphView.sizePolicy().hasHeightForWidth()) self.graphView.setSizePolicy(sizePolicy) self.graphView.setMinimumSize(0, 200) self.lineGraphView = LineGraphView() self.graphView.addWidget(self.lineGraphView) self.barGraphView = BarGraphView() self.graphView.addWidget(self.barGraphView) self.splitterView.addWidget(self.graphView) self.graphView.setCurrentIndex(1) self.splitterView.setStretchFactor(0, 1) self.splitterView.setStretchFactor(1, 0) self.verticalLayout.addWidget(self.splitterView) def _setupColumns(self): h = self.tableView.horizontalHeader() h.setMovable(True) # column drag & drop reorder #--- QWidget override def setFocus(self): self.etable.view.setFocus() #--- Public def fitViewsForPrint(self, viewPrinter): hidden = self.model.mainwindow.hidden_areas viewPrinter.fitTable(self.etable) if PaneArea.BottomGraph not in hidden: viewPrinter.fit(self.graphView.currentWidget(), 300, 150, expandH=True, expandV=True) def restoreSubviewsSize(self): graphHeight = self.model.graph_height_to_restore if graphHeight: splitterHeight = self.splitterView.height() sizes = [splitterHeight-graphHeight, graphHeight] self.splitterView.setSizes(sizes) #--- model --> view def refresh_reconciliation_button(self): if self.model.can_toggle_reconciliation_mode: self.reconciliationButton.setEnabled(True) self.reconciliationButton.setChecked(self.model.reconciliation_mode) else: self.reconciliationButton.setEnabled(False) self.reconciliationButton.setChecked(False) def show_bar_graph(self): self.graphView.setCurrentIndex(1) def show_line_graph(self): self.graphView.setCurrentIndex(0) def update_visibility(self): hidden = self.model.mainwindow.hidden_areas self.graphView.setHidden(PaneArea.BottomGraph in hidden)
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, app, debug=False): super(MainWindow, self).__init__() # Tab management private attributes, modify at own risk self.__tabMoved = False self.__tabAreaInformation = (-1, -1, []) self.__tabConnections = set() self.app = app self.debug = debug self.sched = scheduler.sched self.vfs = vfs.vfs() self.createRootNodes() self.dialog = Dialog(self) self.initCallback() self.setupUi(self) self.translation() self.setWindowModality(QtCore.Qt.ApplicationModal) self.resize( QtCore.QSize(QtCore.QRect(0, 0, 1014, 693).size()).expandedTo( self.minimumSizeHint())) self.shellActions = ShellActions(self) self.interpreterActions = InterpreterActions(self) self.setCentralWidget(None) self.setDockNestingEnabled(True) self.init() self.status = QStackedWidget() sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(1) self.status.setSizePolicy(sizePolicy) self.statusBar().addWidget(self.status) def setupUi(self, MainWindow): self.actionWizard = QAction(self) icon = QIcon() icon.addPixmap(QPixmap(QString.fromUtf8(":/wizard")), QIcon.Normal, QIcon.Off) self.actionWizard.setIcon(icon) self.actionWizard.setObjectName(QString.fromUtf8("actionWizard")) Ui_MainWindow.setupUi(self, MainWindow) self.menuFile.insertAction(self.actionOpen_evidence, self.actionWizard) self.retranslateUi(MainWindow) if REPORT_EDITOR: self.actionReport = QAction(self) icon = QIcon() icon.addPixmap(QPixmap(QString.fromUtf8(":report")), QIcon.Normal, QIcon.Off) self.actionReport.setIcon(icon) self.actionReport.setObjectName(QString.fromUtf8("actionReport")) self.menuReport = QMenu(self.menubar) self.menuReport.setObjectName(QString.fromUtf8("menuReport")) self.menuReport.addAction(self.actionReport) self.actionReport.setText( QApplication.translate("MainWindow", "Report", None, QApplication.UnicodeUTF8)) self.actionReport.setToolTip( QApplication.translate("MainWindow", "Open the report editor", None, QApplication.UnicodeUTF8)) try: self.menuReport.setTitle( QApplication.translate("MainWindow", "Report", None, QApplication.UnicodeUTF8)) except AttributeError: pass def retranslateUi(self, MainWindow): Ui_MainWindow.retranslateUi(self, MainWindow) self.actionWizard.setText( QApplication.translate("MainWindow", "Wizard", None, QApplication.UnicodeUTF8)) def init(self): self.initConnection() # Set up toolbar self.initToolbarList() self.setupToolBar() # Set up modules menu self.MenuTags = MenuTags(self, self) self.refreshTabifiedDockWidgets() def cclose(self): #stats = yappi.get_func_stats() #stats.save("func_stats_cgrind.out", type="callgrind") #stats.save("func_stats_pstat.out", type="pstat") #threadstats = yappi.get_thread_stats() #threadstats.save("thread_stats_cgrind.out", type="callgrind") #threadstats.save("thread_stats_pstat.out", type="pstat") self.close() def initConnection(self): ## File menu self.connect(self.actionOpen_evidence, SIGNAL("triggered()"), self.dialog.addFiles) self.connect(self.actionOpen_device, SIGNAL("triggered()"), self.dialog.addDevices) self.connect(self.actionExit, SIGNAL("triggered()"), self.cclose) ## Edit menu self.connect(self.actionPreferences, SIGNAL("triggered()"), self.dialog.preferences) ## Module menu self.connect(self.actionLoadModule, SIGNAL("triggered()"), self.dialog.loadDriver) self.connect(self.actionBrowse_modules, SIGNAL("triggered()"), self.dialog.manager) ## Ide menu #self.connect(self.actionIdeOpen, SIGNAL("triggered()"), self.addIde) ## View menu self.connect(self.actionMaximize, SIGNAL("triggered()"), self.maximizeDockwidget) self.connect(self.actionFullscreen_mode, SIGNAL("triggered()"), self.fullscreenMode) self.connect(self.actionNodeBrowser, SIGNAL("triggered()"), self.addNodeBrowser) self.connect(self.actionShell, SIGNAL("triggered()"), self.shellActions.create) self.connect(self.actionPython_interpreter, SIGNAL("triggered()"), self.interpreterActions.create) ## About menu self.connect(self.actionHelp, SIGNAL("triggered()"), self.addHelpWidget) self.connect(self.actionAbout, SIGNAL("triggered()"), self.dialog.about) self.connect(self.actionWizard, SIGNAL('triggered()'), self.autoWizard) if REPORT_EDITOR: self.connect(self.actionReport, SIGNAL("triggered()"), self.addReportEdit) self.connect(self, SIGNAL("addReportEdit()"), self.addReportEdit) def initToolbarList(self): self.toolbarList = [ self.actionOpen_evidence, self.actionOpen_device, None, self.actionNodeBrowser, self.actionShell, self.actionPython_interpreter, # self.actionIdeOpen, # self.actionHelp, # None, # self.actionMaximize, # self.actionFullscreen_mode, # self.actionBrowse_modules, ] self.toolbarList.insert(0, self.actionWizard) if REPORT_EDITOR: self.toolbarList.insert( len(self.toolbarList) - 1, self.actionReport) ############# DOCKWIDGETS FUNCTIONS ############### def createDockWidget(self, widget, widgetName): dockwidget = DockWidget(self, widget, widgetName) dockwidget.setAllowedAreas(Qt.AllDockWidgetAreas) return dockwidget def addDockWidgets(self, widget, internalName, master=True): if widget is None: return if self.last_state is not None: self.maximizeDockwidget() if widget.windowTitle() != "": wname = widget.windowTitle() else: wname = widget.name new_master = self.getMasterDockWidget() if new_master != None: self.master = new_master dockwidget = self.createDockWidget(widget, wname) docIndex, docTitle = self.getWidgetName(wname) dockwidget.setWindowTitle(QString.fromUtf8(docTitle)) self.connect(dockwidget, SIGNAL("resizeEvent"), widget.resize) self.addDockWidget(self.masterArea, dockwidget) if master: self.tabifyDockWidget(self.master, dockwidget) else: self.tabifyDockWidget(self.second, dockwidget) if docIndex: self.dockWidget[internalName + str(docIndex)] = dockwidget else: self.dockWidget[internalName] = dockwidget self.refreshTabifiedDockWidgets() def getWidgetName(self, name): did = 0 for d in self.dockWidget: if self.dockWidget[d].windowTitle().startsWith(QString(name)): did += 1 if did > 0: name = name + ' ' + str(did) return (did, name) def addSingleDock(self, name, cl, master=False): try: self.dockWidget[name].show() self.refreshTabifiedDockWidgets() except KeyError: w = cl(self) self.addDockWidgets(w, name, master) def getNodeBrowser(self): nb = self.nodeListWidgets() return nb def addNodeBrowser(self, rootpath=None, selectedNode=None): nb = self.getNodeBrowser() self.addDockWidgets(nb, 'nodeBrowser') nb.setCurrentContext(rootpath, selected=selectedNode) def addSearchTab(self, search): self.addDockWidgets(search, 'Searchr') def addHelpWidget(self): if hasattr(sys, "frozen"): path = os.path.abspath( os.path.join(os.path.dirname(sys.executable), "resources/docs/dff_doc.qhc")) else: conf = Conf() path = conf.docPath file = QFile(path) if not file.exists(path): if path: dialog = QMessageBox.warning( self, self.errorLoadingHelp, QString(path) + ": " + self.notAnHelpFile) else: dialog = QMessageBox.warning(self, self.errorLoadingHelp, self.noSuchHelpFile) return self.addDockWidgets(Help(self, path=path), 'help') def addInterpreter(self): self.addSingleDock("Interpreter", Interpreter) def initDockWidgets(self): """Init Dock in application and init DockWidgets""" widgetPos = [ (Qt.TopLeftCorner, Qt.LeftDockWidgetArea, QTabWidget.North), (Qt.BottomLeftCorner, Qt.BottomDockWidgetArea, QTabWidget.North), (Qt.TopLeftCorner, Qt.TopDockWidgetArea, QTabWidget.North), (Qt.BottomRightCorner, Qt.RightDockWidgetArea, QTabWidget.North) ] for corner, area, point in widgetPos: self.setCorner(corner, area) try: self.setTabPosition(area, point) except AttributeError: pass self.dockWidget = {} self.widget = {} self.masterArea = Qt.TopDockWidgetArea self.secondArea = Qt.BottomDockWidgetArea self.last_state = None self.last_dockwidget = None self.last_widget = None self.createFirstWidgets() self.refreshSecondWidgets() self.refreshTabifiedDockWidgets() if REPORT_EDITOR: self.addReportEdit() self.dockWidget['Report'].setVisible(False) def autoWizard(self): autoWiz = AutoWizard(self) autoWiz.exec_() def nodeListWidgets(self, parent=None): return NodeListWidgets(parent) def createProcessusWidget(self): return Processus(self) def createSTDOutWidget(self): return STDOut(self, self.debug) def createSTDErrWidget(self): return STDErr(self, self.debug) def addReportEdit(self): self.addSingleDock("Report", ReportEditor, master=True) def showReportEdit(self): self.emit(SIGNAL("addReportEdit()")) def createFirstWidgets(self): self.nodeBrowser = self.nodeListWidgets(parent=self) root = self.vfs.getnode('/') self.nodeBrowser.setCurrentContext(root) self.master = self.createDockWidget(self.nodeBrowser, self.nodeBrowser.name) self.master.setAllowedAreas(Qt.AllDockWidgetAreas) self.master.setWindowTitle(self.nodeBrowser.name) self.dockWidget["nodebrowser"] = self.master self.wprocessus = self.createProcessusWidget() self.second = self.createDockWidget(self.wprocessus, "Task manager") self.second.setAllowedAreas(Qt.AllDockWidgetAreas) self.second.setWindowTitle(self.wprocessus.windowTitle()) self.dockWidget["Task manager"] = self.second self.addDockWidget(self.masterArea, self.master) self.addDockWidget(self.secondArea, self.second) self.timer = QTimer(self) self.connect(self.timer, SIGNAL("timeout()"), self.refreshSecondWidgets) self.timer.start(2000) self.wstdout = self.createSTDOutWidget() self.wstderr = self.createSTDErrWidget() self.addDockWidgets(self.wstdout, 'stdout', master=False) self.addDockWidgets(self.wstderr, 'stderr', master=False) self.wmodules = Modules(self) self.addDockWidgets(self.wmodules, 'modules', master=False) self.preview = Preview(self) self.addDockWidgets(self.preview, 'preview', master=False) self.connect(self, SIGNAL("previewUpdate"), self.preview.update) self.wpostprocess = PostProcessStateWidget(self) self.addDockWidgets(self.wpostprocess, "Post Process State", False) def maximizeDockwidget(self): if self.last_state is None: self.last_state = self.saveState() focus_widget = QApplication.focusWidget() for key, dock in self.dockWidget.iteritems(): dock.hide() if dock.isAncestorOf(focus_widget): self.last_dockwidget = dock if self.last_dockwidget != None: self.last_widget = self.last_dockwidget.widget() self.last_dockwidget.toggleViewAction().setDisabled(True) self.setCentralWidget(self.last_dockwidget.widget()) self.last_dockwidget.visibility_changed(True) self.actionNodeBrowser.setEnabled(False) self.actionShell.setEnabled(False) self.actionPython_interpreter.setEnabled(False) #self.actionIdeOpen.setEnabled(False) self.actionHelp.setEnabled(False) else: self.last_state = None else: self.last_dockwidget.setWidget(self.last_widget) self.last_dockwidget.toggleViewAction().setEnabled(True) self.setCentralWidget(None) self.restoreState(self.last_state) self.last_dockwidget.setFocus() self.last_state = None self.last_widget = None self.last_dockwidget = None self.refreshTabifiedDockWidgets() self.actionNodeBrowser.setEnabled(True) self.actionShell.setEnabled(True) self.actionPython_interpreter.setEnabled(True) #self.actionIdeOpen.setEnabled(True) self.actionHelp.setEnabled(True) def fullscreenMode(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def refreshSecondWidgets(self): if self.dockWidget["Task manager"].visibility(): self.wprocessus.LoadInfoProcess() if self.dockWidget["modules"].visibility(): self.wmodules.LoadInfoModules() # # Following methods are in charge of tab management # By default tabified dock widgets are not movable. # And juste setting setMovable(True) is not enough. # All the magic is done in private class of Qt and # can't be overloaded. The last chance is to play # with signal and children management. # def updateTabConnections(self): children = self.children() for child in children: if child.inherits("QTabBar"): tabCount = child.count() child.setMovable(True) if child not in self.__tabConnections: child.tabMoved.connect(self.tabMoved, type=Qt.UniqueConnection) self.__tabConnections.add(child) # overloaded to add connections on new tabbar item def tabifyDockWidget(self, first, second): QMainWindow.tabifyDockWidget(self, first, second) self.updateTabConnections() # overloaded to connect to dockwidget location update def addDockWidget(self, area, dockwidget): dockwidget.dockLocationChanged.connect(self.updateTabConnections) return QMainWindow.addDockWidget(self, area, dockwidget) def event(self, event): mouse_status = int(QApplication.mouseButtons()) # if updating tabbar while mouse pressed, behaviour can be weird if mouse_status & 0x00000001 == 0 and self.__tabMoved: self.__tabMoved = False self.updateTabBar() return QMainWindow.event(self, event) def updateTabBar(self): tab, to, _from = self.__tabAreaInformation dockwidget, siblings = self.findDockWidgetsFromTabBar(tab) updated_siblings = [] visible = [] hidden = [] for sibling in siblings: if sibling.isVisible(): visible.append(sibling) else: hidden.append(sibling) if to == 0: updated_siblings.append(dockwidget) updated_siblings += visible + hidden else: master = visible.pop(0) updated_siblings.append(master) visible.insert(to - 1, dockwidget) updated_siblings += visible + hidden master = updated_siblings.pop(0) for sibling in updated_siblings: # always check if sibling != None or result in segfault if happens if sibling is not None: self.tabifyDockWidget(master, sibling) tab.setCurrentIndex(to) self.refreshTabifiedDockWidgets() # returns the location of the master dock widget (first created browser) def getMasterDockWidget(self): x_max = self.geometry().bottomRight().x() y_max = self.geometry().bottomRight().y() children = self.children() item = None for child in children: if child.inherits("QTabBar") or (child.inherits("QDockWidget") and child.isVisible()): child_x = child.geometry().topLeft().x() child_y = child.geometry().topLeft().y() if child_x >= 0 and child_y >= 0 and child_x <= x_max and child_y <= y_max: x_max = child_x y_max = child_y item = child if item is not None: if item.inherits("QDockWidget"): return item else: title = item.tabText(0) for dockwidget in self.dockWidget.values(): if title.startsWith(dockwidget.windowTitle()): return dockwidget else: # at init, there's no information, return None and keep self.master as is return None def refreshTabifiedDockWidgets(self): children = self.children() for child in children: if child.inherits("QTabBar"): tabCount = child.count() for idx in xrange(0, tabCount): for v in self.dockWidget.values(): if v.widget() and child.tabText(idx).startsWith( v.windowTitle( )) and not v.widget().windowIcon().isNull(): child.setTabIcon(idx, v.widget().windowIcon()) # to and _from are volontary swapped here compared to the sent signal. def tabMoved(self, to, _from): tab = self.sender() self.__tabAreaInformation = (tab, to, _from) self.__tabMoved = True # gather all widgets associated to a tabbar # returns a tuple with first element being the # widget associated to currentIndex and the second # elements being its siblings def findDockWidgetsFromTabBar(self, tab): tabwidget = None tabname = "" if not isinstance(tab, QTabBar): return None siblings = [] current_widget = None for i in xrange(0, tab.count()): title = tab.tabText(i) for dockwidget in self.dockWidget.values(): if title == dockwidget.windowTitle(): if i == tab.currentIndex(): current_widget = dockwidget else: siblings.append(dockwidget) return (current_widget, siblings) ############# END OF DOCKWIDGETS FUNCTIONS ############### def applyModule(self, modname, modtype, selected): appMod = ApplyModule(self) appMod.openApplyModule(modname, modtype, selected) def initCallback(self): self.sched.set_callback("add_qwidget", self.qwidgetResult) self.connect(self, SIGNAL("qwidgetResultView"), self.qwidgetResultView) self.connect(self, SIGNAL("strResultView"), self.strResultView) def qwidgetResult(self, qwidget): self.emit(SIGNAL("qwidgetResultView"), qwidget) def strResult(self, proc): self.emit(SIGNAL("strResultView"), proc) def qwidgetResultView(self, proc): proc.inst.g_display() self.addDockWidgets(proc.inst, proc.name) proc.inst.updateWidget() def strResultView(self, proc): widget = TextEdit(proc) try: res = '' txt = proc.stream.get(0) res += txt while txt: txt = proc.stream.get(0) res += txt except Empty: pass if res and res != '': widget.emit(SIGNAL("puttext"), res) self.addDockWidgets(widget, proc.name) def addToolBars(self, action): """ Init Toolbar""" if not action: #Add separator self.toolBar.addSeparator() else: action.setText(action.text()) self.toolBar.addAction(action) def addAction(self, name, text, func=None, iconName=None, iconText=None): self.action[name] = QtGui.QAction(self) self.action[name].setObjectName("action" + name) self.action[name].setText(text) if iconName: self.action[name].setIcon(QIcon(iconName)) if iconText: self.action[name].setIconText(iconText) if func: self.connect(self.action[name], SIGNAL("triggered()"), func) def setupToolBar(self): for action in self.toolbarList: self.addToolBars(action) def createRootNodes(self): root = self.vfs.getnode('/') self.devicenode = deviceNode(root, str('Local devices')) self.logicalenode = logicalNode(root, str('Logical files')) self.modulesrootnode = ModulesRootNode(VFS.Get(), root) self.booknode = bookNode(root, str('Bookmarks')) def changeEvent(self, event): """ Search for a language change event This event have to call retranslateUi to change interface language on the fly. """ if event.type() == QEvent.LanguageChange: self.retranslateUi(self) self.translation() else: QMainWindow.changeEvent(self, event) def translation(self): self.errorLoadingHelp = self.tr('Error while loading help') self.onlineHelp = self.tr( '<br>You can check on-line help at <a href=\"http://wiki.digital-forensic.org/\">http://wiki.digital-forensic.org</a>.' ) self.notAnHelpFile = self.tr('Not an help file.') + self.onlineHelp self.noSuchHelpFile = self.tr( 'Documentation path not found.') + self.onlineHelp
class AnalysisWindow(QMainWindow): def __init__(self, parent, controller): # create window QMainWindow.__init__(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("Tracking Analysis") self.setGeometry(100, 200, 10, 10) # set controller self.controller = controller # create main widget & layout self.main_widget = QWidget(self) self.main_widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.main_layout = QGridLayout(self.main_widget) # create left widget & layout self.left_widget = QWidget(self) self.main_layout.addWidget(self.left_widget, 0, 0) self.left_layout = QVBoxLayout(self.left_widget) self.left_layout.setAlignment(Qt.AlignTop) # create list of tracking items self.tracking_list_items = [] self.tracking_list = QListWidget(self) self.tracking_list.currentRowChanged.connect(self.controller.switch_tracking_file) self.left_layout.addWidget(self.tracking_list) # create tracking list buttons self.tracking_list_buttons = QHBoxLayout(self) self.left_layout.addLayout(self.tracking_list_buttons) self.add_tracking_button = QPushButton('+') self.add_tracking_button.clicked.connect(self.controller.select_and_open_tracking_files) self.add_tracking_button.setToolTip("Add tracking file.") self.tracking_list_buttons.addWidget(self.add_tracking_button) self.remove_tracking_button = QPushButton('-') self.remove_tracking_button.clicked.connect(self.controller.remove_tracking_file) self.remove_tracking_button.setToolTip("Remove selected tracking file.") self.tracking_list_buttons.addWidget(self.remove_tracking_button) self.prev_tracking_button = QPushButton('<') self.prev_tracking_button.clicked.connect(self.controller.prev_tracking_file) self.prev_tracking_button.setToolTip("Switch to previous tracking file.") self.tracking_list_buttons.addWidget(self.prev_tracking_button) self.next_tracking_button = QPushButton('>') self.next_tracking_button.clicked.connect(self.controller.next_tracking_file) self.next_tracking_button.setToolTip("Switch to next tracking file.") self.tracking_list_buttons.addWidget(self.next_tracking_button) # create right widget & layout self.right_widget = QWidget(self) self.right_widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.main_layout.addWidget(self.right_widget, 0, 1) self.right_layout = QVBoxLayout(self.right_widget) self.right_layout.setAlignment(Qt.AlignTop) self.right_layout.setSpacing(5) # create button layout for main widget plot_horiz_layout = QHBoxLayout() self.right_layout.addLayout(plot_horiz_layout) # add param labels & textboxes plot_type_label = QLabel() plot_type_label.setText("Plot:") plot_horiz_layout.addWidget(plot_type_label) plot_horiz_layout.addStretch(1) # create tab widget for plot type self.plot_tabs_widget = QTabBar() self.plot_tabs_widget.setDrawBase(False) self.plot_tabs_widget.setExpanding(False) self.plot_tabs_widget.currentChanged.connect(self.controller.change_plot_type) plot_horiz_layout.addWidget(self.plot_tabs_widget) # create button layout for main widget crop_horiz_layout = QHBoxLayout() self.right_layout.addLayout(crop_horiz_layout) # add param labels & textboxes crop_type_label = QLabel() crop_type_label.setText("Crop #:") crop_horiz_layout.addWidget(crop_type_label) crop_horiz_layout.addStretch(1) # create tab widget for crop number self.crop_tabs_widget = QTabBar() self.crop_tabs_widget.setDrawBase(False) self.crop_tabs_widget.setExpanding(False) self.crop_tabs_widget.currentChanged.connect(self.controller.change_crop) crop_horiz_layout.addWidget(self.crop_tabs_widget) # create button layout for main widget button_layout = QHBoxLayout() button_layout.addStretch(1) self.right_layout.addLayout(button_layout) # add buttons self.show_tracking_params_button = QPushButton('Tracking Parameters', self) self.show_tracking_params_button.setMinimumHeight(30) self.show_tracking_params_button.clicked.connect(self.controller.show_tracking_params) button_layout.addWidget(self.show_tracking_params_button) # create stacked widget & layout self.stacked_widget = QStackedWidget(self) self.stacked_widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.right_layout.addWidget(self.stacked_widget) self.create_tail_tracking_widget(self.stacked_widget) self.create_body_tracking_widget(self.stacked_widget) # self.right_layout = QVBoxLayout(self.right_widget) # self.right_layout.setAlignment(Qt.AlignTop) # self.right_layout.setSpacing(5) # self.main_widget.setFocus() self.setCentralWidget(self.main_widget) # set window titlebar buttons self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint) self.show() def update_plot(self, array, plot_type, extra_tracking=None, keep_xlim=True): print("Updating plot") if plot_type == "tail": self.stacked_widget.setCurrentIndex(0) self.plot_window.plot_tail_angle_array(array, extra_tracking=extra_tracking, keep_xlim=keep_xlim) elif plot_type == "body": self.stacked_widget.setCurrentIndex(1) self.plot_window.plot_heading_angle_array(array, keep_xlim=keep_xlim) else: pass def switch_tracking_item(self, row_number): print("Switching tracking item") tracking_params = self.controller.tracking_params[row_number] self.change_selected_tracking_row(row_number) self.plot_tabs_widget.blockSignals(True) # add plot tabs for i in range(self.plot_tabs_widget.count()-1, -1, -1): self.plot_tabs_widget.removeTab(i) if tracking_params['type'] == "freeswimming": if tracking_params['track_tail']: self.plot_tabs_widget.addTab("Tail") self.plot_tabs_widget.addTab("Body") if tracking_params['track_eyes']: self.plot_tabs_widget.addTab("Eyes") else: self.plot_tabs_widget.addTab("Tail") self.plot_tabs_widget.blockSignals(False) self.crop_tabs_widget.blockSignals(True) for i in range(self.crop_tabs_widget.count()-1, -1, -1): self.crop_tabs_widget.removeTab(i) # add crop tabs n_crops = len(tracking_params['crop_params']) for i in range(n_crops): self.crop_tabs_widget.addTab("{}".format(i+1)) self.crop_tabs_widget.blockSignals(False) def add_tracking_item(self, item_name): print("Adding tracking item") self.tracking_list_items.append(QListWidgetItem(item_name, self.tracking_list)) # self.update_plot() def change_selected_tracking_row(self, row_number): self.tracking_list.blockSignals(True) self.tracking_list.setCurrentRow(row_number) self.tracking_list.blockSignals(False) def create_tail_tracking_widget(self, parent_widget): # create tail tab widget & layout tail_tab_widget = QWidget() tail_tab_layout = QVBoxLayout(tail_tab_widget) # create button layout for tail tab bottom_tail_button_layout = QVBoxLayout() # bottom_tail_button_layout.setSpacing(5) bottom_tail_button_layout.addStretch(1) tail_tab_layout.addLayout(bottom_tail_button_layout) # add buttons track_bouts_button = QPushButton('Track Bouts', self) track_bouts_button.setMinimumHeight(30) track_bouts_button.setMaximumWidth(100) track_bouts_button.clicked.connect(lambda:self.controller.track_bouts()) bottom_tail_button_layout.addWidget(track_bouts_button) track_freqs_button = QPushButton('Track Freq', self) track_freqs_button.setMinimumHeight(30) track_freqs_button.setMaximumWidth(100) track_freqs_button.clicked.connect(lambda:self.controller.track_freqs()) bottom_tail_button_layout.addWidget(track_freqs_button) # add checkbox for switching plots self.smoothed_deriv_checkbox = QCheckBox("Show smoothed derivative") self.smoothed_deriv_checkbox.toggled.connect(lambda:self.show_smoothed_deriv(self.smoothed_deriv_checkbox)) bottom_tail_button_layout.addWidget(self.smoothed_deriv_checkbox) # add param labels & textboxes smoothing_window_label = QLabel() smoothing_window_label.setText("Smoothing window:") bottom_tail_button_layout.addWidget(smoothing_window_label) self.smoothing_window_param_box = QLineEdit(self) self.smoothing_window_param_box.setMinimumHeight(20) self.smoothing_window_param_box.setMaximumWidth(40) self.smoothing_window_param_box.setText(str(self.controller.smoothing_window_width)) bottom_tail_button_layout.addWidget(self.smoothing_window_param_box) threshold_label = QLabel() threshold_label.setText("Threshold:") bottom_tail_button_layout.addWidget(threshold_label) self.threshold_param_box = QLineEdit(self) self.threshold_param_box.setMinimumHeight(20) self.threshold_param_box.setMaximumWidth(40) self.threshold_param_box.setText(str(self.controller.threshold)) bottom_tail_button_layout.addWidget(self.threshold_param_box) min_width_label = QLabel() min_width_label.setText("Min width:") bottom_tail_button_layout.addWidget(min_width_label) self.min_width_param_box = QLineEdit(self) self.min_width_param_box.setMinimumHeight(20) self.min_width_param_box.setMaximumWidth(40) self.min_width_param_box.setText(str(self.controller.min_width)) bottom_tail_button_layout.addWidget(self.min_width_param_box) parent_widget.addWidget(tail_tab_widget) def create_body_tracking_widget(self, parent_widget): # create head tab widget & layout head_tab_widget = QWidget() head_tab_layout = QVBoxLayout(head_tab_widget) # create button layout for head tab bottom_head_button_layout = QHBoxLayout() head_tab_layout.addLayout(bottom_head_button_layout) # add buttons track_position_button = QPushButton('Track Pos', self) track_position_button.setMinimumHeight(30) track_position_button.setMaximumWidth(100) track_position_button.clicked.connect(lambda:self.track_position()) bottom_head_button_layout.addWidget(track_position_button) # add checkbox for switching plots speed_checkbox = QCheckBox("Show speed") speed_checkbox.toggled.connect(lambda:self.show_speed(self.speed_checkbox)) bottom_head_button_layout.addWidget(speed_checkbox) parent_widget.addWidget(head_tab_widget) def create_crops(self, parent_layout): crop_tabs_widget = QTabWidget() crop_tabs_widget.currentChanged.connect(self.change_crop) crop_tabs_widget.setElideMode(Qt.ElideLeft) crop_tabs_layout = QVBoxLayout(crop_tabs_widget) parent_layout.addWidget(crop_tabs_widget) self.crop_tabs_widgets.append(crop_tabs_widget) self.crop_tabs_layouts.append(crop_tabs_layout) n_crops = len(self.controller.tracking_params[self.controller.curr_tracking_num]['crop_params']) for k in range(n_crops): self.create_crop() def clear_crops(self): self.crop_tab_layouts = [[]] self.crop_tab_widgets = [[]] self.plot_tab_layouts = [{'tail': [], 'eyes': [], 'body': []}] self.plot_tab_widgets = [{'tail': [], 'eyes': [], 'body': []}] self.plot_tabs_widgets = [[]] self.plot_tabs_layouts = [[]] self.head_angle_arrays = [] self.tail_angle_arrays = [] for c in range(self.n_crops-1, -1, -1): # remove tab self.crop_tabs_widget.removeTab(c) self.n_crops = 0 self.current_crop = -1 def show_smoothed_deriv(self, checkbox): if self.smoothed_abs_deriv_abs_angle_array != None: if checkbox.isChecked(): self.tail_canvas.plot_tail_angle_array(self.smoothed_abs_deriv_abs_angle_array, self.bouts, keep_limits=True) else: self.tail_canvas.plot_tail_angle_array(self.tail_end_angle_array[self.current_crop], self.bouts, self.peak_maxes_y, self.peak_maxes_x, self.peak_mins_y, self.peak_mins_x, self.freqs, keep_limits=True) def show_speed(self, checkbox): if self.speed_array != None: if checkbox.isChecked(): self.head_canvas.plot_head_array(self.speed_array, keep_limits=True) else: self.head_canvas.plot_head_array(self.head_angle_array, keep_limits=True) def load_data(self, data_path=None): if data_path == None: # ask the user to select a directory self.path = str(QFileDialog.getExistingDirectory(self, 'Open folder')) else: self.path = data_path # load saved tracking data (self.tail_coords_array, self.spline_coords_array, self.heading_angle_array, self.body_position_array, self.eye_coords_array, self.params) = an.open_saved_data(self.path) if self.params != None: # calculate tail angles if self.params['type'] == "freeswimming" and self.params['track_tail']: self.tail_angle_array = an.get_freeswimming_tail_angles(self.tail_coords_array, self.heading_angle_array, self.body_position_array) elif self.params['type'] == "headfixed": self.tail_angle_array = an.get_headfixed_tail_angles(self.tail_coords_array, self.params['tail_direction']) # get array of average angle of the last few points of the tail # self.tail_end_angle_array = np.mean(self.tail_angle_array[:, :, -3:], axis=-1) # self.tail_end_angle_array = self.tail_angle_array[:, :, -1] self.tail_end_angle_array = an.get_tail_end_angles(self.tail_angle_array, num_to_average=3) # clear crops self.clear_crops() # get number of saved crops n_crops_total = len(self.params['crop_params']) for k in range(n_crops_total): # create a crop self.create_crop() # plot heading angle if self.heading_angle_array is not None: self.plot_canvases[k].plot_heading_angle_array(self.heading_angle_array[k]) # plot tail angle if self.tail_angle_array is not None: self.tail_canvases[k].plot_tail_angle_array(self.tail_end_angle_array[k]) # def track_bouts(self): # if self.tail_angle_array != None: # # get params # self.smoothing_window_width = int(self.smoothing_window_param_box.text()) # self.threshold = float(self.threshold_param_box.text()) # self.min_width = int(self.min_width_param_box.text()) # # get smoothed derivative # abs_angle_array = np.abs(self.tail_end_angle_array[self.current_crop]) # deriv_abs_angle_array = np.gradient(abs_angle_array) # abs_deriv_abs_angle_array = np.abs(deriv_abs_angle_array) # normpdf = scipy.stats.norm.pdf(range(-int(self.smoothing_window_width/2),int(self.smoothing_window_width/2)),0,3) # self.smoothed_abs_deriv_abs_angle_array = np.convolve(abs_deriv_abs_angle_array, normpdf/np.sum(normpdf),mode='valid') # # calculate bout periods # self.bouts = an.contiguous_regions(self.smoothed_abs_deriv_abs_angle_array > self.threshold) # # remove bouts that don't have the minimum bout length # for i in range(self.bouts.shape[0]-1, -1, -1): # if self.bouts[i, 1] - self.bouts[i, 0] < self.min_width: # self.bouts = np.delete(self.bouts, (i), 0) # # update plot # self.smoothed_deriv_checkbox.setChecked(False) # self.tail_canvas.plot_tail_angle_array(self.tail_end_angle_array[self.current_crop], self.bouts, keep_limits=True) # def track_freqs(self): # if self.bouts != None: # # initiate bout maxima & minima coord lists # self.peak_maxes_y = [] # self.peak_maxes_x = [] # self.peak_mins_y = [] # self.peak_mins_x = [] # # initiate instantaneous frequency array # self.freqs = np.zeros(self.tail_angle_array.shape[0]) # for i in range(self.bouts.shape[0]): # # get local maxima & minima # peak_max, peak_min = peakdetect.peakdet(self.tail_end_angle_array[self.current_crop][self.bouts[i, 0]:self.bouts[i, 1]], 0.02) # # change local coordinates (relative to the start of the bout) to global coordinates # peak_max[:, 0] += self.bouts[i, 0] # peak_min[:, 0] += self.bouts[i, 0] # # add to the bout maxima & minima coord lists # self.peak_maxes_y += list(peak_max[:, 1]) # self.peak_maxes_x += list(peak_max[:, 0]) # self.peak_mins_y += list(peak_min[:, 1]) # self.peak_mins_x += list(peak_min[:, 0]) # # calculate instantaneous frequencies # for i in range(len(self.peak_maxes_x)-1): # self.freqs[self.peak_maxes_x[i]:self.peak_maxes_x[i+1]] = 1.0/(self.peak_maxes_x[i+1] - self.peak_maxes_x[i]) # # update plot # self.smoothed_deriv_checkbox.setChecked(False) # self.tail_canvas.plot_tail_angle_array(self.tail_end_angle_array[self.current_crop], self.bouts, self.peak_maxes_y, self.peak_maxes_x, self.peak_mins_y, self.peak_mins_x, self.freqs, keep_limits=True) def track_position(self): if self.head_angle_array != None: # get params self.smoothing_window_width = int(self.smoothing_window_param_box.text()) abs_angle_array = np.abs(self.tail_angle_array) deriv_abs_angle_array = np.gradient(abs_angle_array) abs_deriv_abs_angle_array = np.abs(deriv_abs_angle_array) self.smoothed_abs_deriv_abs_angle_array = np.convolve(abs_deriv_abs_angle_array, np.ones((self.smoothing_window_width,))/self.smoothing_window_width, mode='valid') positions_y, positions_x, self.speed_array = an.get_position_history(self.path, plot=False) def fileQuit(self): self.close() def closeEvent(self, event): self.controller.close_all() def resizeEvent(self, re): QMainWindow.resizeEvent(self, re)
def __setupUi(self): """Set up the UI. """ if self.__macUnified: self.tab = QToolBar() self.addToolBar(Qt.TopToolBarArea, self.tab) self.setUnifiedTitleAndToolBarOnMac(True) # This does not seem to work self.setWindowFlags(self.windowFlags() & \ ~Qt.MacWindowToolBarButtonHint) self.tab.actionTriggered[QAction].connect( self.__macOnToolBarAction) central = QStackedWidget() central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) else: self.tab = central = QTabWidget(self) # Add a close button to the bottom of the dialog # (to satisfy GNOME 3 which shows the dialog without a title bar). container = container_widget_helper() container.layout().addWidget(central) buttonbox = QDialogButtonBox(QDialogButtonBox.Close) buttonbox.rejected.connect(self.close) container.layout().addWidget(buttonbox) self.setCentralWidget(container) self.stack = central # General Tab tab = QWidget() self.addTab(tab, self.tr("General"), toolTip=self.tr("General Options")) form = QFormLayout() tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) nodes = QWidget(self, objectName="nodes") nodes.setLayout(QVBoxLayout()) nodes.layout().setContentsMargins(0, 0, 0, 0) cb_anim = QCheckBox(self.tr("Enable node animations"), objectName="enable-node-animations", toolTip=self.tr( "Enable shadow and ping animations for node " "items in the scheme.")) self.bind(cb_anim, "checked", "schemeedit/enable-node-animations") nodes.layout().addWidget(cb_anim) form.addRow(self.tr("Nodes"), nodes) links = QWidget(self, objectName="links") links.setLayout(QVBoxLayout()) links.layout().setContentsMargins(0, 0, 0, 0) cb_show = QCheckBox(self.tr("Show channel names between widgets"), objectName="show-channel-names", toolTip=self.tr( "Show source and sink channel names " "over the links.")) self.bind(cb_show, "checked", "schemeedit/show-channel-names") links.layout().addWidget(cb_show) form.addRow(self.tr("Links"), links) quickmenu = QWidget(self, objectName="quickmenu-options") quickmenu.setLayout(QVBoxLayout()) quickmenu.layout().setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("On double click"), toolTip=self.tr("Open quick menu on a double click " "on an empty spot in the canvas")) cb2 = QCheckBox(self.tr("On right click"), toolTip=self.tr("Open quick menu on a right click " "on an empty spot in the canvas")) cb3 = QCheckBox(self.tr("On space key press"), toolTip=self.tr("On Space key press while the mouse" "is hovering over the canvas.")) cb4 = QCheckBox(self.tr("On any key press"), toolTip=self.tr("On any key press while the mouse" "is hovering over the canvas.")) self.bind(cb1, "checked", "quickmenu/trigger-on-double-click") self.bind(cb2, "checked", "quickmenu/trigger-on-right-click") self.bind(cb3, "checked", "quickmenu/trigger-on-space-key") self.bind(cb4, "checked", "quickmenu/trigger-on-any-key") quickmenu.layout().addWidget(cb1) quickmenu.layout().addWidget(cb2) quickmenu.layout().addWidget(cb3) quickmenu.layout().addWidget(cb4) form.addRow(self.tr("Open quick menu on"), quickmenu) startup = QWidget(self, objectName="startup-group") startup.setLayout(QVBoxLayout()) startup.layout().setContentsMargins(0, 0, 0, 0) cb_splash = QCheckBox(self.tr("Show splash screen"), self, objectName="show-splash-screen") cb_welcome = QCheckBox(self.tr("Show welcome screen"), self, objectName="show-welcome-screen") self.bind(cb_splash, "checked", "startup/show-splash-screen") self.bind(cb_welcome, "checked", "startup/show-welcome-screen") startup.layout().addWidget(cb_splash) startup.layout().addWidget(cb_welcome) form.addRow(self.tr("On startup"), startup) toolbox = QWidget(self, objectName="toolbox-group") toolbox.setLayout(QVBoxLayout()) toolbox.layout().setContentsMargins(0, 0, 0, 0) exclusive = QCheckBox(self.tr("Only one tab can be open at a time")) self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive") toolbox.layout().addWidget(exclusive) form.addRow(self.tr("Tool box"), toolbox) tab.setLayout(form) # Output Tab tab = QWidget() self.addTab(tab, self.tr("Output"), toolTip="Output Redirection") form = QFormLayout() box = QWidget(self, objectName="streams") layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Standard output")) cb2 = QCheckBox(self.tr("Standard error")) self.bind(cb1, "checked", "output/redirect-stdout") self.bind(cb2, "checked", "output/redirect-stderr") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Redirect output"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) combo = QComboBox() combo.addItems([ self.tr("Critical"), self.tr("Error"), self.tr("Warn"), self.tr("Info"), self.tr("Debug") ]) cb = QCheckBox(self.tr("Show output on 'Error'"), objectName="focus-on-error") self.bind(combo, "currentIndex", "logging/level") self.bind(cb, "checked", "output/show-on-error") layout.addWidget(combo) layout.addWidget(cb) box.setLayout(layout) form.addRow(self.tr("Logging"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Stay on top"), objectName="stay-on-top") cb2 = QCheckBox(self.tr("Dockable"), objectName="output-dockable") self.bind(cb1, "checked", "output/stay-on-top") self.bind(cb2, "checked", "output/dockable") layout.addWidget(cb1) layout.addWidget(cb2) box.setLayout(layout) form.addRow(self.tr("Output window"), box) box = QWidget() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) cb1 = QCheckBox(self.tr("Open in external browser"), objectName="open-in-external-browser") cb2 = QCheckBox(self.tr("Stay on top"), objectName="help-stay-on-top") cb3 = QCheckBox(self.tr("Dockable"), objectName="help-dockable") self.bind(cb1, "checked", "help/open-in-external-browser") self.bind(cb2, "checked", "help/stay-on-top") self.bind(cb3, "checked", "help/dockable") layout.addWidget(cb1) layout.addWidget(cb2) layout.addWidget(cb3) box.setLayout(layout) form.addRow(self.tr("Help window"), box) tab.setLayout(form) # Categories Tab tab = QWidget() layout = QVBoxLayout() view = QListView() from .. import registry reg = registry.global_registry() model = QStandardItemModel() settings = QSettings() for cat in reg.categories(): item = QStandardItem() item.setText(cat.name) item.setCheckable(True) visible, _ = category_state(cat, settings) item.setCheckState(Qt.Checked if visible else Qt.Unchecked) model.appendRow([item]) view.setModel(model) layout.addWidget(view) tab.setLayout(layout) model.itemChanged.connect(lambda item: save_category_state( reg.category(str(item.text())), _State(item.checkState() == Qt.Checked, -1), settings)) self.addTab(tab, "Categories") if self.__macUnified: # Need some sensible size otherwise mac unified toolbar 'takes' # the space that should be used for layout of the contents self.adjustSize()