class SerialPortSelector(QWidget): open_port = pyqtSignal(str, int) close_port = pyqtSignal() def __init__(self, *args): super(SerialPortSelector, self).__init__(*args) self.disabled = False self.init_ui() self.add_ports() def init_ui(self): layout = QHBoxLayout() self.setLayout(layout) self.ports_list_combobox = QComboBox() layout.addWidget(self.ports_list_combobox) self.baud_rate_combobox = QComboBox() self.baud_rate_combobox.addItems([ '300', '600', '1200', '2400', '4800', '9600', '19200', '38400', '43000', '56000', '57600', '115200' ]) self.baud_rate_combobox.setCurrentText('115200') self.baud_rate_combobox.setEditable(True) layout.addWidget(self.baud_rate_combobox) self.open_btn = QPushButton('打开') self.open_btn.clicked.connect(self.handle_open_port) layout.addWidget(self.open_btn) self.refresh_btn = QPushButton('刷新') self.refresh_btn.clicked.connect(self.add_ports) layout.addWidget(self.refresh_btn) def add_ports(self): self.ports_list_combobox.clear() for port in comports(False): self.ports_list_combobox.addItem(port.name, port) def handle_open_port(self): if self.disabled: self.close_port.emit() else: port = self.ports_list_combobox.currentText() if port == "": return baud_rate = int(self.baud_rate_combobox.currentText()) self.open_port.emit(port, baud_rate) def set_disable(self, b): self.disabled = b self.ports_list_combobox.setDisabled(b) self.baud_rate_combobox.setDisabled(b) if self.disabled: self.open_btn.setText('关闭') else: self.open_btn.setText('打开')
class YTdownloader(QWidget): def __init__(self): super().__init__() # setup some flags self.isFetching = False self.isDownloading = False # default output path self.outputPath = f'{QDir.homePath()}/videos' # setup some window specific things self.setWindowTitle('YouTube Downloader') self.setWindowIcon(QIcon('assets/yt-icon.ico')) self.setFixedSize(705, 343) # parent layout layout = QVBoxLayout() layout.setContentsMargins(15, 15, 15, 10) self.setLayout(layout) # top bar layout topBar = QHBoxLayout() # detail section detailSec = QHBoxLayout() metaSec = QVBoxLayout() # download section downloadSec = QHBoxLayout() downloadBtn = QVBoxLayout() # output path link button self.outputBtn = QPushButton('📂 Output Path') self.outputBtn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.outputBtn.setToolTip(self.outputPath) self.outputBtn.clicked.connect(self.setOutputPath) # status bar self.statusBar = QStatusBar() # message box self.message = QMessageBox() # setting up widgets self.urlBox = QLineEdit() self.urlBox.setFocusPolicy(Qt.FocusPolicy.ClickFocus or Qt.FocusPolicy.NoFocus) self.urlBox.setPlaceholderText('🔍 Enter or paste video URL...') self.button = QPushButton('Get') self.button.setDefault(True) self.button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.button.clicked.connect(self.getDetails) # thumbnail pixmap = QPixmap('assets\placeholder.jpg') self.thumb = QLabel() self.thumb.setFixedSize(250, 141) self.thumb.setScaledContents(True) self.thumb.setPixmap(pixmap) # detail widgets self.title = QLabel('Title: ') self.author = QLabel('Author: ') self.length = QLabel('Duration: ') self.publish_date = QLabel('Published: ') # progress bar self.progress_bar = QProgressBar() # download options self.download = QComboBox() self.download.setPlaceholderText('Download Video') self.download.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.download.activated.connect(lambda: self.getContent(0)) self.download.setEnabled(False) # download audio button self.download_audio = QPushButton('Download Audio') self.download_audio.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.download_audio.clicked.connect(lambda: self.getContent(1)) self.download_audio.setEnabled(False) # add widgets and layouts topBar.addWidget(self.urlBox) topBar.addWidget(self.button) # detail section metaSec.addWidget(self.title) metaSec.addWidget(self.author) metaSec.addWidget(self.length) metaSec.addWidget(self.publish_date) detailSec.addWidget(self.thumb) detailSec.addSpacing(20) detailSec.addLayout(metaSec) # download section downloadBtn.addWidget(self.download) downloadBtn.addWidget(self.download_audio) downloadSec.addWidget(self.progress_bar) downloadSec.addSpacing(10) downloadSec.addLayout(downloadBtn) # status bar self.statusBar.setSizeGripEnabled(False) self.statusBar.addPermanentWidget(self.outputBtn) # add content to parent layout layout.addLayout(topBar) layout.addSpacing(20) layout.addLayout(detailSec) layout.addSpacing(5) layout.addLayout(downloadSec) layout.addWidget(self.statusBar) # setup a connection thread to keep checking internet connectivity self.connection = ConnectionThread() self.connection.start() # catch the connection response signal self.connection.con_response.connect(self.connection_slot) # connection slot def connection_slot(self, status): curMsg = self.statusBar.currentMessage() # connection succeeded if status: if curMsg == '🔴 Disconnected': self.statusBar.showMessage('🟢 Connection restored!', 3000) elif curMsg != '🟢 Connected': self.statusBar.showMessage('🟢 Connected') # connection failed elif curMsg == '🟢 Connected': self.statusBar.showMessage('🔴 Connection interrupted!', 3000) elif curMsg != '🔴 Disconnected': self.statusBar.showMessage('🔴 Disconnected') # set output path slot def setOutputPath(self): # update the output path path = str(QFileDialog.getExistingDirectory(self, "Select Output Directory")) if path: self.outputPath = path # update tooltip self.outputBtn.setToolTip(path) # get button slot def getDetails(self): curMsg = self.statusBar.currentMessage() if curMsg == '🔴 Disconnected' or curMsg == '🔴 Connection interrupted!': self.message.critical( self, 'Error', 'Connection failed!\nAre you sure you\'re connected to the internet ? ' ) elif self.button.text() == 'Get': self.button.setText('Stop') # indicate progress bar as busy self.progress_bar.setRange(0, 0) # set fetching flag self.isFetching = True # setup a worker thread to keep UI responsive self.worker = WorkerThread(self.urlBox.text()) self.worker.start() # catch the finished signal self.worker.finished.connect(self.finished_slot) # catch the response signal self.worker.worker_response.connect(self.response_slot) # catch the error signal self.worker.worker_err_response.connect(self.err_slot) elif self.button.text() == 'Stop': if self.isFetching: # stop worker thread self.worker.terminate() # set back the button text self.button.setText('Get') elif self.isDownloading: # stop download thread self.download_thread.terminate() # show the warning message self.message.information( self, 'Interrupted', 'Download interrupted!\nThe process was aborted while the file was being downloaded... ' ) # reset pogress bar self.progress_bar.reset() # download options slot def getContent(self, id): if self.isFetching: # show the warning message self.message.warning( self, 'Warning', 'Please wait!\nWait while the details are being fetched... ' ) else: # disable the download options self.download.setDisabled(True) self.download_audio.setDisabled(True) # set downloading flag self.isDownloading = True # set button to stop self.button.setText('Stop') # setup download thread if id == 0: self.download_thread = DownloadThread(self.yt, self.download.currentText()[:4], self.outputPath) else: self.download_thread = DownloadThread(self.yt, 'audio', self.outputPath) # start the thread self.download_thread.start() # catch the finished signal self.download_thread.finished.connect(self.download_finished_slot) # catch the response signal self.download_thread.download_response.connect(self.download_response_slot) # catch the complete signal self.download_thread.download_complete.connect(self.download_complete_slot) # catch the error signal self.download_thread.download_err.connect(self.download_err_slot) # finished slot def finished_slot(self): # remove progress bar busy indication self.progress_bar.setRange(0, 100) # unset fetching flag self.isFetching = False # response slot def response_slot(self, res): # set back the button text self.button.setText('Get') # save the yt object for speeding up download self.yt = res[0] # set the actual thumbnail of requested video self.thumb.setPixmap(res[1]) # slice the title if it is more than the limit if len(res[2]) > 50: self.title.setText(f'Title: {res[2][:50]}...') else: self.title.setText(f'Title: {res[2]}') # set leftover details self.author.setText(f'Author: {res[3]}') self.length.setText(f'Duration: {timedelta(seconds=res[4])}') self.publish_date.setText(f'Published: {res[5].strftime("%d/%m/%Y")}') # clear any previous items if any self.download.clear() # add resolutions as items to the download button and enable them self.download.addItems([item for item in res[6]]) self.download.setDisabled(False) self.download_audio.setDisabled(False) # error slot def err_slot(self): # show the warning message self.message.warning( self, 'Warning', 'Something went wrong!\nProbably a broken link or some restricted content... ' ) # set back the button text self.button.setText('Get') # download finished slot def download_finished_slot(self): # set back the button text self.button.setText('Get') # now enable the download options self.download.setDisabled(False) self.download_audio.setDisabled(False) # unset downloading flag self.isDownloading = False # reset pogress bar self.progress_bar.reset() # download response slot def download_response_slot(self, per): # update progress bar self.progress_bar.setValue(per) # adjust the font color to maintain the contrast if per > 52: self.progress_bar.setStyleSheet('QProgressBar { color: #fff }') else: self.progress_bar.setStyleSheet('QProgressBar { color: #000 }') # download complete slot def download_complete_slot(self, location): # use native separators location = QDir.toNativeSeparators(location) # show the success message if self.message.information( self, 'Downloaded', f'Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?', QMessageBox.StandardButtons.Open, QMessageBox.StandardButtons.Cancel ) is QMessageBox.StandardButtons.Open: subprocess.Popen(f'explorer /select,{location}') # download error slot def download_err_slot(self): # show the error message self.message.critical( self, 'Error', 'Error!\nSomething unusual happened and was unable to download...' )
class Madoka(QWidget): global serialPort, baud, fmt def __init__(self): global serialPort, baud, fmt self.serialLogState = False self.fmts = ['bin', 'oct', 'dec', 'hex', 'csv', 'csv+', 'ascii'] self.baudList = [ 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600, 76800, 115200, 230400, 460800, 921600, 1000000, 2000000, 4000000 ] fmt = self.fmts.index(fmt) super().__init__() layoutA = QHBoxLayout() self.baudBox = QComboBox() self.getBaud() layoutA.addWidget(self.baudBox) self.formatBox = QComboBox() self.getFormat() layoutA.addWidget(self.formatBox) layoutB = QHBoxLayout() self.portBox = QComboBox() self.getPorts() layoutB.addWidget(self.portBox) parentLayout = QVBoxLayout() parentLayout.addLayout(layoutB) parentLayout.addLayout(layoutA) self.setLayout(parentLayout) self.portBox.currentTextChanged.connect(self.portSelected) self.portBox.setCurrentIndex(len(self.ports) - 1) self.baudBox.currentTextChanged.connect(self.baudSelected) try: self.baudBox.setCurrentIndex(self.baudList.index(baud)) except: self.baudBox.setCurrentIndex(-1) self.formatBox.currentTextChanged.connect(self.formatSelected) self.formatBox.setCurrentIndex(fmt) self.button1 = QPushButton() self.button1.setText("start") self.button1.released.connect(self.btn1Clicked) layoutA.addWidget(self.button1) def portSelected(self, text): global serialPort if (text != ''): serialPort = self.ports[self.portsIndex.index(text)] # print(serialPort) # self.erandano.setText(self.port) def baudSelected(self, text): global baud if (text != ''): baud = text # print(baud) def formatSelected(self, text): global fmt if (text != ''): fmt = self.fmts.index(text) # print(text) def getPorts(self): global serialPort # sys.stderr.write("\n--- Available ports:\n") self.ports = [] self.portsIndex = [] for n, (port, desc, devid) in enumerate(sorted(comports()), 1): # sys.stderr.write("--- {:2}: {:20} {!r} \n".format(n, port, desc)) self.ports.append(port) print(port) self.portsIndex.append("{:2}: {:20} {!r}".format(n, port, desc)) try: serialPort = self.ports.index(serialPort) except: serialPort = self.ports[len(self.ports) - 1] for s in self.portsIndex: self.portBox.addItem(s) def getBaud(self): for b in self.baudList: self.baudBox.addItem(str(b)) def getFormat(self): global fmt for f in self.fmts: self.formatBox.addItem(f) def btn1Clicked(self): if (self.button1.text() == 'start'): self.baudBox.setDisabled(True) self.formatBox.setDisabled(True) self.button1.setText('stop') self.serialLogState = True th = threading.Thread(target=printData, daemon=True) th.start() elif (self.button1.text() == 'stop'): self.button1.setText('start') self.baudBox.setDisabled(False) self.formatBox.setDisabled(False) self.serialLogState = False