def make_slider(MAX_RANGE, INIT_VALUE): label = QLabel("") label.setAlignment(Qt.AlignmentFlag.AlignCenter) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, SLIDER_MAX_RANGE_INT) slider.setValue(int(SLIDER_MAX_RANGE_INT*(INIT_VALUE/MAX_RANGE))) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(int(SLIDER_MAX_RANGE_INT*0.1)) slider.setSingleStep(1) return slider, label
class AppGUI(QtGui.QWidget): def __init__(self): super().__init__() self.X = pydicom.dcmread('images/.dcm').pixel_array self.z = self.X.shape[0] // 2 self.y = self.X.shape[1] // 2 self.x = self.X.shape[2] // 2 self.init_ui() self.qt_connections() def init_ui(self): pg.setConfigOption('background', 'w') pg.setConfigOption('imageAxisOrder', 'row-major') self.zname = 'Head', 'Feet' self.yname = 'Face', 'Back' self.xname = 'Left Hand', 'Right Hand' self.layout = QVBoxLayout() self.setGeometry(0, 0, 1440, 900) self.setWindowTitle('DICOM Viewer') self.z_slice_label = QLabel(f'Z axis [{self.zname[0]} - {self.zname[1]}] Slice: {self.z + 1}/{self.X.shape[0]}') self.y_slice_label = QLabel(f'Y axis [{self.yname[0]} - {self.yname[1]}] Slice: {self.y + 1}/{self.X.shape[1]}') self.x_slice_label = QLabel(f'X axis [{self.xname[0]} - {self.xname[1]}] Slice: {self.x + 1}/{self.X.shape[2]}') # slices plots ---------------------------------------------------------------- self.autolevels = True self.levels = (0, 100) self.glayout = pg.GraphicsLayoutWidget() self.glayout.ci.layout.setContentsMargins(0, 0, 0, 0) self.glayout.ci.layout.setSpacing(0) self.zi = pg.ImageItem(self.X[self.z, : , : ], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='r', width=3)) self.yi = pg.ImageItem(self.X[: , self.y, : ], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='g', width=3)) self.xi = pg.ImageItem(self.X[: , : , self.x], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='b', width=3)) self.zp = self.glayout.addPlot() self.yp = self.glayout.addPlot() self.xp = self.glayout.addPlot() # self.z_slice_plot.setTitle(f'Z axis [{self.z_axis_name[0]} - {self.z_axis_name[1]}]') # self.y_slice_plot.setTitle(f'Y axis [{self.y_axis_name[0]} - {self.y_axis_name[1]}]') # self.x_slice_plot.setTitle(f'X axis [{self.x_axis_name[0]} - {self.x_axis_name[1]}]') self.zp.setAspectLocked() self.yp.setAspectLocked() self.xp.setAspectLocked() self.zp.setMouseEnabled(x=False, y=False) self.yp.setMouseEnabled(x=False, y=False) self.xp.setMouseEnabled(x=False, y=False) self.z_slice_plot_y_helper1 = self.zp.plot([0 , self.X.shape[2]], [self.y , self.y ], pen='g') self.z_slice_plot_y_helper2 = self.zp.plot([0 , self.X.shape[2]], [self.y + 1, self.y + 1 ], pen='g') self.z_slice_plot_x_helper1 = self.zp.plot([self.x , self.x ], [0 , self.X.shape[1]], pen='b') self.z_slice_plot_x_helper2 = self.zp.plot([self.x + 1, self.x + 1 ], [0 , self.X.shape[1]], pen='b') self.y_slice_plot_z_helper1 = self.yp.plot([0 , self.X.shape[2]], [self.z , self.z ], pen='r') self.y_slice_plot_z_helper2 = self.yp.plot([0 , self.X.shape[2]], [self.z + 1, self.z + 1 ], pen='r') self.y_slice_plot_x_helper1 = self.yp.plot([self.x , self.x ], [0 , self.X.shape[0]], pen='b') self.y_slice_plot_x_helper2 = self.yp.plot([self.x + 1, self.x + 1 ], [0 , self.X.shape[0]], pen='b') self.x_slice_plot_z_helper1 = self.xp.plot([0 , self.X.shape[1]], [self.z , self.z ], pen='r') self.x_slice_plot_z_helper2 = self.xp.plot([0 , self.X.shape[1]], [self.z + 1, self.z + 1 ], pen='r') self.x_slice_plot_y_helper1 = self.xp.plot([self.y , self.y ], [0 , self.X.shape[0]], pen='g') self.x_slice_plot_y_helper2 = self.xp.plot([self.y + 1, self.y + 1 ], [0 , self.X.shape[0]], pen='g') self.zp.invertY(True) self.yp.invertY(True) self.xp.invertY(True) self.zp.setLabel('bottom', f'X axis [{self.xname[0]} - {self.xname[1]}]') self.yp.setLabel('bottom', f'X axis [{self.xname[0]} - {self.xname[1]}]') self.zp.setLabel('left' , f'Y axis [{self.yname[1]} - {self.yname[0]}]') self.xp.setLabel('bottom', f'Y axis [{self.yname[0]} - {self.yname[1]}]') self.yp.setLabel('left' , f'Z axis [{self.zname[1]} - {self.zname[0]}]') self.xp.setLabel('left' , f'Z axis [{self.zname[1]} - {self.zname[0]}]') self.zp.addItem(self.zi) self.yp.addItem(self.yi) self.xp.addItem(self.xi) self.zi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[2], self.X.shape[1])) self.yi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[2], self.X.shape[0])) self.xi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[1], self.X.shape[0])) self.zi.setZValue(-1) self.yi.setZValue(-1) self.xi.setZValue(-1) self.zs = QSlider() self.ys = QSlider() self.xs = QSlider() self.zs.setStyleSheet('background-color: rgba(255, 0, 0, 0.2)') self.ys.setStyleSheet('background-color: rgba(0, 255, 0, 0.2)') self.xs.setStyleSheet('background-color: rgba(0, 0, 255, 0.2)') self.zs.setOrientation(Qt.Orientation.Horizontal) self.ys.setOrientation(Qt.Orientation.Horizontal) self.xs.setOrientation(Qt.Orientation.Horizontal) self.zs.setRange(0, self.X.shape[0] - 1) self.ys.setRange(0, self.X.shape[1] - 1) self.xs.setRange(0, self.X.shape[2] - 1) self.zs.setValue(self.z) self.ys.setValue(self.y) self.xs.setValue(self.x) self.zs.setTickPosition(QSlider.TickPosition.TicksBelow) self.ys.setTickPosition(QSlider.TickPosition.TicksBelow) self.xs.setTickPosition(QSlider.TickPosition.TicksBelow) self.zs.setTickInterval(1) self.ys.setTickInterval(1) self.xs.setTickInterval(1) self.layout.addWidget(self.zs) self.layout.addWidget(self.ys) self.layout.addWidget(self.xs) self.layout.addWidget(self.z_slice_label) self.layout.addWidget(self.y_slice_label) self.layout.addWidget(self.x_slice_label) self.layout.addWidget(self.glayout) self.setLayout(self.layout) self.show() def qt_connections(self): self.zs.valueChanged.connect(self.zs_changed) self.ys.valueChanged.connect(self.ys_changed) self.xs.valueChanged.connect(self.xs_changed) def wheelEvent(self, event): if self.zi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())): self.z = np.clip(self.z + np.sign(event.angleDelta().y()), 0, self.X.shape[0] - 1) # change bounds 0..N-1 => 1..N self.zs.setValue(self.z) elif self.yi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())): self.y = np.clip(self.y + np.sign(event.angleDelta().y()), 0, self.X.shape[1] - 1) # change bounds 0..N-1 => 1..N self.ys.setValue(self.y) elif self.xi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())): self.x = np.clip(self.x + np.sign(event.angleDelta().y()), 0, self.X.shape[2] - 1) # change bounds 0..N-1 => 1..N self.xs.setValue(self.x) def update_slice_helpers_lines(self): self.z_slice_plot_y_helper1.setData([0 , self.X.shape[2]], [self.y , self.y ]) self.z_slice_plot_y_helper2.setData([0 , self.X.shape[2]], [self.y + 1, self.y + 1 ]) self.z_slice_plot_x_helper1.setData([self.x , self.x ], [0 , self.X.shape[1]]) self.z_slice_plot_x_helper2.setData([self.x + 1 , self.x + 1 ], [0 , self.X.shape[1]]) self.y_slice_plot_z_helper1.setData([0 , self.X.shape[2]], [self.z , self.z ]) self.y_slice_plot_z_helper2.setData([0 , self.X.shape[2]], [self.z + 1, self.z + 1 ]) self.y_slice_plot_x_helper1.setData([self.x , self.x ], [0 , self.X.shape[0]]) self.y_slice_plot_x_helper2.setData([self.x + 1 , self.x + 1 ], [0 , self.X.shape[0]]) self.x_slice_plot_z_helper1.setData([0 , self.X.shape[1]], [self.z , self.z ]) self.x_slice_plot_z_helper2.setData([0 , self.X.shape[1]], [self.z + 1, self.z + 1 ]) self.x_slice_plot_y_helper1.setData([self.y , self.y ], [0 , self.X.shape[0]]) self.x_slice_plot_y_helper2.setData([self.y + 1 , self.y + 1 ], [0 , self.X.shape[0]]) def zs_changed(self): self.z = self.zs.value() self.z_slice_label.setText(f'Z axis [{self.zname[0]} - {self.zname[1]}] Slice: {self.z + 1}/{self.X.shape[0]}') self.zi.setImage(self.X[self.z, :, :]) self.update_slice_helpers_lines() def ys_changed(self): self.y = self.ys.value() self.y_slice_label.setText(f'Y axis [{self.yname[0]} - {self.yname[1]}] Slice: {self.y + 1}/{self.X.shape[1]}') self.yi.setImage(self.X[:, self.y, :]) self.update_slice_helpers_lines() def xs_changed(self): self.x = self.xs.value() self.x_slice_label.setText(f'X axis [{self.xname[0]} - {self.xname[1]}] Slice: {self.x + 1}/{self.X.shape[2]}') self.xi.setImage(self.X[:, :, self.x]) self.update_slice_helpers_lines()
def __init__(self): super(MPPIOptionsWindow, self).__init__() self.horizon_steps = controller_mppi.mpc_samples self.num_rollouts = controller_mppi.num_rollouts self.dd_weight = controller_mppi.dd_weight self.ep_weight = controller_mppi.ep_weight self.ekp_weight = controller_mppi.ekp_weight * 1.0e1 self.ekc_weight = controller_mppi.ekc_weight * 1.0e-1 self.cc_weight = controller_mppi.cc_weight * 1.0e-2 self.ccrc_weight = controller_mppi.ccrc_weight * 1.0e-2 self.R = controller_mppi.R # How much to punish Q self.LBD = controller_mppi.LBD # Cost parameter lambda self.NU = controller_mppi.NU # Exploration variance layout = QVBoxLayout() ### Set Horizon Length horizon_options_layout = QVBoxLayout() self.horizon_label = QLabel("") self.horizon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) horizon_options_layout.addWidget(self.horizon_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(10, 300) slider.setValue(self.horizon_steps) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(10) slider.setSingleStep(10) horizon_options_layout.addWidget(slider) slider.valueChanged.connect(self.horizon_length_changed) ### Set Number of Rollouts rollouts_options_layout = QVBoxLayout() self.rollouts_label = QLabel("") self.rollouts_label.setAlignment(Qt.AlignmentFlag.AlignCenter) rollouts_options_layout.addWidget(self.rollouts_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(10, 3000) slider.setValue(self.num_rollouts) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(10) slider.setSingleStep(10) rollouts_options_layout.addWidget(slider) slider.valueChanged.connect(self.num_rollouts_changed) ### Set Cost Weights cost_weight_layout = QVBoxLayout() # Distance difference cost self.dd_weight_label = QLabel("") self.dd_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.dd_weight_label) self.dd_label = QLabel("") self.dd_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.dd_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 990) slider.setValue(self.dd_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(10) slider.setSingleStep(10) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.dd_weight_changed) # Potential energy cost self.ep_weight_label = QLabel("") self.ep_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ep_weight_label) self.ep_label = QLabel("") self.ep_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ep_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 1e5 - 1e3) slider.setValue(self.ep_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(1e3) slider.setSingleStep(1e3) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.ep_weight_changed) # Pole kinetic energy cost self.ekp_weight_label = QLabel("") self.ekp_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ekp_weight_label) self.ekp_label = QLabel("") self.ekp_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ekp_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 99) slider.setValue(self.ekp_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(1) slider.setSingleStep(1) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.ekp_weight_changed) # Cart kinetic energy cost self.ekc_weight_label = QLabel("") self.ekc_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ekc_weight_label) self.ekc_label = QLabel("") self.ekc_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ekc_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 99) slider.setValue(self.ekc_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(1) slider.setSingleStep(1) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.ekc_weight_changed) # Control cost self.cc_weight_label = QLabel("") self.cc_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.cc_weight_label) self.cc_label = QLabel("") self.cc_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.cc_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 99) slider.setValue(self.cc_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(1) slider.setSingleStep(1) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.cc_weight_changed) # Control change rate cost self.ccrc_weight_label = QLabel("") self.ccrc_weight_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ccrc_weight_label) self.ccrc_label = QLabel("") self.ccrc_label.setAlignment(Qt.AlignmentFlag.AlignCenter) cost_weight_layout.addWidget(self.ccrc_label) slider = QSlider(orientation=Qt.Orientation.Horizontal) slider.setRange(0, 99) slider.setValue(self.ccrc_weight) slider.setTickPosition(QSlider.TickPosition.TicksBelow) slider.setTickInterval(1) slider.setSingleStep(1) cost_weight_layout.addWidget(slider) slider.valueChanged.connect(self.ccrc_weight_changed) ### Set some more MPPI constants mppi_constants_layout = QVBoxLayout() # Quadratic cost penalty R textbox = QLineEdit() textbox.setText(str(self.R)) textbox.textChanged.connect(self.R_changed) h_layout = QHBoxLayout() h_layout.addWidget(QLabel("Quadratic input cost penalty R =")) h_layout.addWidget(textbox) mppi_constants_layout.addLayout(h_layout) # Quadratic cost penalty LBD textbox = QLineEdit() textbox.setText(str(self.LBD)) textbox.textChanged.connect(self.LBD_changed) h_layout = QHBoxLayout() h_layout.addWidget(QLabel("Importance of higher-cost rollouts LBD =")) h_layout.addWidget(textbox) mppi_constants_layout.addLayout(h_layout) # Quadratic cost penalty NU textbox = QLineEdit() textbox.setText(str(self.NU)) textbox.textChanged.connect(self.NU_changed) h_layout = QHBoxLayout() h_layout.addWidget(QLabel("Exploration variance NU =")) h_layout.addWidget(textbox) mppi_constants_layout.addLayout(h_layout) # Sampling type h_layout = QHBoxLayout() btn1 = QRadioButton("iid") if btn1.text() == controller_mppi.SAMPLING_TYPE: btn1.setChecked(True) btn1.toggled.connect(lambda: self.toggle_button(btn1)) h_layout.addWidget(btn1) btn2 = QRadioButton("random_walk") if btn2.text() == controller_mppi.SAMPLING_TYPE: btn2.setChecked(True) btn2.toggled.connect(lambda: self.toggle_button(btn2)) h_layout.addWidget(btn2) btn3 = QRadioButton("uniform") if btn3.text() == controller_mppi.SAMPLING_TYPE: btn3.setChecked(True) btn3.toggled.connect(lambda: self.toggle_button(btn3)) h_layout.addWidget(btn3) btn4 = QRadioButton("repeated") if btn4.text() == controller_mppi.SAMPLING_TYPE: btn4.setChecked(True) btn4.toggled.connect(lambda: self.toggle_button(btn4)) h_layout.addWidget(btn4) btn5 = QRadioButton("interpolated") if btn5.text() == controller_mppi.SAMPLING_TYPE: btn5.setChecked(True) btn5.toggled.connect(lambda: self.toggle_button(btn5)) h_layout.addWidget(btn5) mppi_constants_layout.addWidget(QLabel("Sampling type:")) mppi_constants_layout.addLayout(h_layout) ### Put together layout self.update_labels() self.update_slider_labels() layout.addLayout(horizon_options_layout) layout.addLayout(rollouts_options_layout) layout.addLayout(cost_weight_layout) layout.addLayout(mppi_constants_layout) self.setLayout(layout) self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) self.setGeometry(0, 0, 400, 50) self.show() self.setWindowTitle("MPPI Options") self.timer = QTimer() self.timer.timeout.connect(self.update_labels) self.timer.start(100)
class PhotoEditorGUI(QMainWindow): def __init__(self): super().__init__() self.initializeUI() self.image = QImage() def initializeUI(self): self.setMinimumSize(300, 200) self.setWindowTitle("Photo Editor") self.showMaximized() self.zoom_factor = 1 self.createMainLabel() self.createEditingBar() self.createMenu() self.createToolBar() self.show() def createMenu(self): """Set up the menubar.""" # Actions for Photo Editor menu about_act = QAction('About', self) about_act.triggered.connect(self.aboutDialog) self.exit_act = QAction(QIcon(os.path.join(icon_path, "exit.png")), 'Quit Photo Editor', self) self.exit_act.setShortcut('Ctrl+Q') self.exit_act.triggered.connect(self.close) # Actions for File menu self.new_act = QAction(QIcon(os.path.join(icon_path, "new.png")), 'New...') self.open_act = QAction(QIcon(os.path.join(icon_path, "open.png")), 'Open...', self) self.open_act.setShortcut('Ctrl+O') self.open_act.triggered.connect(self.image_label.openImage) self.print_act = QAction(QIcon(os.path.join(icon_path, "print.png")), "Print...", self) self.print_act.setShortcut('Ctrl+P') #self.print_act.triggered.connect(self.printImage) self.print_act.setEnabled(False) self.save_act = QAction(QIcon(os.path.join(icon_path, "save.png")), "Save...", self) self.save_act.setShortcut('Ctrl+S') self.save_act.triggered.connect(self.image_label.saveImage) self.save_act.setEnabled(False) # Actions for Edit menu self.revert_act = QAction("Revert to Original", self) self.revert_act.triggered.connect(self.image_label.revertToOriginal) self.revert_act.setEnabled(False) # Actions for Tools menu self.crop_act = QAction(QIcon(os.path.join(icon_path, "crop.png")), "Crop", self) self.crop_act.setShortcut('Shift+X') self.crop_act.triggered.connect(self.image_label.cropImage) self.resize_act = QAction(QIcon(os.path.join(icon_path, "resize.png")), "Resize", self) self.resize_act.setShortcut('Shift+Z') self.resize_act.triggered.connect(self.image_label.resizeImage) self.rotate90_cw_act = QAction( QIcon(os.path.join(icon_path, "rotate90_cw.png")), 'Rotate 90º CW', self) self.rotate90_cw_act.triggered.connect( lambda: self.image_label.rotateImage90("cw")) self.rotate90_ccw_act = QAction( QIcon(os.path.join(icon_path, "rotate90_ccw.png")), 'Rotate 90º CCW', self) self.rotate90_ccw_act.triggered.connect( lambda: self.image_label.rotateImage90("ccw")) self.flip_horizontal = QAction( QIcon(os.path.join(icon_path, "flip_horizontal.png")), 'Flip Horizontal', self) self.flip_horizontal.triggered.connect( lambda: self.image_label.flipImage("horizontal")) self.flip_vertical = QAction( QIcon(os.path.join(icon_path, "flip_vertical.png")), 'Flip Vertical', self) self.flip_vertical.triggered.connect( lambda: self.image_label.flipImage('vertical')) self.zoom_in_act = QAction( QIcon(os.path.join(icon_path, "zoom_in.png")), 'Zoom In', self) self.zoom_in_act.setShortcut('Ctrl++') self.zoom_in_act.triggered.connect(lambda: self.zoomOnImage(1.25)) self.zoom_in_act.setEnabled(False) self.zoom_out_act = QAction( QIcon(os.path.join(icon_path, "zoom_out.png")), 'Zoom Out', self) self.zoom_out_act.setShortcut('Ctrl+-') self.zoom_out_act.triggered.connect(lambda: self.zoomOnImage(0.8)) self.zoom_out_act.setEnabled(False) self.normal_size_Act = QAction("Normal Size", self) self.normal_size_Act.setShortcut('Ctrl+=') self.normal_size_Act.triggered.connect(self.normalSize) self.normal_size_Act.setEnabled(False) # Actions for Views menu #self.tools_menu_act = QAction(QIcon(os.path.join(icon_path, "edit.png")),'Tools View...', self, checkable=True) # Create menubar menu_bar = self.menuBar() menu_bar.setNativeMenuBar(False) # Create Photo Editor menu and add actions main_menu = menu_bar.addMenu('Photo Editor') main_menu.addAction(about_act) main_menu.addSeparator() main_menu.addAction(self.exit_act) # Create file menu and add actions file_menu = menu_bar.addMenu('File') file_menu.addAction(self.open_act) file_menu.addAction(self.save_act) file_menu.addSeparator() file_menu.addAction(self.print_act) edit_menu = menu_bar.addMenu('Edit') edit_menu.addAction(self.revert_act) tool_menu = menu_bar.addMenu('Tools') tool_menu.addAction(self.crop_act) tool_menu.addAction(self.resize_act) tool_menu.addSeparator() tool_menu.addAction(self.rotate90_cw_act) tool_menu.addAction(self.rotate90_ccw_act) tool_menu.addAction(self.flip_horizontal) tool_menu.addAction(self.flip_vertical) tool_menu.addSeparator() tool_menu.addAction(self.zoom_in_act) tool_menu.addAction(self.zoom_out_act) tool_menu.addAction(self.normal_size_Act) views_menu = menu_bar.addMenu('Views') views_menu.addAction(self.tools_menu_act) def createToolBar(self): """Set up the toolbar.""" tool_bar = QToolBar("Main Toolbar") tool_bar.setIconSize(QSize(26, 26)) self.addToolBar(tool_bar) # Add actions to the toolbar tool_bar.addAction(self.open_act) tool_bar.addAction(self.save_act) tool_bar.addAction(self.print_act) tool_bar.addAction(self.exit_act) tool_bar.addSeparator() tool_bar.addAction(self.crop_act) tool_bar.addAction(self.resize_act) tool_bar.addSeparator() tool_bar.addAction(self.rotate90_ccw_act) tool_bar.addAction(self.rotate90_cw_act) tool_bar.addAction(self.flip_horizontal) tool_bar.addAction(self.flip_vertical) tool_bar.addSeparator() tool_bar.addAction(self.zoom_in_act) tool_bar.addAction(self.zoom_out_act) def createEditingBar(self): """Create dock widget for editing tools.""" #TODO: Add a tab widget for the different editing tools self.editing_bar = QDockWidget("Tools") self.editing_bar.setAllowedAreas( Qt.DockWidgetAreas.LeftDockWidgetArea | Qt.DockWidgetAreas.RightDockWidgetArea) self.editing_bar.setMinimumWidth(90) # Create editing tool buttons filters_label = QLabel("Filters") convert_to_grayscale = QToolButton() convert_to_grayscale.setIcon( QIcon(os.path.join(icon_path, "grayscale.png"))) convert_to_grayscale.clicked.connect(self.image_label.convertToGray) convert_to_RGB = QToolButton() convert_to_RGB.setIcon(QIcon(os.path.join(icon_path, "rgb.png"))) convert_to_RGB.clicked.connect(self.image_label.convertToRGB) convert_to_sepia = QToolButton() convert_to_sepia.setIcon(QIcon(os.path.join(icon_path, "sepia.png"))) convert_to_sepia.clicked.connect(self.image_label.convertToSepia) change_hue = QToolButton() change_hue.setIcon(QIcon(os.path.join(icon_path, ""))) change_hue.clicked.connect(self.image_label.changeHue) brightness_label = QLabel("Brightness") self.brightness_slider = QSlider(Qt.Orientations.Horizontal) self.brightness_slider.setRange(-255, 255) self.brightness_slider.setTickInterval(35) self.brightness_slider.setTickPosition(QSlider.TickPosition.TicksAbove) self.brightness_slider.valueChanged.connect( self.image_label.changeBrighteness) contrast_label = QLabel("Contrast") self.contrast_slider = QSlider(Qt.Orientations.Horizontal) self.contrast_slider.setRange(-255, 255) self.contrast_slider.setTickInterval(35) self.contrast_slider.setTickPosition(QSlider.TickPosition.TicksAbove) self.contrast_slider.valueChanged.connect( self.image_label.changeContrast) # Set layout for dock widget editing_grid = QGridLayout() #editing_grid.addWidget(filters_label, 0, 0, 0, 2, Qt.AlignTop) editing_grid.addWidget(convert_to_grayscale, 1, 0) editing_grid.addWidget(convert_to_RGB, 1, 1) editing_grid.addWidget(convert_to_sepia, 2, 0) editing_grid.addWidget(change_hue, 2, 1) editing_grid.addWidget(brightness_label, 3, 0) editing_grid.addWidget(self.brightness_slider, 4, 0, 1, 0) editing_grid.addWidget(contrast_label, 5, 0) editing_grid.addWidget(self.contrast_slider, 6, 0, 1, 0) editing_grid.setRowStretch(7, 10) container = QWidget() container.setLayout(editing_grid) self.editing_bar.setWidget(container) self.addDockWidget(Qt.DockWidgetAreas.LeftDockWidgetArea, self.editing_bar) self.tools_menu_act = self.editing_bar.toggleViewAction() def createMainLabel(self): """Create an instance of the imageLabel class and set it as the main window's central widget.""" self.image_label = imageLabel(self) self.image_label.resize(self.image_label.pixmap().size()) self.scroll_area = QScrollArea() self.scroll_area.setBackgroundRole(QPalette.ColorRole.Dark) self.scroll_area.setAlignment(Qt.Alignment.AlignCenter) #self.scroll_area.setWidgetResizable(False) #scroll_area.setMinimumSize(800, 800) self.scroll_area.setWidget(self.image_label) #self.scroll_area.setVisible(False) self.setCentralWidget(self.scroll_area) #self.resize(QApplication.primaryScreen().availableSize() * 3 / 5) def updateActions(self): """Update the values of menu and toolbar items when an image is loaded.""" self.save_act.setEnabled(True) self.revert_act.setEnabled(True) self.zoom_in_act.setEnabled(True) self.zoom_out_act.setEnabled(True) self.normal_size_Act.setEnabled(True) def zoomOnImage(self, zoom_value): """Zoom in and zoom out.""" self.zoom_factor *= zoom_value self.image_label.resize(self.zoom_factor * self.image_label.pixmap().size()) self.adjustScrollBar(self.scroll_area.horizontalScrollBar(), zoom_value) self.adjustScrollBar(self.scroll_area.verticalScrollBar(), zoom_value) self.zoom_in_act.setEnabled(self.zoom_factor < 4.0) self.zoom_out_act.setEnabled(self.zoom_factor > 0.333) def normalSize(self): """View image with its normal dimensions.""" self.image_label.adjustSize() self.zoom_factor = 1.0 def adjustScrollBar(self, scroll_bar, value): """Adjust the scrollbar when zooming in or out.""" scroll_bar.setValue( int(value * scroll_bar.value()) + ((value - 1) * scroll_bar.pageStep() / 2)) def aboutDialog(self): QMessageBox.about( self, "About Photo Editor", "Photo Editor\nVersion 0.2\n\nCreated by Joshua Willman") def keyPressEvent(self, event): """Handle key press events.""" if event.key() == Qt.Key_Escape: self.close() if event.key() == Qt.Key_F1: # fn + F1 on Mac if self.isMaximized(): self.showNormal() else: self.showMaximized() def closeEvent(self, event): pass