def add_settings(self): main_layout = QBoxLayout(QBoxLayout.TopToBottom) for box_title, settings in self.gui_settings.items(): box = QGroupBox(box_title, self) box_layout = QBoxLayout(QBoxLayout.TopToBottom) for name, text in settings.items(): control, layout = self.get_setting_control(name, text) box_layout.addLayout(layout) self.controls[name] = control box.setLayout(box_layout) main_layout.addWidget(box) if main_layout.count() > 2: self.settingsLayout.addItem(main_layout) main_layout = QBoxLayout(QBoxLayout.TopToBottom) main_layout.addStretch() self.settingsLayout.addItem(main_layout)
def remove_all_widgets(layout: QBoxLayout, min_count: int = 0): """Remove all widgets from the given layout Parameters ---------- layout : Union[QBoxLayout, None] - Layout that widgets should be removed from.\n min_count : int, optional - Number of widgets that should remain in the layout, by default 0. """ if layout is not None: while layout.count() - min_count: child = layout.takeAt(0) if child.widget() is not None: child.widget().deleteLater() elif child.layout() is not None: remove_all_widgets(child.layout())
def rebuildTeamHotkeys(self, teamHotkeysHLayout: QBoxLayout, tabWidget: QTabWidget): """ Delete all child widgets """ while teamHotkeysHLayout.count(): child = teamHotkeysHLayout.takeAt(0) if child.widget(): child.widget().deleteLater() bar = tabWidget.tabBar() hotkeyRDict = {v: k for k, v in self.hotkeyDict.items()} # LOG.debug("tab count="+str(bar.count())) for i in range(0, bar.count()): # An apparent bug causes the tabButton (a label) to not have a text attrubite; # so, use the whatsThis attribute instead. callsign = bar.tabWhatsThis(i) hotkey = hotkeyRDict.get(callsign, "") label = QLabel(hotkey) label.setFixedWidth(bar.tabRect(i).width()) label.setStyleSheet("border:0px solid black;margin:0px;font-style:italic;font-size:14px;background-image:url(:/radiolog_ui/blank-computer-key.png) 0 0 30 30;") label.setAlignment(Qt.AlignCenter) teamHotkeysHLayout.addWidget(label) teamHotkeysHLayout.addStretch()
def remove_widget_from_scroll(widget: QWidget, parent_layout: QBoxLayout, data_list: List): """Remove a widget from a scroll area, and remove the associated data. Parameters ---------- widget : QWidget Widget that should be removed parent_layout : QBoxLayout The layout of the scroll area the widget should be removed from data_list : List The list of data related to the widgets in this scroll area. Information ----------- Only removes the widget and data if there will still be widgets in the scroll area after this widget is removed. """ if parent_layout.count() > 2: # ? 2 instead of 1 because of spacer. index = parent_layout.indexOf(widget) del data_list[index] remove_all_widgets(widget.layout()) parent_layout.removeWidget(widget) widget.deleteLater()
class CToolBarArea(QWidget): signalDragStart = pyqtSignal() signalDragEnd = pyqtSignal() signalItemDropped = pyqtSignal(CToolBarAreaItem, QWidget, int) def __init__(self, parent, orientation): super().__init__(parent) self.editor = parent self.orientation = orientation self.dropTargetSpacer = None self.actionContextPosition = QPoint() self.isLocked = False if self.orientation == Qt.Horizontal: self.layout_ = QBoxLayout(QBoxLayout.LeftToRight) else: self.layout_ = QBoxLayout(QBoxLayout.TopToBottom) self.layout_.setSpacing(0) self.layout_.setContentsMargins(0, 0, 0, 0) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.setLayout(self.layout_) self.setAcceptDrops(True) self.setContextMenuPolicy(Qt.CustomContextMenu) # print ( "CToolBarArea.__init__", parent ) def getOrientation(self): return self.orientation def getToolBars(self): items = getAllItems(self) toolBarItems = [] for item in items: if item.getType() != CToolBarAreaType.ToolBar: continue toolBarItems.append(item) return toolBarItems def getLargestItemMinimumSize(self): minSize = self.minimumSize() items = getAllItems(self) for item in items: minSize = minSize.expandedTo(item.getMinimumSize()) return minSize def setOrientation(self, orientation): self.orientation = orientation self.layout_.setDirection(QBoxLayout.LeftToRight if self.orientation == Qt.Horizontal else QBoxLayout.TopToBottom) self.updateLayoutAlignment() items = getAllItems(self) for item in items: item.setOrientation(self.orientation) def setActionContextPosition(self, actionContextPosition): self.actionContextPosition = actionContextPosition def fillContextMenu(self, menu): pass def addItem(self, item, targetIndex): item.signalDragStart.connect(self.onDragStart) item.signalDragEnd.connect(self.onDragEnd) item.setOrientation(self.orientation) item.setLocked(self.isLocked) self.layout_.insertWidget(targetIndex, item) item.setArea(self) def addToolBar(self, toolBar, targetIndex): self.addItem(CToolBarItem(self, toolBar, self.orientation), targetIndex) def addSpacer(self, spacerType, targetIndex): self.addItem(CSpacerItem(self, spacerType, self.orientation), targetIndex) if spacerType == CSpacerType.Expanding: self.layout_.setAlignment(0) def removeItem(self, item): self.layout_.removeWidget(item) item.signalDragStart.disconnect(self) item.signalDragEnd.disconnect(self) self.updateLayoutAlignment(item) def deleteToolBar(self, toolBarItem): if self.isAncestorOf(toolBarItem): qWarning("Trying to remove non-owned toolbar from area") return toolBarItem.deleteLater() def hideAll(self): items = getAllItems(self) for item in items: item.setVisible(False) def findToolBarByName(self, szToolBarName): toolBarItems = self.findChildren(CToolBarItem, "", Qt.FindDirectChildrenOnly) for toolBarItem in toolBarItems: if toolBarItem.getName() == szToolBarName: return toolBarItem print("Toolbar not found: %s" % szToolBarName) return None def setLocked(self, isLocked): self.isLocked = isLocked items = getAllItems(self) for item in items: item.setLocked(isLocked) def getState(self): pass def setState(self, state): pass def dragEnterEvent(self, event): print("CToolBarArea.dragEnterEvent", event) dragDropData = CDragDropData.fromMimeData(event.mimeData()) if dragDropData.hasCustomData(CToolBarAreaItem.getMimeType()): byteArray = dragDropData.getCustomData( CToolBarAreaItem.getMimeType()) stream = QDataStream(byteArray) value = None stream >> value draggedItem = value if isinstance(value, CToolBarAreaItem) else None if not self.parentWidget().isAncestorOf(draggedItem): return event.acceptProposedAction() self.setProperty("dragHover", True) self.style().unpolish(self) self.style().polish(self) def dragMoveEvent(self, event): dragDropData = CDragDropData.fromMimeData(event.mimeData()) if dragDropData.hasCustomData(CToolBarAreaItem.getMimeType()): targetIndex = self.getPlacementIndexFromPosition( self.mapToGlobal(event.pos())) event.acceptProposedAction() if self.dropTargetSpacer and targetIndex == self.layout_.indexOf( self.dropTargetSpacer): return byteArray = dragDropData.getCustomData( CToolBarAreaItem.getMimeType()) stream = QDataStream(byteArray) value = None stream >> value draggedItem = value if isinstance(value, CToolBarAreaItem) else None if not self.parentWidget().isAncestorOf(draggedItem): return spacerWidth = draggedItem.width() spacerHeight = draggedItem.height() if draggedItem.getOrientation() != self.orientation: tempWidth = spacerWidth spacerWidth = spacerHeight spacerHeight = tempWidth if self.dropTargetSpacer == None: self.dropTargetSpacer = QSpacerItem(spacerWidth, spacerHeight, QSizePolicy.Fixed, QSizePolicy.Fixed) else: spacerIndex = self.layout_.indexOf(self.dropTargetSpacer) if spacerIndex == targetIndex - 1 or ( targetIndex == -1 and spacerIndex == self.layout_.count() - 1): return self.removeDropTargetSpacer() self.dropTargetSpacer = QSpacerItem(spacerWidth, spacerHeight, QSizePolicy.Fixed, QSizePolicy.Fixed) self.layout_.insertSpacerItem(targetIndex, self.dropTargetSpacer) def dragLeaveEvent(self, event): self.removeDropTargetSpacer() self.setProperty("dragHover", False) self.style().unpolish(self) self.style().polish(self) def dropEvent(self, event): dragDropData = CDragDropData.fromMimeData(event.mimeData()) if dragDropData.hasCustomData(CToolBarAreaItem.getMimeType()): event.acceptProposedAction() byteArray = dragDropData.getCustomData( CToolBarAreaItem.getMimeType()) stream = QDataStream(byteArray) value = None stream >> value item = value if isinstance(value, CToolBarAreaItem) else None targetIndex = -1 if self.dropTargetSpacer: targetIndex = self.layout_.indexOf(self.dropTargetSpacer) containerIndex = self.layout_.indexOf(item) if containerIndex >= 0 and containerIndex < targetIndex: targetIndex -= 1 self.removeDropTargetSpacer() if targetIndex >= self.layout_.count(): targetIndex = -1 self.signalItemDropped.emit(item, self, targetIndex) def customEvent(self, event): pass def paintEvent(self, event): styleOption = QStyleOption() styleOption.initFrom(self) painter = QPainter(self) self.style().drawPrimitive(QStyle.PE_Widget, styleOption, painter, self) def onDragStart(self, item): self.updateLayoutAlignment(item) item.setVisiable(False) self.signalDragStart.emit() def onDragEnd(self, item): item.setVisiable(True) self.signalDragEnd.emit() def removeDropTargetSpacer(self): if self.dropTargetSpacer: self.layout_.removeItem(self.dropTargetSpacer) self.dropTargetSpacer = None def indexForItem(self, item): return self.layout_.indexOf(item) def moveItem(self, item, destinationIndex): sourceIndex = self.indexForItem(item) if sourceIndex == destinationIndex: return self.layout_.insertWidget(destinationIndex, item) def updateLayoutAlignment(self, itemToBeRemoved=None): spacers = self.findChildren(CSpacerItem, "", Qt.FindDirectChildrenOnly) expandingSpacers = [] for spacer in spacers: if spacer.getSpacerType() == CSpacerType.Expanding: expandingSpacers.append(spacer) if len(expandingSpacers) == 0 or len(expandingSpacers) != 0 or ( len(expandingSpacers) == 1 and expandingSpacers[0] == itemToBeRemoved): if self.orientation == Qt.Horizontal: self.layout_.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) else: self.layout_.setAlignment(Qt.AlignTop | Qt.AlignHCenter) def getItemAtPosition(self, globalPos): object = QApplication.widgetAt(globalPos) if object == None or object == self: return None item = object if isinstance(object, CToolBarAreaItem) else None while item == None and object.parent(): object = object.parent() item = object if isinstance(object, CToolBarAreaItem) else None return item def getPlacementIndexFromPosition(self, globalPos): targetIndex = -1 item = self.getItemAtPosition(globalPos) if item == None: localPos = self.mapFromGlobal(globalPos) if self.dropTargetSpacer: geom = self.dropTargetSpacer.geometry() if geom.contains(localPos): targetIndex = self.layout_.indexOf(self.dropTargetSpacer) return targetIndex targetIndex = self.indexForItem(item) if self.orientation == Qt.Horizontal and item.mapFromGlobal( globalPos).x() > item.width() / 2: targetIndex += 1 elif self.orientation == Qt.Vertical and item.mapFromGlobal( globalPos).y() > item.height() / 2: targetIndex += 1 if targetIndex >= self.layout_.count(): targetIndex = -1 return targetIndex
class Wyrmy(QWidget): def __init__(self, outside): super(Wyrmy, self).__init__() # get/set basic information self.app_control = outside screen_res = self.app_control.desktop().screenGeometry() self.screen_width, self.screen_height = screen_res.width( ), screen_res.height() self.setWindowIcon(QIcon(':/resources/wyrmytheworm.png')) # get images to use self.worms = {} self.index = 0 self.file_names = [] self.images = {} self.load_images() # create first image, determine all dimensions self.label, self.panel_height, self.width, self.height = QLabel( self), 0, 0, 0 self.alive, self.dead = QLabel(), QLabel() self.percent = QLabel() self.open, self.save, self.export = QPushButton(), QPushButton( ), QPushButton() self.curr_img_disp = QLabel() self.alive.setText('Alive: 0') self.dead.setText('Dead: 0') self.percent.setText('Percent: 0%') self.open.setText('Open') self.open.setToolTip('Load a data file') self.save.setText('Save') self.save.setToolTip('Save a data file') self.export.setText('Export') self.export.setToolTip('Export data as CSV') self.open.setMaximumWidth(100) self.save.setMaximumWidth(100) self.export.setMaximumWidth(100) self.open.clicked.connect(self.open_file) self.save.clicked.connect(self.save_file) self.export.clicked.connect(self.export_data) self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.panel_layout = QGridLayout(self.layout.widget()) self.panel_layout.setContentsMargins(15, 0, 15, 0) self.orig = QtGui.QPixmap(self.file_names[self.index]) scale_by = self.screen_height * 0.75 / self.orig.height() self.img = self.orig.scaled(self.orig.width() * scale_by, self.orig.height() * scale_by, QtCore.Qt.KeepAspectRatio) self.panel_height = self.screen_height * 0.1 self.width, self.height = self.img.width( ), self.img.height() + self.panel_height # self.panel = QGroupBox() # self.layout.addWidget(self.panel) # self.panel.move(0, self.img.height()) # self.panel.setFixedWidth(self.width) # self.panel.setFixedHeight(self.panel_height) self.layout.addLayout(self.panel_layout, 1, 0) self.panel_layout.addWidget(self.alive, 0, 0) self.panel_layout.addWidget(self.dead, 1, 0) self.panel_layout.addWidget(self.percent, 2, 0) self.panel_layout.addWidget(self.curr_img_disp, 1, 1) self.panel_layout.addWidget(self.open, 0, 2) self.panel_layout.addWidget(self.save, 1, 2) self.panel_layout.addWidget(self.export, 2, 2) startX = int((self.screen_width / 2) - (self.width / 2)) startY = int(((self.screen_height - 100) / 2) - (self.height / 2)) self.move(startX, startY) self.label.mousePressEvent = self.image_clicked self.dead_pic = QtGui.QPixmap(':/resources/red_target_scaled.png') self.alive_pic = QtGui.QPixmap(':/resources/green_target_scaled.png') self.layout.addWidget(self.label, 0, 0) self.markers = QBoxLayout(QBoxLayout.LeftToRight) self.layout.addChildLayout(self.markers) self.refresh() self.show() def load_images(self): file_names_import = QFileDialog.getOpenFileNames( self, "Open Image", "/", "Image Files (*.png *.jpg *.bmp *.tiff *.tif)") self.file_names = file_names_import[0] for name in self.file_names: self.worms[Wyrmy.pic_name(name)] = WormPic( filename=Wyrmy.pic_name(name)) def refresh(self): # take care of index wraparounds if len(self.file_names) == 0: self.app_control.quit() if self.index > len(self.file_names) - 1: self.index = 0 if self.index < 0: self.index = len(self.file_names) - 1 curr_img_name = self.file_names[self.index] # self.panel.setTitle(Wyrmy.pic_name(self.file_names[self.index])) orig = QtGui.QPixmap(self.file_names[self.index]) scale_by = self.screen_height * 0.75 / orig.height() self.img = orig.scaled(orig.width() * scale_by, orig.height() * scale_by, QtCore.Qt.KeepAspectRatio) self.label.setPixmap(self.img) self.setWindowTitle('Wyrmy - ' + curr_img_name) curr_pic = self.worms[Wyrmy.pic_name(self.file_names[self.index])] self.curr_img_disp.setText(Wyrmy.pic_name(curr_img_name)) num_dead = len(curr_pic.dead) num_alive = len(curr_pic.alive) alive_text = 'Alive: ' + str(num_alive) self.alive.setText(alive_text) dead_text = 'Dead: ' + str(num_dead) self.dead.setText(dead_text) if num_dead + num_alive > 0: percent_text = '% Dead: ' + str(100 * num_dead / (num_dead + num_alive)) else: percent_text = '% Dead: 0' self.percent.setText(percent_text) for ind in reversed(range(self.markers.count())): widget = self.markers.takeAt(ind).widget() if widget is not None: widget.deleteLater() for coords in curr_pic.dead: curr_dead = QLabel(self) curr_dead.setPixmap(self.dead_pic) self.markers.addWidget(curr_dead) curr_dead.move(coords.x * self.img.width() - 25, coords.y * self.img.height() - 12.5) # print(coords.x, ', ', coords.y) for coords in curr_pic.alive: curr_alive = QLabel(self) curr_alive.setPixmap(self.alive_pic) self.markers.addWidget(curr_alive) curr_alive.move(coords.x * self.img.width() - 25, coords.y * self.img.height() - 12.5) # print(coords.x, ', ', coords.y) def safe_index(self, index): if index < 0: new_ind = len(self.file_names) + index elif index > len(self.file_names) - 1: new_ind = index - len(self.file_names) else: new_ind = index if new_ind < 0: new_ind = 0 elif new_ind > len(self.file_names) - 1: new_ind = len(self.file_names) - 1 return new_ind @staticmethod def pic_name(file_name): last_index = file_name.rfind('/') return file_name[last_index + 1:last_index + 4] def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_W: self.index -= 1 if event.key() == QtCore.Qt.Key_S: self.index += 1 if event.key() == QtCore.Qt.Key_A: start = QWidget.mapToGlobal(self.label, self.label.pos()) click_x = (QtGui.QCursor.pos().x() - start.x()) / self.img.width() click_y = (QtGui.QCursor.pos().y() - start.y()) / self.img.height() click = Pair(click_x, click_y) self.worms[Wyrmy.pic_name( self.file_names[self.index])].alive.append(click) if event.key() == QtCore.Qt.Key_D: start = QWidget.mapToGlobal(self.label, self.label.pos()) click_x = (QtGui.QCursor.pos().x() - start.x()) / self.img.width() click_y = (QtGui.QCursor.pos().y() - start.y()) / self.img.height() click = Pair(click_x, click_y) self.worms[Wyrmy.pic_name( self.file_names[self.index])].dead.append(click) if event.key() == QtCore.Qt.Key_X: curr_pic = self.worms[Wyrmy.pic_name(self.file_names[self.index])] curr_pic.alive = [] curr_pic.dead = [] self.refresh() event.accept() def image_clicked(self, event): click = Pair(event.x() / self.img.width(), event.y() / self.img.height()) if event.button() == QtCore.Qt.LeftButton: self.worms[Wyrmy.pic_name( self.file_names[self.index])].alive.append(click) if event.button() == QtCore.Qt.RightButton: self.worms[Wyrmy.pic_name( self.file_names[self.index])].dead.append(click) # if event.button() == QtCore.Qt.MidButton: # curr_pic = self.worms[Wyrmy.pic_name(self.file_names[self.index])] # max_diff_x = 25/self.img.width() # max_diff_y = 25/self.img.height() # for ind, coords in enumerate(curr_pic.alive): # if abs(coords.x-click.x) < max_diff_x and abs(coords.y-click.y) < max_diff_y: # del curr_pic.alive[ind] # break # for ind, coords in enumerate(curr_pic.dead): # if abs(coords.x-click.x) < max_diff_x and abs(coords.y-click.y) < max_diff_y: # del curr_pic.dead[ind] # break self.refresh() event.accept() @pyqtSlot() def open_file(self): input_from = QFileDialog.getOpenFileName(self, caption='Open File', filter='Wyrmy Data (*.wyrm)') if len(input_from[0]) > 0: with open(input_from[0], 'rb') as reading: self.worms = pickle.load(reading) reading.close() self.refresh() @pyqtSlot() def save_file(self): output_at = QFileDialog.getSaveFileName(self, caption='Save File', filter='Wyrmy Data (*.wyrm)') if len(output_at[0]) > 0: with open(output_at[0], 'wb') as out: pickle.dump(self.worms, out) out.close() @pyqtSlot() def export_data(self): output_at = QFileDialog.getSaveFileName( self, caption='Save File', filter='CSV (Comma delimited) (*.csv)') if len(output_at[0]) > 0: out = open(output_at[0], 'a') out.write('Name,Alive,Dead\n') for name in self.file_names: curr = self.worms[Wyrmy.pic_name(name)] this_pic = str(curr.filename) + ',' + str(len( curr.alive)) + ',' + str(len(curr.dead)) + '\n' out.write(this_pic) out.close()