class LoadingWidget(QLabel): def __init__(self, *args, **kwargs): super(LoadingWidget, self).__init__(*args, **kwargs) self.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self._movie = QMovie("loading.gif") self.setMovie(self._movie) def deleteLater(self): self._movie.stop() self._movie.deleteLater() del self._movie super(LoadingWidget, self).deleteLater() def show(self): self.setVisible(True) super(LoadingWidget, self).show() self._movie.start() def closeEvent(self, event): self._movie.stop() self.setVisible(False) super(LoadingWidget, self).closeEvent(event)
class Load(QWidget): def __init__(self): super().__init__() self.movie = QMovie("load.gif", QByteArray(), self) size = self.size() screenGeometry = QDesktopWidget().screenGeometry(-1).size() self.setGeometry((screenGeometry.width() - size.width()) // 2, (screenGeometry.height() - size.height()) // 2, size.width(), size.height()) self.setWindowTitle('') self.movie_screen = QLabel() self.movie_screen.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.movie_screen.setAlignment(Qt.AlignCenter) main_layout = QVBoxLayout() main_layout.addWidget(self.movie_screen) self.setLayout(main_layout) self.movie.setCacheMode(QMovie.CacheAll) self.movie_screen.setMovie(self.movie) self.frame_count = self.movie.frameCount() self.movie.frameChanged.connect(self.frameChanged) self.frame = 0 self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAttribute(Qt.WA_TranslucentBackground, True) self.movie.start() self.app = MainApp() self.movie.loopCount() def frameChanged(self): self.frame += 1 if self.frame >= self.frame_count: self.movie.stop() self.movie.setParent(None) self.movie.deleteLater() self.setAttribute(Qt.WA_NoSystemBackground, False) self.setAttribute(Qt.WA_TranslucentBackground, False) self.destroy() self.app.show()
class VidCutter(QWidget): def __init__(self, parent): super(VidCutter, self).__init__(parent) self.parent = parent self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.videoWidget = VideoWidget() self.videoService = VideoService(self) QFontDatabase.addApplicationFont( os.path.join(self.getAppPath(), 'fonts', 'DroidSansMono.ttf')) QFontDatabase.addApplicationFont( os.path.join(self.getAppPath(), 'fonts', 'HelveticaNeue.ttf')) qApp.setFont(QFont('Helvetica Neue', 10)) self.clipTimes = [] self.inCut = False self.movieFilename = '' self.movieLoaded = False self.timeformat = 'hh:mm:ss' self.finalFilename = '' self.totalRuntime = 0 self.initIcons() self.initActions() self.toolbar = QToolBar( floatable=False, movable=False, iconSize=QSize(28, 28), toolButtonStyle=Qt.ToolButtonTextUnderIcon, styleSheet= 'QToolBar QToolButton { min-width:82px; margin-left:10px; margin-right:10px; font-size:14px; }' ) self.initToolbar() self.aboutMenu, self.cliplistMenu = QMenu(), QMenu() self.initMenus() self.seekSlider = VideoSlider(parent=self, sliderMoved=self.setPosition) self.seekSlider.installEventFilter(self) self.initNoVideo() self.cliplist = QListWidget( sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding), contextMenuPolicy=Qt.CustomContextMenu, uniformItemSizes=True, iconSize=QSize(100, 700), dragDropMode=QAbstractItemView.InternalMove, alternatingRowColors=True, customContextMenuRequested=self.itemMenu, styleSheet='QListView::item { margin:10px 5px; }') self.cliplist.setFixedWidth(185) self.cliplist.model().rowsMoved.connect(self.syncClipList) listHeader = QLabel(pixmap=QPixmap( os.path.join(self.getAppPath(), 'images', 'clipindex.png'), 'PNG'), alignment=Qt.AlignCenter) listHeader.setStyleSheet( '''padding:5px; padding-top:8px; border:1px solid #b9b9b9; border-bottom:none; background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF, stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);''' ) self.runtimeLabel = QLabel('<div align="right">00:00:00</div>', textFormat=Qt.RichText) self.runtimeLabel.setStyleSheet( '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF; background:rgb(106, 69, 114) url(:images/runtime.png) no-repeat left center; padding:2px; padding-right:8px; border:1px solid #b9b9b9; border-top:none;''' ) self.clipindexLayout = QVBoxLayout(spacing=0) self.clipindexLayout.setContentsMargins(0, 0, 0, 0) self.clipindexLayout.addWidget(listHeader) self.clipindexLayout.addWidget(self.cliplist) self.clipindexLayout.addWidget(self.runtimeLabel) self.videoLayout = QHBoxLayout() self.videoLayout.setContentsMargins(0, 0, 0, 0) self.videoLayout.addWidget(self.novideoWidget) self.videoLayout.addLayout(self.clipindexLayout) self.timeCounter = QLabel('00:00:00 / 00:00:00', autoFillBackground=True, alignment=Qt.AlignCenter, sizePolicy=QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed)) self.timeCounter.setStyleSheet( 'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;' ) videoplayerLayout = QVBoxLayout(spacing=0) videoplayerLayout.setContentsMargins(0, 0, 0, 0) videoplayerLayout.addWidget(self.videoWidget) videoplayerLayout.addWidget(self.timeCounter) self.videoplayerWidget = QWidget(self, visible=False) self.videoplayerWidget.setLayout(videoplayerLayout) self.menuButton = QPushButton(icon=self.aboutIcon, flat=True, toolTip='About', statusTip='About', iconSize=QSize(24, 24), cursor=Qt.PointingHandCursor) self.menuButton.setMenu(self.aboutMenu) self.muteButton = QPushButton(icon=self.unmuteIcon, flat=True, toolTip='Mute', statusTip='Toggle audio mute', cursor=Qt.PointingHandCursor, clicked=self.muteAudio) self.volumeSlider = QSlider(Qt.Horizontal, toolTip='Volume', statusTip='Adjust volume level', cursor=Qt.PointingHandCursor, value=50, sizePolicy=QSizePolicy( QSizePolicy.Fixed, QSizePolicy.Minimum), minimum=0, maximum=100, sliderMoved=self.setVolume) self.volumeSlider.setStyleSheet( '''QSlider::groove:horizontal { height:40px; } QSlider::sub-page:horizontal { border:1px outset #6A4572; background:#6A4572; margin:2px; } QSlider::handle:horizontal { image: url(:images/knob.png) no-repeat top left; width:20px; }''' ) self.saveAction = QPushButton( self.parent, icon=self.saveIcon, text='Save Video', flat=True, toolTip='Save Video', clicked=self.cutVideo, cursor=Qt.PointingHandCursor, iconSize=QSize(30, 30), statusTip='Save video clips merged as a new video file', enabled=False) self.saveAction.setStyleSheet( '''QPushButton { color:#FFF; padding:8px; font-size:12pt; border:1px inset #481953; border-radius:4px; background-color:rgb(106, 69, 114); } QPushButton:!enabled { background-color:rgba(0, 0, 0, 0.1); color:rgba(0, 0, 0, 0.3); border:1px inset #CDCDCD; } QPushButton:hover { background-color:rgba(255, 255, 255, 0.8); color:#444; } QPushButton:pressed { background-color:rgba(218, 218, 219, 0.8); color:#444; }''' ) controlsLayout = QHBoxLayout() controlsLayout.addStretch(1) controlsLayout.addWidget(self.toolbar) controlsLayout.addSpacerItem(QSpacerItem(20, 1)) controlsLayout.addWidget(self.saveAction) controlsLayout.addStretch(1) controlsLayout.addWidget(self.muteButton) controlsLayout.addWidget(self.volumeSlider) controlsLayout.addSpacing(1) controlsLayout.addWidget(self.menuButton) layout = QVBoxLayout() layout.setContentsMargins(10, 10, 10, 4) layout.addLayout(self.videoLayout) layout.addWidget(self.seekSlider) layout.addLayout(controlsLayout) self.setLayout(layout) self.mediaPlayer.setVideoOutput(self.videoWidget) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.error.connect(self.handleError) def initNoVideo(self) -> None: novideoImage = QLabel(alignment=Qt.AlignCenter, autoFillBackground=False, pixmap=QPixmap( os.path.join(self.getAppPath(), 'images', 'novideo.png'), 'PNG'), sizePolicy=QSizePolicy( QSizePolicy.Expanding, QSizePolicy.MinimumExpanding)) novideoImage.setBackgroundRole(QPalette.Dark) novideoImage.setContentsMargins(0, 20, 0, 20) self.novideoLabel = QLabel(alignment=Qt.AlignCenter, autoFillBackground=True, sizePolicy=QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Minimum)) self.novideoLabel.setBackgroundRole(QPalette.Dark) self.novideoLabel.setContentsMargins(0, 20, 15, 60) novideoLayout = QVBoxLayout(spacing=0) novideoLayout.addWidget(novideoImage) novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop) self.novideoMovie = QMovie( os.path.join(self.getAppPath(), 'images', 'novideotext.gif')) self.novideoMovie.frameChanged.connect(self.setNoVideoText) self.novideoMovie.start() self.novideoWidget = QWidget(self, autoFillBackground=True) self.novideoWidget.setBackgroundRole(QPalette.Dark) self.novideoWidget.setLayout(novideoLayout) def initIcons(self) -> None: self.appIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'vidcutter.png')) self.openIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'addmedia.png')) self.playIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'play.png')) self.pauseIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'pause.png')) self.cutStartIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'cut-start.png')) self.cutEndIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'cut-end.png')) self.saveIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'save.png')) self.muteIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'muted.png')) self.unmuteIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'unmuted.png')) self.upIcon = QIcon(os.path.join(self.getAppPath(), 'images', 'up.png')) self.downIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'down.png')) self.removeIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'remove.png')) self.removeAllIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'remove-all.png')) self.successIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'success.png')) self.aboutIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'about.png')) self.completePlayIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'complete-play.png')) self.completeOpenIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'complete-open.png')) self.completeRestartIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'complete-restart.png')) self.completeExitIcon = QIcon( os.path.join(self.getAppPath(), 'images', 'complete-exit.png')) def initActions(self) -> None: self.openAction = QAction(self.openIcon, 'Add Media', self, statusTip='Select media source', triggered=self.openFile) self.playAction = QAction(self.playIcon, 'Play Video', self, statusTip='Play selected media', triggered=self.playVideo, enabled=False) self.cutStartAction = QAction(self.cutStartIcon, 'Set Start', self, toolTip='Set Start', statusTip='Set start marker', triggered=self.cutStart, enabled=False) self.cutEndAction = QAction(self.cutEndIcon, 'Set End', self, statusTip='Set end marker', triggered=self.cutEnd, enabled=False) self.moveItemUpAction = QAction( self.upIcon, 'Move Up', self, statusTip='Move clip position up in list', triggered=self.moveItemUp, enabled=False) self.moveItemDownAction = QAction( self.downIcon, 'Move Down', self, statusTip='Move clip position down in list', triggered=self.moveItemDown, enabled=False) self.removeItemAction = QAction( self.removeIcon, 'Remove clip', self, statusTip='Remove selected clip from list', triggered=self.removeItem, enabled=False) self.removeAllAction = QAction(self.removeAllIcon, 'Clear list', self, statusTip='Clear all clips from list', triggered=self.clearList, enabled=False) self.aboutAction = QAction('About %s' % qApp.applicationName(), self, statusTip='Credits and acknowledgements', triggered=self.aboutInfo) self.aboutQtAction = QAction('About Qt', self, statusTip='About Qt', triggered=qApp.aboutQt) self.mediaInfoAction = QAction( 'Media Information', self, statusTip='Media information from loaded video file', triggered=self.mediaInfo, enabled=False) def initToolbar(self) -> None: self.toolbar.addAction(self.openAction) self.toolbar.addAction(self.playAction) self.toolbar.addSeparator() self.toolbar.addAction(self.cutStartAction) self.toolbar.addAction(self.cutEndAction) self.toolbar.addSeparator() def initMenus(self) -> None: self.aboutMenu.addAction(self.mediaInfoAction) self.aboutMenu.addSeparator() self.aboutMenu.addAction(self.aboutQtAction) self.aboutMenu.addAction(self.aboutAction) self.cliplistMenu.addAction(self.moveItemUpAction) self.cliplistMenu.addAction(self.moveItemDownAction) self.cliplistMenu.addSeparator() self.cliplistMenu.addAction(self.removeItemAction) self.cliplistMenu.addAction(self.removeAllAction) def setRunningTime(self, runtime: str) -> None: self.runtimeLabel.setText('<div align="right">%s</div>' % runtime) @pyqtSlot(int) def setNoVideoText(self, frame: int) -> None: self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap()) def itemMenu(self, pos: QPoint) -> None: globalPos = self.cliplist.mapToGlobal(pos) self.moveItemUpAction.setEnabled(False) self.moveItemDownAction.setEnabled(False) self.removeItemAction.setEnabled(False) self.removeAllAction.setEnabled(False) index = self.cliplist.currentRow() if index != -1: if not self.inCut: if index > 0: self.moveItemUpAction.setEnabled(True) if index < self.cliplist.count() - 1: self.moveItemDownAction.setEnabled(True) if self.cliplist.count() > 0: self.removeItemAction.setEnabled(True) if self.cliplist.count() > 0: self.removeAllAction.setEnabled(True) self.cliplistMenu.exec_(globalPos) def moveItemUp(self) -> None: index = self.cliplist.currentRow() tmpItem = self.clipTimes[index] del self.clipTimes[index] self.clipTimes.insert(index - 1, tmpItem) self.renderTimes() def moveItemDown(self) -> None: index = self.cliplist.currentRow() tmpItem = self.clipTimes[index] del self.clipTimes[index] self.clipTimes.insert(index + 1, tmpItem) self.renderTimes() def removeItem(self) -> None: index = self.cliplist.currentRow() del self.clipTimes[index] if self.inCut and index == self.cliplist.count() - 1: self.inCut = False self.initMediaControls() self.renderTimes() def clearList(self) -> None: self.clipTimes.clear() self.cliplist.clear() self.inCut = False self.renderTimes() self.initMediaControls() def mediaInfo(self) -> None: if self.mediaPlayer.isMetaDataAvailable(): content = '<table cellpadding="4">' for key in self.mediaPlayer.availableMetaData(): val = self.mediaPlayer.metaData(key) if type(val) is QSize: val = '%s x %s' % (val.width(), val.height()) content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % ( key, val) content += '</table>' mbox = QMessageBox(windowTitle='Media Information', windowIcon=self.parent.windowIcon(), textFormat=Qt.RichText) mbox.setText('<b>%s</b>' % os.path.basename( self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile())) mbox.setInformativeText(content) mbox.exec_() else: QMessageBox.critical( self.parent, 'Could not retrieve media information', '''There was a problem in tring to retrieve media information. This DOES NOT mean there is a problem with the file and you should be able to continue using it.''') def aboutInfo(self) -> None: about_html = '''<style> a { color:#441d4e; text-decoration:none; font-weight:bold; } a:hover { text-decoration:underline; } </style> <p style="font-size:26pt; font-weight:bold;">%s</p> <p> <span style="font-size:13pt;"><b>Version: %s</b></span> <span style="font-size:10pt;position:relative;left:5px;">( %s )</span> </p> <p style="font-size:13px;"> Copyright © 2016 <a href="mailto:[email protected]">Pete Alexandrou</a> <br/> Website: <a href="%s">%s</a> </p> <p style="font-size:13px;"> Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b> projects for all their hard and much appreciated work. </p> <p style="font-size:11px;"> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. </p> <p style="font-size:11px;"> This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a> </p>''' % (qApp.applicationName(), qApp.applicationVersion(), platform.architecture()[0], qApp.organizationDomain(), qApp.organizationDomain()) QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(), about_html) def openFile(self) -> None: filename, _ = QFileDialog.getOpenFileName(self.parent, caption='Select video', directory=QDir.homePath()) if filename != '': self.loadFile(filename) def loadFile(self, filename: str) -> None: self.movieFilename = filename if not os.path.exists(filename): return self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.initMediaControls(True) self.cliplist.clear() self.clipTimes = [] self.parent.setWindowTitle( '%s - %s' % (qApp.applicationName(), os.path.basename(filename))) if not self.movieLoaded: self.videoLayout.replaceWidget(self.novideoWidget, self.videoplayerWidget) self.novideoMovie.stop() self.novideoMovie.deleteLater() self.novideoWidget.deleteLater() self.videoplayerWidget.show() self.videoWidget.show() self.movieLoaded = True if self.mediaPlayer.isVideoAvailable(): self.mediaPlayer.setPosition(1) self.mediaPlayer.play() self.mediaPlayer.pause() def playVideo(self) -> None: if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() self.playAction.setText('Play Video') else: self.mediaPlayer.play() self.playAction.setText('Pause Video') def initMediaControls(self, flag: bool = True) -> None: self.playAction.setEnabled(flag) self.saveAction.setEnabled(False) self.cutStartAction.setEnabled(flag) self.cutEndAction.setEnabled(False) self.mediaInfoAction.setEnabled(flag) if flag: self.seekSlider.setRestrictValue(0) def setPosition(self, position: int) -> None: self.mediaPlayer.setPosition(position) def positionChanged(self, progress: int) -> None: self.seekSlider.setValue(progress) currentTime = self.deltaToQTime(progress) totalTime = self.deltaToQTime(self.mediaPlayer.duration()) self.timeCounter.setText('%s / %s' % (currentTime.toString( self.timeformat), totalTime.toString(self.timeformat))) @pyqtSlot() def mediaStateChanged(self) -> None: if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playAction.setIcon(self.pauseIcon) else: self.playAction.setIcon(self.playIcon) def durationChanged(self, duration: int) -> None: self.seekSlider.setRange(0, duration) def muteAudio(self, muted: bool) -> None: if self.mediaPlayer.isMuted(): self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted()) self.muteButton.setIcon(self.unmuteIcon) self.muteButton.setToolTip('Mute') else: self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted()) self.muteButton.setIcon(self.muteIcon) self.muteButton.setToolTip('Unmute') def setVolume(self, volume: int) -> None: self.mediaPlayer.setVolume(volume) def toggleFullscreen(self) -> None: self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen()) def cutStart(self) -> None: self.clipTimes.append([ self.deltaToQTime(self.mediaPlayer.position()), '', self.captureImage() ]) self.cutStartAction.setDisabled(True) self.cutEndAction.setEnabled(True) self.seekSlider.setRestrictValue(self.seekSlider.value() + 1000) self.mediaPlayer.setPosition(self.seekSlider.restrictValue) self.inCut = True self.renderTimes() def cutEnd(self) -> None: item = self.clipTimes[len(self.clipTimes) - 1] selected = self.deltaToQTime(self.mediaPlayer.position()) if selected.__lt__(item[0]): QMessageBox.critical( self.parent, 'Invalid END Time', 'The clip end time must come AFTER it\'s start time. Please try again.' ) return item[1] = selected self.cutStartAction.setEnabled(True) self.cutEndAction.setDisabled(True) self.seekSlider.setRestrictValue(0) self.inCut = False self.renderTimes() @pyqtSlot(QModelIndex, int, int, QModelIndex, int) def syncClipList(self, parent: QModelIndex, start: int, end: int, destination: QModelIndex, row: int) -> None: if start < row: index = row - 1 else: index = row clip = self.clipTimes.pop(start) self.clipTimes.insert(index, clip) def renderTimes(self) -> None: self.cliplist.clear() self.seekSlider.setCutMode(self.inCut) if len(self.clipTimes) > 4: self.cliplist.setFixedWidth(200) else: self.cliplist.setFixedWidth(185) self.totalRuntime = 0 for item in self.clipTimes: endItem = '' if type(item[1]) is QTime: endItem = item[1].toString(self.timeformat) self.totalRuntime += item[0].msecsTo(item[1]) listitem = QListWidgetItem() listitem.setTextAlignment(Qt.AlignVCenter) if type(item[2]) is QPixmap: listitem.setIcon(QIcon(item[2])) self.cliplist.addItem(listitem) marker = QLabel( '''<style>b { font-size:8pt; } p { margin:5px; }</style> <p><b>START</b><br/>%s</p><p><b>END</b><br/>%s</p>''' % (item[0].toString(self.timeformat), endItem)) self.cliplist.setItemWidget(listitem, marker) listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled) if len(self.clipTimes) and not self.inCut: self.saveAction.setEnabled(True) if self.inCut or len(self.clipTimes) == 0 or not type( self.clipTimes[0][1]) is QTime: self.saveAction.setEnabled(False) self.setRunningTime( self.deltaToQTime(self.totalRuntime).toString(self.timeformat)) @staticmethod def deltaToQTime(millisecs: int) -> QTime: secs = millisecs / 1000 return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60, (secs * 1000) % 1000) def captureImage(self) -> None: frametime = self.deltaToQTime( self.mediaPlayer.position()).addSecs(1).toString(self.timeformat) inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile( ) imagecap = self.videoService.capture(inputfile, frametime) if type(imagecap) is QPixmap: return imagecap def cutVideo(self) -> bool: self.setCursor(Qt.BusyCursor) clips = len(self.clipTimes) filename, filelist = '', [] source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile() _, sourceext = os.path.splitext(source) if clips > 0: self.finalFilename, _ = QFileDialog.getSaveFileName( self.parent, 'Save video', source, 'Video files (*%s)' % sourceext) if self.finalFilename != '': self.saveAction.setDisabled(True) self.showProgress(clips) file, ext = os.path.splitext(self.finalFilename) index = 1 self.progress.setLabelText('Cutting video clips...') qApp.processEvents() for clip in self.clipTimes: duration = self.deltaToQTime(clip[0].msecsTo( clip[1])).toString(self.timeformat) filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext) filelist.append(filename) self.videoService.cut(source, filename, clip[0].toString(self.timeformat), duration) index += 1 if len(filelist) > 1: self.joinVideos(filelist, self.finalFilename) else: QFile.remove(self.finalFilename) QFile.rename(filename, self.finalFilename) self.unsetCursor() self.progress.setLabelText('Complete...') qApp.processEvents() self.saveAction.setEnabled(True) self.progress.close() self.progress.deleteLater() self.complete() self.saveAction.setEnabled(True) self.unsetCursor() self.saveAction.setDisabled(True) return True self.unsetCursor() self.saveAction.setDisabled(True) return False def joinVideos(self, joinlist: list, filename: str) -> None: listfile = os.path.normpath( os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list')) fobj = open(listfile, 'w') for file in joinlist: fobj.write('file \'%s\'\n' % file.replace("'", "\\'")) fobj.close() self.videoService.join(listfile, filename) try: QFile.remove(listfile) for file in joinlist: if os.path.isfile(file): QFile.remove(file) except: pass def showProgress(self, steps: int, label: str = 'Processing video...') -> None: self.progress = QProgressDialog(label, None, 0, steps, self.parent, windowModality=Qt.ApplicationModal, windowIcon=self.parent.windowIcon(), minimumDuration=0, minimumWidth=500) self.progress.show() for i in range(steps): self.progress.setValue(i) qApp.processEvents() time.sleep(1) def complete(self) -> None: info = QFileInfo(self.finalFilename) mbox = QMessageBox(windowTitle='Success', windowIcon=self.parent.windowIcon(), minimumWidth=500, iconPixmap=self.successIcon.pixmap(48, 49), textFormat=Qt.RichText) mbox.setText( ''' <style> table.info { margin:8px; padding:4px 15px; } td.label { font-weight:bold; font-size:9pt; text-align:right; background-color:#444; color:#FFF; } td.value { background-color:#FFF !important; font-size:10pt; } </style> <p>Your video was successfully created.</p> <p align="center"> <table class="info" cellpadding="6" cellspacing="0"> <tr> <td class="label"><b>Filename</b></td> <td class="value" nowrap>%s</td> </tr> <tr> <td class="label"><b>Size</b></td> <td class="value">%s</td> </tr> <tr> <td class="label"><b>Runtime</b></td> <td class="value">%s</td> </tr> </table> </p> <p>How would you like to proceed?</p>''' % (QDir.toNativeSeparators( self.finalFilename), self.sizeof_fmt(int(info.size())), self.deltaToQTime(self.totalRuntime).toString(self.timeformat))) play = mbox.addButton('Play', QMessageBox.AcceptRole) play.setIcon(self.completePlayIcon) play.clicked.connect(self.openResult) fileman = mbox.addButton('Open', QMessageBox.AcceptRole) fileman.setIcon(self.completeOpenIcon) fileman.clicked.connect(self.openFolder) end = mbox.addButton('Exit', QMessageBox.AcceptRole) end.setIcon(self.completeExitIcon) end.clicked.connect(self.close) new = mbox.addButton('Restart', QMessageBox.AcceptRole) new.setIcon(self.completeRestartIcon) new.clicked.connect(self.startNew) mbox.setDefaultButton(new) mbox.setEscapeButton(new) mbox.exec_() def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str: for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 return "%.1f%s%s" % (num, 'Y', suffix) @pyqtSlot() def openFolder(self) -> None: self.openResult(pathonly=True) @pyqtSlot(bool) def openResult(self, pathonly: bool = False) -> None: self.startNew() if len(self.finalFilename) and os.path.exists(self.finalFilename): target = self.finalFilename if not pathonly else os.path.dirname( self.finalFilename) QDesktopServices.openUrl(QUrl.fromLocalFile(target)) @pyqtSlot() def startNew(self) -> None: self.unsetCursor() self.clearList() self.seekSlider.setValue(0) self.seekSlider.setRange(0, 0) self.mediaPlayer.setMedia(QMediaContent()) self.initNoVideo() self.videoLayout.replaceWidget(self.videoplayerWidget, self.novideoWidget) self.initMediaControls(False) self.parent.setWindowTitle('%s' % qApp.applicationName()) def wheelEvent(self, event: QWheelEvent) -> None: if self.mediaPlayer.isVideoAvailable( ) or self.mediaPlayer.isAudioAvailable(): if event.angleDelta().y() > 0: newval = self.seekSlider.value() - 1000 else: newval = self.seekSlider.value() + 1000 self.seekSlider.setValue(newval) self.seekSlider.setSliderPosition(newval) self.mediaPlayer.setPosition(newval) event.accept() def keyPressEvent(self, event: QKeyEvent) -> None: if self.mediaPlayer.isVideoAvailable( ) or self.mediaPlayer.isAudioAvailable(): addtime = 0 if event.key() == Qt.Key_Left: addtime = -1000 elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up: addtime = -10000 elif event.key() == Qt.Key_Right: addtime = 1000 elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down: addtime = 10000 elif event.key() == Qt.Key_Enter: self.toggleFullscreen() elif event.key( ) == Qt.Key_Escape and self.videoWidget.isFullScreen(): self.videoWidget.setFullScreen(False) if addtime != 0: newval = self.seekSlider.value() + addtime self.seekSlider.setValue(newval) self.seekSlider.setSliderPosition(newval) self.mediaPlayer.setPosition(newval) event.accept() def mousePressEvent(self, event: QMouseEvent) -> None: if event.button() == Qt.BackButton and self.cutStartAction.isEnabled(): self.cutStart() event.accept() elif event.button( ) == Qt.ForwardButton and self.cutEndAction.isEnabled(): self.cutEnd() event.accept() else: super(VidCutter, self).mousePressEvent(event) def eventFilter(self, obj: QObject, event: QEvent) -> bool: if event.type() == QEvent.MouseButtonRelease and isinstance( obj, VideoSlider): if obj.objectName() == 'VideoSlider' and ( self.mediaPlayer.isVideoAvailable() or self.mediaPlayer.isAudioAvailable()): obj.setValue( QStyle.sliderValueFromPosition(obj.minimum(), obj.maximum(), event.x(), obj.width())) self.mediaPlayer.setPosition(obj.sliderPosition()) return QWidget.eventFilter(self, obj, event) @pyqtSlot(QMediaPlayer.Error) def handleError(self, error: QMediaPlayer.Error) -> None: self.unsetCursor() self.startNew() if error == QMediaPlayer.ResourceError: QMessageBox.critical( self.parent, 'Error', 'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s' % (self.movieFilename, self.mediaPlayer.errorString())) else: QMessageBox.critical(self.parent, 'Error', self.mediaPlayer.errorString()) def getAppPath(self) -> str: return ':' def closeEvent(self, event: QCloseEvent) -> None: self.parent.closeEvent(event)
class UIConfiguracionesModal(modal): def __init__(self,**kwargs): super(UIConfiguracionesModal,self).__init__(**kwargs) def setupUI(self): ConfiguracionesModal = self ConfiguracionesModal.setObjectName("ConfiguracionesModal") ConfiguracionesModal.setWindowModality(QtCore.Qt.WindowModal) ConfiguracionesModal.resize(497, 720) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(ConfiguracionesModal.sizePolicy().hasHeightForWidth()) ConfiguracionesModal.setSizePolicy(sizePolicy) ConfiguracionesModal.setMinimumSize(QtCore.QSize(480, 720)) font = QtGui.QFont() font.setFamily("Noto Serif") font.setPointSize(11) font.setBold(True) font.setWeight(75) ConfiguracionesModal.setFont(font) ConfiguracionesModal.setContextMenuPolicy(QtCore.Qt.NoContextMenu) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../../../../../.designer/if_16_1751363.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off) ConfiguracionesModal.setWindowIcon(icon) ConfiguracionesModal.setStyleSheet("background-color: rgb(255, 255, 255);") ConfiguracionesModal.setInputMethodHints(QtCore.Qt.ImhSensitiveData) self.verticalLayout = QtWidgets.QVBoxLayout(ConfiguracionesModal) self.verticalLayout.setObjectName("verticalLayout") self.MainFrame = QtWidgets.QFrame(ConfiguracionesModal) self.MainFrame.setMinimumSize(QtCore.QSize(360, 0)) self.MainFrame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.MainFrame.setFrameShadow(QtWidgets.QFrame.Raised) self.MainFrame.setObjectName("MainFrame") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.MainFrame) self.verticalLayout_2.setContentsMargins(0, 0, 0, 20) self.verticalLayout_2.setSpacing(0) self.verticalLayout_2.setObjectName("verticalLayout_2") self.TitleFrame = QtWidgets.QFrame(self.MainFrame) self.TitleFrame.setMaximumSize(QtCore.QSize(16777215, 16777215)) self.TitleFrame.setFrameShape(QtWidgets.QFrame.NoFrame) self.TitleFrame.setFrameShadow(QtWidgets.QFrame.Plain) self.TitleFrame.setObjectName("TitleFrame") self.gridLayout = QtWidgets.QGridLayout(self.TitleFrame) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName("gridLayout") self.lblUDB = QtWidgets.QLabel(self.TitleFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lblUDB.sizePolicy().hasHeightForWidth()) self.lblUDB.setSizePolicy(sizePolicy) self.lblUDB.setMinimumSize(QtCore.QSize(0, 116)) self.lblUDB.setMaximumSize(QtCore.QSize(16777215, 116)) self.lblUDB.setStyleSheet("background-color: rgb(65, 105, 225);\n" "margin:0px;") self.lblUDB.setFrameShape(QtWidgets.QFrame.NoFrame) self.lblUDB.setText("") self.lblUDB.setPixmap(QtGui.QPixmap(":/source/img/logo.png")) self.lblUDB.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lblUDB.setObjectName("lblUDB") self.gridLayout.addWidget(self.lblUDB, 0, 0, 1, 1) self.ExitFrame = QtWidgets.QFrame(self.TitleFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.ExitFrame.sizePolicy().hasHeightForWidth()) self.ExitFrame.setSizePolicy(sizePolicy) self.ExitFrame.setMinimumSize(QtCore.QSize(0, 116)) self.ExitFrame.setMaximumSize(QtCore.QSize(16777215, 116)) self.ExitFrame.setStyleSheet("background-color: rgb(65, 105, 225);") self.ExitFrame.setFrameShape(QtWidgets.QFrame.NoFrame) self.ExitFrame.setFrameShadow(QtWidgets.QFrame.Plain) self.ExitFrame.setLineWidth(0) self.ExitFrame.setObjectName("ExitFrame") self.gridLayout_4 = QtWidgets.QGridLayout(self.ExitFrame) self.gridLayout_4.setContentsMargins(0, 0, 0, 0) self.gridLayout_4.setSpacing(0) self.gridLayout_4.setObjectName("gridLayout_4") self.btnExit = QtWidgets.QPushButton(self.ExitFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.btnExit.sizePolicy().hasHeightForWidth()) self.btnExit.setSizePolicy(sizePolicy) self.btnExit.setMaximumSize(QtCore.QSize(32, 32)) self.btnExit.setLayoutDirection(QtCore.Qt.RightToLeft) self.btnExit.setText("") icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap(":/source/img/Cancelar.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.btnExit.setIcon(icon1) self.btnExit.setIconSize(QtCore.QSize(24, 24)) self.btnExit.setFlat(True) self.btnExit.setObjectName("btnExit") self.gridLayout_4.addWidget(self.btnExit, 0, 1, 1, 1) self.lblIIE = QtWidgets.QLabel(self.ExitFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lblIIE.sizePolicy().hasHeightForWidth()) self.lblIIE.setSizePolicy(sizePolicy) self.lblIIE.setMaximumSize(QtCore.QSize(16777215, 116)) self.lblIIE.setStyleSheet("background-color: rgb(65, 105, 225);\n" "margin:0px;") self.lblIIE.setText("") self.lblIIE.setPixmap(QtGui.QPixmap(":/source/img/iiie.png")) self.lblIIE.setScaledContents(False) self.lblIIE.setAlignment(QtCore.Qt.AlignCenter) self.lblIIE.setObjectName("lblIIE") self.gridLayout_4.addWidget(self.lblIIE, 0, 0, 2, 1) self.gridLayout.addWidget(self.ExitFrame, 0, 2, 1, 1) self.lblSCADA = QtWidgets.QLabel(self.TitleFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lblSCADA.sizePolicy().hasHeightForWidth()) self.lblSCADA.setSizePolicy(sizePolicy) self.lblSCADA.setMinimumSize(QtCore.QSize(250, 116)) self.lblSCADA.setMaximumSize(QtCore.QSize(16777215, 116)) font = QtGui.QFont() font.setPointSize(21) font.setBold(True) font.setWeight(75) self.lblSCADA.setFont(font) self.lblSCADA.setAutoFillBackground(False) self.lblSCADA.setStyleSheet("color: rgb(255, 255, 0);background-color: rgb(65, 105, 225);\n" "margin:0px;") self.lblSCADA.setObjectName("lblSCADA") self.gridLayout.addWidget(self.lblSCADA, 0, 1, 1, 1) self.verticalLayout_2.addWidget(self.TitleFrame) self.lblTitle = QtWidgets.QLabel(self.MainFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.lblTitle.sizePolicy().hasHeightForWidth()) self.lblTitle.setSizePolicy(sizePolicy) self.lblTitle.setMaximumSize(QtCore.QSize(16777215, 51)) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(14) font.setBold(True) font.setWeight(75) self.lblTitle.setFont(font) self.lblTitle.setStyleSheet("margin-top:5px;") self.lblTitle.setAlignment(QtCore.Qt.AlignCenter) self.lblTitle.setObjectName("lblTitle") self.verticalLayout_2.addWidget(self.lblTitle) self.ContentBox = QtWidgets.QGroupBox(self.MainFrame) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.ContentBox.sizePolicy().hasHeightForWidth()) self.ContentBox.setSizePolicy(sizePolicy) self.ContentBox.setMinimumSize(QtCore.QSize(401, 520)) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(11) font.setBold(True) font.setWeight(75) self.ContentBox.setFont(font) self.ContentBox.setStyleSheet("background-color: rgb(255, 255, 255);") self.ContentBox.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignJustify) self.ContentBox.setFlat(False) self.ContentBox.setObjectName("ContentBox") self.ContentLayout = QtWidgets.QVBoxLayout(self.ContentBox) self.ContentLayout.setContentsMargins(10, 10, 10, 10) self.ContentLayout.setSpacing(10) self.ContentLayout.setObjectName("ContentLayout") self.ContentAPILocal = QtWidgets.QGroupBox() font = QtGui.QFont() font.setFamily("Roboto") font.setBold(True) font.setWeight(75) self.ContentAPILocal.setFont(font) self.ContentAPILocal.setObjectName("ContentAPILocal") self.ContentLocalLayout = QtWidgets.QGridLayout(self.ContentAPILocal) self.ContentLocalLayout.setContentsMargins(0, 0, 0, 0) self.ContentLocalLayout.setSpacing(0) self.ContentLocalLayout.setObjectName("ContentLocalLayout") self.lblIPLocal = QtWidgets.QLabel(self.ContentAPILocal) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblIPLocal.setFont(font) self.lblIPLocal.setLineWidth(0) self.lblIPLocal.setObjectName("lblIPLocal") self.ContentLocalLayout.addWidget(self.lblIPLocal, 0, 0, 1, 1) self.txtIPLocal = QtWidgets.QLineEdit(self.ContentAPILocal) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtIPLocal.setFont(font) self.txtIPLocal.setStyleSheet("border-bottom:1px solid black;border-top:none;") self.txtIPLocal.setObjectName("txtIPLocal") self.ContentLocalLayout.addWidget(self.txtIPLocal, 0, 1, 1, 1) self.lblPuertoLocal = QtWidgets.QLabel(self.ContentAPILocal) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblPuertoLocal.setFont(font) self.lblPuertoLocal.setLineWidth(0) self.lblPuertoLocal.setObjectName("lblPuertoLocal") self.ContentLocalLayout.addWidget(self.lblPuertoLocal, 0, 2, 1, 1) self.txtPuertoLocal = QtWidgets.QLineEdit(self.ContentAPILocal) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtPuertoLocal.setFont(font) self.txtPuertoLocal.setStyleSheet("border-bottom:1px solid black;border-top:none;") self.txtPuertoLocal.setObjectName("txtPuertoLocal") self.ContentLocalLayout.addWidget(self.txtPuertoLocal, 0, 3, 1, 1) self.ContentMongoDB = QtWidgets.QGroupBox() #asd font = QtGui.QFont() font.setBold(True) font.setWeight(75) self.ContentMongoDB.setFont(font) self.ContentMongoDB.setObjectName("ContentMongoDB") self.ContentMongoLayout = QtWidgets.QGridLayout(self.ContentMongoDB) self.ContentMongoLayout.setContentsMargins(0, 0, 0, 0) self.ContentMongoLayout.setSpacing(0) self.ContentMongoLayout.setObjectName("ContentMongoLayout") self.lblIPMongo = QtWidgets.QLabel(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblIPMongo.setFont(font) self.lblIPMongo.setLineWidth(0) self.lblIPMongo.setObjectName("lblIPMongo") self.ContentMongoLayout.addWidget(self.lblIPMongo, 0, 0, 1, 1) self.txtIPMongo = QtWidgets.QLineEdit(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtIPMongo.setFont(font) self.txtIPMongo.setStyleSheet("border-bottom:1px solid black;border-top:none;") self.txtIPMongo.setObjectName("txtIPMongo") self.ContentMongoLayout.addWidget(self.txtIPMongo, 0, 1, 1, 1) self.lblPuertoMOngo = QtWidgets.QLabel(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblPuertoMOngo.setFont(font) self.lblPuertoMOngo.setLineWidth(0) self.lblPuertoMOngo.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lblPuertoMOngo.setObjectName("lblPuertoMOngo") self.ContentMongoLayout.addWidget(self.lblPuertoMOngo, 0, 2, 1, 1) self.txtPuertoMongo = QtWidgets.QLineEdit(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtPuertoMongo.setFont(font) self.txtPuertoMongo.setStyleSheet("border-bottom:1px solid black;border-top:none;") self.txtPuertoMongo.setObjectName("txtPuertoMongo") self.ContentMongoLayout.addWidget(self.txtPuertoMongo, 0, 3, 1, 1) self.lblUsuarioMongo = QtWidgets.QLabel(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblUsuarioMongo.setFont(font) self.lblUsuarioMongo.setLineWidth(0) self.lblUsuarioMongo.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lblUsuarioMongo.setObjectName("lblUsuarioMongo") self.ContentMongoLayout.addWidget(self.lblUsuarioMongo, 1, 0, 1, 1) self.txtUsuarioMongo = QtWidgets.QLineEdit(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtUsuarioMongo.setFont(font) self.txtUsuarioMongo.setStyleSheet("border-bottom:1px solid black;border-top:none;") #self.txtUsuarioMongo.setInputMask("") self.txtUsuarioMongo.setText("") self.txtUsuarioMongo.setObjectName("txtUsuarioMongo") self.ContentMongoLayout.addWidget(self.txtUsuarioMongo, 1, 1, 1, 1) self.lblPasswordMongo = QtWidgets.QLabel(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblPasswordMongo.setFont(font) self.lblPasswordMongo.setLineWidth(0) self.lblPasswordMongo.setObjectName("lblPasswordMongo") self.ContentMongoLayout.addWidget(self.lblPasswordMongo, 1, 2, 1, 1) self.txtPassMongo = QtWidgets.QLineEdit(self.ContentMongoDB) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtPassMongo.setFont(font) self.txtPassMongo.setStyleSheet("border-bottom:1px solid black;border-top:none;") self.txtPassMongo.setInputMask("") self.txtPassMongo.setText("") self.txtPassMongo.setObjectName("txtPassMongo") self.ContentMongoLayout.addWidget(self.txtPassMongo, 1, 3, 1, 1) self.ContentAPIExterna = QtWidgets.QGroupBox() # asd font = QtGui.QFont() font.setBold(True) font.setWeight(75) self.ContentAPIExterna.setFont(font) self.ContentAPIExterna.setObjectName("ContentAPIExterna") self.ContentExternaLayout = QtWidgets.QGridLayout(self.ContentAPIExterna) self.ContentExternaLayout.setContentsMargins(0, 0, 0, 0) self.ContentExternaLayout.setSpacing(0) self.ContentExternaLayout.setObjectName("ContentExternaLayout") self.lblIPExterna = QtWidgets.QLabel(self.ContentAPIExterna) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.lblIPExterna.setFont(font) self.lblIPExterna.setLineWidth(0) self.lblIPExterna.setObjectName("lblIPExterna") self.ContentExternaLayout.addWidget(self.lblIPExterna, 0, 0, 1, 1) self.txtIPExterna = QtWidgets.QLineEdit(self.ContentAPIExterna) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.txtIPExterna.setFont(font) self.txtIPExterna.setStyleSheet("border-bottom:1px solid black;border-top:none;") #self.txtIPExterna.setInputMask("") self.txtIPExterna.setObjectName("txtIPExterna") self.ContentExternaLayout.addWidget(self.txtIPExterna, 0, 1, 1, 1) self.btnAceptar = QtWidgets.QPushButton() self.btnAceptar.setMinimumSize(QtCore.QSize(128, 48)) self.btnAceptar.setMaximumSize(QtCore.QSize(128, 48)) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(11) font.setBold(True) font.setWeight(75) self.btnAceptar.setFont(font) self.btnAceptar.setStyleSheet("border:1px solid green;") icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap(":/source/img/OK.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.btnAceptar.setIcon(icon2) self.btnAceptar.setIconSize(QtCore.QSize(24, 24)) self.btnAceptar.setShortcut("") self.btnAceptar.setCheckable(False) self.btnAceptar.setFlat(True) self.btnAceptar.setObjectName("btnAceptar") #UtilsFrame self.UtilsFrame = QtWidgets.QFrame(self.ContentBox) self.UtilsFrame.setGeometry(QtCore.QRect(30, 20, 256, 132)) self.UtilsFrame.setMinimumSize(QtCore.QSize(256, 132)) self.UtilsFrame.setMaximumSize(QtCore.QSize(128, 128)) self.UtilsFrame.setStyleSheet("background-color: rgb(252, 252, 252);") self.UtilsFrame.setFrameShape(QtWidgets.QFrame.NoFrame) self.UtilsFrame.setFrameShadow(QtWidgets.QFrame.Plain) self.UtilsFrame.setLineWidth(0) self.UtilsFrame.setObjectName("UtilsFrame") self.utilsLayout = QtWidgets.QVBoxLayout(self.UtilsFrame) self.utilsLayout.setContentsMargins(0, 0, 0, 0) self.utilsLayout.setSpacing(0) self.utilsLayout.setObjectName("utilsLayout") self.Status = QtWidgets.QLabel(self.UtilsFrame) self.Status.setMinimumSize(QtCore.QSize(64,64)) self.Status.setMaximumSize(QtCore.QSize(64, 64)) self.Status.setText("") self.Status.setPixmap(QtGui.QPixmap(":/source/img/Cargando.gif")) self.Status.setScaledContents(True) self.Status.setObjectName("Status") self.utilsLayout.addWidget(self.Status, 0, QtCore.Qt.AlignHCenter) self.lblStatus = QtWidgets.QLabel(self.UtilsFrame) self.lblStatus.setMaximumSize(QtCore.QSize(16777215, 24)) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(11) font.setBold(True) font.setWeight(75) self.lblStatus.setFont(font) self.lblStatus.setAlignment(QtCore.Qt.AlignCenter) self.lblStatus.setObjectName("lblStatus") self.utilsLayout.addWidget(self.lblStatus) self.btnReload = QtWidgets.QPushButton(self.UtilsFrame) self.btnReload.setMinimumSize(QtCore.QSize(0, 16)) self.btnReload.setMaximumSize(QtCore.QSize(132, 42)) font = QtGui.QFont() font.setFamily("Roboto") font.setPointSize(11) font.setBold(True) font.setWeight(75) self.btnReload.setFont(font) self.btnReload.setStyleSheet("border: 1px solid rgb(0, 170, 255);padding:5px;") icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/source/img/retry.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.btnReload.setIcon(icon) self.btnReload.setIconSize(QtCore.QSize(24, 24)) self.btnReload.setFlat(True) self.btnReload.setObjectName("btnReload") self.utilsLayout.addWidget(self.btnReload, 0, QtCore.Qt.AlignHCenter) self.verticalLayout_2.addWidget(self.ContentBox, 0, QtCore.Qt.AlignHCenter) self.verticalLayout.addWidget(self.MainFrame) self.retranslateUi(ConfiguracionesModal) QtCore.QMetaObject.connectSlotsByName(ConfiguracionesModal) self.btnReload.hide() #Movie self.movie = QMovie(":/source/img/Cargando.gif") self.movie.setScaledSize(QtCore.QSize(64,64)) self.Status.setMovie(self.movie) self.ContentLayout.addWidget(self.UtilsFrame, 0, QtCore.Qt.AlignHCenter) # listener self.parent.signals.resize.connect(self.center) self.btnExit.clicked.connect(self.exit) self.btnAceptar.clicked.connect(self.btnAceptar_Click) self.btnReload.clicked.connect(self.ObtenerConfguraciones) self.shortcut = QtWidgets.QShortcut(QtCore.Qt.Key_Return,self) self.shortcut.activated.connect(self.btnAceptar_Click) def showEvent(self,event): self.center() self.ObtenerConfguraciones() def ObtenerConfguraciones(self): self.btnReload.hide() self.lblStatus.setText("Cargando...") self.movie = QMovie(":/source/img/Cargando.gif") self.movie.setScaledSize(QtCore.QSize(64,64)) self.movie.start() self.Status.setMovie(self.movie) worker = Worker(Logica.ObtenerConfiguraciones,**{"access_token":self.session.access_token}) worker.signals.finished.connect(self.obtenerConfiguracionesCallBack) self.threadpool.start(worker) def obtenerConfiguracionesCallBack(self,response): if isinstance(response,Exception): self.btnReload.show() self.lblStatus.setText("¡Error! Ha ocurrido un error") self.movie = QMovie(":/source/img/Error.png") self.movie.setScaledSize(QtCore.QSize(64,64)) self.movie.start() self.Status.setMovie(self.movie) return if len(response) == 0: self.btnReload.show() self.lblStatus.setText("¡Vacio! Aún no hay nada") self.movie = QMovie(":/source/img/Empty.png") self.movie.setScaledSize(QtCore.QSize(64,64)) self.movie.start() self.Status.setMovie(self.movie) return self.btnReload.clicked.disconnect(self.ObtenerConfguraciones) self.UtilsFrame.deleteLater() self.config = configuracion(response) self.txtIPLocal.setText(response["APISCADA"]["Host"]) self.txtPuertoLocal.setText(response["APISCADA"]["Port"]) self.txtIPMongo.setText(response["MONGO"]["MONGO_HOST"]) self.txtPuertoMongo.setText(response["MONGO"]["MONGO_PORT"]) self.txtUsuarioMongo.setText(response["MONGO"]["MONGO_USER"]) self.txtPassMongo.setText(response["MONGO"]["MONGO_PASS"]) self.txtIPExterna.setText(response["APIPARTICLE"]["URI"]) self.ContentLayout.addWidget(self.ContentAPILocal) self.ContentLayout.addWidget(self.ContentMongoDB) self.ContentLayout.addWidget(self.ContentAPIExterna) self.ContentLayout.addWidget(self.btnAceptar, 0, QtCore.Qt.AlignHCenter) def btnAceptar_Click(self): try: self.config.apiLocal.IP = self.txtIPLocal.text() self.config.apiLocal.puerto = self.txtPuertoLocal.text() self.config.mongoDB.IP = self.txtIPMongo.text() self.config.mongoDB.puerto = self.txtPuertoMongo.text() self.config.mongoDB.usuario = self.txtUsuarioMongo.text() self.config.mongoDB.password = self.txtPassMongo.text() self.config.apiExterna.uri = self.txtIPExterna.text() except ValueError as e: QMessageBox.warning(self,"¡Error!",str(e)) return response = QMessageBox.question(self,"¡Confirmacion!","¿Seguro que desea guardar estas configuraciones?\nCualquier error podria provocar que API SCADA\nno se ejecute correctamente") if response == QMessageBox.Yes: self.guardarConfiguracione() def guardarConfiguracione(self): self.btnAceptar.hide() self.Status = QtWidgets.QLabel(self.ContentBox) self.Status.setMaximumSize(QtCore.QSize(64, 64)) self.Status.setText("") self.Status.setPixmap(QtGui.QPixmap(":/source/img/Cargando.gif")) self.Status.setScaledContents(True) self.Status.setObjectName("Status") self.movie = QMovie(":/source/img/Cargando.gif") self.movie.start() self.Status.setMovie(self.movie) self.ContentLayout.addWidget(self.Status, 0, QtCore.Qt.AlignHCenter) worker = Worker(Logica.GuardarConfiguraciones,**{"access_token":self.getAccessToken(),"data":self.config.toJSON() } ) worker.signals.finished.connect(self.guardarConfiguracionesCallback) self.threadpool.start(worker) def guardarConfiguracionesCallback(self,response): self.movie.deleteLater() self.Status.deleteLater() if isinstance(response,Exception): QMessageBox.warning(self,"¡Error!",str(response)) self.btnAceptar.show() return if response["success"] == "false": QMessageBox.warning(self,"¡Error!",str(response["Message"])) self.btnAceptar.show() else: QMessageBox.information(self,"¡Exito!","¡Exito! Configuraciones actualizadas\nSurtiran efecto la proxima vez que\nse inicie el servicio API SCADA") self.success(None) def disconnectSignals(self): self.parent.signals.resize.disconnect(self.center) self.btnExit.clicked.disconnect(self.exit) self.btnAceptar.clicked.disconnect(self.btnAceptar_Click) def retranslateUi(self, ConfiguracionesModal): _translate = QtCore.QCoreApplication.translate ConfiguracionesModal.setWindowTitle(_translate("ConfiguracionesModal", "Sistema SCADA")) self.lblSCADA.setText(_translate("ConfiguracionesModal", "SCADA")) self.lblTitle.setText(_translate("ConfiguracionesModal", "Configuraciones")) self.ContentBox.setTitle(_translate("ConfiguracionesModal", "Conexión")) self.ContentAPILocal.setTitle(_translate("ConfiguracionesModal", "API Local")) self.lblIPLocal.setText(_translate("ConfiguracionesModal", "Dirección IP:")) self.txtIPLocal.setText(_translate("ConfiguracionesModal", "127.0.0.1")) self.lblPuertoLocal.setText(_translate("ConfiguracionesModal", "Puerto:")) self.txtPuertoLocal.setText(_translate("ConfiguracionesModal", "8080")) self.ContentMongoDB.setTitle(_translate("ConfiguracionesModal", "MongoDB")) self.lblIPMongo.setText(_translate("ConfiguracionesModal", "Dirección IP:")) self.txtIPMongo.setText(_translate("ConfiguracionesModal", "127.0.0.1")) self.lblPuertoMOngo.setText(_translate("ConfiguracionesModal", "Puerto:")) self.txtPuertoMongo.setText(_translate("ConfiguracionesModal", "8080")) self.lblUsuarioMongo.setText(_translate("ConfiguracionesModal", "Usuario:")) self.lblPasswordMongo.setText(_translate("ConfiguracionesModal", "Contraseña:")) self.ContentAPIExterna.setTitle(_translate("ConfiguracionesModal", "API Externa")) self.lblIPExterna.setText(_translate("ConfiguracionesModal", "Dirección IP:")) self.txtIPExterna.setText(_translate("ConfiguracionesModal", "api.particle.io/v1/devices")) self.btnAceptar.setText(_translate("ConfiguracionesModal", "Aceptar")) self.lblStatus.setText(_translate("Form", "Cargando...")) self.btnReload.setText(_translate("Form", "Reintentar"))
class MainWindow(QMainWindow): registration_thread: RegisterThread sponsor: object device_type: object mac_address: object username: object movie: QMovie gif_label: QLabel email_label: QLabel email_textbox: QLineEdit button_clicked: bool user_type: str light_mode_icon: QIcon dark_mode_icon: QIcon def __init__(self): super(MainWindow, self).__init__() self.thread_pool = QThreadPool() self.ui = uic.loadUi(_UI, self) self.config = configparser.RawConfigParser() self.center() self.mw = windows.ModernWindow(self) self.initUI() self.init_config() def initUI(self): self.setWindowIcon(QIcon(_logo)) self.dark_mode_icon = QIcon('night_mode.ico') self.light_mode_icon = QIcon('light_mode.ico') self.ui.actionAbout.triggered.connect(self.show_about) self.ui.actionHelp.triggered.connect(self.show_help) self.ui.actionAdd_user_using_website.triggered.connect( lambda: webbrowser.open_new_tab( 'http://fsunac-1.framingham.edu/administration')) self.ui.student_checkbox.stateChanged.connect(self.on_state_change) self.ui.faculty_checkbox.stateChanged.connect(self.on_state_change) self.ui.other_checkbox.stateChanged.connect(self.on_state_change) self.user_type = 'student' self.button_clicked = False self.mw.show() def init_config(self): try: # Open our config file and load configs if applicable with open(_config, 'r') as config_file: if os.path.getsize(_config): self.config.read_file(config_file) self.ui.sponsor_textbox.setText( self.config.get('Default', 'sponsor')) if self.config.getboolean('Default', 'dark_mode'): self.ui.change_mode.setIconSize(QSize(35, 35)) self.ui.change_mode.setIcon(self.light_mode_icon) styles.dark_mode(QApplication.instance()) self.ui.change_mode.setToolTip( "<i><b>Light Mode</b></i>") else: self.ui.change_mode.setIconSize(QSize(25, 25)) self.ui.change_mode.setIcon(self.dark_mode_icon) styles.light_mode(QApplication.instance()) self.ui.change_mode.setToolTip( "<i><b>Dark Mode</b></i>") else: raise FileNotFoundError except FileNotFoundError: # Create config file if no config found self.config.add_section('Default') self.config['Default']['sponsor'] = '' self.config['Default']['dark_mode'] = 'true' self.ui.change_mode.setIcon(self.light_mode_icon) self.ui.change_mode.setIconSize(QSize(35, 35)) self.ui.change_mode.setIcon(self.light_mode_icon) styles.dark_mode(QApplication.instance()) self.ui.change_mode.setToolTip("<i><b>Light Mode</b></i>") with open(_config, 'w') as config_file: self.config.write(config_file) def disable_widgets(self, bool_val): objects = [QPushButton, QLineEdit, QMenu, QMenuBar] for item in objects: for child in self.findChildren(item): if bool_val: child.setEnabled(False) else: child.setEnabled(True) def other_checked(self, other_checked=True): if other_checked: self.ui.username_label.setText( '<html><head/><body><p><span style=" color:#ff0000;">*</span>Full Name</p></body></html>' ) self.ui.progress_label.move(10, 355) self.ui.register_button.move(230, 320) self.ui.sponsor_label.setGeometry(95, 265, 151, 41) self.ui.sponsor_textbox.move(250, 278) self.email_label = QLabel( '<html><head/><body><p><span style=" color:#ff0000;">*</span>User ' 'Email</p></body></html>', self) self.email_label.setStyleSheet('font: 16pt "Verdana";') self.email_label.setGeometry(94, 230, 151, 61) self.email_textbox = QLineEdit(self) self.email_textbox.setGeometry(250, 250, 221, 21) self.email_textbox.setStyleSheet('font: 11pt "Verdana";') self.setTabOrder(self.ui.device_textbox, self.ui.email_textbox) self.ui.email_textbox.returnPressed.connect( lambda: self.ui.register_button.animateClick()) self.email_label.show() self.email_textbox.show() else: try: self.ui.username_label.setText( '<html><head/><body><p><span style=" color:#ff0000;">*</span>Username</p></body></html>' ) self.email_textbox.deleteLater() self.email_label.deleteLater() self.ui.progress_label.move(10, 330) self.ui.register_button.move(230, 280) self.ui.sponsor_label.setGeometry(95, 220, 151, 41) self.ui.sponsor_textbox.move(250, 230) except AttributeError: pass except RuntimeError: pass @pyqtSlot(int) def on_state_change(self, state): if state == Qt.Checked: if self.sender() == self.ui.student_checkbox: self.other_checked(other_checked=False) self.user_type = 'student' self.ui.faculty_checkbox.setChecked(False) self.ui.other_checkbox.setChecked(False) elif self.sender() == self.ui.faculty_checkbox: self.other_checked(other_checked=False) self.user_type = 'faculty' self.ui.student_checkbox.setChecked(False) self.ui.other_checkbox.setChecked(False) elif self.sender() == self.ui.other_checkbox: self.other_checked() self.user_type = 'other' self.ui.student_checkbox.setChecked(False) self.ui.faculty_checkbox.setChecked(False) else: if not self.ui.student_checkbox.isChecked( ) and not self.ui.faculty_checkbox.isChecked( ) and not self.ui.other_checkbox.isChecked(): self.ui.student_checkbox.setChecked(True) def set_button_clicked(self, bool_val): if bool_val: self.button_clicked = True else: self.button_clicked = False # Function to display an error if we get one def popup_msg(self, title, error_string): QMessageBox.about(self, title, error_string) # Center our application instead of putting it in the top left def center(self): frame_gm = self.frameGeometry() screen = QApplication.desktop().screenNumber( QApplication.desktop().cursor().pos()) center_point = QApplication.desktop().screenGeometry(screen).center() frame_gm.moveCenter(center_point) self.move(frame_gm.topLeft()) def clear_textboxes(self): self.ui.username_textbox.clear() self.ui.mac_textbox.clear() self.ui.device_textbox.clear() try: self.ui.email_textbox.clear() except RuntimeError as r: pass except AttributeError as a: pass def change_ui(self): with open(_config, 'r'): if self.config.getboolean('Default', 'dark_mode'): styles.light_mode(QApplication.instance()) self.ui.change_mode.setIcon(self.dark_mode_icon) self.ui.change_mode.setIconSize(QSize(25, 25)) self.ui.change_mode.setToolTip("<i><b>Dark Mode</b></i>") with open(_config, 'w') as config: self.config['Default']['dark_mode'] = 'false' self.config.write(config) else: styles.dark_mode(QApplication.instance()) self.ui.change_mode.setIcon(self.light_mode_icon) self.ui.change_mode.setIconSize(QSize(35, 35)) self.ui.change_mode.setToolTip("<i><b>Light Mode</b></i>") with open(_config, 'w') as config: self.config['Default']['dark_mode'] = 'true' self.config.write(config) def show_about(self): with open(_about, 'r') as about: self.popup_msg('About', about.read()) def show_help(self): with open(_help, 'r') as about: self.popup_msg('Help', about.read()) def update_label(self, label_text): self.ui.progress_label.setText(label_text) @Slot() def on_change_mode_clicked(self): self.change_ui() def play_splash(self, bool_val): def blur_objects(blur=True): objects = [ QLabel, QPushButton, QLineEdit, QCheckBox, QMenu, QMenuBar ] for item in objects: for child in self.findChildren(item): if child is not self.ui.progress_label and child is not self.gif_label: if blur: child.setGraphicsEffect(QGraphicsBlurEffect()) else: child.setGraphicsEffect( QGraphicsBlurEffect().setBlurRadius(0)) if bool_val: self.gif_label = QLabel(self) self.gif_label.setScaledContents(True) self.gif_label.setGeometry(140, 20, 301, 307) self.movie = QMovie(_gif) self.gif_label.setMovie(self.movie) blur_objects(blur=True) self.gif_label.show() self.movie.start() else: blur_objects(blur=False) self.movie.stop() self.movie.deleteLater() self.gif_label.deleteLater() @Slot() def on_register_button_clicked(self): self.button_clicked = True # Get the texts entered in the textbox and pass them to the thread self.username = self.ui.username_textbox.text() self.mac_address = self.ui.mac_textbox.text() self.device_type = self.ui.device_textbox.text() self.sponsor = self.ui.sponsor_textbox.text() if self.user_type == 'other': self.registration_thread = RegisterThread( self.username, self.mac_address, self.device_type, self.sponsor, user_type=self.email_textbox.text()) else: self.registration_thread = RegisterThread(self.username, self.mac_address, self.device_type, self.sponsor, user_type=self.user_type) self.registration_thread.signals.clear_textboxes_signal.connect( self.clear_textboxes) self.registration_thread.signals.disable_widgets_signal.connect( self.disable_widgets) self.registration_thread.signals.popup_signal.connect(self.popup_msg) self.registration_thread.signals.label_update_signal.connect( self.update_label) self.registration_thread.signals.play_splash_signal.connect( self.play_splash) self.registration_thread.signals.set_button_clicked_signal.connect( self.set_button_clicked) self.thread_pool.start(self.registration_thread) # If the user clicks the red button to exit the window @Slot() def closeEvent(self, event): if self.button_clicked: while True: try: self.registration_thread.browser.quit() break except AttributeError: pass with open(_config, 'r'): self.config['Default']['sponsor'] = self.ui.sponsor_textbox.text() with open(_config, 'w') as config: self.config.write(config) event.accept()
class VidCutter(QWidget): def __init__(self, parent): super(VidCutter, self).__init__(parent) self.novideoWidget = QWidget(self, autoFillBackground=True) self.parent = parent self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface) self.videoWidget = VideoWidget(self) self.videoService = VideoService(self) QFontDatabase.addApplicationFont( MainWindow.get_path('fonts/DroidSansMono.ttf')) QFontDatabase.addApplicationFont( MainWindow.get_path('fonts/OpenSans.ttf')) fontSize = 12 if sys.platform == 'darwin' else 10 appFont = QFont('Open Sans', fontSize, 300) qApp.setFont(appFont) self.clipTimes = [] self.inCut = False self.movieFilename = '' self.movieLoaded = False self.timeformat = 'hh:mm:ss' self.finalFilename = '' self.totalRuntime = 0 self.initIcons() self.initActions() self.toolbar = QToolBar(floatable=False, movable=False, iconSize=QSize(40, 36)) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.toolbar.setStyleSheet('''QToolBar { spacing:10px; } QToolBar QToolButton { border:1px solid transparent; min-width:95px; font-size:11pt; font-weight:400; border-radius:5px; padding:1px 2px; color:#444; } QToolBar QToolButton:hover { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.85); } QToolBar QToolButton:pressed { border:1px inset #6A4572; color:#6A4572; background-color:rgba(255, 255, 255, 0.25); } QToolBar QToolButton:disabled { color:#999; }''') self.initToolbar() self.appMenu, self.cliplistMenu = QMenu(), QMenu() self.initMenus() self.seekSlider = VideoSlider(parent=self, sliderMoved=self.setPosition) self.initNoVideo() self.cliplist = QListWidget( sizePolicy=QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding), contextMenuPolicy=Qt.CustomContextMenu, uniformItemSizes=True, iconSize=QSize(100, 700), dragDropMode=QAbstractItemView.InternalMove, alternatingRowColors=True, customContextMenuRequested=self.itemMenu, dragEnabled=True) self.cliplist.setStyleSheet( 'QListView { border-radius:0; border:none; border-left:1px solid #B9B9B9; ' + 'border-right:1px solid #B9B9B9; } QListView::item { padding:10px 0; }' ) self.cliplist.setFixedWidth(185) self.cliplist.model().rowsMoved.connect(self.syncClipList) listHeader = QLabel(pixmap=QPixmap( MainWindow.get_path('images/clipindex.png'), 'PNG'), alignment=Qt.AlignCenter) listHeader.setStyleSheet( '''padding:5px; padding-top:8px; border:1px solid #b9b9b9; background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFF, stop: 0.5 #EAEAEA, stop: 0.6 #EAEAEA stop:1 #FFF);''' ) self.runtimeLabel = QLabel('<div align="right">00:00:00</div>', textFormat=Qt.RichText) self.runtimeLabel.setStyleSheet( '''font-family:Droid Sans Mono; font-size:10pt; color:#FFF; background:rgb(106, 69, 114) url(:images/runtime.png) no-repeat left center; padding:2px; padding-right:8px; border:1px solid #B9B9B9;''') self.clipindexLayout = QVBoxLayout(spacing=0) self.clipindexLayout.setContentsMargins(0, 0, 0, 0) self.clipindexLayout.addWidget(listHeader) self.clipindexLayout.addWidget(self.cliplist) self.clipindexLayout.addWidget(self.runtimeLabel) self.videoLayout = QHBoxLayout() self.videoLayout.setContentsMargins(0, 0, 0, 0) self.videoLayout.addWidget(self.novideoWidget) self.videoLayout.addLayout(self.clipindexLayout) self.timeCounter = QLabel('00:00:00 / 00:00:00', autoFillBackground=True, alignment=Qt.AlignCenter, sizePolicy=QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed)) self.timeCounter.setStyleSheet( 'color:#FFF; background:#000; font-family:Droid Sans Mono; font-size:10.5pt; padding:4px;' ) videoplayerLayout = QVBoxLayout(spacing=0) videoplayerLayout.setContentsMargins(0, 0, 0, 0) videoplayerLayout.addWidget(self.videoWidget) videoplayerLayout.addWidget(self.timeCounter) self.videoplayerWidget = QWidget(self, visible=False) self.videoplayerWidget.setLayout(videoplayerLayout) self.muteButton = QPushButton(icon=self.unmuteIcon, flat=True, toolTip='Mute', statusTip='Toggle audio mute', iconSize=QSize(16, 16), cursor=Qt.PointingHandCursor, clicked=self.muteAudio) self.volumeSlider = QSlider(Qt.Horizontal, toolTip='Volume', statusTip='Adjust volume level', cursor=Qt.PointingHandCursor, value=50, minimum=0, maximum=100, sliderMoved=self.setVolume) self.menuButton = QPushButton( icon=self.menuIcon, flat=True, toolTip='Menu', statusTip='Media + application information', iconSize=QSize(24, 24), cursor=Qt.PointingHandCursor) self.menuButton.setMenu(self.appMenu) toolbarLayout = QHBoxLayout() toolbarLayout.addWidget(self.toolbar) toolbarLayout.setContentsMargins(2, 2, 2, 2) toolbarGroup = QGroupBox() toolbarGroup.setFlat(False) toolbarGroup.setCursor(Qt.PointingHandCursor) toolbarGroup.setLayout(toolbarLayout) toolbarGroup.setStyleSheet( '''QGroupBox { background-color:rgba(0, 0, 0, 0.1); border:1px inset #888; border-radius:5px; }''') controlsLayout = QHBoxLayout(spacing=0) controlsLayout.addStretch(1) controlsLayout.addWidget(toolbarGroup) controlsLayout.addStretch(1) controlsLayout.addWidget(self.muteButton) controlsLayout.addWidget(self.volumeSlider) controlsLayout.addSpacing(1) controlsLayout.addWidget(self.menuButton) layout = QVBoxLayout() layout.setContentsMargins(10, 10, 10, 4) layout.addLayout(self.videoLayout) layout.addWidget(self.seekSlider) layout.addSpacing(5) layout.addLayout(controlsLayout) layout.addSpacing(2) self.setLayout(layout) self.mediaPlayer.setVideoOutput(self.videoWidget) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.error.connect(self.handleError) def initNoVideo(self) -> None: novideoImage = QLabel( alignment=Qt.AlignCenter, autoFillBackground=False, pixmap=QPixmap(MainWindow.get_path('images/novideo.png'), 'PNG'), sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.MinimumExpanding)) novideoImage.setBackgroundRole(QPalette.Dark) novideoImage.setContentsMargins(0, 20, 0, 20) self.novideoLabel = QLabel(alignment=Qt.AlignCenter, autoFillBackground=True, sizePolicy=QSizePolicy( QSizePolicy.Expanding, QSizePolicy.Minimum)) self.novideoLabel.setBackgroundRole(QPalette.Dark) self.novideoLabel.setContentsMargins(0, 20, 15, 60) novideoLayout = QVBoxLayout(spacing=0) novideoLayout.addWidget(novideoImage) novideoLayout.addWidget(self.novideoLabel, alignment=Qt.AlignTop) self.novideoMovie = QMovie( MainWindow.get_path('images/novideotext.gif')) self.novideoMovie.frameChanged.connect(self.setNoVideoText) self.novideoMovie.start() self.novideoWidget.setBackgroundRole(QPalette.Dark) self.novideoWidget.setLayout(novideoLayout) def initIcons(self) -> None: self.appIcon = QIcon(MainWindow.get_path('images/vidcutter.png')) self.openIcon = icon('fa.film', color='#444', color_active='#6A4572', scale_factor=0.9) self.playIcon = icon('fa.play-circle-o', color='#444', color_active='#6A4572', scale_factor=1.1) self.pauseIcon = icon('fa.pause-circle-o', color='#444', color_active='#6A4572', scale_factor=1.1) self.cutStartIcon = icon('fa.scissors', scale_factor=1.15, color='#444', color_active='#6A4572') endicon_normal = icon('fa.scissors', scale_factor=1.15, color='#444').pixmap(QSize(36, 36)).toImage() endicon_active = icon('fa.scissors', scale_factor=1.15, color='#6A4572').pixmap(QSize(36, 36)).toImage() self.cutEndIcon = QIcon() self.cutEndIcon.addPixmap( QPixmap.fromImage( endicon_normal.mirrored(horizontal=True, vertical=False)), QIcon.Normal, QIcon.Off) self.cutEndIcon.addPixmap( QPixmap.fromImage( endicon_active.mirrored(horizontal=True, vertical=False)), QIcon.Active, QIcon.Off) self.saveIcon = icon('fa.video-camera', color='#6A4572', color_active='#6A4572') self.muteIcon = QIcon(MainWindow.get_path('images/muted.png')) self.unmuteIcon = QIcon(MainWindow.get_path('images/unmuted.png')) self.upIcon = icon('ei.caret-up', color='#444') self.downIcon = icon('ei.caret-down', color='#444') self.removeIcon = icon('ei.remove', color='#B41D1D') self.removeAllIcon = icon('ei.trash', color='#B41D1D') self.successIcon = QIcon(MainWindow.get_path('images/success.png')) self.menuIcon = icon('fa.cog', color='#444', scale_factor=1.15) self.completePlayIcon = icon('fa.play', color='#444') self.completeOpenIcon = icon('fa.folder-open', color='#444') self.completeRestartIcon = icon('fa.retweet', color='#444') self.completeExitIcon = icon('fa.sign-out', color='#444') self.mediaInfoIcon = icon('fa.info-circle', color='#444') self.updateCheckIcon = icon('fa.cloud-download', color='#444') def initActions(self) -> None: self.openAction = QAction(self.openIcon, 'Open', self, statusTip='Open media file', triggered=self.openMedia) self.playAction = QAction(self.playIcon, 'Play', self, statusTip='Play media file', triggered=self.playMedia, enabled=False) self.cutStartAction = QAction(self.cutStartIcon, ' Start', self, toolTip='Start', statusTip='Set clip start marker', triggered=self.setCutStart, enabled=False) self.cutEndAction = QAction(self.cutEndIcon, ' End', self, toolTip='End', statusTip='Set clip end marker', triggered=self.setCutEnd, enabled=False) self.saveAction = QAction(self.saveIcon, 'Save', self, statusTip='Save clips to a new video file', triggered=self.cutVideo, enabled=False) self.moveItemUpAction = QAction( self.upIcon, 'Move up', self, statusTip='Move clip position up in list', triggered=self.moveItemUp, enabled=False) self.moveItemDownAction = QAction( self.downIcon, 'Move down', self, statusTip='Move clip position down in list', triggered=self.moveItemDown, enabled=False) self.removeItemAction = QAction( self.removeIcon, 'Remove clip', self, statusTip='Remove selected clip from list', triggered=self.removeItem, enabled=False) self.removeAllAction = QAction(self.removeAllIcon, 'Clear list', self, statusTip='Clear all clips from list', triggered=self.clearList, enabled=False) self.mediaInfoAction = QAction( self.mediaInfoIcon, 'Media information', self, statusTip='View current media file information', triggered=self.mediaInfo, enabled=False) self.updateCheckAction = QAction( self.updateCheckIcon, 'Check for updates...', self, statusTip='Check for application updates', triggered=self.updateCheck) self.aboutQtAction = QAction('About Qt', self, statusTip='About Qt', triggered=qApp.aboutQt) self.aboutAction = QAction('About %s' % qApp.applicationName(), self, statusTip='Credits and licensing', triggered=self.aboutInfo) def initToolbar(self) -> None: self.toolbar.addAction(self.openAction) self.toolbar.addAction(self.playAction) self.toolbar.addAction(self.cutStartAction) self.toolbar.addAction(self.cutEndAction) self.toolbar.addAction(self.saveAction) def initMenus(self) -> None: self.appMenu.addAction(self.mediaInfoAction) self.appMenu.addAction(self.updateCheckAction) self.appMenu.addSeparator() self.appMenu.addAction(self.aboutQtAction) self.appMenu.addAction(self.aboutAction) self.cliplistMenu.addAction(self.moveItemUpAction) self.cliplistMenu.addAction(self.moveItemDownAction) self.cliplistMenu.addSeparator() self.cliplistMenu.addAction(self.removeItemAction) self.cliplistMenu.addAction(self.removeAllAction) @staticmethod def getSpacer() -> QWidget: spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) return spacer def setRunningTime(self, runtime: str) -> None: self.runtimeLabel.setText('<div align="right">%s</div>' % runtime) @pyqtSlot(int) def setNoVideoText(self) -> None: self.novideoLabel.setPixmap(self.novideoMovie.currentPixmap()) def itemMenu(self, pos: QPoint) -> None: globalPos = self.cliplist.mapToGlobal(pos) self.moveItemUpAction.setEnabled(False) self.moveItemDownAction.setEnabled(False) self.removeItemAction.setEnabled(False) self.removeAllAction.setEnabled(False) index = self.cliplist.currentRow() if index != -1: if not self.inCut: if index > 0: self.moveItemUpAction.setEnabled(True) if index < self.cliplist.count() - 1: self.moveItemDownAction.setEnabled(True) if self.cliplist.count() > 0: self.removeItemAction.setEnabled(True) if self.cliplist.count() > 0: self.removeAllAction.setEnabled(True) self.cliplistMenu.exec_(globalPos) def moveItemUp(self) -> None: index = self.cliplist.currentRow() tmpItem = self.clipTimes[index] del self.clipTimes[index] self.clipTimes.insert(index - 1, tmpItem) self.renderTimes() def moveItemDown(self) -> None: index = self.cliplist.currentRow() tmpItem = self.clipTimes[index] del self.clipTimes[index] self.clipTimes.insert(index + 1, tmpItem) self.renderTimes() def removeItem(self) -> None: index = self.cliplist.currentRow() del self.clipTimes[index] if self.inCut and index == self.cliplist.count() - 1: self.inCut = False self.initMediaControls() self.renderTimes() def clearList(self) -> None: self.clipTimes.clear() self.cliplist.clear() self.inCut = False self.renderTimes() self.initMediaControls() def mediaInfo(self) -> None: if self.mediaPlayer.isMetaDataAvailable(): content = '<table cellpadding="4">' for key in self.mediaPlayer.availableMetaData(): val = self.mediaPlayer.metaData(key) if type(val) is QSize: val = '%s x %s' % (val.width(), val.height()) content += '<tr><td align="right"><b>%s:</b></td><td>%s</td></tr>\n' % ( key, val) content += '</table>' mbox = QMessageBox(windowTitle='Media Information', windowIcon=self.parent.windowIcon(), textFormat=Qt.RichText) mbox.setText('<b>%s</b>' % os.path.basename( self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile())) mbox.setInformativeText(content) mbox.exec_() else: QMessageBox.critical( self.parent, 'MEDIA ERROR', '<h3>Could not probe media file.</h3>' + '<p>An error occurred while analyzing the media file for its metadata details.' + '<br/><br/><b>This DOES NOT mean there is a problem with the file and you should ' + 'be able to continue using it.</b></p>') def aboutInfo(self) -> None: about_html = '''<style> a { color:#441d4e; text-decoration:none; font-weight:bold; } a:hover { text-decoration:underline; } </style> <div style="min-width:650px;"> <p style="font-size:26pt; font-weight:bold; color:#6A4572;">%s</p> <p> <span style="font-size:13pt;"><b>Version: %s</b></span> <span style="font-size:10pt;position:relative;left:5px;">( %s )</span> </p> <p style="font-size:13px;"> Copyright © 2016 <a href="mailto:[email protected]">Pete Alexandrou</a> <br/> Website: <a href="%s">%s</a> </p> <p style="font-size:13px;"> Thanks to the folks behind the <b>Qt</b>, <b>PyQt</b> and <b>FFmpeg</b> projects for all their hard and much appreciated work. </p> <p style="font-size:11px;"> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. </p> <p style="font-size:11px;"> This software uses libraries from the <a href="https://www.ffmpeg.org">FFmpeg</a> project under the <a href="https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html">LGPLv2.1</a> </p></div>''' % (qApp.applicationName(), qApp.applicationVersion(), platform.architecture()[0], qApp.organizationDomain(), qApp.organizationDomain()) QMessageBox.about(self.parent, 'About %s' % qApp.applicationName(), about_html) def openMedia(self) -> None: filename, _ = QFileDialog.getOpenFileName(self.parent, caption='Select video', directory=QDir.homePath()) if filename != '': self.loadFile(filename) def loadFile(self, filename: str) -> None: self.movieFilename = filename if not os.path.exists(filename): return self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename))) self.initMediaControls(True) self.cliplist.clear() self.clipTimes = [] self.parent.setWindowTitle( '%s - %s' % (qApp.applicationName(), os.path.basename(filename))) if not self.movieLoaded: self.videoLayout.replaceWidget(self.novideoWidget, self.videoplayerWidget) self.novideoMovie.stop() self.novideoMovie.deleteLater() self.novideoWidget.deleteLater() self.videoplayerWidget.show() self.videoWidget.show() self.movieLoaded = True if self.mediaPlayer.isVideoAvailable(): self.mediaPlayer.setPosition(1) self.mediaPlayer.play() self.mediaPlayer.pause() def playMedia(self) -> None: if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() self.playAction.setText('Play') else: self.mediaPlayer.play() self.playAction.setText('Pause') def initMediaControls(self, flag: bool = True) -> None: self.playAction.setEnabled(flag) self.saveAction.setEnabled(False) self.cutStartAction.setEnabled(flag) self.cutEndAction.setEnabled(False) self.mediaInfoAction.setEnabled(flag) if flag: self.seekSlider.setRestrictValue(0) def setPosition(self, position: int) -> None: self.mediaPlayer.setPosition(position) def positionChanged(self, progress: int) -> None: self.seekSlider.setValue(progress) currentTime = self.deltaToQTime(progress) totalTime = self.deltaToQTime(self.mediaPlayer.duration()) self.timeCounter.setText('%s / %s' % (currentTime.toString( self.timeformat), totalTime.toString(self.timeformat))) @pyqtSlot() def mediaStateChanged(self) -> None: if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playAction.setIcon(self.pauseIcon) else: self.playAction.setIcon(self.playIcon) def durationChanged(self, duration: int) -> None: self.seekSlider.setRange(0, duration) def muteAudio(self) -> None: if self.mediaPlayer.isMuted(): self.muteButton.setIcon(self.unmuteIcon) self.muteButton.setToolTip('Mute') else: self.muteButton.setIcon(self.muteIcon) self.muteButton.setToolTip('Unmute') self.mediaPlayer.setMuted(not self.mediaPlayer.isMuted()) def setVolume(self, volume: int) -> None: self.mediaPlayer.setVolume(volume) def toggleFullscreen(self) -> None: self.videoWidget.setFullScreen(not self.videoWidget.isFullScreen()) def setCutStart(self) -> None: self.clipTimes.append([ self.deltaToQTime(self.mediaPlayer.position()), '', self.captureImage() ]) self.cutStartAction.setDisabled(True) self.cutEndAction.setEnabled(True) self.seekSlider.setRestrictValue(self.seekSlider.value(), True) self.inCut = True self.renderTimes() def setCutEnd(self) -> None: item = self.clipTimes[len(self.clipTimes) - 1] selected = self.deltaToQTime(self.mediaPlayer.position()) if selected.__lt__(item[0]): QMessageBox.critical( self.parent, 'Invalid END Time', 'The clip end time must come AFTER it\'s start time. Please try again.' ) return item[1] = selected self.cutStartAction.setEnabled(True) self.cutEndAction.setDisabled(True) self.seekSlider.setRestrictValue(0, False) self.inCut = False self.renderTimes() @pyqtSlot(QModelIndex, int, int, QModelIndex, int) def syncClipList(self, parent: QModelIndex, start: int, end: int, destination: QModelIndex, row: int) -> None: if start < row: index = row - 1 else: index = row clip = self.clipTimes.pop(start) self.clipTimes.insert(index, clip) def renderTimes(self) -> None: self.cliplist.clear() if len(self.clipTimes) > 4: self.cliplist.setFixedWidth(200) else: self.cliplist.setFixedWidth(185) self.totalRuntime = 0 for item in self.clipTimes: endItem = '' if type(item[1]) is QTime: endItem = item[1].toString(self.timeformat) self.totalRuntime += item[0].msecsTo(item[1]) listitem = QListWidgetItem() listitem.setTextAlignment(Qt.AlignVCenter) if type(item[2]) is QPixmap: listitem.setIcon(QIcon(item[2])) self.cliplist.addItem(listitem) marker = QLabel( '''<style>b { font-size:7pt; } p { margin:2px 5px; }</style> <p><b>START</b><br/>%s<br/><b>END</b><br/>%s</p>''' % (item[0].toString(self.timeformat), endItem)) marker.setStyleSheet('border:none;') self.cliplist.setItemWidget(listitem, marker) listitem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled) if len(self.clipTimes) and not self.inCut: self.saveAction.setEnabled(True) if self.inCut or len(self.clipTimes) == 0 or not type( self.clipTimes[0][1]) is QTime: self.saveAction.setEnabled(False) self.setRunningTime( self.deltaToQTime(self.totalRuntime).toString(self.timeformat)) @staticmethod def deltaToQTime(millisecs: int) -> QTime: secs = millisecs / 1000 return QTime((secs / 3600) % 60, (secs / 60) % 60, secs % 60, (secs * 1000) % 1000) def captureImage(self) -> QPixmap: frametime = self.deltaToQTime(self.mediaPlayer.position()).toString( self.timeformat) inputfile = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile( ) imagecap = self.videoService.capture(inputfile, frametime) if type(imagecap) is QPixmap: return imagecap def cutVideo(self) -> bool: clips = len(self.clipTimes) filename, filelist = '', [] source = self.mediaPlayer.currentMedia().canonicalUrl().toLocalFile() _, sourceext = os.path.splitext(source) if clips > 0: self.finalFilename, _ = QFileDialog.getSaveFileName( self.parent, 'Save video', source, 'Video files (*%s)' % sourceext) if self.finalFilename == '': return False qApp.setOverrideCursor(Qt.BusyCursor) self.saveAction.setDisabled(True) self.showProgress(clips) file, ext = os.path.splitext(self.finalFilename) index = 1 self.progress.setLabelText('Cutting media files...') qApp.processEvents() for clip in self.clipTimes: duration = self.deltaToQTime(clip[0].msecsTo( clip[1])).toString(self.timeformat) filename = '%s_%s%s' % (file, '{0:0>2}'.format(index), ext) filelist.append(filename) self.videoService.cut(source, filename, clip[0].toString(self.timeformat), duration) index += 1 if len(filelist) > 1: self.joinVideos(filelist, self.finalFilename) else: QFile.remove(self.finalFilename) QFile.rename(filename, self.finalFilename) self.progress.setLabelText('Complete...') self.progress.setValue(100) qApp.processEvents() self.progress.close() self.progress.deleteLater() qApp.restoreOverrideCursor() self.complete() return True return False def joinVideos(self, joinlist: list, filename: str) -> None: listfile = os.path.normpath( os.path.join(os.path.dirname(joinlist[0]), '.vidcutter.list')) fobj = open(listfile, 'w') for file in joinlist: fobj.write('file \'%s\'\n' % file.replace("'", "\\'")) fobj.close() self.videoService.join(listfile, filename) QFile.remove(listfile) for file in joinlist: if os.path.isfile(file): QFile.remove(file) def updateCheck(self) -> None: self.updater = Updater() self.updater.updateAvailable.connect(self.updateHandler) self.updater.start() def updateHandler(self, updateExists: bool, version: str = None): if updateExists: if Updater.notify_update(self, version) == QMessageBox.AcceptRole: self.updater.install_update(self) else: Updater.notify_no_update(self) def showProgress(self, steps: int, label: str = 'Analyzing media...') -> None: self.progress = QProgressDialog(label, None, 0, steps, self.parent, windowModality=Qt.ApplicationModal, windowIcon=self.parent.windowIcon(), minimumDuration=0, minimumWidth=500) self.progress.show() for i in range(steps): self.progress.setValue(i) qApp.processEvents() time.sleep(1) def complete(self) -> None: info = QFileInfo(self.finalFilename) mbox = QMessageBox(windowTitle='VIDEO PROCESSING COMPLETE', minimumWidth=500, textFormat=Qt.RichText) mbox.setText( ''' <style> table.info { margin:6px; padding:4px 15px; } td.label { font-weight:bold; font-size:10.5pt; text-align:right; } td.value { font-size:10.5pt; } </style> <table class="info" cellpadding="4" cellspacing="0"> <tr> <td class="label"><b>File:</b></td> <td class="value" nowrap>%s</td> </tr> <tr> <td class="label"><b>Size:</b></td> <td class="value">%s</td> </tr> <tr> <td class="label"><b>Length:</b></td> <td class="value">%s</td> </tr> </table><br/>''' % (QDir.toNativeSeparators( self.finalFilename), self.sizeof_fmt(int(info.size())), self.deltaToQTime(self.totalRuntime).toString(self.timeformat))) play = mbox.addButton('Play', QMessageBox.AcceptRole) play.setIcon(self.completePlayIcon) play.clicked.connect(self.openResult) fileman = mbox.addButton('Open', QMessageBox.AcceptRole) fileman.setIcon(self.completeOpenIcon) fileman.clicked.connect(self.openFolder) end = mbox.addButton('Exit', QMessageBox.AcceptRole) end.setIcon(self.completeExitIcon) end.clicked.connect(self.close) new = mbox.addButton('Restart', QMessageBox.AcceptRole) new.setIcon(self.completeRestartIcon) new.clicked.connect(self.parent.restart) mbox.setDefaultButton(new) mbox.setEscapeButton(new) mbox.adjustSize() mbox.exec_() def sizeof_fmt(self, num: float, suffix: chr = 'B') -> str: for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 return "%.1f%s%s" % (num, 'Y', suffix) @pyqtSlot() def openFolder(self) -> None: self.openResult(pathonly=True) @pyqtSlot(bool) def openResult(self, pathonly: bool = False) -> None: self.parent.restart() if len(self.finalFilename) and os.path.exists(self.finalFilename): target = self.finalFilename if not pathonly else os.path.dirname( self.finalFilename) QDesktopServices.openUrl(QUrl.fromLocalFile(target)) @pyqtSlot() def startNew(self) -> None: qApp.restoreOverrideCursor() self.clearList() self.seekSlider.setValue(0) self.seekSlider.setRange(0, 0) self.mediaPlayer.setMedia(QMediaContent()) self.initNoVideo() self.videoLayout.replaceWidget(self.videoplayerWidget, self.novideoWidget) self.initMediaControls(False) self.parent.setWindowTitle('%s' % qApp.applicationName()) def wheelEvent(self, event: QWheelEvent) -> None: if self.mediaPlayer.isVideoAvailable( ) or self.mediaPlayer.isAudioAvailable(): if event.angleDelta().y() > 0: newval = self.seekSlider.value() - 1000 else: newval = self.seekSlider.value() + 1000 self.seekSlider.setValue(newval) self.seekSlider.setSliderPosition(newval) self.mediaPlayer.setPosition(newval) event.accept() def keyPressEvent(self, event: QKeyEvent) -> None: if self.mediaPlayer.isVideoAvailable( ) or self.mediaPlayer.isAudioAvailable(): addtime = 0 if event.key() == Qt.Key_Left: addtime = -1000 elif event.key() == Qt.Key_PageUp or event.key() == Qt.Key_Up: addtime = -10000 elif event.key() == Qt.Key_Right: addtime = 1000 elif event.key() == Qt.Key_PageDown or event.key() == Qt.Key_Down: addtime = 10000 elif event.key() == Qt.Key_Enter: self.toggleFullscreen() elif event.key( ) == Qt.Key_Escape and self.videoWidget.isFullScreen(): self.videoWidget.setFullScreen(False) if addtime != 0: newval = self.seekSlider.value() + addtime self.seekSlider.setValue(newval) self.seekSlider.setSliderPosition(newval) self.mediaPlayer.setPosition(newval) event.accept() def mousePressEvent(self, event: QMouseEvent) -> None: if event.button() == Qt.BackButton and self.cutStartAction.isEnabled(): self.setCutStart() event.accept() elif event.button( ) == Qt.ForwardButton and self.cutEndAction.isEnabled(): self.setCutEnd() event.accept() else: super(VidCutter, self).mousePressEvent(event) @pyqtSlot(QMediaPlayer.Error) def handleError(self, error: QMediaPlayer.Error) -> None: qApp.restoreOverrideCursor() self.startNew() if error == QMediaPlayer.ResourceError: QMessageBox.critical( self.parent, 'INVALID MEDIA', 'Invalid media file detected at:<br/><br/><b>%s</b><br/><br/>%s' % (self.movieFilename, self.mediaPlayer.errorString())) else: QMessageBox.critical(self.parent, 'ERROR NOTIFICATION', self.mediaPlayer.errorString()) def closeEvent(self, event: QCloseEvent) -> None: self.parent.closeEvent(event)