def __init__(self, parent=None, min=0, max=999999): super(intLineEdit, self).__init__(parent) self.min = min self.max = max qiv = QIntValidator() qiv.setRange(min, max) self.setValidator(qiv)
def __init__(self): super().__init__() self.resize(800, 150) self.setWindowTitle('AI智能打轴 (测试版)') layout = QGridLayout() self.setLayout(layout) layout.addWidget(QLabel('前侧留白(ms)'), 0, 0, 1, 1) self.beforeEdit = QLineEdit('20') validator = QIntValidator() validator.setRange(0, 5000) self.beforeEdit.setValidator(validator) self.beforeEdit.setFixedWidth(50) layout.addWidget(self.beforeEdit, 0, 1, 1, 1) layout.addWidget(QLabel(''), 0, 2, 1, 1) layout.addWidget(QLabel('后侧留白(ms)'), 0, 3, 1, 1) self.afterEdit = QLineEdit('300') self.afterEdit.setValidator(validator) self.afterEdit.setFixedWidth(50) layout.addWidget(self.afterEdit, 0, 4, 1, 1) layout.addWidget(QLabel(''), 0, 5, 1, 1) self.autoFill = QPushButton('填充字符') self.autoFill.setStyleSheet('background-color:#3daee9') self.autoFill.clicked.connect(self.setAutoFill) layout.addWidget(self.autoFill, 0, 6, 1, 1) self.fillWord = QLineEdit('#AI自动识别#') layout.addWidget(self.fillWord, 0, 7, 1, 1) layout.addWidget(QLabel(''), 0, 8, 1, 1) self.autoSpan = QPushButton('自动合并') self.autoSpan.setStyleSheet('background-color:#3daee9') self.autoSpan.clicked.connect(self.setAutoSpan) layout.addWidget(self.autoSpan, 0, 9, 1, 1) self.multiCheck = QPushButton('启用多进程') self.multiCheck.setStyleSheet('background-color:#3daee9') self.multiCheck.clicked.connect(self.setMultipleThread) layout.addWidget(self.multiCheck, 0, 10, 1, 1) self.processBar = QProgressBar() layout.addWidget(self.processBar, 1, 0, 1, 10) self.checkButton = QPushButton('开始') self.checkButton.setFixedWidth(100) self.checkButton.clicked.connect(self.separateProcess) layout.addWidget(self.checkButton, 1, 10, 1, 1)
class MainWindow(QMainWindow): def __init__(self, ui_file, devMode, parent=None): super(MainWindow, self).__init__() self.uiFileName = ui_file # use this if there are multiple UI files if devMode == "development": # use ui file directly made by QDesigner uiFile = QFile(ui_file) uiFile.open(QFile.ReadOnly) # read in UI for the form loader = QUiLoader() self.window = loader.load(uiFile) uiFile.close() else: #deployment - use .py version of ui file, with slightly different setup self.ui = Ui_MainWindow() self.ui.setupUi(self) self.getParts() # identify form objects self.connectParts() # connect buttons to operations self.convs = Converters( ) # load converters for main conversion categories self.yc = YearConverters( ) # load converters for japanese years, zodiac years self.mess = Mess() # instructions and messages in local language self.validFloat = QDoubleValidator() self.validYear = QIntValidator() self.validYear.setRange(1, 9999) # used for years self.btnConvert.hide() self.widgetSetup("start") # initial conditions for form if devMode == "development": self.window.show() else: self.show() def widgetSetup(self, themode): # based on which main button is pressed self.themode = themode # this is set when a main button is pressed, or on initial load self.emptyConvLayout( ) # get rid of old radio buttons if they exist, hide input/output, etc. thetext, thestyle = makeHeader1( themode, self.uiFileName) # make heading for convType = themode self.header1.setStyleSheet( thestyle) # can have different background colors self.header1.setText(thetext) self.header1.show() self.hideBigInstructions() if themode in self.convs.getValidConvTypes( ): # set up radio buttons for conversions oneConvTypeInfo = self.convs.convTypeToConvInfo( themode) # get convCodes, displays for this convType for ix, (convCode, convDisplay) in enumerate( oneConvTypeInfo ): # iterate through enumerated list of tuples self.oneRadio = QRadioButton( "{}".format(convDisplay)) # text for button self.oneRadio.setObjectName("object_{}".format( convCode)) # to be used later by sender() self.oneRadio.setStyleSheet( makeStyleSheet("radiobutton", self.uiFileName)) self.layoutConv.addWidget(self.oneRadio) self.oneRadio.clicked.connect( self.convSetup ) # convSetup will call sender() to find convCode if ix == 0: self.oneRadio.setFocus( ) # set focus on first button in list if themode == "fromjpyear": # set up radio buttons for modern eras self.fromjpyearmode = "modern" # start with modern eras only self.showJpEras() if themode == "fromjpyearhistoric": # set up radio buttons for all eras self.fromjpyearmode = "all" # start with modern eras only self.showJpEras() if themode == "tojpyear": # no choices, go straight to input box self.showInstructions(self.mess.getToJpYear(self.yc.getMinYear()), "") self.input1.setText(str( self.yc.getNowYear())) # start with current year self.setUpValidator() self.convertUnits() if themode == "zodiac": # no choices, go straight to input box self.showInstructions(self.mess.getEnterYearZodiac(), "") self.input1.setText(str( self.yc.getNowYear())) # start with current year self.setUpValidator() self.convertUnits() if themode == "start": self.btnFromMetric.setFocus() self.showBigInstructions(self.mess.getStartMsg2()) def showBigInstructions(self, themsg): self.instructions2.setText(themsg) self.scrollArea.hide() self.instructions2.show() def hideBigInstructions(self): self.scrollArea.show() self.instructions2.hide() def showJpEras(self): # display radio buttons for Japanese eras jpEraList = self.yc.getEraNamesPlusCodes( self.fromjpyearmode) # modern or all for ix, (eraText, eraCode) in enumerate(jpEraList): self.oneRadio = QRadioButton( "{}".format(eraText)) # text for button self.oneRadio.setObjectName( "object_{}".format(eraCode)) # to be used later by sender() self.oneRadio.setStyleSheet( makeStyleSheet("radiobutton", self.uiFileName)) self.layoutConv.addWidget(self.oneRadio) self.oneRadio.clicked.connect( self.eraSetup) # eraSetup will call sender() to find eraCode if ix == 0: self.oneRadio.setFocus() # set focus on first button if self.fromjpyearmode == "modern": self.oneRadio = QRadioButton( "{}".format("Show more eras")) # now add option for more eras self.oneRadio.setObjectName( "object_{}".format("all")) # to be used later by sender() else: self.oneRadio = QRadioButton("{}".format( "Show fewer eras")) # now add option to return to modern eras self.oneRadio.setObjectName( "object_{}".format("modern")) # to be used later by sender() self.oneRadio.setStyleSheet( makeStyleSheet("radiobutton", self.uiFileName)) self.layoutConv.addWidget(self.oneRadio) self.oneRadio.clicked.connect( self.eraSetup) # eraSetup will call sender() to find eraCode def eraSetup(self): # this is called when radio button is chosen theEra = self.getCodeFromSenderName( ) # era or mode is determined by object name of sending radio button if theEra in ["all", "modern"]: # switch between modern and all self.fromjpyearmode = theEra # set the mode here self.emptyConvLayout() # clear former list if theEra == "all": self.widgetSetup("fromjpyearhistoric") else: self.widgetSetup("fromjpyear") else: # theEra will be eraCode for chosen era self.chosenEra = theEra # set this property if self.yc.getNowEra() == theEra: theHint = "(1- )" # no final year if it's the current era else: theHint = "(1-{})".format( self.yc.getNumYears(theEra)) # final year in that era self.showInstructions( self.mess.getFromJpYear(self.yc.getENameUnparsed(theEra)), theHint) self.output2.hide() self.setUpValidator() def setUpValidator(self): if self.themode in self.convs.getValidConvTypes(): # measures if self.convs.isTempConv(self.theConvCode): self.validFloat.setBottom( -999999) # temperatures can be negative else: self.validFloat.setBottom( 0.0) # other measures can't be negative self.input1.setValidator(self.validFloat) if self.themode in [ "fromjpyear", "fromjpyearhistoric", "tojpyear", "zodiac" ]: self.input1.setValidator( self.validYear) # range already set to 1-9999 def convSetup(self): # set up based on which radio button was set self.theConvCode = self.getCodeFromSenderName( ) # get the code based on the sending button, and set it self.showInstructions(self.mess.getEnterAmt()) self.setUpValidator() self.input1.setText("1") # show units=1 to start self.convertUnits() def getCodeFromSenderName(self): # based on objectName of sender button sending_button = self.sender() return str(sending_button.objectName()).replace("object_", "") def convertUnits(self): # main conversion routine pstart = self.mess.getPstart( ) # get paragraph HTML from Message object pstopstart = self.mess.getPstopstart() pstop = self.mess.getPstop() jcol = self.mess.getJColor() # special color for Japanese text if len(self.input1.text()) < 1: # test for blank input self.output2.setText( self.mess.getBlankConvertMsg()) # show error message self.output2.show() return if self.themode in self.convs.getValidConvTypes( ): # standard conversions: fromjpmeasure, tometric, etc amt1Text = self.input1.text() amt1 = strToNum(amt1Text) if self.convs.isTooCold(self.theConvCode, amt1): eqString = self.mess.getTooCold( ) #check for temp below abs zero else: eqString = self.convs.getEquation(self.theConvCode, amt1, " =" + pstopstart, jcol) self.output2.setText( pstart + eqString + pstop) # use paragraphs for better control of appearance elif self.themode == "tojpyear": # int'l year to Japanese year try: iYear = int(self.input1.text()) yearDisplay = self.mess.makeJYearDisplay( self.yc.iYearToJYear(iYear, jcol), iYear) except ValueError as errorMsg: yearDisplay = pstart + str( errorMsg) + pstop # display the error message self.output2.setText(yearDisplay) #print (yearDisplay) elif self.themode in ["fromjpyear", "fromjpyearhistoric" ]: # Japanese year to int'l year jYear = int(self.input1.text() ) # validator at work, so this should be an integer try: # raise exception if not in range iYear = self.yc.jYearToIYear(self.chosenEra, jYear) yearDisplay = pstart + "{} ({}) {}".format(self.yc.getENameUnparsed(self.chosenEra), \ self.yc.getJName(self.chosenEra, jcol), jYear) + \ pstopstart + self.mess.getIs() + " " + str(iYear) + pstop except ValueError as errorMsg: yearDisplay = pstart + str( errorMsg) + pstop # display the error message self.output2.setText(yearDisplay) elif self.themode == "zodiac": # international year to zodiac sign iYear = int(self.input1.text()) # display is HTML with paragraph, line height, and Japanese characters in color (specified in Mess class) yearDisplay = pstart + str(iYear) + " " + self.mess.getIs() + ":" + pstopstart + \ self.mess.getYearOfThe().capitalize() + " " + self.yc.getZodEName(iYear) + pstop self.output2.setText(yearDisplay) zBig = self.mess.makeZodiacBigDisplay( self.yc.getZodEName(iYear), self.yc.getZodJName(iYear, jcol), self.yc.getZodJZName(iYear, jcol)) self.showBigInstructions(zBig) self.output2.show() # common to all conversion types def emptyConvLayout( self): # clear the radio buttons in the converter/jpYear layout for i in reversed(range( self.layoutConv.count())): # delete them in reverse order self.layoutConv.itemAt(i).widget().deleteLater() self.instructions1.hide() self.input1.setPlaceholderText("") # clear previous hints self.input1.hide() # conversion not chosen yet, so hide this self.btnConvert.hide() # conversion not chosen yet, so hide this self.output2.hide() # conversion not chosen yet, so hide this def showInstructions( self, instructionsText, inputHint=""): # position UI elements, set label, show input box self.instructions1.setText(instructionsText) self.instructions1.show() self.input1.setText("") self.input1.setPlaceholderText(inputHint) self.input1.show() self.btnConvert.show() self.input1.setFocus() def convertQuickly(self): # testing if not self.input1.text(): amt = 0 else: amt = float(self.input1.text()) * 2.0 amtString = '{} kilograms'.format(amt) self.output2.setText(amtString) def getParts(self): # map form elements to object properties self.centralw = self.findWidget(QWidget, 'central_widget') self.input1 = self.findWidget(QLineEdit, 'input1') self.output2 = self.findWidget(QTextEdit, 'label_output2') self.header1 = self.findWidget(QLabel, 'label_header1') self.btnConvert = self.findWidget(QPushButton, 'button_convert') self.btnExit = self.findWidget(QPushButton, 'button_exit') self.btnFromMetric = self.findWidget(QPushButton, 'button_from_metric') self.btnToMetric = self.findWidget(QPushButton, 'button_to_metric') self.btnFromJpMeasure = self.findWidget(QPushButton, 'button_from_jpmeasure') self.btnToJpMeasure = self.findWidget(QPushButton, 'button_to_jpmeasure') self.btnFromJpYear = self.findWidget(QPushButton, 'button_from_jpyear') self.btnToJpYear = self.findWidget(QPushButton, 'button_to_jpyear') self.btnZodiac = self.findWidget(QPushButton, 'button_zodiac') self.scrollArea = self.findWidget(QScrollArea, 'conv_layout') content_widget = QWidget( ) # now add the QVBoxLayout widget programmatically, for scrolling self.scrollArea.setWidget(content_widget) self.scrollArea.setWidgetResizable(True) self.layoutConv = QVBoxLayout(content_widget) self.layoutConv.setAlignment( Qt.AlignTop ) # don't evenly space the radio buttons, but start at the top self.instructions1 = self.findWidget(QLabel, 'label_instructions1') self.instructions2 = self.findWidget(QLabel, 'label_instructions2') self.menuExit = self.findWidget(QAction, 'action_exit') self.menuFromMetric = self.findWidget(QAction, 'action_from_metric') self.menuToMetric = self.findWidget(QAction, 'action_to_metric') self.menuFromJpMeasure = self.findWidget(QAction, 'action_from_jpmeasure') self.menuToJpMeasure = self.findWidget(QAction, 'action_to_jpmeasure') self.menuFromJpYear = self.findWidget(QAction, 'action_from_jpyear') self.menuFromJpYearHistoric = self.findWidget( QAction, 'action_from_jpyear_historic') self.menuToJpYear = self.findWidget(QAction, 'action_to_jpyear') self.menuZodiac = self.findWidget(QAction, 'action_zodiac') self.menubar = self.findWidget(QMenuBar, 'menubar') if self.uiFileName == "main3.ui": self.menubar.setStyleSheet( getMenuStyle("main3.ui")) # in jpconvhelper.py def findWidget(self, widgetType, widgetName): if devMode == "development": return self.window.findChild(widgetType, widgetName) else: return self.findChild(widgetType, widgetName) def connectParts(self): # connect buttons and menu actions to operations self.input1.returnPressed.connect(self.convertUnits) self.btnConvert.clicked.connect(self.convertUnits) self.btnExit.clicked.connect(self.exitHandler) self.menuExit.triggered.connect(self.exitHandler) self.btnFromMetric.clicked.connect( lambda: self.widgetSetup("frommetric")) self.menuFromMetric.triggered.connect( lambda: self.widgetSetup("frommetric")) self.btnToMetric.clicked.connect(lambda: self.widgetSetup("tometric")) self.menuToMetric.triggered.connect( lambda: self.widgetSetup("tometric")) self.btnFromJpMeasure.clicked.connect( lambda: self.widgetSetup("fromjpmeasure")) self.menuFromJpMeasure.triggered.connect( lambda: self.widgetSetup("fromjpmeasure")) self.btnToJpMeasure.clicked.connect( lambda: self.widgetSetup("tojpmeasure")) self.menuToJpMeasure.triggered.connect( lambda: self.widgetSetup("tojpmeasure")) self.btnFromJpYear.clicked.connect( lambda: self.widgetSetup("fromjpyear")) self.menuFromJpYear.triggered.connect( lambda: self.widgetSetup("fromjpyear")) self.menuFromJpYearHistoric.triggered.connect( lambda: self.widgetSetup("fromjpyearhistoric")) self.btnToJpYear.clicked.connect(lambda: self.widgetSetup("tojpyear")) self.menuToJpYear.triggered.connect( lambda: self.widgetSetup("tojpyear")) self.btnZodiac.clicked.connect(lambda: self.widgetSetup("zodiac")) self.menuZodiac.triggered.connect(lambda: self.widgetSetup("zodiac")) def exitHandler(self): app.exit()
class MainWindow(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('MASK标注工具') self.setFixedSize(1000, 650) self.move_to_center() # self.mask_img_size = QSize(512, 512) self.brush_color = QColor(255, 255, 0) self.eraser_color = QColor(0, 0, 0) self.label_img = ImageLabel(self, self.mask_img_size, self.brush_color, self.eraser_color) self.label_img.setAlignment(Qt.AlignCenter) self.label_img.setText('没有选择任何图片') self.label_img.setFixedWidth(700) self.label_img.setFixedHeight(600) # self.btn_select_dir = QPushButton(self) self.btn_select_dir.setText('选择目录...') self.btn_select_dir.clicked.connect(self.on_btn_select_dir) self.btn_prev_img = QPushButton(self) self.btn_prev_img.setText('上一张') self.btn_prev_img.clicked.connect(self.on_btn_prev_img) self.connect(QShortcut(QKeySequence(Qt.Key_Left), self), QtCore.SIGNAL('activated()'), self.btn_prev_img.click) self.btn_next_img = QPushButton(self) self.btn_next_img.setText('下一张') self.btn_next_img.clicked.connect(self.on_btn_next_img) self.connect(QShortcut(QKeySequence(Qt.Key_Right), self), QtCore.SIGNAL('activated()'), self.btn_next_img.click) self.btn_select_brush_color = QPushButton(self) self.btn_select_brush_color.setText('修改画笔颜色') self.btn_select_brush_color.clicked.connect( self.on_btn_select_brush_color) self.label_brush_color = QLabel(self) self.label_brush_color.setFixedWidth(50) pe = QPalette() pe.setColor(QPalette.Window, self.brush_color) self.label_brush_color.setPalette(pe) self.label_brush_color.setAutoFillBackground(True) self.defalut_brush_pixle_size = 5 self.min_brush_pixle_size = 1 self.max_brush_pixle_size = 50 self.label_brush_pixle_size = QLabel('画笔像素大小:') self.label_brush_pixle_size.setFixedWidth(110) self.edit_brush_pixle_size_validator = QIntValidator() self.edit_brush_pixle_size_validator.setRange( self.min_brush_pixle_size, self.max_brush_pixle_size) self.edit_brush_pixle_size = QLineEdit(self) self.edit_brush_pixle_size.setText(f'{self.defalut_brush_pixle_size}') self.edit_brush_pixle_size.setValidator( self.edit_brush_pixle_size_validator) self.edit_brush_pixle_size.textChanged.connect( self.on_edit_brush_pixle_size_change) self.label_img.update_brush_pixle_size(self.defalut_brush_pixle_size) self.defalut_eraser_pixle_size = 50 self.min_eraser_pixle_size = 5 self.max_eraser_pixle_size = 50 self.label_eraser_pixle_size = QLabel('橡皮擦像素大小:') self.label_eraser_pixle_size.setFixedWidth(110) self.edit_eraser_pixle_size_validator = QIntValidator() self.edit_eraser_pixle_size_validator.setRange( self.min_eraser_pixle_size, self.max_eraser_pixle_size) self.edit_eraser_pixle_size = QLineEdit(self) self.edit_eraser_pixle_size.setText( f'{self.defalut_eraser_pixle_size}') self.edit_eraser_pixle_size.setValidator( self.edit_eraser_pixle_size_validator) self.edit_eraser_pixle_size.textChanged.connect( self.on_edit_eraser_pixle_size_change) self.label_img.update_eraser_pixle_size(self.defalut_eraser_pixle_size) self.btn_clear_mask = QPushButton(self) self.btn_clear_mask.setText('全部擦除') self.btn_clear_mask.clicked.connect(self.on_btn_clear_mask) self.btn_roate = QPushButton(self) self.btn_roate.setText('旋转') self.btn_roate.clicked.connect(self.on_btn_roate) self.btn_roate_img = QPushButton(self) self.btn_roate_img.setText('旋转原图') self.btn_roate_img.clicked.connect(self.on_btn_roate_img) self.connect(QShortcut(QKeySequence(Qt.Key_Up), self), QtCore.SIGNAL('activated()'), self.btn_roate_img.click) self.btn_roate_mask = QPushButton(self) self.btn_roate_mask.setText('旋转标注图') self.btn_roate_mask.clicked.connect(self.on_btn_roate_mask) self.label_lable_docs = QLabel(self) self.label_lable_docs.setAlignment(Qt.AlignLeft) self.label_lable_docs.setText(r''' - 鼠标左键拖动,绘制标注内容 - ALT+鼠标左键拖动,擦除标注内容 - CTRL+鼠标滚轮,调整画笔像素大小 - ALT+鼠标滚轮,调整橡皮擦像素大小 - 键盘左方向键切换到上一张图片 - 键盘右方向键切换到下一张图片 - 输入张数加回车跳转到指定张数 ''') self.label_status_running1 = QLabel(self) self.label_status_running1.setAlignment(Qt.AlignLeft) self.label_status_running1.setText('请选择需要标注的目录') self.label_status_page_number_validator = QIntValidator() self.label_status_page_number = QLineEdit(self) self.label_status_page_number.setMaximumWidth(50) self.label_status_page_number.setValidator( self.label_status_page_number_validator) self.label_status_page_number.hide() self.label_status_page_number.returnPressed.connect(self.on_page_jump) self.label_status_running2 = QLabel(self) self.label_status_running2.setAlignment(Qt.AlignLeft) self.label_status_running2.setText('张') self.label_status_running2.hide() # 布局 layout_root = QVBoxLayout() layout_root2 = QHBoxLayout() layout_col1 = QVBoxLayout() layout_col2 = QVBoxLayout() layout_root2.addLayout(layout_col1) layout_root2.addLayout(layout_col2) layout_root_row2 = QHBoxLayout() layout_root.addLayout(layout_root2) layout_root.addLayout(layout_root_row2) layout_root_row2.addWidget(self.label_status_running1) layout_root_row2.addWidget(self.label_status_page_number) layout_root_row2.addWidget(self.label_status_running2) layout_col1.addWidget(self.label_img) layout_col2_row1 = QHBoxLayout() layout_col2_row1.addWidget(self.btn_select_dir) layout_col2_row2 = QHBoxLayout() layout_col2_row2.addWidget(self.btn_prev_img) layout_col2_row2.addWidget(self.btn_next_img) layout_col2_row3 = QHBoxLayout() layout_col2_row3.addWidget(self.label_brush_pixle_size) layout_col2_row3.addWidget(self.edit_brush_pixle_size) layout_col2_row4 = QHBoxLayout() layout_col2_row4.addWidget(self.label_eraser_pixle_size) layout_col2_row4.addWidget(self.edit_eraser_pixle_size) layout_col2_row5 = QHBoxLayout() layout_col2_row5.addWidget(self.btn_select_brush_color) layout_col2_row5.addWidget(self.label_brush_color) layout_col2.addLayout(layout_col2_row1) layout_col2.addLayout(layout_col2_row2) layout_col2.addLayout(layout_col2_row5) layout_col2.addLayout(layout_col2_row3) layout_col2.addLayout(layout_col2_row4) layout_col2.addWidget(self.btn_clear_mask) layout_col2.addWidget(self.btn_roate) layout_col2.addWidget(self.btn_roate_img) layout_col2.addWidget(self.btn_roate_mask) layout_col2.addWidget(self.label_lable_docs) layout_col2.addStretch() self.setLayout(layout_root) # 其他数据 self.directory = None self.all_img_file = [] self.all_img_file_index = 0 self.update_btn_status() def move_to_center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def on_btn_select_dir(self): try: self.directory = QFileDialog.getExistingDirectory(self, '选择图片目录') all_img_file = sorted([ x for x in Path(self.directory).iterdir() if x.is_file() and x.suffix.upper() in ['.JPG', '.JPEG', '.BMP', '.PNG'] ]) if len(all_img_file) <= 0: QMessageBox.information(self, '<提示>', f'{self.directory}\n目录下没有找到图片文件', QMessageBox.Ok) return self.setWindowTitle(f'MASK标注工具: {self.directory}') self.all_img_file = all_img_file self.all_img_file_index = 0 self.show_label_img() finally: self.update_btn_status() def on_btn_next_img(self): try: self.all_img_file_index += 1 self.show_label_img() finally: self.update_btn_status() def on_btn_prev_img(self): try: self.all_img_file_index -= 1 self.show_label_img() finally: self.update_btn_status() def on_btn_select_brush_color(self): self.brush_color = QColorDialog.getColor() self.label_img.update_brush_color(self.brush_color) pe = QPalette() pe.setColor(QPalette.Window, self.brush_color) self.label_brush_color.setPalette(pe) self.label_brush_color.setAutoFillBackground(True) def on_btn_clear_mask(self): self.show_label_img(do_clear=True) def on_btn_roate(self): self.show_label_img(do_roate=True) def on_btn_roate_img(self): self.show_label_img(do_roate_img=True) def on_btn_roate_mask(self): self.show_label_img(do_roate_mask=True) def on_page_jump(self): try: page_num = int(self.label_status_page_number.text()) if page_num >= 1 and page_num <= len(self.all_img_file): self.all_img_file_index = page_num - 1 self.show_label_img() self.setFocus() finally: self.update_btn_status() def wheelEvent(self, event): if QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier: brush_pixle_size = int(self.edit_brush_pixle_size.text()) delta = event.delta() if delta > 0: brush_pixle_size += 1 elif delta < 0: brush_pixle_size -= 1 if brush_pixle_size >= self.min_brush_pixle_size and brush_pixle_size <= self.max_brush_pixle_size: self.edit_brush_pixle_size.setText(f'{brush_pixle_size}') elif QApplication.keyboardModifiers() == QtCore.Qt.AltModifier: eraser_pixle_size = int(self.edit_eraser_pixle_size.text()) delta = event.delta() if delta > 0: eraser_pixle_size += 1 elif delta < 0: eraser_pixle_size -= 1 if eraser_pixle_size >= self.min_eraser_pixle_size and eraser_pixle_size <= self.max_eraser_pixle_size: self.edit_eraser_pixle_size.setText(f'{eraser_pixle_size}') def on_edit_brush_pixle_size_change(self): if self.edit_brush_pixle_size.text(): brush_pixle_size = int(self.edit_brush_pixle_size.text()) self.label_img.update_brush_pixle_size(brush_pixle_size) def on_edit_eraser_pixle_size_change(self): if self.edit_eraser_pixle_size.text(): eraser_pixle_size = int(self.edit_eraser_pixle_size.text()) self.label_img.update_eraser_pixle_size(eraser_pixle_size) def update_btn_status(self): try: self.btn_next_img.setEnabled(False) self.btn_prev_img.setEnabled(False) self.btn_roate.setEnabled(False) self.btn_roate_img.setEnabled(False) self.btn_roate_mask.setEnabled(False) self.btn_clear_mask.setEnabled(False) if not self.all_img_file: self.label_status_running1.setText('请选择需要标注的目录') self.label_status_page_number.hide() self.label_status_running2.hide() else: img_name = self.all_img_file[self.all_img_file_index] # self.label_status_page_number.show() # self.label_status_running2.show() self.label_status_page_number_validator.setRange( 1, len(self.all_img_file)) self.label_status_page_number.setText( f'{self.all_img_file_index+1}') self.label_status_running1.setText( f'当前图片: {img_name} ({self.all_img_file_index + 1}/{len(self.all_img_file)}) 跳转到' ) self.label_status_running2.setText(f'张') if self.all_img_file_index == 0: self.btn_prev_img.setEnabled(False) else: self.btn_prev_img.setEnabled(True) if self.all_img_file_index == len(self.all_img_file) - 1: self.btn_next_img.setEnabled(False) else: self.btn_next_img.setEnabled(True) self.btn_roate.setEnabled(True) self.btn_roate_img.setEnabled(True) self.btn_roate_mask.setEnabled(True) self.btn_clear_mask.setEnabled(True) except: logging.exception('update_btn_status exception') def show_label_img(self, do_roate=False, do_roate_img=False, do_roate_mask=False, do_clear=False): if not self.all_img_file: return img_path = self.all_img_file[self.all_img_file_index] img = QPixmap(str(img_path)) img_mask_path = img_path.parent.joinpath(f'mask/{img_path.stem}.bmp') img_mask_path.parent.mkdir(parents=True, exist_ok=True) if img_mask_path.is_dir(): QMessageBox.warning( self, '<致命错误>', f'标注文件<{img_mask_path}>是一个目录, 请把它拷贝到别的地方, 然后重新打开标注工具!!!', QMessageBox.Ok) sys.exit(-1) if img_mask_path.exists(): img_mask = QPixmap(str(img_mask_path)) if img_mask.size() != self.mask_img_size: os.remove(str(img_mask_path)) if not img_mask_path.exists() or do_clear: img_mask = QPixmap(self.mask_img_size) img_mask.fill(QColor(0, 0, 0)) img_mask.save(str(img_mask_path)) if do_roate: rm = QMatrix() rm.rotate(90) img = img.transformed(rm) img.save(str(img_path)) img_mask = img_mask.transformed(rm) img_mask.save(str(img_mask_path)) if do_roate_img: rm = QMatrix() rm.rotate(90) img = img.transformed(rm) img.save(str(img_path)) if do_roate_mask: rm = QMatrix() rm.rotate(90) img_mask = img_mask.transformed(rm) img_mask.save(str(img_mask_path)) self.label_img.update_label_img(img, img_mask, str(img_mask_path))