def getContextMenus(self, event=None): if self.menu is None: self.menu = QtWidgets.QMenu() self.menu.setTitle(self.name + " options..") green = QtWidgets.QAction("Turn green", self.menu) green.triggered.connect(self.setGreen) self.menu.addAction(green) self.menu.green = green blue = QtWidgets.QAction("Turn blue", self.menu) blue.triggered.connect(self.setBlue) self.menu.addAction(blue) self.menu.green = blue alpha = QtWidgets.QWidgetAction(self.menu) alphaSlider = QtWidgets.QSlider() alphaSlider.setOrientation(QtCore.Qt.Horizontal) alphaSlider.setMaximum(255) alphaSlider.setValue(255) alphaSlider.valueChanged.connect(self.setAlpha) alpha.setDefaultWidget(alphaSlider) self.menu.addAction(alpha) self.menu.alpha = alpha self.menu.alphaSlider = alphaSlider return self.menu
def __init__(self, parent=None, *args, parent_module=None): QtWidgets.QWidget.__init__(self, parent, *args) self.ui = Ui_Form() self.ui.setupUi(self) self.pm = parent_module self.df = self.pm.df_selection self.ui.listWidgetAllColumns.addItems(self.df.columns) self.ui.pushButtonAddCat.clicked.connect( lambda: self.addToColumn(self.ui.listWidgetCategoricals)) self.ui.pushButtonAddNum.clicked.connect( lambda: self.addToColumn(self.ui.listWidgetNumericals)) self.ui.pushButtonClearCat.clicked.connect( self.ui.listWidgetCategoricals.clear) self.ui.pushButtonClearNum.clicked.connect( self.ui.listWidgetNumericals.clear) self.ui.pushButtonFindSignificance.clicked.connect( self.scan_significance) self.transformation_names = [ "none", "square root", "natural logarithm", "reciprocal", "reciprocal sqrt", "exponential" ] for transform in self.transformation_names: self.ui.comboBoxTransformations.addItem(transform) for test in ["kruskal wallis", "oneway anova"]: self.ui.comboBoxTests.addItem(test) self.ui.framePlotWidget.setLayout(QtWidgets.QVBoxLayout()) self.plot = PlotWidget() self.ui.framePlotWidget.layout().addWidget(self.plot) self.ui.framePlotWidget.setVisible(False) self.ui.listWidgetCategoricals.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.listWidgetCategoricals.customContextMenuRequested.connect( self.contextMenuEventCategoricals) self.ui.listWidgetNumericals.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.listWidgetNumericals.customContextMenuRequested.connect( self.contextMenuEventNumericals) #Actions: self.deleteSelectionCategoricalsAction = QtWidgets.QAction( "Delete Selection", self, triggered=lambda: self.deleteSelection(self.ui. listWidgetCategoricals)) self.deleteSelectionNumericalsAction = QtWidgets.QAction( "Delete Selection", self, triggered=lambda: self.deleteSelection(self.ui.listWidgetNumericals ))
def initUI(self): self.setWindowTitle('PyTplot') menubar = self.menuBar() exportMenu = menubar.addMenu('Export') exportDatapngAction = QtWidgets.QAction("PNG", self) exportDatapngAction.triggered.connect(self.exportpng) exportMenu.addAction(exportDatapngAction)
def getMenu(self): """ Create the menu """ if self.menu is None: self.menu = QtWidgets.QMenu() self.save_plot = QtWidgets.QAction("Save Figure", self.menu) self.save_plot.triggered.connect(self.export) self.menu.addAction(self.save_plot) return self.menu
def __init__(self, dat: RegularDataArray = None, **kwargs): """2D image view with extra features. :param lut: Name of colormap to initialize with :type lut: str """ super().__init__(dat, **kwargs) # ------------- # Colormap menu # ------------- self.cmap_menu = QtWidgets.QMenu('Color Map') # Reset colormap self.cmap_reset_action = QtWidgets.QAction('Reset') self.cmap_reset_action.triggered.connect(self.cmap_reset) self.cmap_menu.addAction(self.cmap_reset_action) # Scale to view self.cmap_to_view_action = QtWidgets.QAction('Scale to view') self.cmap_to_view_action.triggered.connect(self.cmap_to_range) self.cmap_menu.addAction(self.cmap_to_view_action) # Change colormap self.change_cmap_menu = QtWidgets.QMenu('Change colormap') self.change_cmap_actions = [] def callback_prototype(imgbase, cmap_name="viridis"): ct = CMap().load_ct(cmap_name) imgbase.baselut = ct imgbase.set_lut(ct) for name in CMap().cmaps: action = QtWidgets.QAction(name) action.triggered.connect(partial(callback_prototype, self, name)) self.change_cmap_actions.append(action) self.change_cmap_menu.addAction(action) self.cmap_menu.addMenu(self.change_cmap_menu) # Edit colormap self.edit_cmap_action = QtWidgets.QAction('Edit Color Map') self.edit_cmap_action.triggered.connect(self.edit_cmap) self.cmap_menu.addAction(self.edit_cmap_action) self.menu.addMenu(self.cmap_menu) # self.cmap_editor = QtWidgets.QWidget() self.build_cmap_form()
def init_savepng(self, exporter): # Set up the save PNG button/call exportpng function that activates when user presses button exportdatapngaction = QtWidgets.QAction("Save PNG", self) exportdatapngaction.triggered.connect( lambda: self.exportpng(exporter)) # Set up menu bar to display and call creation of save PNG button menubar = self.menuBar() menubar.setNativeMenuBar(False) menubar.addAction(exportdatapngaction) self.setWindowTitle('PyTplot Window')
def __init__(self, parent = None, *args, parent_module = None): QtWidgets.QWidget.__init__(self, parent, *args) self.ui = Ui_Form() self.ui.setupUi(self) self.show() self.pm = parent_module self.df = self.pm.df_selection self.ui.framePlotWidget.setLayout(QtWidgets.QVBoxLayout()) self.plot = PlotWidget() self.ui.framePlotWidget.layout().addWidget(self.plot) self.ui.listWidgetAllColumns.addItems(self.df.columns) self.transformation_names = ["none", "square root", "natural logarithm", "reciprocal", "reciprocal sqrt", "exponential"] for transform in self.transformation_names: self.ui.comboBoxTransformations.addItem(transform) for test in ["MannWhitney U", "Students T-test"]: self.ui.comboBoxTests.addItem(test) self.ui.pushButtonAddNum.clicked.connect(lambda: self.addToColumn(self.ui.listWidgetNumericals)) self.ui.pushButtonAddCat.clicked.connect(self.addCategorical) self.ui.pushButtonRunPairs.clicked.connect(self.scan) self.ui.listWidgetCategorical.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ui.listWidgetCategorical.customContextMenuRequested.connect(self.contextMenuEventCategorical) self.ui.listWidgetNumericals.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ui.listWidgetNumericals.customContextMenuRequested.connect(self.contextMenuEventNumericals) # Actions: self.deleteSelectionCategoricalAction = QtWidgets.QAction("Delete Selection", self, triggered=lambda: self.deleteSelection( self.ui.listWidgetCategorical)) self.deleteSelectionNumericalsAction = QtWidgets.QAction("Delete Selection", self, triggered=lambda: self.deleteSelection( self.ui.listWidgetNumericals))
def __init__(self): QtGui.QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.astroImageView) # QtDesigner does not support QActionGroup (radio button), so we handle # it outside of the generated UI file self.roiSelectGroup = QtWidgets.QActionGroup(self) self.roiSelectGroup.addAction(self.ui.actionDisable_ROI) self.roiSelectGroup.addAction(self.ui.actionRectangular_ROI) self.roiSelectGroup.addAction(self.ui.actionPolygonal_ROI) self.roiSelectGroup.addAction(self.ui.actionElliptic_ROI) self.roiSelectGroup.addAction(self.ui.actionSemi_automatic_ROI) # Populate "Recent Files" menu recentFiles = Settings.getRecentFiles() for i, path in enumerate(recentFiles): if i == 0: self.ui.action_NoRecentFile.setVisible(False) recentFileAction = QtWidgets.QAction(self) recentFileAction.setText("%i | %s" % (i, path)) openFileFunction = self._createOpenFileFunction(path) recentFileAction.triggered.connect(openFileFunction) self.ui.menuRecent_Files.insertAction(self.ui.actionClear_Menu, recentFileAction) if i == len(recentFiles) - 1: self.ui.menuRecent_Files.insertSeparator( self.ui.actionClear_Menu) # Connect actions to event handlers self.ui.actionExit.triggered.connect(QtWidgets.qApp.quit) self.ui.actionOpen.triggered.connect(self._open_file) self.ui.actionClear_Menu.triggered.connect(self._clearRecentFiles) self.ui.actionSee_FITS_header.triggered.connect( self._openFitsHeaderDialog ) self.roiSelectGroup.triggered.connect(self._selectROI) self.ui.actionAbout.triggered.connect(self._openAboutDialog) self.fitsFileStatusBarLabel = QtGui.QLabel() self.ui.statusbar.addPermanentWidget(self.fitsFileStatusBarLabel) self.dialog = None self.fitsFile = None
def create_action(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QtWidgets.QAction(text, self) if icon is not None: action.setIcon(QtGui.QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: action.triggered.connect(slot) if checkable: action.setCheckable(True) return action
def __init__(self, dat: Union[RegularDataArray, None] = None, parent=None, name=None, labels=None, title=None, viewBox=None, axisItems=None, enableMenu=True, **kwargs): super().__init__(parent, name, labels, title, viewBox, axisItems, enableMenu, **kwargs) self.img = pg.ImageItem(parent=self) self.addItem(self.img) if dat is not None: if dat.ndim != 2: warnings.warn('Data should have exactly 2 dimensions.') if dat.ndim < 2: raise NotImplementedError('This tool cannot yet insert one dimension.') else: raise NotImplementedError('This tool cannot yet sample exactly two dimensions.') else: self.set_data(dat) # Create the menu self.menu = QtWidgets.QMenu('Image') self.aspect_menu = QtWidgets.QMenu('Aspect Ratio') self.aspect_ui = AspectRatioForm() self.build_aspect_form() self.edit_cmap_action = QtWidgets.QAction('Edit Color Map') self.edit_cmap_action.triggered.connect(self.edit_cmap) self.cmap_editor = QtWidgets.QWidget() self.menu.addAction(self.edit_cmap_action) self.ctrl_down = False
def __init__(self, options, viewSetupInfo, *args, **kwargs): super().__init__(*args, **kwargs) # Widget factory self.factory = widgets.WidgetFactory(options) self.docks = {} self.widgets = {} # Menu Bar menuBar = self.menuBar() file = menuBar.addMenu('&File') tools = menuBar.addMenu('&Tools') self.loadParamsAction = QtWidgets.QAction('Load parameters from saved HDF5 file…', self) self.loadParamsAction.setShortcut('Ctrl+O') self.loadParamsAction.triggered.connect(self.sigLoadParamsFromHDF5) file.addAction(self.loadParamsAction) self.pickSetupAction = QtWidgets.QAction('Pick setup…', self) self.pickSetupAction.triggered.connect(self.sigPickSetup) tools.addAction(self.pickSetupAction) # Window self.setWindowTitle('ImSwitch') self.cwidget = QtWidgets.QWidget() layout = QtWidgets.QHBoxLayout() self.cwidget.setLayout(layout) self.setCentralWidget(self.cwidget) # Dock area dockArea = DockArea() enabledDockKeys = viewSetupInfo.availableWidgets prevRightDock = None prevRightDockYPosition = -1 def addRightDock(widgetKey, dockInfo): nonlocal prevRightDock, prevRightDockYPosition self.docks[widgetKey] = Dock(dockInfo.name, size=(1, 1)) self.widgets[widgetKey] = self.factory.createWidget( getattr(widgets, f'{widgetKey}Widget') ) self.docks[widgetKey].addWidget(self.widgets[widgetKey]) if prevRightDock is None: dockArea.addDock(self.docks[widgetKey]) elif dockInfo.yPosition > prevRightDockYPosition: dockArea.addDock(self.docks[widgetKey], 'bottom', prevRightDock) else: dockArea.addDock(self.docks[widgetKey], 'above', prevRightDock) prevRightDock = self.docks[widgetKey] prevRightDockYPosition = dockInfo.yPosition rightDocks = { 'FocusLock': _DockInfo(name='Focus Lock', yPosition=0), 'SLM': _DockInfo(name='SLM', yPosition=0), 'Laser': _DockInfo(name='Laser Control', yPosition=0), 'Positioner': _DockInfo(name='Positioner', yPosition=1), 'Scan': _DockInfo(name='Scan', yPosition=2), 'BeadRec': _DockInfo(name='Bead Rec', yPosition=3), 'AlignmentLine': _DockInfo(name='Alignment Tool', yPosition=3), 'AlignAverage': _DockInfo(name='Axial Alignment Tool', yPosition=3), 'AlignXY': _DockInfo(name='Rotational Alignment Tool', yPosition=3), 'ULenses': _DockInfo(name='uLenses Tool', yPosition=3), 'FFT': _DockInfo(name='FFT Tool', yPosition=3) } for widgetKey, dockInfo in rightDocks.items(): if widgetKey in enabledDockKeys: addRightDock(widgetKey, dockInfo) if 'Image' in enabledDockKeys: self.docks['Image'] = Dock('Image Display', size=(1, 1)) self.widgets['Image'] = self.factory.createWidget(widgets.ImageWidget) self.docks['Image'].addWidget(self.widgets['Image']) dockArea.addDock(self.docks['Image'], 'left') prevLeftDock = None prevLeftDockYPosition = -1 def addLeftDock(widgetKey, dockInfo): nonlocal prevLeftDock, prevLeftDockYPosition self.docks[widgetKey] = Dock(dockInfo.name, size=(1, 1)) self.widgets[widgetKey] = self.factory.createWidget( getattr(widgets, f'{widgetKey}Widget') ) self.docks[widgetKey].addWidget(self.widgets[widgetKey]) if prevLeftDock is None: dockArea.addDock(self.docks[widgetKey], 'left') elif dockInfo.yPosition > prevLeftDockYPosition: dockArea.addDock(self.docks[widgetKey], 'bottom', prevLeftDock) else: dockArea.addDock(self.docks[widgetKey], 'above', prevLeftDock) prevLeftDock = self.docks[widgetKey] prevLeftDockYPosition = dockInfo.yPosition leftDocks = { 'Settings': _DockInfo(name='Detector Settings', yPosition=0), 'View': _DockInfo(name='Image Controls', yPosition=1), 'Recording': _DockInfo(name='Recording', yPosition=2), 'Console': _DockInfo(name='Console', yPosition=3) } for widgetKey, dockInfo in leftDocks.items(): if widgetKey in enabledDockKeys: addLeftDock(widgetKey, dockInfo) # Add dock area to layout layout.addWidget(dockArea) # Maximize window self.showMaximized() # Adjust dock sizes if 'Settings' in self.docks: self.docks['Settings'].setStretch(1, 10) self.docks['Settings'].container().setStretch(3, 1) if prevRightDock is not None: prevRightDock.setStretch(1, 10) if 'Image' in self.docks: self.docks['Image'].setStretch(10, 1)
def setupUi(self, parent=None, chart=None, configure=False): self.gridLayout = QtWidgets.QGridLayout(parent) self.toolBar = QtWidgets.QToolBar(parent) self.toolBar.setObjectName("toolBar") # new self.actionNew = QtWidgets.QAction(parent) self.actionNew.setIconText("New") self.actionNew.setObjectName("actionNew") # open self.actionOpen = QtWidgets.QAction(parent) self.actionOpen.setIconText("Open") self.actionOpen.setObjectName("actionOpen") # save self.actionSave = QtWidgets.QAction(parent) self.actionSave.setIconText("Save") self.actionSave.setObjectName("actionSave") # save self.actionSaveAs = QtWidgets.QAction(parent) self.actionSaveAs.setIconText("Save As") self.actionSaveAs.setObjectName("actionSaveAs") # apply self.actionApply = QtWidgets.QAction(parent) self.actionApply.setIconText("Apply") self.actionApply.setObjectName("actionApply") # configure if configure: self.actionConfigure = QtWidgets.QAction(parent) self.actionConfigure.setIconText("Configure") self.actionConfigure.setObjectName("actionConfigure") # profile # self.actionProfiler = QtWidgets.QAction(parent) # self.actionProfiler.setIconText("Profiler") # self.actionProfiler.setObjectName("actionProfiler") # reset self.actionReset = QtWidgets.QAction(parent) self.actionReset.setIconText("Reset") self.actionReset.setObjectName("actionReset") # console self.actionConsole = QtWidgets.QAction(parent) self.actionConsole.setIconText("Console") self.actionConsole.setObjectName("actionConsole") # Arrange self.actionArrange = QtWidgets.QAction(parent) self.actionArrange.setIconText("Arrange") self.actionArrange.setObjectName("actionArrange") # home self.actionHome = QtWidgets.QAction(parent) self.actionHome.setIconText("Home") self.actionHome.setObjectName("actionHome") self.navGroup = QtWidgets.QActionGroup(parent) # pan self.actionPan = QtWidgets.QAction(parent) self.actionPan.setIconText("Pan") self.actionPan.setObjectName("actionPan") self.actionPan.setCheckable(True) self.actionPan.setChecked(True) self.navGroup.addAction(self.actionPan) # select self.actionSelect = QtWidgets.QAction(parent) self.actionSelect.setIconText("Select") self.actionSelect.setObjectName("actionSelect") self.actionSelect.setCheckable(True) self.navGroup.addAction(self.actionSelect) # comment self.actionComment = QtWidgets.QAction(parent) self.actionComment.setIconText("Comment") self.actionComment.setObjectName("actionComment") self.actionComment.setCheckable(True) self.navGroup.addAction(self.actionComment) self.toolBar.addAction(self.actionNew) self.toolBar.addAction(self.actionOpen) self.toolBar.addAction(self.actionSave) self.toolBar.addAction(self.actionSaveAs) if configure: self.toolBar.addAction(self.actionConfigure) self.toolBar.addAction(self.actionApply) self.toolBar.addAction(self.actionReset) self.toolBar.addAction(self.actionConsole) # self.toolBar.addAction(self.actionProfiler) if configure: self.toolBar.insertSeparator(self.actionConfigure) else: self.toolBar.insertSeparator(self.actionApply) # self.toolBar.addAction(self.actionArrange) self.toolBar.addAction(self.actionHome) self.toolBar.addAction(self.actionPan) self.toolBar.addAction(self.actionSelect) self.toolBar.addAction(self.actionComment) # self.toolBar.insertSeparator(self.actionArrange) self.toolBar.insertSeparator(self.actionHome) widget = self.toolBar.widgetForAction(self.actionApply) widget.setObjectName("actionApply") self.source_model = build_model() self.source_search = QtGui.QLineEdit() self.source_search.setPlaceholderText('Search Sources...') self.source_tree = build_tree(self.source_model, parent) self.node_model = build_model() self.node_search = QtGui.QLineEdit() self.node_search.setPlaceholderText('Search Operations...') self.node_tree = build_tree(self.node_model, parent) self.gridLayout.addWidget(self.toolBar, 0, 0, 1, -1) self.node_dock = dockarea.Dock('nodes', size=(400, 1000)) self.node_dock.hideTitleBar() self.node_dock.setOrientation('vertical') self.node_dock.addWidget(self.source_search, 1, 0, 1, 1) self.node_dock.addWidget(self.source_tree, 2, 0, 1, 1) self.node_dock.addWidget(self.node_search, 3, 0, 1, 1) self.node_dock.addWidget(self.node_tree, 4, 0, 1, 1) chart.addDock(self.node_dock, 'left') self.rateLbl = QtWidgets.QLabel("") self.rateLbl.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignBottom) self.libraryConfigure = QtWidgets.QPushButton("Manage Library") self.gridLayout.addWidget(chart, 1, 1, 1, -1) self.gridLayout.addWidget(self.libraryConfigure, 2, 1) self.gridLayout.addWidget(self.rateLbl, 2, 2, 1, -1) self.gridLayout.setRowStretch(1, 10) self.gridLayout.setColumnStretch(2, 10) self.node_search.textChanged.connect(self.node_search_text_changed) self.source_search.textChanged.connect(self.source_search_text_changed) self.pending = set()
def __init__(self): QtGui.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.main_path = sys.path[0] if len(self.main_path) == 0: self.main_path == os.curdir self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowIcon( QtGui.QIcon(os.path.join(self.main_path, "icons/centerfinder.png"))) self.project_path = None self.ui.menuTools.setEnabled(True) self.ui.tabWidgetFolders.currentChanged.connect(self.update_tree) self.ui.actionNew_Project.triggered.connect(self.new_project) self.ui.actionScrape_New_Data.triggered.connect(self.scrape_new_data) self.ui.treeWidgetDataFolder.itemDoubleClicked.connect( self.item_double_clicked) self.ui.treeWidgetProjectFolder.itemDoubleClicked.connect( self.item_double_clicked) self.ui.treeWidgetProjectFolder.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.treeWidgetProjectFolder.customContextMenuRequested.connect( self.contextMenuEvent) self.ui.actionCenterfinding_All.triggered.connect( lambda: self.open_centerfinding_window(mode="all")) self.ui.actionCenterfinding_selection.triggered.connect( lambda: self.open_centerfinding_window(mode="selection")) self.ui.actionParameters_and_Metadata_for_all.triggered.connect( lambda: self.calculate_parameters(mode="all")) self.ui.actionParameters_for_selection.triggered.connect( lambda: self.calculate_parameters(mode="selection")) self.ui.actionMissing_Parameter.triggered.connect( lambda: self.calculate_parameters(mode="missing")) self.ui.actionOnly_Metadata.triggered.connect(self.calculate_metadata) self.ui.actionLoad_Project.triggered.connect(self.load_project) self.ui.actionLoad_Existing_Dataset.triggered.connect( self.load_project_to_add) self.ui.actionOpen_Data_Explorer.triggered.connect( self.open_data_explorer) """ Actions """ self.delAct = QtWidgets.QAction( "&Delete", self, statusTip= "Delete selected folder(s) along with contents from project. This does not delete anything from the raw data folder.", triggered=self.delete) self.redoCenterAct = QtWidgets.QAction("Redo Center", self, triggered=self.redoCenter) self.redoParamsAct = QtWidgets.QAction("Redo Parameters", self, triggered=self.redoParams) self.toCSVAct = QtWidgets.QAction("Convert to CSV", self, triggered=self.toCSV) self.updateProjectAct = QtWidgets.QAction("Update Project View", self, triggered=self.update_tree)
def __init__(self, file_lock, local_path, dirname, sphere, arch, arch_ids, arches, data_1d, data_2d, parent=None): super().__init__(parent) self.local_path = local_path self.file_lock = file_lock self.dirname = dirname # Data Objects self.sphere = sphere self.arch = arch self.arch_ids = arch_ids self.arches = arches self.data_1d = data_1d self.data_2d = data_2d self.new_scan = True self.update_2d = True self.auto_last = True self.latest_idx = None self.new_scan_loaded = False # self.new_scan_name = None # Link UI self.ui = Ui_Form() self.ui.setupUi(self) # self.layout = self.ui.layout self.layout = self.ui.gridLayout self.defaultWidget = defaultWidget() self.defaultWidget.sigSetUserDefaults.connect(self.set_user_defaults) # Toolbar setup self.toolbar = QtWidgets.QToolBar('Tools') # File menu setup (part of toolbar) self.actionOpenFolder = QtWidgets.QAction() self.actionOpenFolder.setText('Open Folder') self.actionSetDefaults = QtWidgets.QAction() self.actionSetDefaults.setText('Advanced...') self.actionSaveDataAs = QtWidgets.QAction() self.actionSaveDataAs.setText('Save As') self.actionNewFile = QtWidgets.QAction() self.actionNewFile.setText('New') self.exportMenu = QtWidgets.QMenu() self.exportMenu.setTitle('Export') self.actionSaveImage = QtWidgets.QAction() self.actionSaveImage.setText('Current Image') self.exportMenu.addAction(self.actionSaveImage) self.actionSaveArray = QtWidgets.QAction() self.actionSaveArray.setText('Current 1D Array') self.exportMenu.addAction(self.actionSaveArray) self.paramMenu = QtWidgets.QMenu() self.paramMenu.setTitle('Config') self.actionSaveParams = QtWidgets.QAction() self.actionSaveParams.setText('Save') self.actionSaveParams.triggered.connect( self.defaultWidget.save_defaults) self.paramMenu.addAction(self.actionSaveParams) self.actionLoadParams = QtWidgets.QAction() self.actionLoadParams.setText('Load') self.actionLoadParams.triggered.connect( self.defaultWidget.load_defaults) self.paramMenu.addAction(self.actionLoadParams) self.paramMenu.addAction(self.actionSetDefaults) self.fileMenu = QtWidgets.QMenu() self.fileMenu.addAction(self.actionOpenFolder) self.fileMenu.addAction(self.actionNewFile) self.fileMenu.addAction(self.actionSaveDataAs) self.fileMenu.addMenu(self.exportMenu) self.fileButton = QtWidgets.QToolButton() self.fileButton.setText('File') self.fileButton.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.fileButton.setMenu(self.fileMenu) self.paramButton = QtWidgets.QToolButton() self.paramButton.setText('Config') self.paramButton.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.paramButton.setMenu(self.paramMenu) # End file menu setup self.toolbar.addWidget(self.fileButton) self.toolbar.addWidget(self.paramButton) # End toolbar setup self.layout.addWidget(self.toolbar, 0, 0, 1, 2) self.actionSetDefaults.triggered.connect(self.defaultWidget.show) self.ui.listScans.itemDoubleClicked.connect(self.scans_clicked) # self.ui.listScans.itemActivated.connect(self.scans_clicked) self.ui.listData.itemSelectionChanged.connect(self.data_changed) self.ui.listData.itemClicked.connect(self.data_changed) self.ui.show_all.clicked.connect(self.show_all) self.actionOpenFolder.triggered.connect(self.open_folder) self.actionSaveDataAs.triggered.connect(self.save_data_as) self.actionNewFile.triggered.connect(self.new_file) self.file_thread = fileHandlerThread(self.sphere, self.arch, self.file_lock, arch_ids=self.arch_ids, arches=self.arches, data_1d=self.data_1d, data_2d=self.data_2d) self.file_thread.sigTaskDone.connect(self.thread_finished) self.file_thread.sigNewFile.connect(self.sigNewFile.emit) self.file_thread.sigUpdate.connect(self.sigUpdate.emit) self.file_thread.start(Qt.QtCore.QThread.LowPriority)
def __init__(self, parent = None, *args, data_path = False, project_path, selection = False): QtWidgets.QWidget.__init__(self, parent, *args) self.ui = Ui_Form() self.ui.setupUi(self) self.setWindowTitle("CenterFinder") self.setWindowIcon(QtGui.QIcon("icons/centerfinder.png")) self.ui.graphicsView.ui.menuBtn.hide() self.ui.graphicsView.ui.roiBtn.hide() # self.ui.graphicsView.ui.histogram.hide() self.ui.pushButtonOverwrite.setEnabled(True) self.show() self.ui.pushButtonOverwrite.clicked.connect(self.overwrite_center) self.ui.checkBoxOverwrite.clicked.connect(self.toggle_overwrite) self.overwrite = False self.data_path = data_path if self.data_path == False: self.data_path = QtWidgets.QFileDialog.getExistingDirectory(caption = "Select directory or drive that contains the video files you are working with") self.all_videos = [os.path.join(root, f) for root, dirs, files in os.walk(self.data_path) for f in files if ".avi" in f] self.project_path = project_path self.vid_dict = {} self.videos_not_found = [] self.selection = selection for selected in self.selection: try: vid = [x for x in self.all_videos if selected.text(0) in x] self.vid_dict[selected.text(0)] = vid[0] except: self.videos_not_found.append(selected) if len(self.videos_not_found) > 0: QtWidgets.QMessageBox.warning(self, "Videos Not Found", str(len(self.videos_not_found))+" out of "+ str(len(self.selection)) +" not found in selected path") for vid in self.videos_not_found: self.selection.remove(vid) self.ui.comboBoxThresholdMethod.addItem("NONE") self.ui.comboBoxThresholdMethod.addItem("BINARY") self.ui.comboBoxThresholdMethod.addItem("TOZERO") self.ui.comboBoxThresholdMethod.currentTextChanged.connect(self.check_thresholding_method) self.ui.comboBoxThresholdMethod.currentIndexChanged.connect(self.process_item) self.ui.pushButtonRedoCenter.clicked.connect(self.process_item) self.ui.listWidgetVideos.itemSelectionChanged.connect(self.process_item) self.ui.listWidgetVideos.itemDoubleClicked.connect(self.process_item) self.ui.pushButtonStartStop.clicked.connect(self.start_stop) self.check_thresholding_method(self.ui.comboBoxThresholdMethod.currentText()) self.counter = 0 self.ui.listWidgetVideos.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.ui.listWidgetVideos.customContextMenuRequested.connect(self.contextMenuEvent_listwidgetVideos) self.flag_dataKeepaction = QtWidgets.QAction("&Flag and keep center", self, triggered = lambda: self.flag_data(delete = False)) self.flag_dataDeleteaction = QtWidgets.QAction("&Flag and delete center", self, triggered = lambda: self.flag_data(delete = True)) for item in self.selection: to_add = QtGui.QListWidgetItem(item.text(0)) to_add.setData(1,item.text(1)) if "center.txt" in os.listdir(item.text(1)): to_add.setBackground(QtGui.QColor("lightblue")) self.ui.listWidgetVideos.addItem(to_add)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setWindowTitle('Image Reconstruction') # self parameters self.r_l_text = 'Right/Left' self.u_d_text = 'Up/Down' self.b_f_text = 'Back/Forth' self.timepoints_text = 'Timepoints' self.p_text = 'pos' self.n_text = 'neg' # Actions in menubar menuBar = self.menuBar() file = menuBar.addMenu('&File') saveReconAction = QtWidgets.QAction('Save reconstruction', self) saveReconAction.setShortcut('Ctrl+S') saveReconAction.triggered.connect(self.sigSaveReconstruction) file.addAction(saveReconAction) saveCoeffsAction = QtWidgets.QAction('Save coefficients', self) saveCoeffsAction.setShortcut('Ctrl+A') saveCoeffsAction.triggered.connect(self.sigSaveCoeffs) file.addAction(saveCoeffsAction) setDataFolder = QtWidgets.QAction('Set data folder', self) setDataFolder.triggered.connect(self.sigSetDataFolder) file.addAction(setDataFolder) setSaveFolder = QtWidgets.QAction('Set save folder', self) setSaveFolder.triggered.connect(self.sigSetSaveFolder) file.addAction(setSaveFolder) self.dataFrame = DataFrame() self.multiDataFrame = MultiDataFrame() btnFrame = BtnFrame() btnFrame.sigReconstuctCurrent.connect(self.sigReconstuctCurrent) btnFrame.sigReconstructMulti.connect(self.sigReconstructMulti) btnFrame.sigQuickLoadData.connect(self.sigQuickLoadData) btnFrame.sigUpdate.connect(self.sigUpdate) self.reconstructionWidget = ReconstructionView() self.parTree = ReconParTree() self.showPatBool = self.parTree.p.param('Show pattern') self.showPatBool.sigValueChanged.connect( lambda _, v: self.sigShowPatternChanged.emit(v)) self.bleachBool = self.parTree.p.param('Bleaching correction') self.findPatBtn = self.parTree.p.param('Pattern').param('Find pattern') self.findPatBtn.sigActivated.connect(self.sigFindPattern) self.scanParWinBtn = self.parTree.p.param('Scanning parameters') self.scanParWinBtn.sigActivated.connect(self.sigShowScanParamsClicked) self.parTree.p.param('Pattern').sigTreeStateChanged.connect( self.sigPatternParamsChanged) self.scanParamsDialog = ScanParamsDialog(self, self.r_l_text, self.u_d_text, self.b_f_text, self.timepoints_text, self.p_text, self.n_text) parameterFrame = QtWidgets.QFrame() parameterGrid = QtWidgets.QGridLayout() parameterFrame.setLayout(parameterGrid) parameterGrid.addWidget(self.parTree, 0, 0) DataDock = DockArea() MultiDataDock = Dock('Multidata management') MultiDataDock.addWidget(self.multiDataFrame) DataDock.addDock(MultiDataDock) CurrentDataDock = Dock('Current data') CurrentDataDock.addWidget(self.dataFrame) DataDock.addDock(CurrentDataDock, 'above', MultiDataDock) layout = QtWidgets.QHBoxLayout() self.cwidget = QtWidgets.QWidget() self.setCentralWidget(self.cwidget) self.cwidget.setLayout(layout) leftContainer = QtWidgets.QVBoxLayout() leftContainer.setContentsMargins(0, 0, 0, 0) rightContainer = QtWidgets.QVBoxLayout() rightContainer.setContentsMargins(0, 0, 0, 0) leftContainer.addWidget(parameterFrame, 1) leftContainer.addWidget(btnFrame, 0) leftContainer.addWidget(DataDock, 1) rightContainer.addWidget(self.reconstructionWidget) layout.addLayout(leftContainer, 1) layout.addLayout(rightContainer, 3) pg.setConfigOption('imageAxisOrder', 'row-major') self.showMaximized()
def __init__(self, parent=None, *args, df="none", path=None): QtGui.QMainWindow.__init__(self) Ui_MainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowTitle("Data Explorer") self.setWindowIcon(QtGui.QIcon("icons/pickle.png")) self.show() self.ui.treeWidgetAllColums.setVisible(False) self.path = path try: if type(df) == str: if not df.endswith("pickle"): self.df = pd.read_csv(df, delimiter="\t") else: self.df = pd.read_pickle(df) self.path = os.path.dirname(df) elif type(df) == pd.core.frame.DataFrame: self.df = df except Exception as e: self.df = pd.DataFrame() # QtWidgets.QMessageBox.warning(self, "Could not open DataFrame", str(e)) self.set_data(self.ui.tableWidgetAllData, self.df) self.set_column_widget() self.filters = {} self.ui.pushButtonAddToSelection.clicked.connect(self.add_to_selection) self.ui.pushButtonRemoveFromSelection.clicked.connect( self.remove_from_selection) self.ui.comboBoxSelectedColumns.currentIndexChanged.connect( self.update_unique_values) self.ui.pushButtonApplyFilters.clicked.connect(self.apply_new_filter) self.ui.checkBoxUnique.clicked.connect(self.update_unique_values) self.ui.checkBoxSorting.clicked.connect(self.update_unique_values) self.ui.listWidgetUniqueValues.itemSelectionChanged.connect( self.row_selection) self.ui.checkBoxPreviewSelection.clicked.connect(self.row_selection) self.ui.tableWidgetSelectedData.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.tableWidgetSelectedData.customContextMenuRequested.connect( self.contextMenuEvent_SelectedData) self.ui.actionLoad_Dataset_into_Viewer.triggered.connect( self.load_dataset) self.ui.action_as_CSV.triggered.connect( lambda: self.save_selection(as_type="csv")) self.ui.action_as_Pickle.triggered.connect( lambda: self.save_selection(as_type="pickle")) self.toCSVAct = QtWidgets.QAction( "&Save Selection to CSV", self, triggered=lambda: self.save_selection(as_type="csv")) self.removeAllFiltersAct = QtWidgets.QAction( "&Remove all Filters", self, triggered=self.remove_all_filters) self.ui.actionRoaming.triggered.connect( lambda: self.start_roaming_calculator(self.df_selection)) self.ui.actionData.triggered.connect(lambda: self.hide_unhide_widget( self.ui.tabWidget, self.ui.actionData)) self.ui.actionPlotting.triggered.connect( lambda: self.hide_unhide_widget(self.ui.groupBoxPlotting, self.ui. actionPlotting)) self.ui.actionData_Selection.triggered.connect( lambda: self.hide_unhide_widget(self.ui.groupBox, self.ui. actionData_Selection)) self.ui.actionTransforms.triggered.connect( lambda: self.hide_unhide_widget(self.ui.frameTransform, self.ui. actionTransforms)) self.ui.actionStatistics.triggered.connect( lambda: self.hide_unhide_widget(self.ui.frameStatistics, self.ui. actionStatistics)) self.ui.frameConsole.setVisible(False) self.ui.frameStatistics.setVisible(False) self.ui.actionConsole.triggered.connect(self.open_console) self.ui.pushButtonCloseConsole.clicked.connect(self.close_console) self.ui.actionData_Selection.setIcon( QtGui.QIcon("icons/checkmark.png")) self.ui.actionData.setIcon(QtGui.QIcon("icons/checkmark.png")) self.ui.groupBoxPlotting.setVisible(False) self.ui.framePlotWidget.setLayout(QtWidgets.QVBoxLayout()) self.plot = PlotWidget() self.ui.framePlotWidget.layout().addWidget(self.plot) self.ui.pushButtonPlot.clicked.connect(self.update_plot) for plottype in ["violinplot", "lineplot", "scatterplot"]: self.ui.comboBoxPlotType.addItem(plottype) self.ui.listWidgetAppliedFilters.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.listWidgetAppliedFilters.customContextMenuRequested.connect( self.contextMenuEvent_AppliedFilters) self.removeFilteract = QtWidgets.QAction("&Remove Selected Filter(s)", self, triggered=self.remove_filters) self.loadFiltersetact = QtWidgets.QAction( "&Load Filterset", self, triggered=self.load_filterset) self.saveFiltersetact = QtWidgets.QAction( "&Save Filterset", self, triggered=self.save_filterset) self.ui.groupBoxPlotting.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.ui.groupBoxPlotting.customContextMenuRequested.connect( self.contextMenuEvent_PlottingMenu) self.updatePlottingUIact = QtWidgets.QAction( "&Update...", self, triggered=self.update_plotting_ui) self.ui.comboBoxPlotType.currentTextChanged.connect( self.toggle_colorselection) self.ui.comboBoxColorParam.setVisible(False) self.ui.labelColorParam.setVisible(False) self.ui.frameTransform.setVisible(False) for transform in [ "none", "square root", "natural logarithm", "reciprocal", "reciprocal sqrt", "exponential" ]: self.ui.comboBoxTransformations.addItem(transform) self.ui.pushButtonTransformData.clicked.connect(self.transform) self.ui.pushButtonAddTransformToData.clicked.connect( self.add_transformation_to_data) self.ui.actionScan_for_significance.triggered.connect( self.start_significance_scanner) self.ui.actionPairwaise_Test_Categorical.triggered.connect( self.start_pairwise_scanner) self.ui.actionminmax_scale.triggered.connect( lambda: self.normalize_data("minmax")) self.ui.actionmean_scale.triggered.connect( lambda: self.normalize_data("mean")) self.ui.pushButtonTest.clicked.connect(self.run_statistics) for test in ["Kruskal-Wallis", "Oneway ANOVA"]: self.ui.comboBoxStatisticalTests.addItem(test) self.ui.actionRename_items_in_column.triggered.connect( self.start_item_renamer)
def spikesort_gui(load_previous_dataset=True): def load_config(): global raw_data global sampling_frequency global number_of_channels_in_binary_file global prb_file global duration_of_time_plot global spike_info global channel_map global num_of_shanks_for_vis config = configparser.ConfigParser() config.read( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaults.ini')) if not 'SPIKES' in config: return all_exist = True spike_info_file = config['SPIKES']['spike_info_file'] if 'spike_info.df' in spike_info_file: spike_info = pd.read_pickle(spike_info_file) update_templates_table() generate_scater_plot() file_name = config['BINARY DATA FILE']['binary_data_filename'] if not os.path.isfile(file_name): all_exist = False prb_file = config['PROBE']['prb_file'] if not os.path.isfile(prb_file): all_exist = False channel_map_file = config['PROBE']['channel_map_file'] if not os.path.isfile(channel_map_file): all_exist = False else: channel_map = np.squeeze(np.load(channel_map_file)) if all_exist: type = config['BINARY DATA FILE']['type'] types = { 'int16': np.int16, 'uint16': np.uint16, 'int32': np.int32, 'uint32': np.uint32, 'int64': np.int64, 'uint32': np.uint64, 'float16': np.float16, 'float32': np.float32, 'float64': np.float64 } number_of_channels_in_binary_file = int( config['BINARY DATA FILE'] ['number_of_channels_in_binary_file']) order = config['BINARY DATA FILE']['order'] raw_data = np.memmap(file_name, mode='r', dtype=types[type]) raw_data = np.reshape(raw_data, (number_of_channels_in_binary_file, int(raw_data.shape[0] / number_of_channels_in_binary_file)), order=order) sampling_frequency = int( config['BINARY DATA FILE']['sampling_frequency']) electrodes = len(channel_map) for i in range(electrodes): electrode_curve = pg.PlotCurveItem() spikes_time_plot.addItem(electrode_curve) avg_electrode_curves.append(electrode_curve) duration_of_time_plot = float( config['TIME PLOT']['duration_of_time_plot']) num_of_shanks_for_vis = config['PROBE'][ 'num_of_shanks_for_vis'] if num_of_shanks_for_vis != 'None': num_of_shanks_for_vis = int(num_of_shanks_for_vis) else: num_of_shanks_for_vis = None else: print( 'Some file paths in the configuration file do not point to existing files. ' 'Load your data manually') elif spike_info_file == 'empty': print('Configuration file is empty. Load your data manually') def action_load_new_data(): config = configparser.ConfigParser() config.read( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaults.ini')) if config['SPIKES']['spike_info_file'] == 'empty': directory = None else: directory = os.path.dirname(config['SPIKES']['spike_info_file']) fname = QtWidgets.QFileDialog.getOpenFileName( caption='Load spike_info.df file', directory=directory) spike_info_file = fname[0] if 'spike_info.df' in spike_info_file: config['SPIKES']['spike_info_file'] = spike_info_file else: print('Point to a file named spike_info.df') return fname = QtWidgets.QFileDialog.getOpenFileName( caption='Load binary data file', directory=directory) binary_data_filename = fname[0] config['BINARY DATA FILE'][ 'binary_data_filename'] = binary_data_filename fname = QtWidgets.QFileDialog.getOpenFileName(caption='Load prb file', directory=directory) prb_file = fname[0] config['PROBE']['prb_file'] = prb_file fname = QtWidgets.QFileDialog.getOpenFileName( caption='Load channel map file', directory=directory) channel_map_file = fname[0] config['PROBE']['channel_map_file'] = channel_map_file types = { 'int16': np.int16, 'uint16': np.uint16, 'int32': np.int32, 'uint32': np.uint32, 'int64': np.int64, 'uint64': np.uint64, 'float16': np.float16, 'float32': np.float32, 'float64': np.float64 } type, ok = QtWidgets.QInputDialog.getItem( central_window, 'Binary Data Type', 'Input the type of the binary data file', sorted(list(types.keys()))) if ok: config['BINARY DATA FILE']['type'] = type order, ok = QtWidgets.QInputDialog.getItem( central_window, 'Binary Data Order', 'Input the order of the binary data file', ['F', 'C']) if ok: config['BINARY DATA FILE']['order'] = order sampling_frequency, ok = QtWidgets.QInputDialog.getInt( central_window, 'Sampling Frequency', 'Input the sampling frequency of the binary data', 30000, 1000, 100000, 1) if ok: config['BINARY DATA FILE']['sampling_frequency'] = str( sampling_frequency) number_of_channels_in_binary_file, ok = QtWidgets.QInputDialog.getInt( central_window, 'Number of Channels in Binary File', 'Input the total number of channels in ' 'the binary data', 128, 4, 10000, 1) if ok: config['BINARY DATA FILE'][ 'number_of_channels_in_binary_file'] = str( number_of_channels_in_binary_file) duration_of_time_plot, ok = QtWidgets.QInputDialog.getInt( central_window, 'Duration of Time Plot', 'Input the duration of the time plot in ms', 4, 1, 20, 1) if ok: config['TIME PLOT']['duration_of_time_plot'] = str( duration_of_time_plot / 1000) num_of_shanks_for_vis, ok = QtWidgets.QInputDialog.getItem( central_window, 'Number of Shanks for Visual', 'Input the number of pieces the probe should be ' 'visualized in (for long single shank probes). ' 'Use None for multi-shank probes with shanks ' 'defined in the prb file', ['None', '1', '2', '3', '4', '5', '6']) if ok: config['PROBE']['num_of_shanks_for_vis'] = num_of_shanks_for_vis with open( os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaults.ini'), 'w') as configfile: config.write(configfile) load_config() def action_save_info(): global spike_info fname = QtWidgets.QFileDialog.getExistingDirectory( caption='Select directory to save the new ' 'spike_info.pd file. Any existing' ' spike_info.pd file will be overwritten.') print(fname) spike_info.to_pickle(os.path.join(fname, 'spike_info.df')) config = configparser.ConfigParser() config.read('defaults.ini') config.set('SPIKES', 'spike_info_file', os.path.join(fname, 'spike_info.df')) with open('defaults.ini', 'w') as configfile: config.write(configfile) def action_undo_template_assignment(): global currently_selected_spikes global currently_selected_templates spike_info.loc[currently_selected_spikes, 'template_after_sorting'] = spike_info.loc[ currently_selected_spikes, 'template_after_cleaning'] spike_info.loc[currently_selected_spikes, 'type_after_sorting'] = spike_info.loc[ currently_selected_spikes, 'type_after_cleaning'] update_templates_table() def on_roi_selection(all_rois, freeform=False): global currently_selected_spikes global currently_selected_templates global selectable_spikes global on_spikes global tsne_spots global spike_info currently_selected_spikes = np.empty(0) points = np.array( [tsne_spots[i]['pos'] for i in range(len(tsne_spots))]) for i in range(len(all_rois)): if not freeform: shape = all_rois[i].parentBounds() bounds = shape else: shape = all_rois[i].shape() bounds = shape.boundingRect() selected = np.argwhere( (points[:, 0] > bounds.x()) * (points[:, 0] < bounds.x() + bounds.width()) * (points[:, 1] > bounds.y()) * (points[:, 1] < bounds.y() + bounds.height())).flatten() if freeform: currently_selected_spikes = np.append( currently_selected_spikes, [ i for i in selected if shape.contains( QtCore.QPointF(points[i][0], points[i][1])) ]) else: currently_selected_spikes = np.append( currently_selected_spikes, selected) all_selectable_spikes = np.intersect1d(selectable_spikes, on_spikes) currently_selected_spikes = currently_selected_spikes[np.in1d( currently_selected_spikes, all_selectable_spikes)] currently_selected_templates = hf.find_templates_of_spikes( spike_info, currently_selected_spikes) selected_templates_text_box.setText('Selected templates: ' + ', '.join( str(template) for template in currently_selected_templates)) print('Number of spikes selected: ' + str(len(currently_selected_spikes))) show_selected_points() def on_roi_deletion(): global currently_selected_spikes global currently_selected_templates currently_selected_spikes = np.empty(0) currently_selected_templates = np.empty(0) def show_selected_points(): global spike_info scatter_selected_item.clear() scatter_selected_item.setData(pos=np.array([ spike_info['tsne_x'].iloc[currently_selected_spikes].values, spike_info['tsne_y'].iloc[currently_selected_spikes].values ]).T) scatter_selected_item.setBrush((255, 255, 255)) def generate_scater_plot(): global number_of_spikes global selectable_spikes global on_spikes global tsne_spots global spike_info global all_points_brush global all_points_symbols scatter_item.clear() scatter_selected_item.clear() number_of_spikes = len(spike_info) selectable_spikes = np.arange(number_of_spikes) on_spikes = selectable_spikes number_of_templates = len( np.unique(spike_info['template_after_sorting'])) progdialog = QtWidgets.QProgressDialog() progdialog.setGeometry(500, 500, 400, 40) progdialog.setWindowTitle('Loading t-sne plot') progdialog.setMinimum(0) progdialog.setMaximum(5) progdialog.setWindowModality(QtCore.Qt.WindowModal) progdialog.setLabelText( 'Generating colors _____________________________________') progdialog.show() progdialog.setValue(0) QtWidgets.QApplication.processEvents() color = [ pg.intColor(spike_info['template_after_sorting'].iloc[i], hues=number_of_templates, values=1, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255, alpha=255) for i in range(number_of_spikes) ] progdialog.setLabelText( 'Generating brushes ____________________________________') progdialog.setValue(1) brush = [pg.mkBrush(color[i]) for i in range(number_of_spikes)] all_points_brush = brush progdialog.setLabelText( 'Generating positions __________________________________') progdialog.setValue(2) tsne_spots = [{ 'pos': [spike_info['tsne_x'].iloc[i], spike_info['tsne_y'].iloc[i]], 'data': 1, 'brush': brush[i], 'size': 2 } for i in range(number_of_spikes)] progdialog.setLabelText( 'Adding points to plot __________________________________') progdialog.setValue(3) scatter_item.addPoints(tsne_spots) progdialog.setLabelText( 'Generating symbols ... Almost done ______________________') progdialog.setValue(4) symbol = [] for i in range(number_of_spikes): symbol.append( hf.symbol_from_type(spike_info['type_after_sorting'].iloc[i])) scatter_item.setSymbol(symbol) all_points_symbols = symbol progdialog.setValue(5) progdialog.close() def update_average_spikes_plot(): global currently_selected_spikes global data_of_selected_spikes global spike_info global channel_map electrodes = len(channel_map) half_duration = duration_of_time_plot / 2 time_points = int(half_duration * sampling_frequency) time_axis = np.arange(-half_duration, half_duration, 1 / sampling_frequency) data_of_selected_spikes = np.zeros((electrodes, 2 * time_points)) times = spike_info['times'].iloc[currently_selected_spikes] progdialog = QtWidgets.QProgressDialog() progdialog.setMinimum(0) progdialog.setMaximum(len(times)) progdialog.setWindowTitle('Loading spikes') progdialog.setGeometry(500, 500, 400, 40) progdialog.setWindowModality(QtCore.Qt.WindowModal) progdialog.show() prog = 0 for t in times: add_data = raw_data[channel_map, int(t - time_points):int(t + time_points)] data_of_selected_spikes = data_of_selected_spikes + add_data prog += 1 if prog % 100 == 0: progdialog.setValue(prog) progdialog.setLabelText('Loading spike: ' + str(prog) + ' of ' + str(len(times))) QtWidgets.QApplication.processEvents() data_of_selected_spikes = data_of_selected_spikes / len(times) for i in np.arange(electrodes): avg_electrode_curves[i].setData(time_axis, data_of_selected_spikes[i, :]) avg_electrode_curves[i].setPen( pg.mkPen(color=(i, number_of_channels_in_binary_file * 1.3), width=0.5)) progdialog.setValue(len(times)) progdialog.close() def update_templates_table(): global number_of_spikes global spike_info templates_table.setData( hf.get_templates_with_number_of_spikes_from_spike_info(spike_info)) templates_table.setHorizontalHeaderLabels( ['Template number', 'Number of spikes in template']) templates_table.setSelectionMode( QtGui.QAbstractItemView.ExtendedSelection) templates_table.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows) header = templates_table.horizontalHeader() header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents) header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents) def update_heatmap_plot(): global data_of_selected_spikes global channel_map global number_of_channels_in_binary_file global num_of_shanks_for_vis connected = channel_map connected_binary = np.in1d( np.arange(number_of_channels_in_binary_file), connected) bad_channels = np.squeeze( np.argwhere(connected_binary == False).astype(np.int)) sh.create_heatmap_on_matplotlib_widget( heatmap_plot, data_of_selected_spikes, prb_file, window_size=60, bad_channels=bad_channels, num_of_shanks=num_of_shanks_for_vis, rotate_90=True, flip_ud=False, flip_lr=False) heatmap_plot.draw() def update_autocorelogram(): global currently_selected_spikes global spike_info spike_times = spike_info['times'].iloc[ currently_selected_spikes].values.astype(np.int64) diffs, norm = hf.crosscorrelate_spike_trains(spike_times, spike_times, lag=1000) hist, edges = np.histogram(diffs, bins=50) autocorelogram_curve.setData(x=edges, y=hist, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150)) # On press button functions --------- def on_press_button_update(): update_average_spikes_plot() update_autocorelogram() update_heatmap_plot() def on_tsne_filter_for_type_combo_box_change(index): global number_of_spikes global spike_info global on_spikes global all_points_brush state = tsne_color_scheme_combo_box.currentIndex() if state == 0: type_state = 'type_after_sorting' template_state = 'template_after_sorting' else: type_state = 'type_after_cleaning' template_state = 'template_after_cleaning' if index > 0: type_to_show = hf.template_types[index] spikes_to_show = np.array(spike_info[spike_info[type_state] == type_to_show].index.values) elif index == 0: spikes_to_show = range(number_of_spikes) on_spikes = spikes_to_show progdialog = QtWidgets.QProgressDialog() progdialog.setGeometry(500, 500, 400, 40) progdialog.setWindowTitle('Filtering t-SNE according to type') progdialog.setMinimum(0) progdialog.setWindowModality(QtCore.Qt.WindowModal) progdialog.setLabelText( 'Generating colors and symbols ... ') progdialog.show() progdialog.setValue(0) QtWidgets.QApplication.processEvents() color_scheme = spike_info[template_state] brush = list(all_points_brush) spikes_to_hide = np.array( range(number_of_spikes) )[~np.in1d(np.array(range(number_of_spikes)), spikes_to_show)] i = 0 progdialog.setMaximum(len(spikes_to_hide)) for s in spikes_to_hide: brush[s] = pg.intColor(color_scheme.iloc[s], hues=50, values=1, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255, alpha=0) if i % 10000 == 0: progdialog.setValue(i) progdialog.setLabelText('Hiding spikes ' + str(i) + ' of ' + str(len(spikes_to_hide)) + ' spikes.') QtWidgets.QApplication.processEvents() i = i + 1 progdialog.setMaximum(2) progdialog.setLabelText( 'Applying colors ... ') progdialog.setValue(1) QtWidgets.QApplication.processEvents() scatter_item.setBrush(brush) progdialog.setValue(2) progdialog.close() def on_tsne_color_scheme_combo_box_change(index): global number_of_spikes global spike_info global all_points_brush progdialog = QtWidgets.QProgressDialog() progdialog.setGeometry(500, 500, 400, 40) progdialog.setWindowTitle('Changing spike color scheme') progdialog.setMinimum(0) progdialog.setMaximum(2) progdialog.setWindowModality(QtCore.Qt.WindowModal) progdialog.setLabelText( 'Generating colors and symbols ... ') progdialog.show() progdialog.setValue(0) QtWidgets.QApplication.processEvents() spikes_to_change = range(number_of_spikes) print(index) if index == 0: number_of_templates = len( np.unique(spike_info['template_after_sorting'])) color_scheme = spike_info['template_after_sorting'] brush = [ pg.intColor(color_scheme.iloc[i], hues=number_of_templates, values=1, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255, alpha=255) for i in spikes_to_change ] symbol = [] for i in spikes_to_change: symbol.append( hf.symbol_from_type( spike_info['type_after_sorting'].iloc[i])) elif index == 1: number_of_templates = len( np.unique(spike_info['template_after_cleaning'])) color_scheme = spike_info['template_after_cleaning'] brush = [ pg.intColor(color_scheme.iloc[i], hues=number_of_templates, values=1, maxValue=255, minValue=150, maxHue=360, minHue=0, sat=255, alpha=255) for i in spikes_to_change ] symbol = [] for i in spikes_to_change: symbol.append( hf.symbol_from_type( spike_info['type_after_cleaning'].iloc[i])) progdialog.setValue(1) progdialog.setLabelText( 'Applying colors and symbols ... ') QtWidgets.QApplication.processEvents() all_points_brush = brush scatter_item.setBrush(brush) scatter_item.setSymbol(symbol) progdialog.setValue(2) progdialog.close() def on_press_button_freeze_spikes(): global selectable_spikes global currently_selected_spikes global number_of_spikes selectable_spikes = np.array(range(number_of_spikes))[~np.in1d( np.array(range(number_of_spikes)), currently_selected_spikes)] def on_press_button_unfreeze_all(): global selectable_spikes global number_of_spikes selectable_spikes = np.array(range(number_of_spikes)) def on_templates_table_selection(): global currently_selected_templates global currently_selected_spikes global spike_info currently_selected_templates = [] rows_selected = templates_table.selectedItems() for i in np.arange(0, len(rows_selected), 2): currently_selected_templates.append(int(rows_selected[i].text())) currently_selected_templates = np.array(currently_selected_templates) currently_selected_spikes = np.empty(0) for selected_template in currently_selected_templates: currently_selected_spikes = np.append( currently_selected_spikes, spike_info.loc[spike_info['template_after_sorting'] == selected_template].index) show_selected_points() def on_press_button_show_cross_correlograms(): global currently_selected_templates global spike_info num_of_templates = len(currently_selected_templates) if num_of_templates > 1: cross_cor_window.show() num_of_windows = (num_of_templates**2 - num_of_templates) / 2 + num_of_templates cross_corelogram_plots = [] column = 0 row = 0 width = 1 height = 1 progdialog = QtWidgets.QProgressDialog() progdialog.setMinimum(0) progdialog.setMaximum(num_of_windows) progdialog.setWindowTitle('Making cross correlograms') progdialog.setGeometry(500, 500, 400, 40) progdialog.setWindowModality(QtCore.Qt.WindowModal) progdialog.show() prog = 0 for window_index in np.arange(num_of_windows): cross_corelogram_plots.append(pg.PlotWidget()) cross_cor_grid_layout.addWidget(cross_corelogram_plots[-1], column, row, height, width) spike_times_a = spike_info['times'].loc[ spike_info['template_after_sorting'] == currently_selected_templates[column]].values.astype( np.int64) spike_times_b = spike_info['times'].loc[ spike_info['template_after_sorting'] == currently_selected_templates[row]].values.astype(np.int64) diffs, norm = hf.crosscorrelate_spike_trains(spike_times_a, spike_times_b, lag=3000) cross_corelogram_curve = pg.PlotCurveItem() cross_corelogram_plots[-1].addItem(cross_corelogram_curve) hist, edges = np.histogram(diffs, bins=100) cross_corelogram_curve.setData(x=edges, y=hist, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150)) row += width if row > column: column += height row = 0 prog += 1 progdialog.setValue(prog) progdialog.setLabelText('Making cross correlogram: ' + str(prog) + ' of ' + str(num_of_windows)) QtWidgets.QApplication.processEvents() progdialog.setValue(num_of_windows) progdialog.close() def on_press_button_remove_from_template(): global currently_selected_spikes global spike_info if len(currently_selected_spikes) > 0: spike_info.loc[currently_selected_spikes, 'template_after_sorting'] = 0 update_templates_table() def on_press_button_make_template(): global currently_selected_spikes global spike_info global all_points_brush if len(currently_selected_spikes) > 0: all_templates = spike_info['template_after_sorting'].values max_template = np.max(all_templates) index, ok = QtWidgets.QInputDialog.getInt( central_window, 'New Template Index', 'Input the index of the new template', max_template + 1, 1, max_template + 10000, 1) if ok: spike_info.loc[currently_selected_spikes, 'template_after_sorting'] = index list_of_types = [ 'Noise', 'Single Unit', 'Single Unit Contaminated', 'Single Unit Putative', 'MUA', 'Unspecified 1', 'Unspecified 2', 'Unspecified 3' ] item, ok = QtWidgets.QInputDialog.getItem( central_window, 'New Template Type', 'Input the type of the new template', list_of_types) if ok: spike_info.loc[currently_selected_spikes, 'type_after_sorting'] = hf.template_types[ list_of_types.index(item)] update_templates_table() # ---------------------------------- # Main Window ---------------------- app = QtGui.QApplication([]) main_window = QtGui.QMainWindow() main_toolbar = main_window.addToolBar('Tools') main_window.setWindowTitle('Spikesort on T-sne') main_window.resize(1300, 900) central_window = pg.GraphicsLayoutWidget() main_window.setCentralWidget(central_window) grid_layout = QtGui.QGridLayout() central_window.setLayout(grid_layout) # ---------------------------------- # T-sne Scatter Plot --------------- custom_viewbox = cv.CustomViewBox() scatter_plot = pg.PlotWidget(title='T-sne plot', viewBox=custom_viewbox) grid_layout.addWidget(scatter_plot, 0, 0, 2, 4) scatter_plot.plotItem.ctrlMenu = None scatter_plot.scene().contextMenu = None scatter_item = pg.ScatterPlotItem(size=7, pen=None, pxMode=True) scatter_plot.addItem(scatter_item) scatter_selected_item = pg.ScatterPlotItem(size=8, pen=None, pxMode=True) scatter_plot.addItem(scatter_selected_item) # --------------------------------- # Average spike time course plot -- spikes_time_plot = pg.PlotWidget( title='Average time plot of selected spikes') grid_layout.addWidget(spikes_time_plot, 2, 0, 2, 2) # --------------------------------- # Autocorelogram plot ------------- autocorelogram_plot = pg.PlotWidget(title='Autocorrelogram') grid_layout.addWidget(autocorelogram_plot, 2, 2, 2, 2) autocorelogram_curve = pg.PlotCurveItem() autocorelogram_plot.addItem(autocorelogram_curve) # --------------------------------- # Heatmap plot -------------------- heatmap_plot = matplotlib_widget.MatplotlibWidget(toolbar_on=False) grid_layout.addWidget(heatmap_plot, 0, 4, 6, 2) # --------------------------------- # Templates Table widget ----------- templates_table = pg.TableWidget(editable=False, sortable=True) templates_table.cellClicked.connect(on_templates_table_selection) grid_layout.addWidget(templates_table, 0, 6, 3, 1) # ---------------------------------- # Cross correlogram window --------- cross_cor_window = QtGui.QMainWindow() cross_cor_window.setWindowTitle('Cross correlograms') cross_cor_window.resize(1300, 900) central_cross_cor_window = pg.GraphicsLayoutWidget() cross_cor_window.setCentralWidget(central_cross_cor_window) cross_cor_grid_layout = QtGui.QGridLayout() central_cross_cor_window.setLayout(cross_cor_grid_layout) # ---------------------------------- # Tool bar ------------------------- current_path = os.path.dirname(os.path.realpath(__file__)) scroll_icon_file = os.path.join(current_path, 'Icons', 'scroll_icon.png') zoom_icon_file = os.path.join(current_path, 'Icons', 'zoom_icon.png') select_icon_file = os.path.join(current_path, 'Icons', 'select_icon.png') select_freeform_icon_file = os.path.join(current_path, 'Icons', 'select_freeform_icon.png') undo_icon_file = os.path.join(current_path, 'Icons', 'undo_icon.jpg') menu_bar = main_window.menuBar() file_menu = menu_bar.addMenu('File') load_data = QtWidgets.QAction('Load Data', main_window) load_data.triggered.connect(action_load_new_data) file_menu.addAction(load_data) save_info = QtWidgets.QAction('Save spike info', main_window) save_info.triggered.connect(action_save_info) file_menu.addAction(save_info) scroll = QtWidgets.QAction(QtGui.QIcon(scroll_icon_file), "scroll", main_window) scroll.triggered.connect(custom_viewbox.set_to_pan_mode) main_toolbar.addAction(scroll) zoom = QtWidgets.QAction(QtGui.QIcon(zoom_icon_file), "zoom", main_window) zoom.triggered.connect(custom_viewbox.set_to_rect_mode) main_toolbar.addAction(zoom) select = QtWidgets.QAction(QtGui.QIcon(select_icon_file), "select", main_window) select.triggered.connect(custom_viewbox.draw_square_roi) main_toolbar.addAction(select) select_freeform = QtWidgets.QAction(QtGui.QIcon(select_freeform_icon_file), 'select freeform', main_window) select_freeform.triggered.connect(custom_viewbox.draw_freeform_roi) custom_viewbox.connect_on_roi_select = on_roi_selection custom_viewbox.connect_on_roi_delete = on_roi_deletion main_toolbar.addAction(select_freeform) undo_assignment = QtWidgets.QAction( QtGui.QIcon(undo_icon_file), 'assign selected spikes back to their original templates', main_window) undo_assignment.triggered.connect(action_undo_template_assignment) main_toolbar.addAction(undo_assignment) # ---------------------------------- # Buttons -------------------------- button_update = QtGui.QPushButton('Update') button_update.setStyleSheet("font-size:20px; font-family: Helvetica") button_update.clicked.connect(on_press_button_update) grid_layout.addWidget(button_update, 7, 0, 1, 1) button_show_cross_correlograms = QtGui.QPushButton( 'Show Crosscorrelograms') button_show_cross_correlograms.setStyleSheet( "font-size:20px; font-family: Helvetica") button_show_cross_correlograms.clicked.connect( on_press_button_show_cross_correlograms) grid_layout.addWidget(button_show_cross_correlograms, 7, 1, 1, 1) tsne_color_scheme_combo_box = QtGui.QComboBox() tsne_color_scheme_combo_box.setStyleSheet( "font-size:20px; font-family: Helvetica") tsne_color_scheme_combo_box.addItem('After sorting') tsne_color_scheme_combo_box.addItem('Before sorting') tsne_color_scheme_combo_box.activated.connect( on_tsne_color_scheme_combo_box_change) grid_layout.addWidget(tsne_color_scheme_combo_box, 7, 2, 1, 1) tsne_filter_for_type_combo_box = QtGui.QComboBox() tsne_filter_for_type_combo_box.setStyleSheet( "font-size:20px; font-family: Helvetica") tsne_filter_for_type_combo_box.addItem('All') tsne_filter_for_type_combo_box.addItem('Single Unit') tsne_filter_for_type_combo_box.addItem('Single Unit Contaminated') tsne_filter_for_type_combo_box.addItem('Single Unit Putative') tsne_filter_for_type_combo_box.addItem('Multi Unit') tsne_filter_for_type_combo_box.addItem('Unspecified 1') tsne_filter_for_type_combo_box.addItem('Unspecified 2') tsne_filter_for_type_combo_box.addItem('Unspecified 3') tsne_filter_for_type_combo_box.activated.connect( on_tsne_filter_for_type_combo_box_change) grid_layout.addWidget(tsne_filter_for_type_combo_box, 6, 2, 1, 1) button_freeze_spikes = QtGui.QPushButton('Freeze selected spikes') button_freeze_spikes.setStyleSheet( "font-size:20px; font-family: Helvetica") button_freeze_spikes.clicked.connect(on_press_button_freeze_spikes) grid_layout.addWidget(button_freeze_spikes, 5, 3, 1, 1) button_unfreeze_all = QtGui.QPushButton('Unfreeze all spikes') button_unfreeze_all.setStyleSheet("font-size:20px; font-family: Helvetica") button_unfreeze_all.clicked.connect(on_press_button_unfreeze_all) grid_layout.addWidget(button_unfreeze_all, 6, 3, 1, 1) button_remove_from_template = QtGui.QPushButton( 'Remove selected spikes from template') button_remove_from_template.setStyleSheet( "font-size:20px; font-family: Helvetica") button_remove_from_template.clicked.connect( on_press_button_remove_from_template) grid_layout.addWidget(button_remove_from_template, 4, 6, 1, 1) button_make_template = QtGui.QPushButton('Make new template from spikes') button_make_template.setStyleSheet( "font-size:20px; font-family: Helvetica") button_make_template.clicked.connect(on_press_button_make_template) grid_layout.addWidget(button_make_template, 5, 6, 1, 1) # ---------------------------------- # Text Box ------------------------- selected_templates_text_box = QtWidgets.QLineEdit() selected_templates_text_box.setReadOnly(True) selected_templates_text_box.setWindowTitle('Selected Templates') selected_templates_text_box.setStyleSheet( "font-size:20px; font-family: Helvetica") grid_layout.addWidget(selected_templates_text_box, 7, 3, 1, 2) # ---------------------------------- if load_previous_dataset: load_config() main_window.show() if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): sys.exit(app.exec_())
def initUI(self): """ This method instantiates every widget and arranges them all inside the main window. This is where the puzzle pieces are assembled. """ # General window properties # self.setWindowTitle('MuControl v1.0.3') self.resize(1280, 720) # Non- maximized size self.setWindowState(QtCore.Qt.WindowMaximized) # Make menu bar at the top of the window mainMenu = self.menuBar() # mainMenu.setStyleSheet("""QMenuBar { background-color: #F0F0F0; }""") # Makes the menu bar grey-ish fileMenu = mainMenu.addMenu('File') # Adds the file button helpMenu = mainMenu.addMenu('Help') # Settings button settingsButton = QtGui.QAction("&Settings", self) settingsButton.setShortcut('Ctrl+Alt+S') settingsButton.triggered.connect( self.config.show ) # when the settings button is clicked, window is shown fileMenu.addAction( settingsButton) # Adds the settings button to the file menu # Exit Button exitButton = QtWidgets.QAction('Exit', self) exitButton.setShortcut('Ctrl+Q') exitButton.triggered.connect(self.close) fileMenu.addAction(exitButton) # User Guide button in help menu userguideButton = QtGui.QAction("Open User Guide", self) userguideButton.setShortcut('Ctrl+H') userguideButton.triggered.connect( lambda: QtGui.QDesktopServices.openUrl( QtCore.QUrl( "https://czimm79.github.io/mucontrol-userguide/index.html") )) helpMenu.addAction(userguideButton) # Create an empty box to hold all the following widgets self.mainbox = QtGui.QWidget() self.setCentralWidget( self.mainbox) # Put it in the center of the main window layout = QtWidgets.QGridLayout( ) # All the widgets will be in a grid in the main box self.mainbox.setLayout(layout) # set the layout # Instantiate the plots from plots.py self.p1 = SignalPlot() self.p1.setYRange(-self.config.defaults['vmulti'], self.config.defaults['vmulti']) self.p2 = ThreeDPlot( funcg_rate=self.config.funcg_rate, writechunksize=self.config.writechunksize, vmulti=self.config.defaults['vmulti'], freq=self.config.defaults['freq'], camber=self.config.defaults['camber'], zphase=self.config.defaults['zphase'], ) self.p2.setSizePolicy( self.p1.sizePolicy()) # 2D plot size = 3D plot size # Create control descriptions self.keyboardlbl = QtWidgets.QLabel('<h3> Keyboard Controls </h3>\ <span style="font-size: 10pt;">To enable keyboard controls, left click once anywhere on the signal plot. </span> \ <p> <strong> Toggle Output: </strong> T; <strong> +Voltage Multiplier: </strong> W; <strong> -Voltage Multiplier: </strong> Q </p> \ <p> <strong> +Frequency: </strong> G, <strong> -Frequency: </strong> F; <strong> +Camber: </strong> B; \ <strong> -Camber: </strong> V </p>') self.gamepadlbl = QtWidgets.QLabel('<h3> Gamepad Controls </h3>\ <span style="font-size: 10pt;">To enable gamepad controls, plug in the controller before starting the program. </span> \ <p> <strong> Toggle Output: </strong> Left Thumb Click; <strong> +Voltage Multiplier: </strong> RB; <strong> -Voltage Multiplier: </strong> LB </p> \ <p> <strong> +Frequency: </strong> Y, <strong> -Frequency: </strong> X; <strong> +Camber: </strong> B; \ <strong> -Camber: </strong> A </p>') # self.keyboardlbl.setFont(QtGui.QFont("Default", 11)) # Optionally, change font size # self.gamepadlbl.setFont(QtGui.QFont("Default", 11)) # Create plot labels self.p1lbl = QtWidgets.QLabel('<b><u>Live Signal Plot</u></b>') self.p2lbl = QtWidgets.QLabel( '<b><u>Parametrized Output Visualization</u></b>') # Parameter Tree widget self.t = MyParamTree(self.config) # From ParameterTree.py self.t.paramChange.connect( self.change ) # Connect the output signal from changes in the param tree to change # Add widgets to the layout in their proper positions layout.addWidget(self.p1lbl, 0, 0) layout.addWidget(self.p2lbl, 0, 1) layout.addWidget(self.t, 3, 0, 1, 2) # row, col, rowspan, colspan layout.addWidget(self.p1, 1, 0) layout.addWidget(self.keyboardlbl, 2, 0, 1, 3) layout.addWidget(self.gamepadlbl, 2, 1, 1, 3) layout.addWidget(self.p2, 1, 1)