class DynamicView(QGraphicsView): viewChanged = Signal(QRect, float, int, int) def __init__(self, image, parent=None): super(DynamicView, self).__init__(parent) self.scene = QGraphicsScene() self.scene.setBackgroundBrush(Qt.darkGray) self.setScene(self.scene) self.set_image(image) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.ZOOM_STEP = 0.2 self.mouse_pressed = False self.next_fit = False self.fit_scale = 0 self.zoom_fit() def set_image(self, image): if type(image) is QPixmap: pixmap = image elif type(image) is QImage: pixmap = QPixmap.fromImage(image) elif type(image) is np.ndarray: pixmap = QPixmap.fromImage(mat2img(image)) else: raise TypeError( self.tr('DynamicView.set_image: Unsupported type: {}'.format( type(image)))) if not self.scene.items(): self.scene.addPixmap(pixmap) else: self.scene.items()[0].setPixmap(pixmap) self.scene.setSceneRect(QRectF(pixmap.rect())) def zoom_full(self): self.set_scaling(1) self.next_fit = True self.notify_change() def zoom_fit(self): self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) self.fit_scale = self.matrix().m11() if self.fit_scale > 1: self.fit_scale = 1 self.zoom_full() else: self.next_fit = False self.notify_change() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = True QGraphicsView.mousePressEvent(self, event) def mouseMoveEvent(self, event): QGraphicsView.mouseMoveEvent(self, event) if self.mouse_pressed: self.notify_change() def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = False QGraphicsView.mouseReleaseEvent(self, event) def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: if self.next_fit: self.zoom_fit() else: self.zoom_full() QGraphicsView.mouseDoubleClickEvent(self, event) def wheelEvent(self, event): if event.delta() > 0: self.change_zoom(+1) else: self.change_zoom(-1) def resizeEvent(self, event): # FIXME: Se la finestra viene massimizzata, il valore di fit_scale non si aggiorna if self.matrix().m11() <= self.fit_scale: self.zoom_fit() else: self.notify_change() QGraphicsView.resizeEvent(self, event) def change_zoom(self, direction): level = math.log2(self.matrix().m11()) if direction > 0: level += self.ZOOM_STEP else: level -= self.ZOOM_STEP scaling = 2**level if scaling < self.fit_scale: scaling = self.fit_scale self.next_fit = False elif scaling > 1: # scaling = 1 if scaling > 4: scaling = 4 self.next_fit = True self.set_scaling(scaling) self.notify_change() def set_scaling(self, scaling): matrix = QMatrix() matrix.scale(scaling, scaling) self.setMatrix(matrix) def change_view(self, _, new_scaling, new_horiz, new_vert): old_factor = self.matrix().m11() old_horiz = self.horizontalScrollBar().value() old_vert = self.verticalScrollBar().value() if new_scaling != old_factor or new_horiz != old_horiz or new_vert != old_vert: self.set_scaling(new_scaling) self.horizontalScrollBar().setValue(new_horiz) self.verticalScrollBar().setValue(new_vert) self.notify_change() def notify_change(self): scene_rect = self.get_rect() horiz_scroll = self.horizontalScrollBar().value() vert_scroll = self.verticalScrollBar().value() zoom_factor = self.matrix().m11() self.viewChanged.emit(scene_rect, zoom_factor, horiz_scroll, vert_scroll) def get_rect(self): top_left = self.mapToScene(0, 0).toPoint() if top_left.x() < 0: top_left.setX(0) if top_left.y() < 0: top_left.setY(0) view_size = self.viewport().size() bottom_right = self.mapToScene(view_size.width(), view_size.height()).toPoint() image_size = self.sceneRect().toRect() if bottom_right.x() >= image_size.width(): bottom_right.setX(image_size.width() - 1) if bottom_right.y() >= image_size.height(): bottom_right.setY(image_size.height() - 1) return QRect(top_left, bottom_right)
QGraphicsRectItem, QWidget, QGraphicsSimpleTextItem, QGraphicsEllipseItem, QGraphicsPolygonItem, ) from PySide2.QtGui import (QPen, QBrush, QPolygonF) from PySide2.QtCore import (Qt, QRectF, QPoint, QPointF) import sys if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(-50, -50, 400, 200)) rect = QGraphicsRectItem(scene.sceneRect().adjusted(1, 1, -1, -1)) rect.setPen(QPen(Qt.red, 1)) scene.addItem(rect) rectItem = QGraphicsRectItem(QRectF(-25, 25, 200, 40)) rectItem.setPen(QPen(Qt.red, 3, Qt.DashDotLine)) rectItem.setBrush(Qt.gray) scene.addItem(rectItem) print(f'Rect Pos: {rectItem.pos()}') textItem = QGraphicsSimpleTextItem("Foundation of Qt") scene.addItem(textItem) print(f'Text Pos: {textItem.pos()}') textItem.setPos(50, 0) print(f'Text Pos: {textItem.pos()}')
ellipseItem.setBrush(Qt.white) # scene.addItem(ellipseItem) return rectItem def timerHandler(): global sec item2.setTransformOriginPoint(300, 100) item2.setRotation(sec) sec += 1 if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(0, 0, 1000, 200)) scene.addItem(QGraphicsRectItem(scene.sceneRect())) item1 = createItem(0, scene) sec = 0 item2 = createItem(200, scene) timer = QTimer() timer.setInterval(1000) timer.timeout.connect(timerHandler) # item2.mapToScene(300, 100) # # item2.translate(300, 100) item2.setTransformOriginPoint(300, 100) item2.setRotation(30) # # item2.translate(-300, -100) # item2.mapToScene(-300, -100)
class Apartment2D(object): def __init__(self, ui): #self.ui = ui #self.ui.guiDlg.resize(QDesktopWidget().availableGeometry(self).size() * 0.6) self.scene = QGraphicsScene() self.dim = { "HMIN": -3000, "VMIN": -4000, "WIDTH": 6000, "HEIGHT": 8000 } self.scene.setSceneRect(self.dim['HMIN'], self.dim['VMIN'], self.dim['WIDTH'], self.dim['HEIGHT']) ui.graphicsView.setScene(self.scene) self.boxes = [] #self.ui.graphicsView.setViewport(QGLWidget()) ui.graphicsView.scale(1, -1) ui.graphicsView.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) ui.graphicsView.setTransformationAnchor(QGraphicsView.NoAnchor) ui.graphicsView.setResizeAnchor(QGraphicsView.NoAnchor) self.persons = {} self.pixmapSize = (0, 0) self.initializeWorld() def addPerson(self, pos, angle=0, color=-1, size=100): colors = QColor.colorNames() color = colors[random.randint(0, len(colors) - 1)] if color == -1 else color pos = [pos[0], pos[2]] if len(pos) > 2 else pos p = self.scene.addEllipse(pos[0] - size // 2, pos[1] - size // 2, size, size, pen=QPen(QColor(color), 20), brush=QBrush(color=QColor(color))) # pixmap pixmap = QPixmap("person.png").scaled(600, 300) self.pixmapSize = (pixmap.width() / 2, pixmap.height() / 2) pixItem = QGraphicsPixmapItem(pixmap) pixItem.setTransformOriginPoint(pixItem.boundingRect().center()) pixItem.setZValue(20) self.scene.addItem(pixItem) self.persons[p] = pixItem return p def movePerson(self, elipse, pos, size=100): #elipse.setPos(pos[0], pos[1]) pos = [pos[0], pos[2]] if len(pos) > 2 else pos color = elipse.pen().color() self.scene.addEllipse(pos[0] - size // 2, pos[1] - size // 2, size, size, pen=QPen(QColor(color), 20), brush=QBrush(color=QColor(color))) # pixmap self.persons[elipse].setPos(pos[0] - self.pixmapSize[0], pos[1] - self.pixmapSize[1]) # change rotation value when provided self.persons[elipse].setRotation(180) def wheelEvent(self, event): zoomInFactor = 1.15 zoomOutFactor = 1 / zoomInFactor # Zoom if event.delta() > 0: zoomFactor = zoomInFactor else: zoomFactor = zoomOutFactor self.graphicsView.scale(zoomFactor, zoomFactor) def initializeWorld(self): with open('autonomy.json', 'r') as f: world = json.load(f) #load dimensions dim = world["dimensions"] x_offset = -3200 y_offset = 1850 # load roundtables # for k,v in world["roundTables"].items(): # print(v) # box = self.scene.addEllipse(QRectF(-v[2]// 2, -v[3]// 2, v[2], v[3]), QPen(QColor("Khaki")), QBrush(QColor("Khaki"))); # box.setPos(v[4]+x_offset, v[5]+x_offset); # self.boxes.append(box) # load tables for k, v in world["tables"].items(): box = self.scene.addRect( QRectF(-v[2] // 2, -v[3] // 2, v[2], v[3]), QPen(QColor("SandyBrown")), QBrush(QColor("SandyBrown"))) box.setPos(v[4] + x_offset, v[5] + y_offset) box.setTransformOriginPoint(box.mapFromScene(QPointF(0, 0))) box.setRotation(v[6]) self.boxes.append(box) # load walls for k, v in world['walls'].items(): box = self.scene.addRect( QRectF(-v[2] // 2, -v[3] // 2, v[2], v[3]), QPen(QColor("Brown")), QBrush(QColor("Brown"))) box.setPos(v[4] + x_offset, v[5] + y_offset) box.setTransformOriginPoint(box.mapFromScene(QPointF(0, 0))) box.setRotation(v[6]) self.boxes.append(box) # } # //load points # QVariantMap points = mainMap[QString("points")].toMap(); # for (auto &t : points) # { # QVariantList object = t.toList(); # auto box = scene.addRect(QRectF(-object[2].toFloat() / 2, -object[3].toFloat() / 2, object[2].toFloat(), object[3].toFloat()), QPen(QColor("Brown")), QBrush(QColor("Brown"))); # box->setPos(object[4].toFloat()+x_offset, object[5].toFloat()+y_offset); # boxes.push_back(box); # } # //load boxes # QVariantMap cajas = mainMap[QString("boxes")].toMap(); # for (auto &t : cajas) # { # QVariantList object = t.toList(); # auto box = scene.addRect(QRectF(-object[2].toFloat() / 2, -object[3].toFloat() / 2, object[2].toFloat(), object[3].toFloat()), QPen(QColor("Brown")), QBrush(QColor("Orange"))); # box->setPos(object[4].toFloat()+x_offset, object[5].toFloat()+y_offset); # //box->setPos(object[4].toFloat(), object[5].toFloat()); # //box->setRotation(object[6].toFloat()*180/M_PI2); # box->setFlag(QGraphicsItem::ItemIsMovable); # boxes.push_back(box); # } # QTransform t; # //t.translate(3200, -1850); # t.rotate(-90); # //t.translate(-3200, 1850); # for(auto &item : boxes) # { # item->setPos(t.map(item->pos())); # item->setRotation(item->rotation() + 90); # item->setPos(item->pos() + QPointF(1850,3200)); # } # ///////////// # //AXIS self.scene.addLine(0, 0, 400, 0, QPen(QBrush(QColor("red")), 20)) self.scene.addLine(0, 0, 0, 400, QPen(QBrush(QColor("blue")), 20))
class Window(QObject): def __init__(self, ui): super().__init__() self.ui = ui ui.show() self.ui.setWindowTitle("Display Configuration") self.ui.screenCombo.currentTextChanged.connect(self.monitor_selected) self.ui.replicaOf.currentTextChanged.connect(self.replica_changed) self.ui.orientationCombo.currentIndexChanged.connect( self.orientation_changed) self.xrandr_info = {} self.get_xrandr_info() self.orig_xrandr_info = deepcopy(self.xrandr_info) self.fill_ui() self.ui.horizontalScale.valueChanged.connect(self.scale_changed) self.ui.verticalScale.valueChanged.connect(self.scale_changed) self.ui.modes.currentTextChanged.connect(self.mode_changed) self.ui.applyButton.clicked.connect(self.do_apply) self.ui.okButton.clicked.connect(self.do_ok) self.ui.resetButton.clicked.connect(self.do_reset) self.ui.cancelButton.clicked.connect(self.ui.reject) self.ui.scaleModeCombo.currentTextChanged.connect( self.scale_mode_changed) self.ui.primary.stateChanged.connect(self.primary_changed) self.ui.enabled.stateChanged.connect(self.enabled_changed) self.pos_label = QLabel(self.ui.sceneView) self.pos_label.move(5, 5) def enabled_changed(self): mon = self.ui.screenCombo.currentText() enabled = self.ui.enabled.isChecked() print(f"Setting {mon} enabled status to {enabled}") monitor = self.xrandr_info[mon] monitor["enabled"] = enabled if enabled and not monitor["current_mode"]: # Choose a mode self.ui.modes.setCurrentIndex(0) self.mode_changed() self.update_replica_of_data() for _, mon in self.xrandr_info.items(): mon["item"].update_visuals(mon) self.adjust_view() def primary_changed(self): mon = self.ui.screenCombo.currentText() primary = self.ui.primary.isChecked() # Update visuals on all monitos for name, monitor in self.xrandr_info.items(): if name == mon: monitor["primary"] = primary else: if primary: # There can only be one primary monitor["primary"] = False monitor["item"].update_visuals(monitor) def scale_mode_changed(self): mon = self.ui.screenCombo.currentText() scale_mode = self.ui.scaleModeCombo.currentText() print(f"Set {mon} scale mode to {scale_mode}") if scale_mode == "Manual": self.ui.horizontalScale.setEnabled(True) self.ui.verticalScale.setEnabled(True) try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass elif scale_mode == "Disabled (1x1)": self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.setEnabled(False) self.ui.horizontalScale.setValue(1000) self.ui.verticalScale.setValue(1000) try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass elif scale_mode == "Automatic: physical dimensions": # Calculate scale factors so that the logical pixels will be the same # size as in the primary window if self.ui.primary.isChecked(): print("Has no effect on primary display.") return # Find the primary monitor primary = [ k for k in self.xrandr_info if self.xrandr_info[k]["primary"] ] if not primary: print("Oops, no primary!") return primary = self.xrandr_info[primary[0]] monitor = self.xrandr_info[mon] prim_density_x = primary["res_x"] / primary["w_in_mm"] prim_density_y = primary["res_y"] / primary["h_in_mm"] dens_x = monitor["res_x"] / monitor["w_in_mm"] dens_y = monitor["res_y"] / monitor["h_in_mm"] try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass self.ui.horizontalScale.setEnabled(False) self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.setValue(prim_density_x / dens_x * 1000) self.ui.verticalScale.setValue(prim_density_y / dens_y * 1000) elif scale_mode == "Manual, same in both dimensions": self.ui.horizontalScale.setEnabled(True) self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.valueChanged.connect( self.ui.verticalScale.setValue) self.ui.verticalScale.setValue(self.ui.horizontalScale.value()) def replica_changed(self): mon = self.ui.screenCombo.currentText() replicate = self.ui.replicaOf.currentText() print(f"Making {mon} a replica of {replicate}") if replicate in ("None", "", None): print("TODO: make things non-replicas") return mon = self.xrandr_info[mon] replicate = self.xrandr_info[replicate] # Making a replica implies: # Set the same position mon["pos_x"] = replicate["pos_x"] mon["pos_y"] = replicate["pos_y"] # Set the same mode if possible if replicate["current_mode"] in mon["modes"]: mon["current_mode"] = replicate["current_mode"] else: # Keep the current mode, and change scaling so it # has the same effective size as the desired mode mod_x, mod_y = [int(x) for x in mon["current_mode"].split("x")] target_x, target_y = [replicate[x] for x in ["res_x", "res_y"]] scale_x = 1000 * target_x / mod_x scale_y = 1000 * target_y / mod_y self.ui.horizontalScale.setValue(scale_x) self.ui.verticalScale.setValue(scale_y) self.update_replica_of_data() for _, mon in self.xrandr_info.items(): mon["item"].update_visuals(mon) def do_reset(self): for n in self.xrandr_info: self.xrandr_info[n].update(self.orig_xrandr_info[n]) self.fill_ui() def do_ok(self): self.do_apply() self.ui.accept() def do_apply(self): cli = gen_xrandr_from_data(self.xrandr_info) print(cli) subprocess.check_call(shlex.split(cli)) def fill_ui(self): """Load data from xrandr and setup the whole thing.""" self.scene = QGraphicsScene(self) self.ui.sceneView.setScene(self.scene) self.ui.screenCombo.clear() for name, monitor in self.xrandr_info.items(): self.ui.screenCombo.addItem(name) mon_item = MonitorItem( data=monitor, window=self, name=name, ) self.scene.addItem(mon_item) monitor["item"] = mon_item self.ui.screenCombo.setCurrentText(self.choose_a_monitor()) self.adjust_view() self.scale_changed() # Trigger scale labels update def detect_scale_mode(self, monitor): """Given a monitor's data, try to guess what scaling mode it's using. TODO: detect "Automatic: physical dimensions" """ if not monitor["current_mode"]: # Disabled, whatever return None mod_x, mod_y = [int(x) for x in monitor["current_mode"].split("x")] scale_x = monitor["res_x"] / mod_x scale_y = monitor["res_y"] / mod_y if 1 == scale_x == scale_y: print("Scale mode looks like 1x1") return "Disabled (1x1)" elif scale_x == scale_y: print("Looks like Manual, same in both dimensions") return "Manual, same in both dimensions" else: return "Manual" def choose_a_monitor(self): """Choose what monitor to select by default. * Not disabled * Primary, if possible """ candidate = None for name, mon in self.xrandr_info.items(): if not mon["enabled"]: continue if mon["primary"]: return name candidate = name return candidate def orientation_changed(self): mon = self.ui.screenCombo.currentText() orientation = self.ui.orientationCombo.currentIndex() self.xrandr_info[mon]["orientation"] = orientation self.mode_changed() def mode_changed(self): mon = self.ui.screenCombo.currentText() mode = self.ui.modes.currentText() if not mode: return print(f"Changing {mon} to {mode}") self.xrandr_info[mon]["current_mode"] = mode mode_x, mode_y = mode.split("x") # use resolution via scaling if self.xrandr_info[mon]["orientation"] in (0, 2): self.xrandr_info[mon]["res_x"] = int( int(mode_x) * self.ui.horizontalScale.value() / 1000) self.xrandr_info[mon]["res_y"] = int( int(mode_y) * self.ui.verticalScale.value() / 1000) else: self.xrandr_info[mon]["res_x"] = int( int(mode_y) * self.ui.horizontalScale.value() / 1000) self.xrandr_info[mon]["res_y"] = int( int(mode_x) * self.ui.verticalScale.value() / 1000) self.xrandr_info[mon]["item"].update_visuals(self.xrandr_info[mon]) def show_pos(self, x, y): self.pos_label.setText(f"{x},{y}") self.pos_label.resize(self.pos_label.sizeHint()) def monitor_moved(self): "Update xrandr_info with new monitor positions" for _, mon in self.xrandr_info.items(): item = mon["item"] mon["pos_x"] = item.x() mon["pos_y"] = item.y() self.update_replica_of_data() for _, mon in self.xrandr_info.items(): mon["item"].update_visuals(mon) self.adjust_view() def adjust_view(self): self.ui.sceneView.resetTransform() self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100) scale_factor = 0.8 * min( self.ui.sceneView.width() / self.scene.sceneRect().width(), self.ui.sceneView.height() / self.scene.sceneRect().height(), ) self.ui.sceneView.scale(scale_factor, scale_factor) def get_xrandr_info(self): data = subprocess.check_output(["xrandr"]).decode("utf-8").splitlines() name = None for line in data: if (line and line[0] not in "S \t" and "disconnected" not in line): # Output line ( name, primary, res_x, res_y, w_in_mm, h_in_mm, pos_x, pos_y, enabled, orientation, ) = parse_monitor(line) self.xrandr_info[name] = dict( primary=primary, res_x=res_x, res_y=res_y, w_in_mm=w_in_mm, h_in_mm=h_in_mm, pos_x=pos_x, pos_y=pos_y, modes=[], current_mode=None, enabled=enabled, replica_of=[], orientation=orientation, ) elif line[0] == " ": # A mode mode_name = line.strip().split()[0] self.xrandr_info[name]["modes"].append(mode_name) if "*" in line: print(f"Current mode for {name}: {mode_name}") self.xrandr_info[name]["current_mode"] = mode_name self.update_replica_of_data() def update_replica_of_data(self): for a in self.xrandr_info: self.xrandr_info[a]["replica_of"] = [] for b in self.xrandr_info: if a != b and is_replica_of(self.xrandr_info[a], self.xrandr_info[b]): self.xrandr_info[a]["replica_of"].append(b) def monitor_selected(self, name): if not name: return # needed so we don't flip through all modes as they are added self.ui.modes.blockSignals(True) # Show modes self.ui.modes.clear() for mode in self.xrandr_info[name]["modes"]: self.ui.modes.addItem(mode) if (self.xrandr_info[name]["current_mode"] is None): # Happens with turned off monitors self.xrandr_info[name]["enabled"] = False h_scale = v_scale = 1 else: self.ui.modes.setCurrentText( self.xrandr_info[name]["current_mode"]) mod_x, mod_y = [ int(x) for x in self.xrandr_info[name]["current_mode"].split("x") ] if self.xrandr_info[name]["orientation"] in (0, 2): h_scale = self.xrandr_info[name]["res_x"] / mod_x v_scale = self.xrandr_info[name]["res_y"] / mod_y else: h_scale = self.xrandr_info[name]["res_y"] / mod_x v_scale = self.xrandr_info[name]["res_x"] / mod_y self.ui.horizontalScale.setValue(h_scale * 1000) self.ui.verticalScale.setValue(v_scale * 1000) self.ui.primary.setChecked(self.xrandr_info[name]["primary"]) self.ui.enabled.setChecked(self.xrandr_info[name]["enabled"]) self.ui.orientationCombo.setCurrentIndex( self.xrandr_info[name]["orientation"]) self.ui.replicaOf.clear() self.ui.replicaOf.addItem("None") for mon in self.xrandr_info: if mon != name: self.ui.replicaOf.addItem(mon) if mon in self.xrandr_info[name]["replica_of"]: self.ui.replicaOf.setCurrentText(mon) self.ui.modes.blockSignals(False) guessed_scale_mode = self.detect_scale_mode(self.xrandr_info[name]) self.ui.scaleModeCombo.setCurrentText(guessed_scale_mode) self.scale_mode_changed() def scale_changed(self): self.ui.horizontalScaleLabel.setText( f"{int(self.ui.horizontalScale.value()/10)}%") self.ui.verticalScaleLabel.setText( f"{int(self.ui.verticalScale.value()/10)}%") self.mode_changed() # Not really, but it's the same thing
class LabelingWidget(QGraphicsView): def __init__(self): super().__init__() self._tag_brushes = [] self._tag_brushes.append(QBrush(QColor(0, 0, 0, 0))) self._tag_brushes.append(QBrush(QColor(255, 0, 0, 90))) self._image_item = QGraphicsPixmapItem() self._create_default_scene() self.setScene(self._scene) def _create_default_scene(self): self._scene = QGraphicsScene() self._scene.addItem(self._image_item) line_pen = QPen(Qt.black, 2) for i in range(LABELING_TAG_WIDTH + 1): self._scene.addLine(i * LABELING_STEP, 0, i * LABELING_STEP, LABELING_HEIGHT, line_pen) for i in range(LABELING_TAG_HEIGHT + 1): self._scene.addLine(0, i * LABELING_STEP, LABELING_WIDTH, i * LABELING_STEP, line_pen) self._tags = np.zeros((LABELING_TAG_HEIGHT, LABELING_TAG_WIDTH), dtype=int) self._tags_items = [] tag_pen = QPen(Qt.black, 0) for i in range(LABELING_TAG_HEIGHT): tags = [] for j in range(LABELING_TAG_WIDTH): item = self._scene.addRect(j * LABELING_STEP, i * LABELING_STEP, LABELING_STEP, LABELING_STEP, tag_pen, self._tag_brushes[0]) tags.append(item) self._tags_items.append(tags) def _update_tags(self): for i in range(LABELING_TAG_HEIGHT): for j in range(LABELING_TAG_WIDTH): self._tags_items[i][j].setBrush( self._tag_brushes[self._tags[i, j]]) def set_image(self, path): self._image_item.setPixmap(QPixmap(path)) def clear(self): self._tags[:, :] = 0 self._update_tags() def get_tags(self): return self._tags.copy() def set_tags(self, tags): if tags.shape[0] != LABELING_TAG_HEIGHT or tags.shape[ 1] != LABELING_TAG_WIDTH: raise ValueError('Wrong tags shape') self._tags = tags.copy() self._update_tags() def resizeEvent(self, event): super().resizeEvent(event) self.fitInView(self._scene.sceneRect(), Qt.KeepAspectRatio) def mousePressEvent(self, event): point = self.mapToScene(event.pos()) index_x = int(point.x() / LABELING_STEP) index_y = int(point.y() / LABELING_STEP) if event.buttons() & Qt.LeftButton != 0: self._tags[index_y, index_x] = 1 elif event.buttons() & Qt.RightButton != 0: self._tags[index_y, index_x] = 0 self._update_tags() def mouseMoveEvent(self, event): point = self.mapToScene(event.pos()) index_x = int(point.x() / LABELING_STEP) index_y = int(point.y() / LABELING_STEP) if event.buttons() & Qt.LeftButton != 0: self._tags[index_y, index_x] = 1 elif event.buttons() & Qt.RightButton != 0: self._tags[index_y, index_x] = 0 self._update_tags()
self.mapToParent(-(self.boundingRect().width()), -(self.boundingRect().width() + 2))) if not self.scene().sceneRect().contains((newPoint)): print('move to 0, 0') newPoint = self.mapToParent(0, 0) else: self.setPos(newPoint) if __name__ == '__main__': app = QApplication(sys.argv) scene = QGraphicsScene(QRectF(-200, -200, 300, 300)) view = QGraphicsView() view.setRenderHint(QPainter.Antialiasing) boundaryPen = QPen(Qt.red) scene.addRect(scene.sceneRect()) itemCount = 20 for i in range(0, itemCount): item = MyItem() scene.addItem(item) timer = QTimer() timer.setInterval(500) timer.timeout.connect(scene.advance) view.setScene(scene) view.show() sys.exit(app.exec_())
class Window(QObject): def __init__(self, ui): super().__init__() self.ui = ui ui.show() self.ui.setWindowTitle("Display Configuration") self.ui.screenCombo.currentTextChanged.connect(self.monitor_selected) self.ui.replicaOf.currentTextChanged.connect(self.replica_changed) self.ui.orientationCombo.currentIndexChanged.connect( self.orientation_changed) self.get_xrandr_info() self.fill_ui() self.ui.horizontalScale.valueChanged.connect(self.scale_changed) self.ui.verticalScale.valueChanged.connect(self.scale_changed) self.ui.modes.currentTextChanged.connect(self.mode_changed) self.ui.applyButton.clicked.connect(self.do_apply) self.ui.okButton.clicked.connect(self.do_ok) self.ui.resetButton.clicked.connect(self.do_reset) self.ui.cancelButton.clicked.connect(self.ui.reject) self.ui.scaleModeCombo.currentTextChanged.connect( self.scale_mode_changed) self.ui.primary.stateChanged.connect(self.primary_changed) self.ui.enabled.stateChanged.connect(self.enabled_changed) self.pos_label = QLabel(self.ui.sceneView) self.pos_label.move(5, 5) def enabled_changed(self): mon = self.ui.screenCombo.currentText() enabled = self.ui.enabled.isChecked() print(f"Setting {mon} enabled status to {enabled}") monitor = self.screen.monitors[mon] monitor.enabled = enabled if enabled and not monitor.get_current_mode(): # Choose a mode self.ui.modes.setCurrentText(monitor.get_preferred_mode_name()) self.mode_changed() self.screen.update_replica_of() for mon in self.screen.monitors.values(): mon.item.update_visuals(mon) self.adjust_view() def primary_changed(self): mon_name = self.ui.screenCombo.currentText() primary = self.ui.primary.isChecked() if primary: self.screen.set_primary(mon_name) else: self.screen.set_primary("foobar") # no primary for monitor in self.screen.monitors.values(): monitor.item.update_visuals(monitor) def scale_mode_changed(self): mon = self.ui.screenCombo.currentText() scale_mode = self.ui.scaleModeCombo.currentText() print(f"Set {mon} scale mode to {scale_mode}") if scale_mode == "Manual": self.ui.horizontalScale.setEnabled(True) self.ui.verticalScale.setEnabled(True) try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass elif scale_mode == "Disabled (1x1)": self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.setEnabled(False) self.ui.horizontalScale.setValue(1000) self.ui.verticalScale.setValue(1000) try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass elif scale_mode == "Automatic: physical dimensions": # Calculate scale factors so that the logical pixels will be the same # size as in the primary window if self.ui.primary.isChecked(): print("Has no effect on primary display.") return # Find the primary monitor primary = self.screen.get_primary() if not primary: print("Oops, no primary!") return monitor = self.screen.monitors[mon] prim_density_x = primary.res_x / primary.w_in_mm prim_density_y = primary.res_y / primary.h_in_mm dens_x = monitor.res_x / monitor.w_in_mm dens_y = monitor.res_y / monitor.h_in_mm try: self.ui.horizontalScale.valueChanged.disconnect( self.ui.verticalScale.setValue) except RuntimeError: # Not connected pass self.ui.horizontalScale.setEnabled(False) self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.setValue(prim_density_x / dens_x * 1000) self.ui.verticalScale.setValue(prim_density_y / dens_y * 1000) elif scale_mode == "Manual, same in both dimensions": self.ui.horizontalScale.setEnabled(True) self.ui.verticalScale.setEnabled(False) self.ui.horizontalScale.valueChanged.connect( self.ui.verticalScale.setValue) self.ui.verticalScale.setValue(self.ui.horizontalScale.value()) def replica_changed(self): mon_name = self.ui.screenCombo.currentText() replicate = self.ui.replicaOf.currentText() print(f"Making {mon_name} a replica of {replicate}") if replicate in ("None", "", None): print("TODO: make things non-replicas") return mon = self.screen.monitors[mon_name] replicate = self.screen.monitors[replicate] # Making a replica implies: # Set the same position mon.pos_x = replicate.pos_x mon.pos_y = replicate.pos_y # Set the same mode if possible matching_mode = mon.get_matching_mode(replicate.get_current_mode()) if matching_mode: mon.set_current_mode(matching_mode.name) else: # Keep the current mode, and change scaling so it # has the same effective size as the desired mode c_mode = mon.get_current_mode() mod_x, mod_y = c_mode.res_x, c_mode.res_y r_mode = replicate.get_current_mode target_x, target_y = r_mode.res_x, r_mode.res_y scale_x = 1000 * target_x / mod_x scale_y = 1000 * target_y / mod_y self.ui.horizontalScale.setValue(scale_x) self.ui.verticalScale.setValue(scale_y) self.screen.update_replica_of() for mon in self.screen.monitors.values(): mon.item.update_visuals(mon) def run(self, commands): for i, cmd in enumerate(commands, 1): print(f"Running {cmd} [{i}/{len(commands)}]") subprocess.check_call(shlex.split(cmd)) def do_reset(self): self.run(self.reset_screen.generate()) self.fill_ui() def do_ok(self): self.do_apply() self.ui.accept() def do_apply(self): cli = self.screen.generate() self.run(cli) def fill_ui(self): """Configure UI out of our screen data.""" self.scene = QGraphicsScene(self) self.ui.sceneView.setScene(self.scene) self.ui.screenCombo.clear() for name, monitor in self.screen.monitors.items(): self.ui.screenCombo.addItem(name) mon_item = MonitorItem( data=monitor, window=self, name=name, ) self.scene.addItem(mon_item) monitor.item = mon_item self.ui.screenCombo.setCurrentText(self.screen.choose_a_monitor()) self.adjust_view() # self.scale_changed() # Trigger scale labels update def orientation_changed(self): mon_name = self.ui.screenCombo.currentText() orientation = self.ui.orientationCombo.currentText().split()[0].lower() self.screen.monitors[mon_name].orientation = orientation self.mode_changed() def mode_changed(self): mon = self.ui.screenCombo.currentText() mode = self.ui.modes.currentText() if not mode: return print(f"Changing {mon} to {mode}") monitor = self.screen.monitors[mon] monitor.set_current_mode(mode) mode_x, mode_y = ( monitor.get_current_mode().res_x, monitor.get_current_mode().res_y, ) # use resolution via scaling if monitor.orientation in ("normal", "inverted"): monitor.res_x = int(mode_x * self.ui.horizontalScale.value() / 1000) monitor.res_y = int(mode_y * self.ui.verticalScale.value() / 1000) else: monitor.res_x = int(mode_y * self.ui.horizontalScale.value() / 1000) monitor.res_y = int(mode_x * self.ui.verticalScale.value() / 1000) monitor.item.update_visuals(monitor) def show_pos(self, x, y): self.pos_label.setText(f"{x},{y}") self.pos_label.resize(self.pos_label.sizeHint()) def monitor_moved(self): "Update screen with new monitor positions" for mon in self.screen.monitors.values(): item = mon.item mon.pos_x = item.x() mon.pos_y = item.y() self.screen.update_replica_of() for mon in self.screen.monitors.values(): mon.item.update_visuals(mon) # Adjust view a little later QTimer.singleShot(0, self.adjust_view) def possible_snaps(self, name): """Return two lists of values to which the x and y position of monitor "name" could snap to.""" snaps_x = [] snaps_y = [] for output, monitor in self.screen.monitors.items(): if output == name: continue else: mode = monitor.get_current_mode() mod_x, mod_y = mode.res_x, mode.res_y snaps_x.append(monitor.pos_x) snaps_x.append(monitor.pos_x + mod_x) snaps_y.append(monitor.pos_x) snaps_y.append(monitor.pos_x + mod_y) return snaps_x, snaps_y def adjust_view(self): print("Adjusting view") self.ui.sceneView.resetTransform() self.ui.sceneView.ensureVisible(self.scene.sceneRect(), 100, 100) try: scale_factor = 0.8 * min( self.ui.sceneView.width() / self.scene.sceneRect().width(), self.ui.sceneView.height() / self.scene.sceneRect().height(), ) self.ui.sceneView.scale(scale_factor, scale_factor) except ZeroDivisionError: # Don't worry pass def get_xrandr_info(self): _xrandr_data = xrandr.read_data() self.screen = xrandr.parse_data(_xrandr_data) self.screen.update_replica_of() self.reset_screen = xrandr.parse_data(_xrandr_data) def monitor_selected(self, name): if not name: return # needed so we don't flip through all modes as they are added self.ui.modes.blockSignals(True) self.ui.primary.blockSignals(True) # Show modes self.ui.modes.clear() monitor = self.screen.monitors[name] for mode in monitor.modes: self.ui.modes.addItem(mode) mode = monitor.get_current_mode() self.ui.modes.setCurrentText(mode.name) if monitor.orientation in ("normal", "inverted"): h_scale = monitor.res_x / mode.res_x v_scale = monitor.res_y / mode.res_y else: h_scale = monitor.res_y / mode.res_x v_scale = monitor.res_x / mode.res_y self.ui.horizontalScale.setValue(h_scale * 1000) self.ui.verticalScale.setValue(v_scale * 1000) self.ui.primary.setChecked(monitor.primary) self.ui.enabled.setChecked(monitor.enabled) self.ui.orientationCombo.setCurrentText(monitor.orientation) self.ui.replicaOf.clear() self.ui.replicaOf.addItem("None") for mon in self.screen.monitors: if mon != name: self.ui.replicaOf.addItem(mon) if mon in self.screen.monitors[name].replica_of: self.ui.replicaOf.setCurrentText(mon) self.ui.modes.blockSignals(False) self.ui.primary.blockSignals(False) guessed_scale_mode = monitor.guess_scale_mode() self.ui.scaleModeCombo.setCurrentText(guessed_scale_mode) self.scale_mode_changed() def scale_changed(self): self.ui.horizontalScaleLabel.setText( f"{int(self.ui.horizontalScale.value()/10)}%") self.ui.verticalScaleLabel.setText( f"{int(self.ui.verticalScale.value()/10)}%") self.mode_changed() # Not really, but it's the same thing
class Escena(QWidget): def __init__(self): QWidget.__init__(self) self.resize(QDesktopWidget().availableGeometry(self).size() * 0.6) self.scene = QGraphicsScene() self.scene.setSceneRect(-3000, -4000, 6000, 8000) self.view = QGraphicsView(self.scene) self.view.resize(self.size()) self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) self.view.setParent(self) self.view.setTransformationAnchor(QGraphicsView.NoAnchor) self.view.setResizeAnchor(QGraphicsView.NoAnchor) self.view.scale(3, -3) self.lines = [] self.show() def wheelEvent(self, event): zoomInFactor = 1.15 zoomOutFactor = 1 / zoomInFactor # Zoom if event.delta() > 0: zoomFactor = zoomInFactor else: zoomFactor = zoomOutFactor self.view.scale(zoomFactor, zoomFactor) def resizeEvent(self, event): self.view.resize(self.size()) def draw(self, comps, cluster): colors = QColor.colorNames() # for co in sample: # print(co) # x, _, z, _ = co['world'] # self.scene.addEllipse(x-50,z-50,100,100, pen=QPen(QColor('orange'), 5)) # x,_,z,_ = sample[-1]['world'] # self.scene.addRect(x,z,60,60, pen=QPen(QColor('red'), 100)) # x,_,z,_ = sample[0]['world'] # self.scene.addRect(x,z,60,60, pen=QPen(QColor('green'), 100)) # for co in comps: # if sample[co[-1]]['timestamp']-sample[co[0]]['timestamp'] > 200 and len(co)> 4: # color = colors[random.randint(0, len(colors)-1)] # for c in co: # x, _, z, _ = sample[c]['world'] # self.scene.addEllipse(x-15,z-15,30,30, pen=QPen(QColor(color), 100), brush=QBrush(color=QColor(color))) if cluster[-1]['timestamp'] - cluster[0]['timestamp'] > 200 and len( cluster) > 4: color = colors[random.randint(0, len(colors) - 1)] for sample in cluster: x, _, z, _ = sample['world'] self.scene.addEllipse(x - 15, z - 15, 30, 30, pen=QPen(QColor(color), 100), brush=QBrush(color=QColor(color))) def drawTrack(self, clusters): colors = QColor.colorNames() # for line in self.lines: # self.scene.removeItem(line) self.scene.clear() for cluster in clusters: color = colors[random.randint(0, len(colors) - 1)] for t in cluster: self.scene.addLine(t[0][0], t[0][1], t[1][0], t[1][1], pen=QPen(QColor(color), 60))