class comic_export_setting_dialog(QDialog): acbfStylesList = [ "speech", "commentary", "formal", "letter", "code", "heading", "audio", "thought", "sign", "sound", "emphasis", "strong" ] def __init__(self): super().__init__() self.setLayout(QVBoxLayout()) self.setWindowTitle(i18n("Export Settings")) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) mainWidget = QTabWidget() self.layout().addWidget(mainWidget) self.layout().addWidget(buttons) # Set basic crop settings # Set which layers to remove before export. mainExportSettings = QWidget() mainExportSettings.setLayout(QVBoxLayout()) groupExportCrop = QGroupBox(i18n("Crop Settings")) formCrop = QFormLayout() groupExportCrop.setLayout(formCrop) self.chk_toOutmostGuides = QCheckBox(i18n("Crop to outmost guides")) self.chk_toOutmostGuides.setChecked(True) self.chk_toOutmostGuides.setToolTip( i18n( "This will crop to the outmost guides if possible and otherwise use the underlying crop settings." )) formCrop.addRow("", self.chk_toOutmostGuides) btn_fromSelection = QPushButton( i18n("Set Margins from Active Selection")) btn_fromSelection.clicked.connect(self.slot_set_margin_from_selection) # This doesn't work. formCrop.addRow("", btn_fromSelection) self.spn_marginLeft = QSpinBox() self.spn_marginLeft.setMaximum(99999) self.spn_marginLeft.setSuffix(" px") formCrop.addRow(i18n("Left:"), self.spn_marginLeft) self.spn_marginTop = QSpinBox() self.spn_marginTop.setMaximum(99999) self.spn_marginTop.setSuffix(" px") formCrop.addRow(i18n("Top:"), self.spn_marginTop) self.spn_marginRight = QSpinBox() self.spn_marginRight.setMaximum(99999) self.spn_marginRight.setSuffix(" px") formCrop.addRow(i18n("Right:"), self.spn_marginRight) self.spn_marginBottom = QSpinBox() self.spn_marginBottom.setMaximum(99999) self.spn_marginBottom.setSuffix(" px") formCrop.addRow(i18n("Bottom:"), self.spn_marginBottom) groupExportLayers = QGroupBox(i18n("Layers")) formLayers = QFormLayout() groupExportLayers.setLayout(formLayers) self.cmbLabelsRemove = labelSelector() formLayers.addRow(i18n("Label for removal:"), self.cmbLabelsRemove) self.ln_text_layer_name = QLineEdit() self.ln_text_layer_name.setToolTip( i18n( "These are keywords that can be used to identify text layers. A layer only needs to contain the keyword to be recognized. Keywords should be comma separated." )) self.ln_panel_layer_name = QLineEdit() self.ln_panel_layer_name.setToolTip( i18n( "These are keywords that can be used to identify panel layers. A layer only needs to contain the keyword to be recognized. Keywords should be comma separated." )) formLayers.addRow(i18n("Text Layer Key:"), self.ln_text_layer_name) formLayers.addRow(i18n("Panel Layer Key:"), self.ln_panel_layer_name) mainExportSettings.layout().addWidget(groupExportCrop) mainExportSettings.layout().addWidget(groupExportLayers) mainWidget.addTab(mainExportSettings, i18n("General")) # CBZ, crop, resize, which metadata to add. CBZexportSettings = QWidget() CBZexportSettings.setLayout(QVBoxLayout()) self.CBZactive = QCheckBox(i18n("Export to CBZ")) CBZexportSettings.layout().addWidget(self.CBZactive) self.CBZgroupResize = comic_export_resize_widget("CBZ") CBZexportSettings.layout().addWidget(self.CBZgroupResize) self.CBZactive.clicked.connect(self.CBZgroupResize.setEnabled) CBZgroupMeta = QGroupBox(i18n("Metadata to Add")) # CBZexportSettings.layout().addWidget(CBZgroupMeta) CBZgroupMeta.setLayout(QFormLayout()) mainWidget.addTab(CBZexportSettings, i18n("CBZ")) # ACBF, crop, resize, creator name, version history, panel layer, text layers. ACBFExportSettings = QWidget() ACBFform = QFormLayout() ACBFExportSettings.setLayout(QVBoxLayout()) ACBFdocInfo = QGroupBox() ACBFdocInfo.setTitle(i18n("ACBF Document Info")) ACBFdocInfo.setLayout(ACBFform) self.lnACBFID = QLabel() self.lnACBFID.setToolTip( i18n( "By default this will be filled with a generated universal unique identifier. The ID by itself is merely so that comic book library management programs can figure out if this particular comic is already in their database and whether it has been rated. Of course, the UUID can be changed into something else by manually changing the JSON, but this is advanced usage." )) self.spnACBFVersion = QSpinBox() self.ACBFhistoryModel = QStandardItemModel() acbfHistoryList = QListView() acbfHistoryList.setModel(self.ACBFhistoryModel) btn_add_history = QPushButton(i18n("Add History Entry")) btn_add_history.clicked.connect(self.slot_add_history_item) self.chkIncludeTranslatorComments = QCheckBox() self.chkIncludeTranslatorComments.setText( i18n("Include translator's comments")) self.chkIncludeTranslatorComments.setToolTip( i18n( "A PO file can contain translator's comments. If this is checked, the translations comments will be added as references into the ACBF file." )) self.lnTranslatorHeader = QLineEdit() ACBFform.addRow(i18n("ACBF UID:"), self.lnACBFID) ACBFform.addRow(i18n("Version:"), self.spnACBFVersion) ACBFform.addRow(i18n("Version history:"), acbfHistoryList) ACBFform.addRow("", btn_add_history) ACBFform.addRow("", self.chkIncludeTranslatorComments) ACBFform.addRow(i18n("Translator header:"), self.lnTranslatorHeader) ACBFAuthorInfo = QWidget() acbfAVbox = QVBoxLayout(ACBFAuthorInfo) infoLabel = QLabel( i18n( "The people responsible for the generation of the CBZ/ACBF files." )) infoLabel.setWordWrap(True) ACBFAuthorInfo.layout().addWidget(infoLabel) self.ACBFauthorModel = QStandardItemModel(0, 6) labels = [ i18n("Nick Name"), i18n("Given Name"), i18n("Middle Name"), i18n("Family Name"), i18n("Email"), i18n("Homepage") ] self.ACBFauthorModel.setHorizontalHeaderLabels(labels) self.ACBFauthorTable = QTableView() acbfAVbox.addWidget(self.ACBFauthorTable) self.ACBFauthorTable.setModel(self.ACBFauthorModel) self.ACBFauthorTable.verticalHeader().setDragEnabled(True) self.ACBFauthorTable.verticalHeader().setDropIndicatorShown(True) self.ACBFauthorTable.verticalHeader().setSectionsMovable(True) self.ACBFauthorTable.verticalHeader().sectionMoved.connect( self.slot_reset_author_row_visual) AuthorButtons = QHBoxLayout() btn_add_author = QPushButton(i18n("Add Author")) btn_add_author.clicked.connect(self.slot_add_author) AuthorButtons.addWidget(btn_add_author) btn_remove_author = QPushButton(i18n("Remove Author")) btn_remove_author.clicked.connect(self.slot_remove_author) AuthorButtons.addWidget(btn_remove_author) acbfAVbox.addLayout(AuthorButtons) ACBFStyle = QWidget() ACBFStyle.setLayout(QHBoxLayout()) self.ACBFStylesModel = QStandardItemModel() self.ACBFStyleClass = QListView() self.ACBFStyleClass.setModel(self.ACBFStylesModel) ACBFStyle.layout().addWidget(self.ACBFStyleClass) ACBFStyleEdit = QWidget() ACBFStyleEditVB = QVBoxLayout(ACBFStyleEdit) self.ACBFuseFont = QCheckBox(i18n("Use font")) self.ACBFFontList = QListView() self.ACBFFontList.setItemDelegate(font_list_delegate()) self.ACBFuseFont.toggled.connect(self.font_slot_enable_font_view) self.ACBFFontListModel = QStandardItemModel() self.ACBFFontListModel.rowsRemoved.connect( self.slot_font_current_style) self.ACBFFontListModel.itemChanged.connect( self.slot_font_current_style) self.btnAcbfAddFont = QPushButton() self.btnAcbfAddFont.setIcon(Application.icon("list-add")) self.btnAcbfAddFont.clicked.connect(self.font_slot_add_font) self.btn_acbf_remove_font = QPushButton() self.btn_acbf_remove_font.setIcon(Application.icon("edit-delete")) self.btn_acbf_remove_font.clicked.connect(self.font_slot_remove_font) self.ACBFFontList.setModel(self.ACBFFontListModel) self.ACBFdefaultFont = QComboBox() self.ACBFdefaultFont.addItems( ["sans-serif", "serif", "monospace", "cursive", "fantasy"]) acbfFontButtons = QHBoxLayout() acbfFontButtons.addWidget(self.btnAcbfAddFont) acbfFontButtons.addWidget(self.btn_acbf_remove_font) self.ACBFBold = QCheckBox(i18n("Bold")) self.ACBFItal = QCheckBox(i18n("Italic")) self.ACBFStyleClass.clicked.connect(self.slot_set_style) self.ACBFStyleClass.selectionModel().selectionChanged.connect( self.slot_set_style) self.ACBFStylesModel.itemChanged.connect(self.slot_set_style) self.ACBFBold.toggled.connect(self.slot_font_current_style) self.ACBFItal.toggled.connect(self.slot_font_current_style) colorWidget = QGroupBox(self) colorWidget.setTitle(i18n("Text Colors")) colorWidget.setLayout(QVBoxLayout()) self.regularColor = QColorDialog() self.invertedColor = QColorDialog() self.btn_acbfRegColor = QPushButton(i18n("Regular Text"), self) self.btn_acbfRegColor.clicked.connect(self.slot_change_regular_color) self.btn_acbfInvColor = QPushButton(i18n("Inverted Text"), self) self.btn_acbfInvColor.clicked.connect(self.slot_change_inverted_color) colorWidget.layout().addWidget(self.btn_acbfRegColor) colorWidget.layout().addWidget(self.btn_acbfInvColor) ACBFStyleEditVB.addWidget(colorWidget) ACBFStyleEditVB.addWidget(self.ACBFuseFont) ACBFStyleEditVB.addWidget(self.ACBFFontList) ACBFStyleEditVB.addLayout(acbfFontButtons) ACBFStyleEditVB.addWidget(self.ACBFdefaultFont) ACBFStyleEditVB.addWidget(self.ACBFBold) ACBFStyleEditVB.addWidget(self.ACBFItal) ACBFStyleEditVB.addStretch() ACBFStyle.layout().addWidget(ACBFStyleEdit) ACBFTabwidget = QTabWidget() ACBFTabwidget.addTab(ACBFdocInfo, i18n("Document Info")) ACBFTabwidget.addTab(ACBFAuthorInfo, i18n("Author Info")) ACBFTabwidget.addTab(ACBFStyle, i18n("Style Sheet")) ACBFExportSettings.layout().addWidget(ACBFTabwidget) mainWidget.addTab(ACBFExportSettings, i18n("ACBF")) # Epub export, crop, resize, other questions. EPUBexportSettings = QWidget() EPUBexportSettings.setLayout(QVBoxLayout()) self.EPUBactive = QCheckBox(i18n("Export to EPUB")) EPUBexportSettings.layout().addWidget(self.EPUBactive) self.EPUBgroupResize = comic_export_resize_widget("EPUB") EPUBexportSettings.layout().addWidget(self.EPUBgroupResize) self.EPUBactive.clicked.connect(self.EPUBgroupResize.setEnabled) mainWidget.addTab(EPUBexportSettings, i18n("EPUB")) # For Print. Crop, no resize. TIFFExportSettings = QWidget() TIFFExportSettings.setLayout(QVBoxLayout()) self.TIFFactive = QCheckBox(i18n("Export to TIFF")) TIFFExportSettings.layout().addWidget(self.TIFFactive) self.TIFFgroupResize = comic_export_resize_widget("TIFF") TIFFExportSettings.layout().addWidget(self.TIFFgroupResize) self.TIFFactive.clicked.connect(self.TIFFgroupResize.setEnabled) mainWidget.addTab(TIFFExportSettings, i18n("TIFF")) # SVG, crop, resize, embed vs link. #SVGExportSettings = QWidget() #mainWidget.addTab(SVGExportSettings, i18n("SVG")) """ Add a history item to the acbf version history list. """ def slot_add_history_item(self): newItem = QStandardItem() newItem.setText( str(i18n("v{version}-in this version...")).format( version=str(self.spnACBFVersion.value()))) self.ACBFhistoryModel.appendRow(newItem) """ Get the margins by treating the active selection in a document as the trim area. This allows people to snap selections to a vector or something, and then get the margins. """ def slot_set_margin_from_selection(self): doc = Application.activeDocument() if doc is not None: if doc.selection() is not None: self.spn_marginLeft.setValue(doc.selection().x()) self.spn_marginTop.setValue(doc.selection().y()) self.spn_marginRight.setValue(doc.width() - (doc.selection().x() + doc.selection().width())) self.spn_marginBottom.setValue(doc.height() - (doc.selection().y() + doc.selection().height())) """ Add an author with default values initialised. """ def slot_add_author(self): listItems = [] listItems.append(QStandardItem(i18n("Anon"))) # Nick name listItems.append(QStandardItem(i18n("John"))) # First name listItems.append(QStandardItem()) # Middle name listItems.append(QStandardItem(i18n("Doe"))) # Last name listItems.append(QStandardItem()) # email listItems.append(QStandardItem()) # homepage self.ACBFauthorModel.appendRow(listItems) """ Remove the selected author from the author list. """ def slot_remove_author(self): self.ACBFauthorModel.removeRow( self.ACBFauthorTable.currentIndex().row()) """ Ensure that the drag and drop of authors doesn't mess up the labels. """ def slot_reset_author_row_visual(self): headerLabelList = [] for i in range(self.ACBFauthorTable.verticalHeader().count()): headerLabelList.append(str(i)) for i in range(self.ACBFauthorTable.verticalHeader().count()): logicalI = self.ACBFauthorTable.verticalHeader().logicalIndex(i) headerLabelList[logicalI] = str(i + 1) self.ACBFauthorModel.setVerticalHeaderLabels(headerLabelList) """ Set the style item to the gui item's style. """ def slot_set_style(self): index = self.ACBFStyleClass.currentIndex() if index.isValid(): item = self.ACBFStylesModel.item(index.row()) fontUsed = item.data(role=styleEnum.FONT) if fontUsed is not None: self.ACBFuseFont.setChecked(fontUsed) else: self.ACBFuseFont.setChecked(False) self.font_slot_enable_font_view() fontList = item.data(role=styleEnum.FONTLIST) self.ACBFFontListModel.clear() for font in fontList: NewItem = QStandardItem(font) NewItem.setEditable(True) self.ACBFFontListModel.appendRow(NewItem) self.ACBFdefaultFont.setCurrentText( str(item.data(role=styleEnum.FONTGENERIC))) bold = item.data(role=styleEnum.BOLD) if bold is not None: self.ACBFBold.setChecked(bold) else: self.ACBFBold.setChecked(False) italic = item.data(role=styleEnum.ITALIC) if italic is not None: self.ACBFItal.setChecked(italic) else: self.ACBFItal.setChecked(False) """ Set the gui items to the currently selected style. """ def slot_font_current_style(self): index = self.ACBFStyleClass.currentIndex() if index.isValid(): item = self.ACBFStylesModel.item(index.row()) fontList = [] for row in range(self.ACBFFontListModel.rowCount()): font = self.ACBFFontListModel.item(row) fontList.append(font.text()) item.setData(self.ACBFuseFont.isChecked(), role=styleEnum.FONT) item.setData(fontList, role=styleEnum.FONTLIST) item.setData(self.ACBFdefaultFont.currentText(), role=styleEnum.FONTGENERIC) item.setData(self.ACBFBold.isChecked(), role=styleEnum.BOLD) item.setData(self.ACBFItal.isChecked(), role=styleEnum.ITALIC) self.ACBFStylesModel.setItem(index.row(), item) """ Change the regular color """ def slot_change_regular_color(self): if (self.regularColor.exec_() == QDialog.Accepted): square = QPixmap(32, 32) square.fill(self.regularColor.currentColor()) self.btn_acbfRegColor.setIcon(QIcon(square)) """ change the inverted color """ def slot_change_inverted_color(self): if (self.invertedColor.exec_() == QDialog.Accepted): square = QPixmap(32, 32) square.fill(self.invertedColor.currentColor()) self.btn_acbfInvColor.setIcon(QIcon(square)) def font_slot_enable_font_view(self): self.ACBFFontList.setEnabled(self.ACBFuseFont.isChecked()) self.btn_acbf_remove_font.setEnabled(self.ACBFuseFont.isChecked()) self.btnAcbfAddFont.setEnabled(self.ACBFuseFont.isChecked()) self.ACBFdefaultFont.setEnabled(self.ACBFuseFont.isChecked()) if self.ACBFFontListModel.rowCount() < 2: self.btn_acbf_remove_font.setEnabled(False) def font_slot_add_font(self): NewItem = QStandardItem(QFont().family()) NewItem.setEditable(True) self.ACBFFontListModel.appendRow(NewItem) def font_slot_remove_font(self): index = self.ACBFFontList.currentIndex() if index.isValid(): self.ACBFFontListModel.removeRow(index.row()) if self.ACBFFontListModel.rowCount() < 2: self.btn_acbf_remove_font.setEnabled(False) """ Load the UI values from the config dictionary given. """ def setConfig(self, config): if "cropToGuides" in config.keys(): self.chk_toOutmostGuides.setChecked(config["cropToGuides"]) if "cropLeft" in config.keys(): self.spn_marginLeft.setValue(config["cropLeft"]) if "cropTop" in config.keys(): self.spn_marginTop.setValue(config["cropTop"]) if "cropRight" in config.keys(): self.spn_marginRight.setValue(config["cropRight"]) if "cropBottom" in config.keys(): self.spn_marginBottom.setValue(config["cropBottom"]) if "labelsToRemove" in config.keys(): self.cmbLabelsRemove.setLabels(config["labelsToRemove"]) if "textLayerNames" in config.keys(): self.ln_text_layer_name.setText(", ".join( config["textLayerNames"])) else: self.ln_text_layer_name.setText("text") if "panelLayerNames" in config.keys(): self.ln_panel_layer_name.setText(", ".join( config["panelLayerNames"])) else: self.ln_panel_layer_name.setText("panels") self.CBZgroupResize.set_config(config) if "CBZactive" in config.keys(): self.CBZactive.setChecked(config["CBZactive"]) self.EPUBgroupResize.set_config(config) if "EPUBactive" in config.keys(): self.EPUBactive.setChecked(config["EPUBactive"]) self.TIFFgroupResize.set_config(config) if "TIFFactive" in config.keys(): self.TIFFactive.setChecked(config["TIFFactive"]) if "acbfAuthor" in config.keys(): if isinstance(config["acbfAuthor"], list): for author in config["acbfAuthor"]: listItems = [] listItems.append(QStandardItem(author.get("nickname", ""))) listItems.append( QStandardItem(author.get("first-name", ""))) listItems.append(QStandardItem(author.get("initials", ""))) listItems.append(QStandardItem(author.get("last-name", ""))) listItems.append(QStandardItem(author.get("email", ""))) listItems.append(QStandardItem(author.get("homepage", ""))) self.ACBFauthorModel.appendRow(listItems) pass else: listItems = [] listItems.append(QStandardItem( config["acbfAuthor"])) # Nick name for i in range(0, 5): listItems.append(QStandardItem()) # First name self.ACBFauthorModel.appendRow(listItems) if "uuid" in config.keys(): self.lnACBFID.setText(config["uuid"]) elif "acbfID" in config.keys(): self.lnACBFID.setText(config["acbfID"]) else: config["uuid"] = QUuid.createUuid().toString() self.lnACBFID.setText(config["uuid"]) if "acbfVersion" in config.keys(): self.spnACBFVersion.setValue(config["acbfVersion"]) if "acbfHistory" in config.keys(): for h in config["acbfHistory"]: item = QStandardItem() item.setText(h) self.ACBFhistoryModel.appendRow(item) if "acbfStyles" in config.keys(): styleDict = config.get("acbfStyles", {}) for key in self.acbfStylesList: keyDict = styleDict.get(key, {}) style = QStandardItem(key.title()) style.setCheckable(True) if key in styleDict.keys(): style.setCheckState(Qt.Checked) else: style.setCheckState(Qt.Unchecked) fontOn = False if "font" in keyDict.keys() or "genericfont" in keyDict.keys(): fontOn = True style.setData(fontOn, role=styleEnum.FONT) if "font" in keyDict: fontlist = keyDict["font"] if isinstance(fontlist, list): font = keyDict.get("font", QFont().family()) style.setData(font, role=styleEnum.FONTLIST) else: style.setData([fontlist], role=styleEnum.FONTLIST) else: style.setData([QFont().family()], role=styleEnum.FONTLIST) style.setData(keyDict.get("genericfont", "sans-serif"), role=styleEnum.FONTGENERIC) style.setData(keyDict.get("bold", False), role=styleEnum.BOLD) style.setData(keyDict.get("ital", False), role=styleEnum.ITALIC) self.ACBFStylesModel.appendRow(style) keyDict = styleDict.get("general", {}) self.regularColor.setCurrentColor( QColor(keyDict.get("color", "#000000"))) square = QPixmap(32, 32) square.fill(self.regularColor.currentColor()) self.btn_acbfRegColor.setIcon(QIcon(square)) keyDict = styleDict.get("inverted", {}) self.invertedColor.setCurrentColor( QColor(keyDict.get("color", "#FFFFFF"))) square.fill(self.invertedColor.currentColor()) self.btn_acbfInvColor.setIcon(QIcon(square)) else: for key in self.acbfStylesList: style = QStandardItem(key.title()) style.setCheckable(True) style.setCheckState(Qt.Unchecked) style.setData(False, role=styleEnum.FONT) style.setData(QFont().family(), role=styleEnum.FONTLIST) style.setData("sans-serif", role=styleEnum.FONTGENERIC) style.setData(False, role=styleEnum.BOLD) #Bold style.setData(False, role=styleEnum.ITALIC) #Italic self.ACBFStylesModel.appendRow(style) self.CBZgroupResize.setEnabled(self.CBZactive.isChecked()) self.lnTranslatorHeader.setText( config.get("translatorHeader", "Translator's Notes")) self.chkIncludeTranslatorComments.setChecked( config.get("includeTranslComment", False)) """ Store the GUI values into the config dictionary given. @return the config diactionary filled with new values. """ def getConfig(self, config): config["cropToGuides"] = self.chk_toOutmostGuides.isChecked() config["cropLeft"] = self.spn_marginLeft.value() config["cropTop"] = self.spn_marginTop.value() config["cropBottom"] = self.spn_marginRight.value() config["cropRight"] = self.spn_marginBottom.value() config["labelsToRemove"] = self.cmbLabelsRemove.getLabels() config["CBZactive"] = self.CBZactive.isChecked() config = self.CBZgroupResize.get_config(config) config["EPUBactive"] = self.EPUBactive.isChecked() config = self.EPUBgroupResize.get_config(config) config["TIFFactive"] = self.TIFFactive.isChecked() config = self.TIFFgroupResize.get_config(config) authorList = [] for row in range(self.ACBFauthorTable.verticalHeader().count()): logicalIndex = self.ACBFauthorTable.verticalHeader().logicalIndex( row) listEntries = [ "nickname", "first-name", "initials", "last-name", "email", "homepage" ] author = {} for i in range(len(listEntries)): entry = self.ACBFauthorModel.data( self.ACBFauthorModel.index(logicalIndex, i)) if entry is None: entry = " " if entry.isspace() is False and len(entry) > 0: author[listEntries[i]] = entry elif listEntries[i] in author.keys(): author.pop(listEntries[i]) authorList.append(author) config["acbfAuthor"] = authorList config["acbfVersion"] = self.spnACBFVersion.value() versionList = [] for r in range(self.ACBFhistoryModel.rowCount()): index = self.ACBFhistoryModel.index(r, 0) versionList.append( self.ACBFhistoryModel.data(index, Qt.DisplayRole)) config["acbfHistory"] = versionList acbfStylesDict = {} for row in range(0, self.ACBFStylesModel.rowCount()): entry = self.ACBFStylesModel.item(row) if entry.checkState() == Qt.Checked: key = entry.text().lower() style = {} if entry.data(role=styleEnum.FONT): font = entry.data(role=styleEnum.FONTLIST) if font is not None: style["font"] = font genericfont = entry.data(role=styleEnum.FONTGENERIC) if font is not None: style["genericfont"] = genericfont bold = entry.data(role=styleEnum.BOLD) if bold is not None: style["bold"] = bold italic = entry.data(role=styleEnum.ITALIC) if italic is not None: style["ital"] = italic acbfStylesDict[key] = style acbfStylesDict["general"] = { "color": self.regularColor.currentColor().name() } acbfStylesDict["inverted"] = { "color": self.invertedColor.currentColor().name() } config["acbfStyles"] = acbfStylesDict config["translatorHeader"] = self.lnTranslatorHeader.text() config[ "includeTranslComment"] = self.chkIncludeTranslatorComments.isChecked( ) # Turn this into something that retrieves from a line-edit when string freeze is over. config["textLayerNames"] = self.ln_text_layer_name.text().split(",") config["panelLayerNames"] = self.ln_panel_layer_name.text().split(",") return config
class ErrorVisualizeWidget(QWidget): def __init__(self, parent, mainwindow): super().__init__(parent) self.__widget_mainwindow = mainwindow self.__widget_matplotlib = ErrorVisualizeMatplotlibWidget(self) self.__current_layer = 0 self.__widget_label_wrong_dimension = QLabel("", parent=self) self.__widget_label_layer = QLabel("<b>Layer</b>: ", parent=self) self.__widget_list_layer = QListView(self) self.__widget_list_layer.setModel(QStandardItemModel()) self.__widget_list_layer.clicked.connect(self.set_layer) self.__widget_list_layer.selectionModel().selectionChanged.connect(self.set_layer) if not SDB_HAS_MATPLOTLIB: self.__widget_label_layer.setEnabled(False) self.__widget_list_layer.setEnabled(False) hbox_top = QHBoxLayout() hbox_top.addWidget(self.__widget_matplotlib) vbox_top = QVBoxLayout() vbox_top.addWidget(self.__widget_label_layer) vbox_top.addWidget(self.__widget_list_layer) hbox_top.addLayout(vbox_top) hbox_bottom = QHBoxLayout() hbox_bottom.addWidget(self.__widget_label_wrong_dimension) hbox_bottom.addStretch(1) vbox = QVBoxLayout() vbox.addLayout(hbox_top) vbox.addLayout(hbox_bottom) self.setLayout(vbox) def make_update(self, comparison_result): Logger.info("Updating ErrorVisualizeWidget") if not SDB_HAS_MATPLOTLIB: return self.__error_positions = comparison_result.get_error_positions() if self.__error_positions.ndim in (2, 3): self.__current_layer = 0 self.__widget_matplotlib.draw_layer(self.__error_positions, self.__current_layer) self.__widget_list_layer.model().clear() if self.__error_positions.ndim == 3: self.__widget_label_layer.setEnabled(True) self.__widget_list_layer.setEnabled(True) for k in range(self.__error_positions.shape[2]): item = QStandardItem(" %i" % k) if any(self.__error_positions[:, :, k]): item.setIcon(Icon("failure-small.png")) else: item.setIcon(Icon("success.png")) item.setEditable(False) self.__widget_list_layer.model().insertRow(k, item) else: self.__widget_label_layer.setEnabled(False) self.__widget_list_layer.setEnabled(False) self.__widget_label_wrong_dimension.setText("") else: self.__widget_matplotlib.draw_nothing() self.__widget_label_layer.setEnabled(False) self.__widget_list_layer.model().clear() self.__widget_list_layer.setEnabled(False) self.__widget_label_wrong_dimension.setText( "Visualization is only supported for 2 and 3-dimensional fields.") def set_layer(self, layer): if isinstance(layer, QItemSelection): layer = layer.indexes()[0] layer_idx = layer.row() self.__current_layer = layer_idx self.__widget_matplotlib.draw_layer(self.__error_positions, self.__current_layer)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) # self.labelHistogram = QLabel() # self.labelHistogram.setText("Histogram:") # self.histogram = HistogramWidget() # histogramLayout = QHBoxLayout() # histogramLayout.addWidget(self.labelHistogram) # histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() # self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("打开", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) self.fullScreenButton = QPushButton("全屏") self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("颜色选项") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) controlLayout.addWidget(self.fullScreenButton) controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) # layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning( self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo( "%s - %s" % (self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex(self.playlistModel.index( position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000) totalTime = QTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60, (duration * 1000) % 1000) format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString( format) else: tStr = "" self.labelDuration.setText(tStr) def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("亮度", brightnessSlider) layout.addRow("对比度", contrastSlider) layout.addRow("色调", hueSlider) layout.addRow("饱和度", saturationSlider) button = QPushButton("关闭") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("颜色选项") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.script_box = QPlainTextEdit() self.segmentList = QTreeWidget() self.segmentList.setSortingEnabled(True) #self.segmentList.setColumnCount(5) self.segmentList.setColumnCount(4) #self.segmentList.setHeaderLabels(['Product','Start','Label','Tool','Behavior']) self.segmentList.setHeaderLabels(['Start segment', 'End segment', 'Label', 'Event']) ''' self.productTextInput = QLineEdit() self.startTextInput = QLineEdit() self.labelTextInput = QLineEdit() self.toolTextInput = QLineEdit() self.behaviorTextInput = QLineEdit() ''' self.startTextInput = QLineEdit() self.endTextInput = QLineEdit() self.labelTextInput = QLineEdit() self.contentTextInput = QLineEdit() self.addBtn = QPushButton("Add") self.addBtn.clicked.connect(self.addSegment) self.saveBtn = QPushButton("Save") self.saveBtn.clicked.connect(self.saveSegments) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) self.labelHistogram = QLabel() self.labelHistogram.setText("Histogram:") self.histogram = HistogramWidget() histogramLayout = QHBoxLayout() histogramLayout.addWidget(self.labelHistogram) histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("Open", clicked=self.open) if os.path.isdir(VIDEO_DIR): self.open_folder(VIDEO_DIR) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) #self.segmentButton = QPushButton("Segment") #self.segmentButton.clicked.connect(self.createNewSegment) self.startSegmentButton = QPushButton("Start Segment") self.startSegmentButton.clicked.connect(self.createNewStartSegment) # self.segmentButton.setCheckable(True) self.endSegmentButton = QPushButton("End Segment") self.endSegmentButton.clicked.connect(self.createNewEndSegment) #self.fullScreenButton = QPushButton("FullScreen") #self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("Color Options...") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() # videoLayout = QVBoxLayout() # videoLayout.addWidget(self.videoWidget) # videoLayout.addWidget(self.script_box) displayLayout.addWidget(self.videoWidget, 3) editLayout = QVBoxLayout() editLayout.addWidget(self.playlistView, 2) #editLayout.addWidget(self.script_box, 4) editLayout.addWidget(self.segmentList, 3) segmentInputLayout = QHBoxLayout() ''' segmentInputLayout.addWidget(self.productTextInput) segmentInputLayout.addWidget(self.startTextInput) segmentInputLayout.addWidget(self.labelTextInput) segmentInputLayout.addWidget(self.toolTextInput) segmentInputLayout.addWidget(self.behaviorTextInput) ''' segmentInputLayout.addWidget(self.startTextInput) segmentInputLayout.addWidget(self.endTextInput) segmentInputLayout.addWidget(self.labelTextInput) segmentInputLayout.addWidget(self.contentTextInput) editLayout.addLayout(segmentInputLayout,1) displayLayout.addLayout(editLayout, 2) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) #controlLayout.addWidget(self.segmentButton) controlLayout.addWidget(self.startSegmentButton) controlLayout.addWidget(self.endSegmentButton) controlLayout.addWidget(self.addBtn) controlLayout.addWidget(self.saveBtn) #controlLayout.addWidget(self.fullScreenButton) # controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout, 2) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) # layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) #self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def open_folder(self, folder_path): fileNames = [folder_path+x for x in os.listdir(folder_path) if x.endswith('.mp4')] self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def addSegment(self): item = TreeWidgetItem(self.segmentList) ''' item.setText(0, self.productTextInput.text()) item.setText(1, self.startTextInput.text()) item.setText(2, self.labelTextInput.text()) item.setText(3, self.toolTextInput.text()) item.setText(4, self.behaviorTextInput.text()) ''' item.setText(0, self.startTextInput.text()) item.setText(1, self.endTextInput.text()) item.setText(2, self.labelTextInput.text()) item.setText(3, self.contentTextInput.text()) item.setFlags(item.flags() | Qt.ItemIsEditable) self.segmentList.addTopLevelItem(item) self.segmentList.sortByColumn(0, Qt.AscendingOrder) self.clear_input_boxes() self.player.play() def saveSegments(self): itemCnt = self.segmentList.topLevelItemCount() colCnt = self.segmentList.columnCount() save_dict = {'segments':[]} for i in range(itemCnt): item = self.segmentList.topLevelItem(i) temp_data = [] for j in range(colCnt): temp_data.append(item.text(j)) #temp_dict = {'product': temp_data[0], 'start': temp_data[1], 'label': temp_data[2], 'tool': temp_data[3], 'behavior': temp_data[4]} if len(temp_data[0]) > 0 and len(temp_data[1]) > 0 and (':' in temp_data[0]) and (':' in temp_data[1]): start_interval_seconds = 0 j = 0 while j < len(temp_data[0].split(':')): start_interval_seconds += (int(temp_data[0].split(':')[- 1 - j]) * (60 ** j)) j += 1 end_interval_seconds = 0 j = 0 while j < len(temp_data[1].split(':')): end_interval_seconds += (int(temp_data[1].split(':')[- 1 - j]) * (60 ** j)) j += 1 else: start_interval_seconds = '' end_interval_seconds = '' temp_dict = {'start_segment': start_interval_seconds, 'end_segment': end_interval_seconds, 'label': temp_data[2], 'event': temp_data[3]} save_dict['segments'].append(temp_dict) import json file_name = self.playlist.currentMedia().canonicalUrl().fileName() with open(SEGMENT_DIR+file_name.replace('.mp4','.json'),'w') as file: json.dump(save_dict, file) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def clear_input_boxes(self): ''' self.productTextInput.clear() self.startTextInput.clear() self.labelTextInput.clear() self.toolTextInput.clear() self.behaviorTextInput.clear() ''' self.startTextInput.clear() self.endTextInput.clear() self.labelTextInput.clear() self.contentTextInput.clear() def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() file_name = self.playlist.currentMedia().canonicalUrl().fileName() ''' script_file_name = file_name.replace('.mp4','.txt') if os.path.isfile(SCRIPT_DIR+script_file_name): text=open(SCRIPT_DIR+script_file_name).read() self.script_box.setPlainText(text) ''' segment_file_path = SEGMENT_DIR + file_name.replace('.mp4','.json') json_dict = self.open_json(segment_file_path) self.clear_input_boxes() self.segmentList.clear() for segment in json_dict["segments"]: item = TreeWidgetItem(self.segmentList) ''' item.setText(0, segment['product']) item.setText(1, str(segment['start'])) item.setText(2, segment['label']) item.setText(3, segment['tool']) item.setText(4, segment['behavior']) ''' item.setText(0, segment['start_segment']) item.setText(1, segment['end_segment']) item.setText(2, segment['label']) item.setText(3, segment['content']) item.setFlags(item.flags() | Qt.ItemIsEditable) self.segmentList.addTopLevelItem(item) # print([str(x.text()) for x in self.segmentList.currentItem()]) def open_json(self, file_path): import json try: with open(file_path, 'r') as file: json_dict = json.loads(file.read()) except: json_dict = {"segments":[]} # json_dict = {"segments":[{"product":"Sorry","start":"File not found.","label":"","tool":"","behavior":""}]} return json_dict def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): ''' if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) ''' self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr) ''' def createNewSegment(self): self.startTextInput.setText(str(int(self.player.position()/1000))) ''' def createNewStartSegment(self): seconds = int(self.player.position()/1000) self.startTextInput.setText("{:02d}".format(math.floor(seconds / 3600)) + ':' + "{:02d}".format( math.floor((seconds / 60)) - math.floor(seconds / 3600) * 60) + ':' + "{:02d}".format(seconds % 60)) def createNewEndSegment(self): seconds = int(self.player.position() / 1000) self.endTextInput.setText("{:02d}".format(math.floor(seconds / 3600)) + ':' + "{:02d}".format( math.floor((seconds / 60)) - math.floor(seconds / 3600) * 60) + ':' + "{:02d}".format(seconds % 60)) self.player.pause() def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("Brightness", brightnessSlider) layout.addRow("Contrast", contrastSlider) layout.addRow("Hue", hueSlider) layout.addRow("Saturation", saturationSlider) button = QPushButton("Close") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("Color Options") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()
class MainWindow(QMainWindow): _classes_view = None _current_images = [] _image_dirs_combo_box = None _image_spinner = None _next_image_action = None _prev_image_action = None _progress_bar = None _top_images_dir = None _top_labels_dir = None _workspace = None def openImages(self, directory): image_it = QDirIterator(directory, labeler.IMAGE_FILE_TEMPLATES, QDir.Files, QDirIterator.Subdirectories) self._current_images = [] while image_it.hasNext(): self._current_images.append(image_it.next()) self._current_images.sort() self._image_spinner.setRange(0, 0) self._image_spinner.setEnabled(False) self._image_spinner.setValue(0) image_total = len(self._current_images) if image_total > 0: self._progress_bar.setRange(0, image_total) self._image_spinner.setRange(1, image_total) self._image_spinner.setEnabled(True) self._image_spinner.setValue(1) def openDataDir(self): dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.Directory) dialog.setOption(QFileDialog.ReadOnly) if dialog.exec(): datadir = dialog.selectedFiles()[0] classesdir = labeler.classesDir(datadir) classesdir_it = QDirIterator(classesdir, labeler.IMAGE_FILE_TEMPLATES, QDir.Files) classes = [] while classesdir_it.hasNext(): class_image = classesdir_it.next() match = labeler.CLASSES_RE.match(basename(class_image)) if match != None: class_num = int(match.group('cls')) class_name = match.group('name') class_object = Class(class_num, class_name, QPixmap(class_image)) classes.append(class_object) classes.sort(key = lambda c: c.number) classes_model = ClassListModel(classes) self._classes_view.setModel(classes_model) self._workspace.setModel(classes_model) self._classes_view.setEnabled(len(classes) > 0) selMod = self._classes_view.selectionModel() selMod.currentChanged.connect(self._workspace.setDefaultClass) selMod.setCurrentIndex(classes_model.index(0, 0), QItemSelectionModel.Select) self._top_images_dir = labeler.imagesDir(datadir) self._top_labels_dir = labeler.labelsDir(datadir) imagedir_it = QDirIterator(self._top_images_dir, QDir.AllDirs | QDir.NoDotAndDotDot, QDirIterator.Subdirectories) self._image_dirs_combo_box.clear() self._image_dirs_combo_box.addItem(self._top_images_dir) while imagedir_it.hasNext(): img_dir = imagedir_it.next() self._image_dirs_combo_box.addItem(img_dir) lbl_dir = img_dir.replace(self._top_images_dir, self._top_labels_dir) try: makedirs(lbl_dir) except OSError as ex: if ex.errno != errno.EEXIST: raise def loadImage(self, i): self._prev_image_action.setEnabled(i > 1) self._next_image_action.setEnabled(i < self._image_spinner.maximum()) if 1 <= i <= self._image_spinner.maximum(): image_path = self._current_images[i - 1] label_path = image_path.replace(self._top_images_dir, self._top_labels_dir) + '.txt' self._workspace.loadImage(image_path, label_path) def nextImage(self): self._image_spinner.setValue(self._image_spinner.value() + 1) def prevImage(self): self._image_spinner.setValue(self._image_spinner.value() - 1) def __init__(self): super().__init__() self.setWindowTitle('YOLO Labeler') self.resize(800, 600) self.move(qApp.desktop().availableGeometry().center() - self.frameGeometry().center()) quit_action = QAction('&Exit', self) quit_action.setShortcut('W') quit_action.setStatusTip('Quit application') quit_action.triggered.connect(qApp.quit) open_action = QAction('&Open folder', self) open_action.setShortcut('O') open_action.setStatusTip('Open data directory') open_action.triggered.connect(self.openDataDir) self._prev_image_action = QAction('Prev (&A)', self) self._prev_image_action.setEnabled(False) self._prev_image_action.setShortcut('A') self._prev_image_action.setStatusTip('Show previous image') self._prev_image_action.triggered.connect(self.prevImage) shortcut_prev = QtWidgets.QShortcut(QtGui.QKeySequence('F1'), self) shortcut_prev.activated.connect(self.prevImage) self._next_image_action = QAction('Next (&D)', self) self._next_image_action.setEnabled(False) self._next_image_action.setShortcut('D') self._next_image_action.setStatusTip('Show next image') self._next_image_action.triggered.connect(self.nextImage) shortcut_next = QtWidgets.QShortcut(QtGui.QKeySequence('F2'), self) shortcut_next.activated.connect(self.nextImage) menubar = self.menuBar() filemenu = menubar.addMenu('&File') filemenu.addAction(open_action) actionsmenu = menubar.addMenu('&Actions') actionsmenu.addAction(self._prev_image_action) actionsmenu.addAction(self._next_image_action) quitmenu = menubar.addMenu('&Quit') quitmenu.addAction(quit_action) statusbar = self.statusBar() self._progress_bar = QProgressBar() self._progress_bar.setFormat('%p% of %m') self._image_spinner = QSpinBox() self._image_spinner.setEnabled(False) self._image_spinner.valueChanged.connect(self.loadImage) self._image_spinner.valueChanged.connect(self._progress_bar.setValue) statusbar.addWidget(self._image_spinner) statusbar.addWidget(self._progress_bar) statusbar.show() main_split = QSplitter(Qt.Horizontal) left_side = QWidget(main_split) right_side = QWidget(main_split) right_layout = QVBoxLayout() left_layout = QVBoxLayout() right_side.setLayout(right_layout) left_side.setLayout(left_layout) self._workspace = Workspace() self._classes_view = QListView() self._classes_view.setEnabled(False) self._classes_view.setDragEnabled(True) right_layout.addWidget(self._classes_view) left_layout.addWidget(self._workspace) self._image_dirs_combo_box = QComboBox() left_layout.addWidget(self._image_dirs_combo_box) self._image_dirs_combo_box.currentTextChanged.connect(self.openImages) main_split.setStretchFactor(0, 1) main_split.setStretchFactor(1, 0) self.setCentralWidget(main_split)
class HardwareSelector(QDialog): def __init__(self, host, port, parent=None): QDialog.__init__(self, parent) self.host = host self.port = port self._layout = QVBoxLayout(self) self.setWindowTitle("Hardware on " + self.host + ":" + str(self.port)) self.setMinimumSize(800, 600) self.hardware = None self.selectedHW = None self._model = None self._lbl_status = QLabel() self._layout.addWidget(self._lbl_status) self._layoutH = QHBoxLayout() self._layout.addLayout(self._layoutH) self._hwlist = QListView() self._layoutH.addWidget(self._hwlist) self._lbl_hw = QLabel() self._lbl_hw.setMinimumWidth(400) self._lbl_hw.setMargin(10) self._lbl_hw.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont)) #self._layoutH.addWidget(self._hwlabel) self._hwscroll = QScrollArea() self._hwscroll.setWidget(self._lbl_hw) self._layoutH.addWidget(self._hwscroll) self._layoutB = QHBoxLayout() self._layout.addLayout(self._layoutB) self._btn_select = QPushButton("Select") self._layoutB.addWidget(self._btn_select) self._btn_rescan = QPushButton("Rescan") self._layoutB.addWidget(self._btn_rescan) self._btn_cancel = QPushButton("Cancel") self._layoutB.addWidget(self._btn_cancel) self._btn_select.clicked.connect(self._select) self._btn_cancel.clicked.connect(lambda: (self._quitScan(), self.close())) self._btn_rescan.clicked.connect(self._prepareScan) self._disable() self._start() def _start(self, ret=None): if not ret: self._prepareScan() ret = self._getHardware() if ret: self.hardware = ret[0] if len(self.hardware) == 0: self._lbl_status.setText( "No Hardware detected. Is Caffepath set?") elif ret[1] == 0: self._lbl_status.setText("Currently selected CPU: " + ret[0][0]["name"]) else: self._lbl_status.setText("Currently selected GPU " + str(ret[1]) + ": " + ret[0][ret[1]]["name"]) self._model = self.HardwareListModel(self.hardware) self._hwlist.setModel(self._model) self._hwlist.selectionModel().currentChanged.connect( self._onSelection) self._enable() def _enable(self): self._hwlist.setEnabled(True) self._btn_rescan.setEnabled(True) def _disable(self): self._hwlist.setEnabled(False) self._btn_select.setEnabled(False) self._btn_rescan.setEnabled(False) self._lbl_hw.setText("") if self._hwlist.selectionModel(): self._hwlist.selectionModel().currentChanged.disconnect() self._hwlist.setModel(None) def _select(self): self.selectedHW = self._hwlist.currentIndex().row() self.close() def _onSelection(self): row = self._hwlist.currentIndex().row() if row != -1: self._lbl_hw.setText(self._getText(row)) self._lbl_hw.adjustSize() self._btn_select.setEnabled(True) else: self._lbl_hw.setText("") def _getText(self, row): log = self.hardware[row]["log"] text = "" for l in log: text += l + "\n" return text def _prepareScan(self): self._disable() ct = buildTransaction(self.host, self.port) if ct: self.ct = ct msg = {"key": Protocol.SCANHARDWARE} self.ct.send(msg) self.ct.bufferReady.connect(self._processScan) self.ct.socketClosed.connect(self._socketClosed) self._lbl_status.setText("Start scanning...") else: self._lbl_status.setText("Failed to start Scan.") def _processScan(self): msg = self.ct.asyncRead() if msg["status"]: if "finished" in msg.keys(): if msg["finished"]: self._quitScan() self._lbl_status.setText("Scan finished.") self._start([msg["hardware"], msg["current"]]) else: if "id" in msg.keys(): self._lbl_status.setText("Found GPU " + str(msg["id"]) + ": " + msg["name"]) else: self._lbl_status.setText("Found CPU: " + msg["name"]) else: self._lbl_status.setText("Scan failed: " + msg["error"]) def _socketClosed(self): self._quitScan() self._enable() self._lbl_status.setText("Connection lost!") def _quitScan(self): if hasattr(self, "ct"): self.ct.bufferReady.disconnect() self.ct.socketClosed.disconnect() self.ct.close() del self.ct def _getHardware(self): msg = {"key": Protocol.GETHARDWARE} ret = sendMsgToHost(self.host, self.port, msg) if ret and "hardware" in ret: return [ret["hardware"], ret["current"]] class HardwareListModel(QAbstractListModel): def __init__(self, data): QAbstractListModel.__init__(self) self.data = data def rowCount(self, parent=None, *args, **kwargs): return len(self.data) def data(self, index, role=None): row = index.row() if role == Qt.DisplayRole: if row == 0: return "CPU: " + self.data[row]["name"] return "GPU " + str( self.data[row]["id"]) + ": " + self.data[row]["name"]
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None, add_button = None): super(Player, self).__init__(parent) self.add_button = add_button self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) openButton = QPushButton("Open Audio/Video File", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) # button to add decoder if add_button: add_decoder_btn = QPushButton("Decode Keystrokes of Selected Media") add_decoder_btn.clicked.connect(add_button) controlLayout.addWidget(add_decoder_btn) # controlLayout.addStretch(1) controlLayout.addWidget(controls) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def get_current_file(self): inds = self.playlistView.selectedIndexes() if len(inds) == 1: index = inds[0] location = self.playlistModel.m_playlist.media(index.row()).canonicalUrl() return location.path() def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.setStyleSheet(stylesheet(self)) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.url = "" self. settings = QSettings("QAudioPlayer", "QAudioPlayer") self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.error.connect(self.displayErrorMessage) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() # self.playlistView.setSpacing(1) self.playlistView.setStyleSheet(stylesheet(self)) self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.slider.setStyleSheet(stylesheet(self)) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) openButton = QPushButton("Open", clicked=self.open) openButton.setIcon(openButton.style().standardIcon(QStyle.SP_DialogOpenButton)) clearButton = QPushButton("", clicked=self.clearList) clearButton.setFixedWidth(36) clearButton.setIcon(QIcon.fromTheme("edit-delete")) clearButton.setToolTip("clear List") controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) displayLayout = QHBoxLayout() displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addWidget(clearButton) # controlLayout.addStretch(1) controlLayout.addWidget(controls) # controlLayout.addStretch(1) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) self.statusBar = QStatusBar() vlayout = QVBoxLayout() vlayout.addWidget(self.statusBar) layout.addLayout(vlayout) self.statusBar.showMessage("Welcome") self.setWindowTitle("QAudioPlayer") self.setMinimumSize(300, 200) # self.setBackgroundRole(QPalette.Window) self.setContentsMargins(0,0,0,0) self.setLayout(layout) self.readSettings() if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def readSettings(self): if self.settings.contains("url"): self.url = self.settings.value("url") self.addToPlaylist(self.url) def writeSettings(self): self.settings.setValue("url", self.url) def closeEvent(self, event): print("writing settings") self.writeSettings() print("goodbye ...") event.accept() def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files", "/home", "Audio Files *.mp3 *.m4a *.ogg *.wav *.m3u") if fileNames: self.url = fileNames self.addToPlaylist(fileNames) print("added Files to playlist") def openOnStart(self, name): fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) print("added Files to playlist") def clearList(self): self.playlist.clear() def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.statusBar.showMessage("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.statusBar.showMessage(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.statusBar.showMessage("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.statusBar.showMessage(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr)
class Player(QWidget): fullScreenChanged = pyqtSignal(bool) def __init__(self, playlist, parent=None): super(Player, self).__init__(parent) self.colorDialog = None self.trackInfo = "" self.statusInfo = "" self.duration = 0 self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.durationChanged.connect(self.durationChanged) self.player.positionChanged.connect(self.positionChanged) self.player.metaDataChanged.connect(self.metaDataChanged) self.playlist.currentIndexChanged.connect(self.playlistPositionChanged) self.player.mediaStatusChanged.connect(self.statusChanged) self.player.bufferStatusChanged.connect(self.bufferingProgress) self.player.videoAvailableChanged.connect(self.videoAvailableChanged) self.player.error.connect(self.displayErrorMessage) self.videoWidget = VideoWidget() self.player.setVideoOutput(self.videoWidget) self.playlistModel = PlaylistModel() self.playlistModel.setPlaylist(self.playlist) self.playlistView = QListView() self.playlistView.setModel(self.playlistModel) self.playlistView.setCurrentIndex( self.playlistModel.index(self.playlist.currentIndex(), 0)) self.playlistView.activated.connect(self.jump) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, self.player.duration() / 1000) self.labelDuration = QLabel() self.slider.sliderMoved.connect(self.seek) self.labelHistogram = QLabel() self.labelHistogram.setText("Histogram:") self.histogram = HistogramWidget() histogramLayout = QHBoxLayout() histogramLayout.addWidget(self.labelHistogram) histogramLayout.addWidget(self.histogram, 1) self.probe = QVideoProbe() self.probe.videoFrameProbed.connect(self.histogram.processFrame) self.probe.setSource(self.player) openButton = QPushButton("Open", clicked=self.open) controls = PlayerControls() controls.setState(self.player.state()) controls.setVolume(self.player.volume()) controls.setMuted(controls.isMuted()) controls.play.connect(self.player.play) controls.pause.connect(self.player.pause) controls.stop.connect(self.player.stop) controls.next.connect(self.playlist.next) controls.previous.connect(self.previousClicked) controls.changeVolume.connect(self.player.setVolume) controls.changeMuting.connect(self.player.setMuted) controls.changeRate.connect(self.player.setPlaybackRate) controls.stop.connect(self.videoWidget.update) self.player.stateChanged.connect(controls.setState) self.player.volumeChanged.connect(controls.setVolume) self.player.mutedChanged.connect(controls.setMuted) self.fullScreenButton = QPushButton("FullScreen") self.fullScreenButton.setCheckable(True) self.colorButton = QPushButton("Color Options...") self.colorButton.setEnabled(False) self.colorButton.clicked.connect(self.showColorDialog) displayLayout = QHBoxLayout() displayLayout.addWidget(self.videoWidget, 2) displayLayout.addWidget(self.playlistView) controlLayout = QHBoxLayout() controlLayout.setContentsMargins(0, 0, 0, 0) controlLayout.addWidget(openButton) controlLayout.addStretch(1) controlLayout.addWidget(controls) controlLayout.addStretch(1) controlLayout.addWidget(self.fullScreenButton) controlLayout.addWidget(self.colorButton) layout = QVBoxLayout() layout.addLayout(displayLayout) hLayout = QHBoxLayout() hLayout.addWidget(self.slider) hLayout.addWidget(self.labelDuration) layout.addLayout(hLayout) layout.addLayout(controlLayout) layout.addLayout(histogramLayout) self.setLayout(layout) if not self.player.isAvailable(): QMessageBox.warning(self, "Service not available", "The QMediaPlayer object does not have a valid service.\n" "Please check the media service plugins are installed.") controls.setEnabled(False) self.playlistView.setEnabled(False) openButton.setEnabled(False) self.colorButton.setEnabled(False) self.fullScreenButton.setEnabled(False) self.metaDataChanged() self.addToPlaylist(playlist) def open(self): fileNames, _ = QFileDialog.getOpenFileNames(self, "Open Files") self.addToPlaylist(fileNames) def addToPlaylist(self, fileNames): for name in fileNames: fileInfo = QFileInfo(name) if fileInfo.exists(): url = QUrl.fromLocalFile(fileInfo.absoluteFilePath()) if fileInfo.suffix().lower() == 'm3u': self.playlist.load(url) else: self.playlist.addMedia(QMediaContent(url)) else: url = QUrl(name) if url.isValid(): self.playlist.addMedia(QMediaContent(url)) def durationChanged(self, duration): duration /= 1000 self.duration = duration self.slider.setMaximum(duration) def positionChanged(self, progress): progress /= 1000 if not self.slider.isSliderDown(): self.slider.setValue(progress) self.updateDurationInfo(progress) def metaDataChanged(self): if self.player.isMetaDataAvailable(): self.setTrackInfo("%s - %s" % ( self.player.metaData(QMediaMetaData.AlbumArtist), self.player.metaData(QMediaMetaData.Title))) def previousClicked(self): # Go to the previous track if we are within the first 5 seconds of # playback. Otherwise, seek to the beginning. if self.player.position() <= 5000: self.playlist.previous() else: self.player.setPosition(0) def jump(self, index): if index.isValid(): self.playlist.setCurrentIndex(index.row()) self.player.play() def playlistPositionChanged(self, position): self.playlistView.setCurrentIndex( self.playlistModel.index(position, 0)) def seek(self, seconds): self.player.setPosition(seconds * 1000) def statusChanged(self, status): self.handleCursor(status) if status == QMediaPlayer.LoadingMedia: self.setStatusInfo("Loading...") elif status == QMediaPlayer.StalledMedia: self.setStatusInfo("Media Stalled") elif status == QMediaPlayer.EndOfMedia: QApplication.alert(self) elif status == QMediaPlayer.InvalidMedia: self.displayErrorMessage() else: self.setStatusInfo("") def handleCursor(self, status): if status in (QMediaPlayer.LoadingMedia, QMediaPlayer.BufferingMedia, QMediaPlayer.StalledMedia): self.setCursor(Qt.BusyCursor) else: self.unsetCursor() def bufferingProgress(self, progress): self.setStatusInfo("Buffering %d%" % progress) def videoAvailableChanged(self, available): if available: self.fullScreenButton.clicked.connect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.connect( self.fullScreenButton.setChecked) if self.fullScreenButton.isChecked(): self.videoWidget.setFullScreen(True) else: self.fullScreenButton.clicked.disconnect( self.videoWidget.setFullScreen) self.videoWidget.fullScreenChanged.disconnect( self.fullScreenButton.setChecked) self.videoWidget.setFullScreen(False) self.colorButton.setEnabled(available) def setTrackInfo(self, info): self.trackInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def setStatusInfo(self, info): self.statusInfo = info if self.statusInfo != "": self.setWindowTitle("%s | %s" % (self.trackInfo, self.statusInfo)) else: self.setWindowTitle(self.trackInfo) def displayErrorMessage(self): self.setStatusInfo(self.player.errorString()) def updateDurationInfo(self, currentInfo): duration = self.duration if currentInfo or duration: currentTime = QTime((currentInfo/3600)%60, (currentInfo/60)%60, currentInfo%60, (currentInfo*1000)%1000) totalTime = QTime((duration/3600)%60, (duration/60)%60, duration%60, (duration*1000)%1000); format = 'hh:mm:ss' if duration > 3600 else 'mm:ss' tStr = currentTime.toString(format) + " / " + totalTime.toString(format) else: tStr = "" self.labelDuration.setText(tStr) def showColorDialog(self): if self.colorDialog is None: brightnessSlider = QSlider(Qt.Horizontal) brightnessSlider.setRange(-100, 100) brightnessSlider.setValue(self.videoWidget.brightness()) brightnessSlider.sliderMoved.connect( self.videoWidget.setBrightness) self.videoWidget.brightnessChanged.connect( brightnessSlider.setValue) contrastSlider = QSlider(Qt.Horizontal) contrastSlider.setRange(-100, 100) contrastSlider.setValue(self.videoWidget.contrast()) contrastSlider.sliderMoved.connect(self.videoWidget.setContrast) self.videoWidget.contrastChanged.connect(contrastSlider.setValue) hueSlider = QSlider(Qt.Horizontal) hueSlider.setRange(-100, 100) hueSlider.setValue(self.videoWidget.hue()) hueSlider.sliderMoved.connect(self.videoWidget.setHue) self.videoWidget.hueChanged.connect(hueSlider.setValue) saturationSlider = QSlider(Qt.Horizontal) saturationSlider.setRange(-100, 100) saturationSlider.setValue(self.videoWidget.saturation()) saturationSlider.sliderMoved.connect( self.videoWidget.setSaturation) self.videoWidget.saturationChanged.connect( saturationSlider.setValue) layout = QFormLayout() layout.addRow("Brightness", brightnessSlider) layout.addRow("Contrast", contrastSlider) layout.addRow("Hue", hueSlider) layout.addRow("Saturation", saturationSlider) button = QPushButton("Close") layout.addRow(button) self.colorDialog = QDialog(self) self.colorDialog.setWindowTitle("Color Options") self.colorDialog.setLayout(layout) button.clicked.connect(self.colorDialog.close) self.colorDialog.show()