def App(): app = QApplication(sys.argv) win = QWidget() win.setWindowTitle("PyQt6 QLabel Example") win.left = 100 win.top = 100 l1 = QLabel("Hello World") l2 = QLabel("Welcome to Python GUI Programming") # # Because you can't instantiate a QLable directly with a QPixmap. # l3 = QLabel() l3.setPixmap(QPixmap("python-small.png")) l1.setAlignment(Qt.Alignment.AlignCenter) l2.setAlignment(Qt.Alignment.AlignCenter) l3.setAlignment(Qt.Alignment.AlignCenter) vbox = QVBoxLayout() vbox.addWidget(l1) vbox.addStretch() vbox.addWidget(l2) vbox.addStretch() vbox.addWidget(l3) vbox.addStretch() win.setLayout(vbox) win.show() sys.exit(app.exec())
class MainWindow(QMainWindow): def __init__(self): """ MainWindow Constructor """ super().__init__() self.initializeUI() def initializeUI(self): """Initialize settings, call functions that define UI elements, and display the main window.""" self.setMinimumSize(700, 400) self.setWindowTitle("GIF and Image Viewer") # Set up the main window, menu, and dock widgets self.setUpMainWindow() self.show() # Display the main window def setUpMainWindow(self): """Set up the application's main window and widgets.""" self.movie = QMovie() # Create movie object self.media_label = QLabel() # Create label to place images/GIFs on self.media_label.setPixmap(QPixmap("icons/image_label.png")) self.media_label.setFrameShape(QFrame.Shape.StyledPanel) self.media_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setCentralWidget(self.media_label)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.title = "Image Viewer" self.setWindowTitle(self.title) self.lastY = 0 self.image = QImage(2080, 1, QImage.Format.Format_Grayscale8) self.label = QLabel(self) self.resize (2080, 500) def updateImage(self, x, y, c): if not self.lastY == y: self.image = self.image.copy(0, 0, 2080, y+1) self.lastY = y self.image.setPixelColor(x,y,QColor(c,c,c)) def refreshImage(self): pixmap = QPixmap.fromImage(self.image) self.label.setPixmap(pixmap) self.setCentralWidget(self.label) def saveImage(self): name = time.strftime("%Y %m %d-%H %M %S") self.image.save('NOAA ' + str(name) + '.png')
def develop(): from PyQt6.QtWidgets import QApplication, QLabel app = QApplication([]) r = RotatingIcon(icon_size=64) l = QLabel() l.setPixmap(r.first_frame) r.updated.connect(l.setPixmap) r.start() l.show() app.exec()
def initUI(self) -> None: self.setGeometry(0, 0, self.width, self.height) self.setWindowTitle('Message box') label = QLabel(self) pixmap = QPixmap(self.file_name) label.setPixmap(pixmap) self.resize(pixmap.width(),pixmap.height()) self.show()
def __init__(self): super().__init__() img = hopper().resize((1000, 1000)) qimage = ImageQt.ImageQt(img) pixmap1 = ImageQt.QPixmap.fromImage(qimage) QHBoxLayout(self) # hbox lbl = QLabel(self) # Segfault in the problem lbl.setPixmap(pixmap1.copy())
def initUI(self): hbox = QHBoxLayout(self) pixmap = QPixmap('sid.jpg') lbl = QLabel(self) lbl.setPixmap(pixmap) hbox.addWidget(lbl) self.setLayout(hbox) self.move(300, 200) self.setWindowTitle('Sid') self.show()
def InitWindow(self): self.setWindowIcon(QtGui.QIcon(self.iconName)) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) vbox = QVBoxLayout() labelImage = QLabel(self) pixmap = QPixmap("resources/pyqt5_image_window-300x180.png") labelImage.setPixmap(pixmap) vbox.addWidget(labelImage) self.setLayout(vbox) self.show()
def onWizardNextButton(self): self.setPage(1, self.page1) self.trained = False itemsTextList = [ str(self.listSelection.mInput.item(i).text()) for i in range(self.listSelection.mInput.count()) ] # Update list if self.parent.classifyExercises is not None: self.parent.classifyExercises.UpdateExerciseList(itemsTextList) # Set elements on UI self.setMinimumWidth(len(itemsTextList) * 200) self.deleteItemsOfLayout(self.hLayout2) self.images.clear() self.labels.clear() self.buttons.clear() self.recordReady.clear() for x, i in zip(itemsTextList, range(len(itemsTextList))): self.exerciseLayouts.append(QVBoxLayout()) self.buttons.append(QPushButton('Record')) self.recordReady.append(False) image = QLabel() image.setPixmap( QPixmap(os.getcwd() + "/resources/images/" + itemsTextList[i] + ".png")) self.labels.append(QLabel(itemsTextList[i])) self.images.append(image) self.buttons[i].setFixedSize(100, 35) self.buttons[i].clicked.connect( functools.partial(self.onRecordExerciseButtonClicked, x, i)) self.buttons[i].setStyleSheet(CustomQStyles.outlineButtonStyle) self.exerciseLayouts[i].addWidget(self.labels[i]) self.exerciseLayouts[i].addWidget(self.images[i]) self.exerciseLayouts[i].addWidget(self.buttons[i]) self.exerciseLayouts[i].setAlignment(self.labels[i], Qt.Alignment.AlignCenter) self.exerciseLayouts[i].setAlignment(self.images[i], Qt.Alignment.AlignCenter) self.exerciseLayouts[i].setAlignment(self.buttons[i], Qt.Alignment.AlignCenter) self.hLayout2.addLayout(self.exerciseLayouts[i])
def makeTitle(): titleLayout = QHBoxLayout() # titleLayout.setSpacing(42) titleFont = QtGui.QFont("Times", 32, QtGui.QFont.Weight.Bold) title = QLabel(opts.timeclockOpts["title"]) title.setFixedWidth(820) title.setFont(titleFont) logo = QLabel() logoImage = QtGui.QPixmap("../data/assets/" + opts.timeclockOpts["logo"]) logoImage = logoImage.scaled(QtCore.QSize(100, 100)) logo.setPixmap(logoImage) if opts.timeclockOpts["darkTheme"]: logo.setStyleSheet("QLabel {background:white}") titleLayout.addWidget(title) titleLayout.addStretch() titleLayout.addWidget(logo) return titleLayout
def show_qr(self, qr: bytes): title_label = QLabel("请使用微信扫描小程序码完成登陆") title_label.setStyleSheet( "QLabel{color:#ffe3ed;border:none;background-color:transparent;border-radius:5px;}" ) title_label.setAlignment(Qt.Alignment.AlignCenter) title_label.setFixedHeight(20) qr_label = QLabel() pixmap = QPixmap() pixmap.loadFromData(qr) qr_label.setPixmap(pixmap) qr_label.setStyleSheet( "QLabel{color:#ffe3ed;border:none;background-color:transparent;border-radius:5px;}" ) layout_ = QVBoxLayout() layout_.addWidget(title_label, 1) layout_.addWidget(qr_label, 9) self.qr_dialog = QWidget(self) self.qr_dialog.setLayout(layout_) self.main_layout.addWidget(self.qr_dialog, 1, 1, Qt.Alignment.AlignCenter) self.qr_dialog.show()
class Example(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): sld = QSlider(Qt.Orientation.Horizontal, self) sld.setFocusPolicy(Qt.FocusPolicy.NoFocus) sld.setGeometry(30, 40, 200, 30) sld.valueChanged[int].connect(self.changeValue) self.label = QLabel(self) self.label.setPixmap(QPixmap('mute.png')) self.label.setGeometry(250, 40, 80, 30) self.setGeometry(300, 300, 350, 250) self.setWindowTitle('QSlider') self.show() def changeValue(self, value): if value == 0: self.label.setPixmap(QPixmap('mute.png')) elif 0 < value <= 30: self.label.setPixmap(QPixmap('min.png')) elif 30 < value < 80: self.label.setPixmap(QPixmap('med.png')) else: self.label.setPixmap(QPixmap('max.png'))
def test_sanity(tmp_path): # Segfault test app = QApplication([]) ex = Example() assert app # Silence warning assert ex # Silence warning for mode in ("1", "RGB", "RGBA", "L", "P"): # to QPixmap im = hopper(mode) data = ImageQt.toqpixmap(im) assert isinstance(data, QPixmap) assert not data.isNull() # Test saving the file tempfile = str(tmp_path / f"temp_{mode}.png") data.save(tempfile) # Render the image qimage = ImageQt.ImageQt(im) data = QPixmap.fromImage(qimage) qt_format = QImage.Format if ImageQt.qt_version == "6" else QImage qimage = QImage(128, 128, qt_format.Format_ARGB32) painter = QPainter(qimage) image_label = QLabel() image_label.setPixmap(data) image_label.render(painter, QPoint(0, 0), QRegion(0, 0, 128, 128)) painter.end() rendered_tempfile = str(tmp_path / f"temp_rendered_{mode}.png") qimage.save(rendered_tempfile) assert_image_equal_tofile(im.convert("RGBA"), rendered_tempfile) # from QPixmap roundtrip(hopper(mode)) app.quit() app = None
def displayTag(self, tagName): tagWidget = QWidget() tagWidget.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) tagWidget.enterEvent = lambda e: self.setCursor( QCursor(Qt.CursorShape.PointingHandCursor)) tagWidget.leaveEvent = lambda e: self.setCursor( QCursor(Qt.CursorShape.ArrowCursor)) tagWidget.mouseReleaseEvent = lambda e: self.removeTag( self.flowLayout.indexOf(tagWidget), returnTag=False) self.renderStyleSheet(tagWidget) hBoxTag = QHBoxLayout() tagLabel = QLabel() tagLabel.setText(tagName) tagLabel.setStyleSheet(f''' QLabel {{ background-color: transparent; border: none; }} ''') hBoxTag.addWidget(tagLabel) crossIcon = QPixmap('MangoUI/TagBox/img/crossresized.png') crossIconLabel = QLabel() crossIconLabel.setPixmap(crossIcon) crossIconLabel.setStyleSheet(f''' QLabel {{ background-color: transparent; border: none; }} ''') hBoxTag.addWidget(crossIconLabel) hBoxTag.setContentsMargins(10, 6, 6, 6) tagWidget.setLayout(hBoxTag) self.flowLayout.addWidget(tagWidget)
class FileDialogDemo(QWidget): def __init__(self, parent=None): super(FileDialogDemo, self).__init__(parent) layout = QVBoxLayout() self.button = QPushButton("加载图片") self.button.clicked.connect(self.get_file) layout.addWidget(self.button) self.le = QLabel("") layout.addWidget(self.le) self.button1 = QPushButton("加载文本文件") self.button1.clicked.connect(self.get_files) layout.addWidget(self.button1) self.contents = QTextEdit() layout.addWidget(self.contents) self.setLayout(layout) self.setWindowTitle("File Dialog 实例") def get_file(self): fname, _ = QFileDialog.getOpenFileName(self, "Open file", 'C:\\', 'Image files (*.jpg &.gif)') self.le.setPixmap(QPixmap(fname)) def get_files(self): dialog = QFileDialog() dialog.setFileMode(QFileDialog.FileMode.AnyFile) dialog.setFilter(QDir.Filter.Files) if dialog.exec(): filenames = dialog.selectedFiles() f = open(filenames[0], 'r') with f: data = f.read() self.contents.setText(data)
class B23Download(QWidget): def __init__(self): super(B23Download, self).__init__() # setup some flags self.is_fetching = False self.is_downloading = False # default output path basepath = os.path.dirname(os.path.abspath(__file__)) path = os.path.join(basepath, "videos") self.output_path = path # setup some window specific things self.setWindowTitle("Bilibili Favorite Downloader") self.setWindowIcon(QIcon("images/icon_bilibili.ico")) self.setFixedSize(705, 343) # parent layout main_layout = QVBoxLayout() main_layout.setContentsMargins(15, 15, 15, 10) self.setLayout(main_layout) # top bar layout top_layout = QHBoxLayout() # detail section mid_main_layout = QHBoxLayout() mid_right_layout = QVBoxLayout() # download section bottom_main_layout = QHBoxLayout() bottom_right_layout = QVBoxLayout() # output path link button self.output_btn = QPushButton("📂 Output Path") self.output_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.output_btn.setToolTip(self.output_path) self.output_btn.clicked.connect(self.set_output_path) # status bar self.status_bar = QStatusBar() # message box self.message_box = QMessageBox() # setting up widgets self.url_edit = QLineEdit() self.url_edit.setPlaceholderText("🔍 Enter or paste favorite URL...") self.get_btn = QPushButton("Get") self.get_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.get_btn.clicked.connect(self.get_details) # thumbnail pixmap = QPixmap("images/placeholder.png") 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("Videos: ") self.publish_date = QLabel("Published: ") # progress bar self.progress_bar = QProgressBar() # download options self.download_btn = QPushButton(" Download Videos ") self.download_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.download_btn.clicked.connect(self.get_content) self.download_btn.setEnabled(False) self.download_btn.setShortcut("Ctrl+Return") self.download_btn.setMinimumWidth(200) # add widgets and layouts top_layout.addWidget(self.url_edit) top_layout.addWidget(self.get_btn) # detail section mid_right_layout.addWidget(self.title) mid_right_layout.addWidget(self.author) mid_right_layout.addWidget(self.length) mid_right_layout.addWidget(self.publish_date) mid_main_layout.addWidget(self.thumb) mid_main_layout.addSpacing(20) mid_main_layout.addLayout(mid_right_layout) # download section bottom_right_layout.addWidget(self.download_btn) bottom_main_layout.addWidget(self.progress_bar) bottom_main_layout.addSpacing(10) bottom_main_layout.addLayout(bottom_right_layout) # status bar self.status_bar.setSizeGripEnabled(False) self.status_bar.addPermanentWidget(self.output_btn) # add content to parent layout main_layout.addLayout(top_layout) main_layout.addSpacing(20) main_layout.addLayout(mid_main_layout) main_layout.addSpacing(5) main_layout.addLayout(bottom_main_layout) main_layout.addWidget(self.status_bar) # set output path slot def set_output_path(self): # update the output path path = str( QFileDialog.getExistingDirectory(self, "Select Output Directory")) if path: self.output_path = path # update tooltip self.output_btn.setToolTip(path) # get button slot def get_details(self): text = self.url_edit.text().strip() if not text: return if text.find("fid") < 0: self.message_box.warning( self, "Error", ("Input a correct favorite URL!\n" "For example: https://space.bilibili.com/xxx/favlist?fid=xxx..." ), ) return if self.get_btn.text() == "Get": self.get_btn.setText("Stop") # indicate progress bar as busy self.progress_bar.setRange(0, 0) # set fetching flag self.is_fetching = True # setup a worker thread to keep UI responsive self.media_id = text.split("fid=")[-1].split("&")[0] self.worker = WorkerThread(self.media_id) 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.get_btn.text() == "Stop": if self.is_fetching: # stop worker thread self.worker.terminate() # set back the get_btn text self.get_btn.setText("Get") elif self.is_downloading: # stop download thread self.download_thread.terminate() # show the warning message_box self.message_box.information( self, "Interrupted", "Download interrupted!\nThe process was aborted while the file was being downloaded... ", ) # reset progress bar self.progress_bar.reset() # download options slot def get_content(self): if self.is_fetching: # show the warning message self.message_box.critical( self, "Error", "Please wait!\nWait while the details are being fetched... ", ) else: # disable the download options self.download_btn.setDisabled(True) # set downloading flag self.is_downloading = True # set button to stop self.get_btn.setText("Stop") self.download_thread = DownloadThread( self.media_id, self.media_counts, self.first_page_medias, self.output_path, ) # 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) # handling enter key for get/stop button def keyPressEvent(self, event): self.url_edit.setFocus() if (event.key() == Qt.Key.Key_Enter.value or event.key() == Qt.Key.Key_Return.value): self.get_details() # finished slot def finished_slot(self): # remove progress bar busy indication self.progress_bar.setRange(0, 100) # unset fetching flag self.is_fetching = False # response slot def response_slot(self, res): # set back the button text self.get_btn.setText("Get") # set the actual thumbnail of requested video self.thumb.setPixmap(res.thumb_img) # slice the title if it is more than the limit if len(res.title) > 50: self.title.setText(f"Title: {res.title[:50]}...") else: self.title.setText(f"Title: {res.title}") # cache first page medias self.first_page_medias = res.medias self.media_counts = res.media_counts # set leftover details self.author.setText(f"Author: {res.author}") self.length.setText(f"Videos: {res.media_counts}") self.publish_date.setText( f'Published: {time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(res.publish_date))}' ) self.download_btn.setDisabled(False) # error slot def err_slot(self): # show the warning message self.message_box.warning( self, "Warning", "Something went wrong!\nProbably a broken link or some restricted content... ", ) # set back the button text self.get_btn.setText("Get") # download finished slot def download_finished_slot(self): # set back the button text self.get_btn.setText("Get") # now enable the download options self.download_btn.setDisabled(False) # unset downloading flag self.is_downloading = 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_box.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_box.critical( self, "Error", "Error!\nSomething unusual happened and was unable to download...", )
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...' )
def initUI(self): self.setWindowTitle("关于 lanzou-gui") about = f'本项目使用PyQt6实现图形界面,可以完成蓝奏云的大部分功能<br/> \ 得益于 <a href="{self._api_url}">API</a> 的功能,可以间接突破单文件最大 100MB 的限制,同时增加了批量上传/下载的功能<br/> \ Python 依赖见<a href="{self._github }/blob/master/requirements.txt">requirements.txt</a>,\ <a href="{self._github}/releases">releases</a> 有打包好了的 Windows 可执行程序,但可能不是最新的' project_url = f'<a href="{self._home_page}">主页</a> | <a href="{self._github}">repo</a> | \ <a href="{self._gitee}">mirror repo</a>' self.logo = QLabel() # logo self.logo.setPixmap(QPixmap(SRC_DIR + "logo2.gif")) self.logo.setStyleSheet("background-color:rgb(255,255,255);") self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter) self.lb_qt_ver = QLabel("依赖") # QT 版本 self.lb_qt_text = QLabel( f"QT: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}") # QT 版本 self.lb_name = QLabel("版本") # 版本 self.lb_name_text = QPushButton("") # 版本 self.lb_name_text.setToolTip("点击检查更新") ver_style = "QPushButton {border:none; background:transparent;font-weight:bold;color:blue;}" self.lb_name_text.setStyleSheet(ver_style) self.lb_name_text.clicked.connect( lambda: self.check_update.emit(self._ver, True)) self.lb_about = QLabel("关于") # about self.lb_about_text = QLabel() self.lb_about_text.setText(about) self.lb_about_text.setOpenExternalLinks(True) self.lb_author = QLabel("作者") # author self.lb_author_mail = QLabel( "<a href='mailto:[email protected]'>rachpt</a>") self.lb_author_mail.setOpenExternalLinks(True) self.lb_update = QLabel("项目") # 更新 self.lb_update_url = QLabel(project_url) self.lb_update_url.setOpenExternalLinks(True) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.StandardButton.Close) self.buttonBox.button( QDialogButtonBox.StandardButton.Close).setText("关闭") self.buttonBox.rejected.connect(self.reject) self.buttonBox.setStyleSheet(btn_style) self.recommend = QLabel( "<br />大文件推荐使用 <a href='https://github.com/Aruelius/cloud189'>cloud189-cli</a>" ) self.recommend.setOpenExternalLinks(True) self.line = QLine(QPoint(), QPoint(550, 0)) self.lb_line = QLabel('<html><hr /></html>') vbox = QVBoxLayout() vbox.addWidget(self.logo) vbox.addStretch(1) self.form = QFormLayout() self.form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) self.form.setFormAlignment(Qt.AlignmentFlag.AlignLeft) self.form.setHorizontalSpacing(40) self.form.setVerticalSpacing(15) self.form.addRow(self.lb_qt_ver, self.lb_qt_text) self.form.addRow(self.lb_name, self.lb_name_text) self.form.addRow(self.lb_update, self.lb_update_url) self.form.addRow(self.lb_author, self.lb_author_mail) self.form.addRow(self.lb_about, self.lb_about_text) self.form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy. AllNonFixedFieldsGrow) # 覆盖MacOS的默认样式 vbox.addLayout(self.form) vbox.addStretch(1) vbox.addWidget(self.recommend) vbox.addWidget(self.lb_line) donate = QLabel() donate.setText("<b>捐助我</b> 如果你愿意") donate.setAlignment(Qt.AlignmentFlag.AlignCenter) hbox = QHBoxLayout() hbox.addStretch(2) for it in ["wechat", "alipay", "qqpay"]: lb = QLabel() lb.setPixmap(QPixmap(SRC_DIR + f"{it}.jpg")) hbox.addWidget(lb) hbox.addStretch(1) hbox.addWidget(self.buttonBox) vbox.addWidget(donate) vbox.addLayout(hbox) self.setLayout(vbox) self.setMinimumWidth(720)
class MergeFileDialog(QDialog): check_update = pyqtSignal(str, bool) def __init__(self, user_home, parent=None): super(MergeFileDialog, self).__init__(parent) self.cwd = user_home self.selected = "" self.initUI() self.setStyleSheet(others_style) def initUI(self): self.setWindowTitle("合并文件") self.setWindowIcon(QIcon(SRC_DIR + "upload.ico")) self.logo = QLabel() self.logo.setPixmap(QPixmap(SRC_DIR + "logo3.gif")) self.logo.setStyleSheet("background-color:rgb(0,153,255);") self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter) # lable self.choose_lb = QLabel("选择文件夹") # folder self.choose_folder = MyLineEdit(self) self.choose_folder.setObjectName("choose_folder") self.choose_folder.clicked.connect(self.slot_choose_folder) self.status = QLabel(self) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("提取") self.buttonBox.button( QDialogButtonBox.StandardButton.Cancel).setText("关闭") self.buttonBox.setStyleSheet(btn_style) vbox = QVBoxLayout() hbox_head = QHBoxLayout() hbox_button = QHBoxLayout() hbox_head.addWidget(self.choose_lb) hbox_head.addWidget(self.choose_folder) hbox_button.addWidget(self.buttonBox) vbox.addWidget(self.logo) vbox.addStretch(1) vbox.addWidget(self.status) vbox.addLayout(hbox_head) vbox.addStretch(1) vbox.addLayout(hbox_button) self.setLayout(vbox) self.setMinimumWidth(350) # 设置信号 self.buttonBox.accepted.connect(self.slot_btn_ok) self.buttonBox.rejected.connect(self.slot_btn_no) self.buttonBox.rejected.connect(self.reject) def slot_choose_folder(self): dir_choose = QFileDialog.getExistingDirectory(self, "选择文件夹", self.cwd) # 起始路径 if dir_choose == "": return self.selected = dir_choose self.choose_folder.setText(self.selected) self.status.setText("") self.cwd = os.path.dirname(dir_choose) def slot_btn_no(self): self.selected = "" self.choose_folder.setText(self.selected) self.status.setText("") def slot_btn_ok(self): if self.selected: success, msg = un_serialize(self.selected) if success: text = "提取成功✅" else: text = f"提取失败❌, {msg}" else: text = "未选择文件夹📂" self.status.setText(text)
def initUI(self): self.setWindowTitle("设置") logo = QLabel() logo.setPixmap(QPixmap(SRC_DIR + "logo2.gif")) logo.setStyleSheet("background-color:rgb(255,255,255);") logo.setAlignment(Qt.AlignmentFlag.AlignCenter) self.download_threads_lb = QLabel("同时下载文件数") self.download_threads_var = QLineEdit() self.download_threads_var.setPlaceholderText("范围:1-9") self.download_threads_var.setToolTip("范围:1-9") self.download_threads_var.setInputMask("D") self.max_size_lb = QLabel("分卷大小(MB)") self.max_size_var = QLineEdit() self.max_size_var.setPlaceholderText("普通用户最大100,vip用户根据具体情况设置") self.max_size_var.setToolTip("普通用户最大100,vip用户根据具体情况设置") self.max_size_var.setInputMask("D99") self.timeout_lb = QLabel("请求超时(秒)") self.timeout_var = QLineEdit() self.timeout_var.setPlaceholderText("范围:1-99") self.timeout_var.setToolTip("范围:1-99") self.timeout_var.setInputMask("D9") self.upload_delay_lb = QLabel("上传延时(秒)") self.upload_delay_var = QLineEdit() self.upload_delay_var.setPlaceholderText("范围:1-99") self.upload_delay_var.setToolTip("范围:1-99") self.upload_delay_var.setInputMask("D9") self.dl_path_lb = QLabel("下载保存路径") self.dl_path_var = MyLineEdit(self) self.dl_path_var.clicked.connect(self.set_download_path) self.time_fmt_box = QCheckBox("使用[年-月-日]时间格式") self.time_fmt_box.setToolTip("文件上传日期显示格式") self.to_tray_box = QCheckBox("关闭到系统托盘") self.to_tray_box.setToolTip("点击关闭软件按钮是最小化软件至系统托盘") self.watch_clipboard_box = QCheckBox("监听系统剪切板") self.watch_clipboard_box.setToolTip("检测到系统剪切板中有符合规范的蓝奏链接时自动唤起软件,并提取") self.debug_box = QCheckBox("开启调试日志") self.debug_box.setToolTip("记录软件 debug 信息至 debug-lanzou-gui.log 文件") self.set_pwd_box = QCheckBox("上传文件自动设置密码") self.set_pwd_var = AutoResizingTextEdit() self.set_pwd_var.setPlaceholderText(" 2-8 位数字或字母") self.set_pwd_var.setToolTip("2-8 位数字或字母") self.set_desc_box = QCheckBox("上传文件自动设置描述") self.set_desc_var = AutoResizingTextEdit() self.big_file_box = QCheckBox(f"允许上传超过 {self.max_size}MB 的大文件") self.big_file_box.setToolTip("开启大文件上传支持 (功能下线)") self.upgrade_box = QCheckBox("自动检测新版本") self.upgrade_box.setToolTip("在软件打开时自动检测是否有新的版本发布,如有则弹出更新信息") self.time_fmt_box.toggle() self.time_fmt_box.stateChanged.connect(self.change_time_fmt) self.to_tray_box.stateChanged.connect(self.change_to_tray) self.watch_clipboard_box.stateChanged.connect(self.change_watch_clipboard) self.debug_box.stateChanged.connect(self.change_debug) self.set_pwd_box.stateChanged.connect(self.change_set_pwd) self.set_pwd_var.editingFinished.connect(self.check_pwd) self.set_desc_box.stateChanged.connect(self.change_set_desc) self.big_file_box.stateChanged.connect(self.change_big_file) self.upgrade_box.stateChanged.connect(self.change_upgrade) buttonBox = QDialogButtonBox() buttonBox.setOrientation(Qt.Orientation.Horizontal) buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Reset | QDialogButtonBox.StandardButton.Save | QDialogButtonBox.StandardButton.Cancel) buttonBox.button(QDialogButtonBox.StandardButton.Reset).setText("重置") buttonBox.button(QDialogButtonBox.StandardButton.Save).setText("保存") buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消") buttonBox.button(QDialogButtonBox.StandardButton.Reset).clicked.connect(lambda: self.set_values(reset=True)) buttonBox.button(QDialogButtonBox.StandardButton.Save).clicked.connect(self.slot_save) buttonBox.rejected.connect(self.reject) form = QFormLayout() form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) # 覆盖MacOS的默认样式 form.setSpacing(10) form.addRow(self.download_threads_lb, self.download_threads_var) form.addRow(self.timeout_lb, self.timeout_var) form.addRow(self.upload_delay_lb, self.upload_delay_var) form.addRow(self.max_size_lb, self.max_size_var) form.addRow(self.dl_path_lb, self.dl_path_var) vbox = QVBoxLayout() vbox.addWidget(logo) vbox.addStretch(1) vbox.addLayout(form) vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(self.time_fmt_box) hbox.addWidget(self.to_tray_box) hbox.addWidget(self.watch_clipboard_box) hbox.addWidget(self.debug_box) vbox.addLayout(hbox) vbox.addStretch(1) hbox_2 = QHBoxLayout() hbox_2.addWidget(self.set_pwd_box) hbox_2.addWidget(self.set_pwd_var) vbox.addLayout(hbox_2) vbox.addStretch(1) hbox_3 = QHBoxLayout() hbox_3.addWidget(self.set_desc_box) hbox_3.addWidget(self.set_desc_var) vbox.addLayout(hbox_3) hbox_4 = QHBoxLayout() hbox_4.addWidget(self.big_file_box) hbox_4.addWidget(self.upgrade_box) vbox.addStretch(1) vbox.addLayout(hbox_4) vbox.addStretch(2) vbox.addWidget(buttonBox) self.setLayout(vbox) self.setMinimumWidth(500)
class MainWindow(QMainWindow): def __init__(self): """ MainWindow Constructor """ super().__init__() self.initializeUI() def initializeUI(self): """Initialize settings, call functions that define UI elements, and display the main window.""" self.setMinimumSize(700, 400) self.setWindowTitle("GIF and Image Viewer") # Set up the main window and dock widget self.setUpMainWindow() self.displayFilesDock() self.show() # Display the main window def setUpMainWindow(self): """Set up the application's main window and widgets.""" self.movie = QMovie() # Create movie object self.media_label = QLabel() # Create label to place images/GIFs on self.media_label.setPixmap(QPixmap("icons/image_label.png")) self.media_label.setFrameShape(QFrame.Shape.StyledPanel) self.media_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setCentralWidget(self.media_label) def displayFilesDock(self): """Dock widget that displays the movie file location in a QLineEdit widget, provides a button for opening directories with images and GIFs, and shows the media from the selected folder in a QTreeWidget.""" self.files_dock = QDockWidget() self.files_dock.setWindowTitle("Media Folder") self.files_dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea) folder_label = QLabel("Media Location:") # The QLineEdit widget is set to read-only as a quick way to display # the folder path self.folder_line = QLineEdit() self.folder_line.setMinimumWidth(100) self.folder_line.setReadOnly(True) open_button = QPushButton("Open...") folder_h_box = QHBoxLayout() folder_h_box.addWidget(folder_label) folder_h_box.addWidget(self.folder_line) folder_h_box.addWidget(open_button) self.files_tree = QTreeWidget() self.files_tree.setHeaderLabel("Media Files") self.files_tree.setColumnCount(1) # Set up the dock's layout dock_v_box = QVBoxLayout() dock_v_box.addLayout(folder_h_box) dock_v_box.addWidget(self.files_tree) dock_container = QWidget() dock_container.setLayout(dock_v_box) self.files_dock.setWidget(dock_container) self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.files_dock)
class HIMOApp(QMainWindow): EXIT_CODE_REBOOT = -12345678 # or whatever number not already taken def __init__(self): super().__init__() self.statusBar = QStatusBar() self.restartProcessButton = QPushButton('START Myo Connect') self.iconLabel = QLabel() self.informationLabel = QLabel( 'App current information is displayed here...') self.left = int(self.screen().size().width() / 4) self.top = int(self.screen().size().height() / 4) self.width = 640 self.height = 450 self.title = 'Myo Exercises' if MyoService.check_if_process_running(): print("Myo Connect is running!") self.classifyExercises = ClassifyExercises( # subject="Ervin", # nr_of_samples=number_of_samples, epochs=300, # nr_of_gestures=4, exercises=PREDEFINED_EXERCISES, # batch_size=50, training_batch_size=16) else: self.classifyExercises = None # self.informationLabel.setText('Myo Connect is not running! Please start process.') self.table_widget = MainTabWidget(self) self.initUI() self.setStyleSheet("background-color: white;") def initUI(self): self.setWindowTitle(self.title) self.setMinimumHeight(self.height) self.setMinimumWidth(self.width) bar = self.menuBar() about = bar.addMenu("About") config = bar.addMenu("Configuration") info = QAction("App Information", self) about.triggered[QAction].connect(self.aboutThis) about.addAction(info) configAction = QAction("System configuration", self) config.triggered[QAction].connect(self.configWindow) config.addAction(configAction) self.setGeometry(self.left, self.top, self.width, self.height) self.setCentralWidget(self.table_widget) widget = QWidget() container = QHBoxLayout() icon = self.style().standardIcon( QStyle.StandardPixmap.SP_MessageBoxInformation) self.iconLabel.setPixmap(icon.pixmap(QSize(16, 16))) self.restartProcessButton.clicked.connect(self.restartProcess) container.setSpacing(5) container.addWidget(self.iconLabel) container.addWidget(self.informationLabel) if self.classifyExercises is None: self.informationLabel.setText( 'Myo Connect is not running!Please start process.') container.addWidget(self.restartProcessButton) widget.setLayout(container) self.statusBar.addWidget(widget) self.setStatusBar(self.statusBar) self.show() def restartProcess(self): MyoService.start_process() def aboutThis(self, q): # TODO: open dialog with app informations print(q.text() + " is triggered") def configWindow(self): print("config..") widget = ConfigDialog(self) res = widget.exec()
class UIComicListWidget(QWidget): def __init__(self, comic_info: ComicInfo, tab_widget: QTabWidget, down_v_box_layout: QVBoxLayout): super().__init__() self.tabWidget = tab_widget self.comicInfo = comic_info self.down_v_box_layout = down_v_box_layout self.setMinimumHeight(200) # 图片 img = QImage.fromData(comic_info.cover) self.img_label = QLabel(self) self.img_label.setScaledContents(True) w, h = image_resize(comic_info.cover, height=200) self.img_label.resize(QtCore.QSize(w, h)) self.img_label.setGeometry(5, 5, w, h) self.img_label.setPixmap(QPixmap.fromImage(img)) # self.img_label.setPixmap(QtGui.QPixmap("/Users/bo/my/tmp/老夫子2/第1卷/1.jpg")) # 标题 self.title = ButtonQLabel(self) self.title.onclick(self.add_tab) self.title.setText(comic_info.title) self.title.setGeometry(180, 10, 550, 35) title_font = QtGui.QFont() title_font.setPointSize(30) title_font.setBold(True) title_font.setUnderline(True) self.title.setFont(title_font) self.title.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.PointingHandCursor)) # 作者 self.author = QLabel(self) self.author.setText("作者 : " + comic_info.author) self.author.setGeometry(180, 50, 250, 20) # 状态 self.status = QLabel(self) self.status.setText("更新状态 : " + comic_info.status) self.status.setGeometry(500, 50, 150, 20) # 热度 self.status = QLabel(self) self.status.setText("热度 : " + str(comic_info.heat)) self.status.setGeometry(800, 50, 100, 20) # 类型 self.tip = QLabel(self) self.tip.setText("类型 : " + comic_info.tip) self.tip.setGeometry(180, 70, 250, 20) # web self.domain = QLabel(self) self.domain.setText(f"查看原网页 : {comic_info.domain}") self.domain.setText(f'查看原网页 : <a href="{comic_info.url}">{comic_info.domain}</a>') self.domain.setGeometry(500, 70, 250, 20) self.domain.setOpenExternalLinks(True) # 描述 self.describe = QLabel(self) self.describe.setText(" " + comic_info.describe) self.describe.setGeometry(180, 90, 664, 110) self.describe.setWordWrap(True) # 对齐方式 self.describe.setAlignment( QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop) def add_tab(self): if self.comicInfo.url in constant.OPEN_TAB: self.tabWidget.setCurrentIndex(constant.OPEN_TAB.index(self.comicInfo.url) + 3) else: info = UIComicInfoWidget(self.comicInfo, self.down_v_box_layout) self.tabWidget.setCurrentIndex(self.tabWidget.addTab(info, self.comicInfo.title)) constant.OPEN_TAB.append(self.comicInfo.url)
def initUI(self): self.setWindowTitle("登录蓝奏云") self.setWindowIcon(QIcon(SRC_DIR + "login.ico")) logo = QLabel() logo.setPixmap(QPixmap(SRC_DIR + "logo3.gif")) logo.setStyleSheet("background-color:rgb(0,153,255);") logo.setAlignment(Qt.AlignmentFlag.AlignCenter) self.tabs = QTabWidget() self.auto_tab = QWidget() self.hand_tab = QWidget() # Add tabs self.tabs.addTab(self.auto_tab,"自动获取Cookie") self.tabs.addTab(self.hand_tab,"手动输入Cookie") self.auto_get_cookie_ok = AutoResizingTextEdit("🔶点击👇自动获取浏览器登录信息👇") self.auto_get_cookie_ok.setReadOnly(True) self.auto_get_cookie_btn = QPushButton("自动读取浏览器登录信息") auto_cookie_notice = '支持浏览器:Chrome, Chromium, Opera, Edge, Firefox' self.auto_get_cookie_btn.setToolTip(auto_cookie_notice) self.auto_get_cookie_btn.clicked.connect(self.call_auto_get_cookie) self.auto_get_cookie_btn.setStyleSheet("QPushButton {min-width: 210px;max-width: 210px;}") self.name_lb = QLabel("&U 用户") self.name_lb.setAlignment(Qt.AlignmentFlag.AlignCenter) self.name_ed = QLineEdit() self.name_lb.setBuddy(self.name_ed) self.pwd_lb = QLabel("&P 密码") self.pwd_lb.setAlignment(Qt.AlignmentFlag.AlignCenter) self.pwd_ed = QLineEdit() self.pwd_ed.setEchoMode(QLineEdit.EchoMode.Password) self.pwd_lb.setBuddy(self.pwd_ed) self.cookie_lb = QLabel("&Cookie") self.cookie_ed = QTextEdit() notice = "由于滑动验证的存在,需要输入cookie,cookie请使用浏览器获取\n" + \ "cookie会保存在本地,下次使用。其格式如下:\n ylogin=value1; phpdisk_info=value2" self.cookie_ed.setPlaceholderText(notice) self.cookie_lb.setBuddy(self.cookie_ed) self.show_input_cookie_btn = QPushButton("显示Cookie输入框") self.show_input_cookie_btn.setToolTip(notice) self.show_input_cookie_btn.setStyleSheet("QPushButton {min-width: 110px;max-width: 110px;}") self.show_input_cookie_btn.clicked.connect(self.change_show_input_cookie) self.ok_btn = QPushButton("登录") self.ok_btn.clicked.connect(self.change_ok_btn) self.cancel_btn = QPushButton("取消") self.cancel_btn.clicked.connect(self.change_cancel_btn) lb_line_1 = QLabel() lb_line_1.setText('<html><hr />切换用户</html>') lb_line_2 = QLabel() lb_line_2.setText('<html><hr /></html>') self.form = QFormLayout() self.form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) self.form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) # 覆盖MacOS的默认样式 self.form.addRow(self.name_lb, self.name_ed) self.form.addRow(self.pwd_lb, self.pwd_ed) if is_windows: def set_assister_path(): """设置辅助登录程序路径""" assister_path = QFileDialog.getOpenFileName(self, "选择辅助登录程序路径", self._cwd, "EXE Files (*.exe)") if not assister_path[0]: return None assister_path = os.path.normpath(assister_path[0]) # windows backslash if assister_path == self._cookie_assister: return None self.assister_ed.setText(assister_path) self._cookie_assister = assister_path self.assister_lb = QLabel("登录辅助程序") self.assister_lb.setAlignment(Qt.AlignmentFlag.AlignCenter) self.assister_ed = MyLineEdit(self) self.assister_ed.setText(self._cookie_assister) self.assister_ed.clicked.connect(set_assister_path) self.assister_lb.setBuddy(self.assister_ed) self.form.addRow(self.assister_lb, self.assister_ed) hbox = QHBoxLayout() hbox.addWidget(self.show_input_cookie_btn) hbox.addStretch(1) hbox.addWidget(self.ok_btn) hbox.addWidget(self.cancel_btn) user_box = QHBoxLayout() self.user_num = 0 self.user_btns = {} for user in self._config.users_name: user = str(user) # TODO: 可能需要删掉 self.user_btns[user] = QDoublePushButton(user) self.user_btns[user].setStyleSheet("QPushButton {border:none;}") if user == self._config.name: self.user_btns[user].setStyleSheet("QPushButton {background-color:rgb(0,153,2);}") self.tabs.setCurrentIndex(1) self.user_btns[user].setToolTip(f"点击选中,双击切换至用户:{user}") self.user_btns[user].doubleClicked.connect(self.choose_user) self.user_btns[user].clicked.connect(self.delete_chose_user) user_box.addWidget(self.user_btns[user]) self.user_num += 1 user_box.addStretch(1) self.layout = QVBoxLayout(self) self.layout.addWidget(logo) vbox = QVBoxLayout() if self._config.name: vbox.addWidget(lb_line_1) user_box.setAlignment(Qt.AlignmentFlag.AlignCenter) vbox.addLayout(user_box) vbox.addWidget(lb_line_2) if self.user_num > 1: self.del_user_btn = QPushButton("删除账户") self.del_user_btn.setIcon(QIcon(SRC_DIR + "delete.ico")) self.del_user_btn.setStyleSheet("QPushButton {min-width: 180px;max-width: 180px;}") self.del_user_btn.clicked.connect(self.call_del_chose_user) vbox.addWidget(self.del_user_btn) else: self.del_user_btn = None vbox.addStretch(1) vbox.addLayout(self.form) vbox.addStretch(1) vbox.addLayout(hbox) vbox.setAlignment(Qt.AlignmentFlag.AlignCenter) self.hand_tab.setLayout(vbox) auto_cookie_vbox = QVBoxLayout() auto_cookie_vbox.addWidget(self.auto_get_cookie_ok) auto_cookie_vbox.addWidget(self.auto_get_cookie_btn) auto_cookie_vbox.setAlignment(Qt.AlignmentFlag.AlignCenter) self.auto_tab.setLayout(auto_cookie_vbox) self.layout.addWidget(self.tabs) self.setLayout(self.layout) self.update_selection(self._config.name)
class MainWindow(QMainWindow): # Create a QSettings object rather than storing values. Pass in a company # name and an application name settings = QSettings("Custom GUIs", "Image Manager GUI") #print(settings.fileName()) # NOTE: Uncomment to print the path to settings images_path = "Images" # File path to the Images directory image_dir = QDir(images_path) info_dialog = None # Create variable for modeless dialog def __init__(self): """MainWindow Constructor for Image Manager""" super().__init__() # Constructor for QMainWindow self.initializeUI() def initializeUI(self): """Set up the GUI's main window and load initial settings and data.""" self.setWindowTitle("Image Manager") self.setObjectName("ImageManager") # Set up the main window, menu, dock widgets, and initialize the GUI's settings self.setUpMainWindow() self.displayImagePreviewDock() self.createActions() self.createMenus() self.loadStoredImageData() self.getInitialSettings() self.show() # Display the main window def setUpMainWindow(self): """Set up the application's main window containing the QListWidget.""" self.image_view_lw = ImageViewerListWidget(self) # Use signals/slots to interact with the list widget self.image_view_lw.itemSelectionChanged.connect(self.updateDockInfo) self.image_view_lw.itemDoubleClicked.connect(self.displayImageInfoDialog) # Use the list widget's internal model to enable/disable menu items self.image_view_lw.model().rowsInserted.connect(self.manageMenuItems) self.image_view_lw.model().rowsRemoved.connect(self.manageMenuItems) self.setCentralWidget(self.image_view_lw) def createActions(self): """Create the application's menu actions.""" # Create actions for File menu self.import_act = QAction("Import Images...", self, triggered=self.importImages) self.import_act.setShortcut("Ctrl+I") self.preferences_act = QAction("Preferences...", self, triggered=self.showPreferencesDialog) self.quit_act = QAction("Quit Task Manager", self, triggered=self.close) self.quit_act.setShortcut(QKeySequence.StandardKey.Quit) # Ctrl+Q # Create actions for Edit menu self.select_all_act = QAction("Select All", self, triggered=self.image_view_lw.selectAll) self.select_all_act.setShortcut(QKeySequence.StandardKey.SelectAll) # Ctrl+A self.delete_act = QAction("Delete Images", self, triggered=self.deleteImages) self.delete_act.setShortcut(QKeySequence.StandardKey.Delete) # Del self.delete_act.setEnabled(False) # Create actions for View menu # Handle the visibility of the dock widget that displays images self.show_dock_act = self.image_preview_dock.toggleViewAction() self.show_dock_act.setText("Show Image View") self.sort_ascend_act = QAction("Sort Ascending", self, triggered=lambda: self.sortListItems(Qt.SortOrder.AscendingOrder)) self.sort_ascend_act.setEnabled(False) self.sort_descend_act = QAction("Sort Descending", self, triggered=lambda: self.sortListItems(Qt.SortOrder.DescendingOrder)) self.sort_descend_act.setEnabled(False) self.fullscreen_act = QAction("Show Fullscreen", self, triggered=self.displayFullScreen, checkable=True) # Create actions for Help menu self.about_act = QAction("About Image Manager", self, triggered=self.showAboutDialog) def createMenus(self): """Create the application's menu.""" if QSysInfo.productType() == "macos" or "osx": self.menuBar().setNativeMenuBar(False) self.file_menu = self.menuBar().addMenu("&File") self.file_menu.addAction(self.import_act) self.file_menu.addSeparator() self.file_menu.addAction(self.preferences_act) self.file_menu.addSeparator() self.file_menu.addAction(self.quit_act) self.edit_menu = self.menuBar().addMenu("&Edit") self.edit_menu.addAction(self.select_all_act) self.edit_menu.addSeparator() self.edit_menu.addAction(self.delete_act) self.view_menu = self.menuBar().addMenu("&View") self.view_menu.addAction(self.show_dock_act) self.view_menu.addSeparator() self.view_menu.addAction(self.sort_ascend_act) self.view_menu.addAction(self.sort_descend_act) self.view_menu.addSeparator() self.view_menu.addAction(self.fullscreen_act) self.help_menu = self.menuBar().addMenu("&Help") self.help_menu.addAction(self.about_act) def manageMenuItems(self, parent, first, last): """Slot to enable/disable menu items if rows have been added/deleted to QListWidget. The rowsInserted() and rowsRemoved() that trigger this slot return the 'parent', 'first', and 'last' values, but they are not used in this method.""" if self.image_view_lw.count() == 0: self.delete_act.setEnabled(False) self.sort_ascend_act.setEnabled(False) self.sort_descend_act.setEnabled(False) elif self.image_view_lw.count() > 0: self.delete_act.setEnabled(True) self.sort_ascend_act.setEnabled(True) self.sort_descend_act.setEnabled(True) def displayImagePreviewDock(self): """Dock widget that displays a selected image in a scrollable area and uses its file name as the dock's title.""" self.image_preview_dock = QDockWidget() self.image_preview_dock.setObjectName("PreviewDock") self.image_preview_dock.setWindowTitle("Show Image View") self.image_preview_dock.setAllowedAreas(Qt.DockWidgetAreas.RightDockWidgetArea) self.display_image_label = QLabel() self.display_image_label.setAlignment(Qt.Alignment.AlignCenter) self.view_scroll_area = QScrollArea() self.view_scroll_area.setMinimumWidth(300) self.view_scroll_area.setWidgetResizable(True) self.image_preview_dock.setWidget(self.view_scroll_area) # Set initial location of dock widget in the main window self.addDockWidget(Qt.DockWidgetAreas.RightDockWidgetArea, self.image_preview_dock) def updateDockInfo(self): """Slot to update the image that the dock widget displays.""" # Only display an image if one item is selected if (len(self.image_view_lw.selectedItems()) == 0 or len(self.image_view_lw.selectedItems()) > 1): self.image_preview_dock.setWindowTitle("Show Image View") self.display_image_label.clear() else: curr_item = self.image_view_lw.currentItem() self.image_preview_dock.setWindowTitle(curr_item.text()) self.show_dock_act.setText("Show Image View") # Get the current height of the dock widget dock_height = self.image_preview_dock.height() # Get the size of the original image/item icon_size = curr_item.icon().availableSizes()[0] icon_width = icon_size.width() # Return a pixmap from the item's icon and display in the scroll area pixmap = curr_item.icon().pixmap(QSize(icon_width, dock_height)) self.display_image_label.setPixmap(pixmap) self.view_scroll_area.setWidget(self.display_image_label) def importImages(self): """Import the images a user selects, remove duplicates, and add items to the QListWidget.""" duplicate_images = [] # Store the names of duplicate images image_paths, _ = QFileDialog.getOpenFileNames(self, "Select Image Files", "", "Images (*.png *.xpm *.jpg *.jpeg)") if image_paths: if self.image_dir.exists(): for image_path in image_paths: # Pass image path to QFileInfo object image_info = QFileInfo(image_path) file_name = image_info.fileName() item_name = image_info.baseName() # Copy the files into the Images directory, check for files # with the same name new_name = self.image_dir.absolutePath() + f"/{file_name}" file_exists = QFile.copy(image_path, new_name) if file_exists == False: duplicate_images.append(image_path) else: self.createListItems(image_path, item_name, image_info, new_name) if self.is_delete_checked == True: # Handle deleting images QFile.moveToTrash(image_path) else: QMessageBox.warning(self, "Images Location Not Found", """<p>The Images Location cannot be found. Restart the application to recreate the directory.</p>""") # Display a custom dialog to inform the user of duplicate images if len(duplicate_images) != 0: duplicates_dialog = QMessageBox(self) duplicates_dialog.setIcon(QMessageBox.Icon.Information) duplicates_dialog.setWindowTitle("Duplicate Images") duplicates_dialog.setText("""<p>Some images were not imported because they already exist.</p>""") details = '\n'.join([item for item in duplicate_images]) duplicates_dialog.setDetailedText(details) duplicates_dialog.exec() duplicate_images.clear() # Clear the list # Check if window is still in focus. If not, give it focus if self.isActiveWindow() == False: self.activateWindow() def createListItems(self, image_path, item_name, image_info, new_name=None): """Simple method for creating QListWidgetItem objects. 'image_path': the path to the file. 'item_name': the base name used for QListWidgetItem objects. 'image_info': the QFileInfo object. 'new_name': used when importing new photos, making sure the program points to the new image location.""" list_item = QListWidgetItem(QIcon(image_path), item_name) self.image_view_lw.setIconSize(QSize(80, 80)) self.image_view_lw.addItem(list_item) if new_name != None: image_info.setFile(new_name) self.image_view_lw.images_info_list.append(image_info) def sortListItems(self, order): """First, sort the items in the QListWidget using sortItems(). Then handle sorting the QFileInfo objects in the images_info_list using Python's sort() to match how the QListWidget sorts items.""" self.image_view_lw.sortItems(order) if order == Qt.SortOrder.AscendingOrder: self.image_view_lw.images_info_list.sort(key=lambda item: (item.baseName().upper(), item.baseName()[0].islower())) elif order == Qt.SortOrder.DescendingOrder: self.image_view_lw.images_info_list.sort(reverse=True, key=lambda item: (item.baseName().upper(), item.baseName()[0].islower())) def deleteImages(self): """Delete images from the QListWidget and from where images are stored on disk.""" number_of_photos = len(self.image_view_lw.selectedItems()) answer = QMessageBox.warning(self, "Delete Image(s)", f"Are you sure you want to delete {number_of_photos} image(s)?", QMessageBox.StandardButtons.No | QMessageBox.StandardButtons.Yes, QMessageBox.StandardButtons.No) if answer == QMessageBox.StandardButtons.Yes: for item in self.image_view_lw.selectedItems(): index = self.image_view_lw.indexFromItem(item).row() # Get the image's information before deletion image_info = self.image_view_lw.images_info_list[index] # Remove items from the Images directory, from the list widget, # and the images_info_list that stores QFileInfo objects QFile.moveToTrash(image_info.absoluteFilePath()) self.image_view_lw.takeItem(index) del self.image_view_lw.images_info_list[index] del item def loadStoredImageData(self): """Load images from the Images directory. The Images directory is created the first time running the application.""" if not(self.image_dir.exists()): QDir().mkdir(self.images_path) elif self.image_dir.exists(): # Create a list of the files in the Images directory images = self.image_dir.entryInfoList(QDir.Filters.AllEntries | QDir.Filters.NoDotAndDotDot) for image in images: # Imported files are QFileInfo objects item_name = image.baseName() path = image.absoluteFilePath() self.createListItems(path, item_name, image) def displayImageInfoDialog(self, item): """Display image metadata in a modeless dialog box. 'index' is the index of the item that is clicked on.""" index = self.image_view_lw.indexFromItem(item).row() if self.info_dialog == None: self.info_dialog = ImageInfoDialog(self, self.image_view_lw.images_info_list[index]) elif self.info_dialog != None: self.info_dialog.close() self.info_dialog = ImageInfoDialog(self, self.image_view_lw.images_info_list[index]) self.info_dialog.show() def showPreferencesDialog(self): """Display the application's preferences dialog. Save the value of the delete_images_checkbox in the settings.""" prefs_dialog = PreferencesDialog(self, self.image_dir, self.is_delete_checked) response = prefs_dialog.exec() if response == 1: # QDialog.DialogCode.Accepted == 1 self.settings.setValue("delete_images", prefs_dialog.delete_images_checkbox.isChecked()) self.is_delete_checked = self.settings.value("delete_images", type=bool) def displayFullScreen(self, state): """Check the state of checkable fullscreen_act. If True, show the main window as fullscreen.""" if state: self.showFullScreen() else: self.showNormal() def showAboutDialog(self): """Display the application's about dialog.""" QMessageBox.about(self, "Image Manager", """<h3 style='text-align:center'>Image Manager</h3> <p style='font-weight: normal'>The <b><i>Image Manager GUI</i></b> demonstrates how to build an application for managing photos. This program also examines some of the common features found in many GUIs.</p> <p style='font-weight: normal'>This application is part of <b><i>Building Custom UIs with PyQt</i></b>.</p> <p style='font-weight: normal'>Designed by: <b>Joshua Willman</b></p> <p style='font-weight: normal'>Icons created by: <b>Joshua Willman</b></p>""") def getInitialSettings(self): """Get initial settings of the application using QSettings upon startup.""" position = self.settings.value("position", QPoint(200, 0)) size = self.settings.value("size", QSize(800, 500)) self.is_delete_checked = self.settings.value("delete_images", type=bool) # restoreState() is used here to restore the image_preview_dock widget self.restoreState(self.settings.value("window_state", bytes(QByteArray()))) self.resize(size) self.move(position) return self.is_delete_checked def saveSettings(self): """Save the settings of the application.""" self.settings.setValue("position", self.pos()) self.settings.setValue("size", self.size()) self.settings.setValue("window_state", self.saveState()) def closeEvent(self, event): """Save the application's settings in the closeEvent().""" self.saveSettings() event.setAccepted(True)
class MainWindow(QMainWindow): def __init__(self): """ MainWindow Constructor """ super().__init__() self.initializeUI() def initializeUI(self): """Initialize settings, call functions that define UI elements, and display the main window.""" self.setMinimumSize(700, 400) self.setWindowTitle("GIF and Image Viewer") # Set up the main window, menu, and dock widget self.setUpMainWindow() self.displayFilesDock() self.createActions() self.createMenus() self.createToolbar() self.show() # Display the main window def setUpMainWindow(self): """Set up the application's main window and widgets.""" self.movie = QMovie() # Create movie object self.movie.stateChanged.connect(self.changeButtonStates) self.media_label = QLabel() # Create label to place images/GIFs on self.media_label.setPixmap(QPixmap("icons/image_label.png")) self.media_label.setFrameShape(QFrame.Shape.StyledPanel) self.media_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setCentralWidget(self.media_label) def createActions(self): """Create the application's menu actions.""" # Create actions for File menu self.open_act = QAction("Open...", self, triggered=self.openDirectory) self.open_act.setShortcut(QKeySequence.StandardKey.Open) self.quit_act = QAction("Quit Viewer", self, triggered=self.close) self.quit_act.setShortcut(QKeySequence.StandardKey.Quit) # Ctrl+Q # Create actions for View menu # Handle the visibility of the dock widget self.show_dock_act = self.files_dock.toggleViewAction() self.show_dock_act.setText("Show Media Folder") # Create actions for the toolbar (These actions could also be # added to the GUI's menu bar or to a context menu) self.play_act = QAction(QIcon("icons/play.png"), "Play", self, triggered=self.startMovie) self.pause_act = QAction(QIcon("icons/pause.png"), "Pause", self, triggered=self.pauseMovie) self.stop_act = QAction(QIcon("icons/stop.png"), "Stop/Reset", self, triggered=self.stopMovie) self.disableMovieButtons() def createMenus(self): """Create the application's menu.""" # Make the toolbar appear in the main window for macOS users. # More information about this in Chapter 2 - Building the Foundation for GUIs if QSysInfo.productType() == "macos" or "osx": self.menuBar().setNativeMenuBar(False) self.file_menu = self.menuBar().addMenu("&File") self.file_menu.addAction(self.open_act) self.file_menu.addSeparator() self.file_menu.addAction(self.quit_act) self.view_menu = self.menuBar().addMenu("&View") self.view_menu.addAction(self.show_dock_act) def createToolbar(self): """Create the application's toolbar for playing GIFs.""" toolbar = self.addToolBar("GIF Controls Toolbar") toolbar.setIconSize(QSize(24, 24)) # Add actions to the toolbar toolbar.addAction(self.play_act) toolbar.addAction(self.pause_act) toolbar.addAction(self.stop_act) def displayFilesDock(self): """Dock widget that displays the movie file location in a QLineEdit widget, provides a button for opening directories with images and GIFs, and shows the media from the selected folder in a QTreeWidget.""" self.files_dock = QDockWidget() self.files_dock.setWindowTitle("Media Folder") self.files_dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea) folder_label = QLabel("Media Location:") # The QLineEdit widget is set to read-only as a quick way to display # the folder path self.folder_line = QLineEdit() self.folder_line.setMinimumWidth(100) self.folder_line.setReadOnly(True) open_button = QPushButton("Open...") open_button.clicked.connect(self.openDirectory) folder_h_box = QHBoxLayout() folder_h_box.addWidget(folder_label) folder_h_box.addWidget(self.folder_line) folder_h_box.addWidget(open_button) self.files_tree = QTreeWidget() self.files_tree.setHeaderLabel("Media Files") self.files_tree.setColumnCount(1) # Set up the dock's layout dock_v_box = QVBoxLayout() dock_v_box.addLayout(folder_h_box) dock_v_box.addWidget(self.files_tree) dock_container = QWidget() dock_container.setLayout(dock_v_box) self.files_dock.setWidget(dock_container) self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.files_dock) def openDirectory(self): """Open a QFileDialog for selecting a local directory. Only display image and GIF files.""" directory = QFileDialog.getExistingDirectory( self, "Choose Directory", "", QFileDialog.Option.ShowDirsOnly ) # Specify the file mode to only select directories if directory: self.movie.setFileName(directory) # Check if image data is valid before playing if self.movie.isValid(): # Use setMovie() to set the label's contents as the selected GIF self.media_label.setMovie(self.movie) self.startMovie() # Call method to begin playing def startMovie(self): """Start playing the movie.""" self.movie.start() def pauseMovie(self): """Pause the movie.""" self.movie.setPaused(True) def stopMovie(self): """Stop playing the movie and reset the movie back to the first frame.""" self.movie.stop() self.movie.jumpToFrame(0) def changeButtonStates(self, state): """Slot that handles enabling/disabling buttons in the toolbar based on the state of QMovie.""" if state == QMovie.MovieState.Running: # The animation begins playing once control returns to the event loop self.play_act.setEnabled(False) self.pause_act.setEnabled(True) self.stop_act.setEnabled(True) if state == QMovie.MovieState.Paused: self.play_act.setEnabled(True) self.pause_act.setEnabled(False) self.stop_act.setEnabled(False) if state == QMovie.MovieState.NotRunning: self.play_act.setEnabled(True) self.pause_act.setEnabled(False) self.stop_act.setEnabled(False) def disableMovieButtons(self): """Simple method to disable the movie buttons in the toolbar.""" self.play_act.setEnabled(False) self.pause_act.setEnabled(False) self.stop_act.setEnabled(False)
class UIComicInfoWidget(QWidget): load_chapter_list_signa = QtCore.pyqtSignal(ChapterInfo) load_download_task_signa = QtCore.pyqtSignal(DownloadTask) def __init__(self, comic_info: ComicInfo, down_v_box_layout: QVBoxLayout): super().__init__() self.comic_info = comic_info self.down_v_box_layout = down_v_box_layout self.img_label = QLabel(self) self.img_label.setScaledContents(True) img = QImage.fromData(comic_info.cover) w, h = image_resize(comic_info.cover, width=200) self.img_label.resize(QtCore.QSize(w, h)) self.img_label.setGeometry(10, 10, w, h) self.img_label.setPixmap(QPixmap.fromImage(img)) # self.img_label.setPixmap(QtGui.QPixmap("/Users/bo/my/tmp/老夫子2/第1卷/1.jpg")) self.title = QLabel(self) self.title.setGeometry(220, 10, 100, 40) title_font = QtGui.QFont() title_font.setPointSize(16) title_font.setBold(True) title_font.setUnderline(True) self.title.setFont(title_font) self.title.setText(comic_info.title) self.title.setWordWrap(True) info_font = QtGui.QFont() info_font.setPointSize(14) # 作者 self.author = QLabel(self) self.author.setText("作者 : " + comic_info.author) self.author.setGeometry(220, 50, 150, 40) self.author.setWordWrap(True) self.author.setFont(info_font) # 状态 self.status = QLabel(self) self.status.setText("更新状态 : " + comic_info.status) self.status.setGeometry(220, 90, 150, 40) self.status.setFont(info_font) # 热度 self.heat = QLabel(self) self.heat.setText("热度 : " + str(comic_info.heat)) self.heat.setGeometry(220, 130, 150, 40) self.heat.setFont(info_font) # 类型 self.tip = QLabel(self) self.tip.setText("类型 : " + comic_info.tip) self.tip.setGeometry(220, 170, 150, 40) self.tip.setWordWrap(True) self.tip.setFont(info_font) # web self.domain = QLabel(self) self.domain.setText(f"查看原网页 : {comic_info.domain}") self.domain.setText(f'查看原网页 : <a href="{comic_info.url}">{comic_info.domain}</a>') self.domain.setGeometry(220, 210, 150, 40) self.domain.setOpenExternalLinks(True) self.domain.setFont(info_font) # 描述 self.describe = QLabel(self) self.describe.setText(" " + comic_info.describe) self.describe.setGeometry(10, 320, 350, 330) self.describe.setWordWrap(True) # 对齐方式 self.describe.setAlignment( QtCore.Qt.AlignmentFlag.AlignLeading | QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop) # 章节列表,创建一个区域 self.searchHBoxLayout = QHBoxLayout() # self.searchHBoxLayout.addSpacing() self.searchGroupBox = QGroupBox() self.searchGroupBox.setLayout(self.searchHBoxLayout) self.searchScroll = QScrollArea(self) self.searchScroll.setGeometry(370, 10, 574, 590) self.searchScroll.setWidget(self.searchGroupBox) self.searchScroll.setWidgetResizable(True) # 全选 self.check_all = QCheckBox(self) self.check_all.setText("全选") self.check_all.setGeometry(700, 610, 100, 20) self.check_all.stateChanged.connect(self.check_all_fun) # 下载 self.down_button = QPushButton(self) self.down_button.setText("下载") self.down_button.setGeometry(780, 605, 50, 30) self.down_button.clicked.connect(self.download_button_click) self.load_chapter_list_signa.connect(self.load_chapter) self.load_download_task_signa.connect(self.download_callback) # 调用对应的service的接口,获取章节列表 constant.SERVICE.chapter(comic_info, self.load_chapter_list_signa.emit) i = 0 searchVBoxLayout: QVBoxLayout check_box_list: List[QCheckBox] = [] def check_all_fun(self): for check_box in self.check_box_list: check_box.setChecked(self.check_all.isChecked()) def download_callback(self, task: DownloadTask): widget = DownLoadTaskWidget(task) self.down_v_box_layout.addWidget(widget) def download_button_click(self): flag = False for check_box in self.check_box_list: if check_box.isChecked(): constant.SERVICE.parse_image(self.comic_info, check_box.property("chapter_info"), self.load_download_task_signa.emit) if not flag: QMessageBox.information(self, "下载通知", "正在解析选中章节", QMessageBox.StandardButton.Yes) flag = True def load_chapter(self, chapter_info: ChapterInfo): if self.i % 26 == 0: self.searchVBoxLayout = QVBoxLayout() self.searchVBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) # 对齐方式,研究了3个小时 o(╥﹏╥)o self.searchHBoxLayout.addLayout(self.searchVBoxLayout) check_box = QCheckBox() self.check_box_list.append(check_box) check_box.setText(chapter_info.title) check_box.setProperty("chapter_info", chapter_info) task = constant.downloaded_task_map.get(chapter_info.url) if task and task.status == -1: check_box.setStyleSheet('color:red') check_box.setChecked(True) self.searchVBoxLayout.addWidget(check_box) self.i += 1
class AboutDialog(QDialog): check_update = pyqtSignal(str, bool) def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) self._ver = '' self._github = 'https://github.com/rachpt/lanzou-gui' self._api_url = 'https://github.com/zaxtyson/LanZouCloud-API' self._gitee = 'https://gitee.com/rachpt/lanzou-gui' self._home_page = 'https://rachpt.cn/lanzou-gui/' self.initUI() self.setStyleSheet(others_style) def set_values(self, version): self._ver = version self.lb_name_text.setText(f"v{version} (点击检查更新)") # 更新版本 def show_update(self, ver, msg): self.lb_new_ver = QLabel("新版") # 检测新版 self.lb_new_ver_msg = QLabel() self.lb_new_ver_msg.setOpenExternalLinks(True) self.lb_new_ver_msg.setWordWrap(True) if ver != '0': self.lb_name_text.setText(f"{self._ver} ➡ {ver}") self.lb_new_ver_msg.setText(msg) self.lb_new_ver_msg.setMinimumWidth(700) if self.form.rowCount() < 5: self.form.insertRow(1, self.lb_new_ver, self.lb_new_ver_msg) def initUI(self): self.setWindowTitle("关于 lanzou-gui") about = f'本项目使用PyQt6实现图形界面,可以完成蓝奏云的大部分功能<br/> \ 得益于 <a href="{self._api_url}">API</a> 的功能,可以间接突破单文件最大 100MB 的限制,同时增加了批量上传/下载的功能<br/> \ Python 依赖见<a href="{self._github }/blob/master/requirements.txt">requirements.txt</a>,\ <a href="{self._github}/releases">releases</a> 有打包好了的 Windows 可执行程序,但可能不是最新的' project_url = f'<a href="{self._home_page}">主页</a> | <a href="{self._github}">repo</a> | \ <a href="{self._gitee}">mirror repo</a>' self.logo = QLabel() # logo self.logo.setPixmap(QPixmap(SRC_DIR + "logo2.gif")) self.logo.setStyleSheet("background-color:rgb(255,255,255);") self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter) self.lb_qt_ver = QLabel("依赖") # QT 版本 self.lb_qt_text = QLabel( f"QT: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}") # QT 版本 self.lb_name = QLabel("版本") # 版本 self.lb_name_text = QPushButton("") # 版本 self.lb_name_text.setToolTip("点击检查更新") ver_style = "QPushButton {border:none; background:transparent;font-weight:bold;color:blue;}" self.lb_name_text.setStyleSheet(ver_style) self.lb_name_text.clicked.connect( lambda: self.check_update.emit(self._ver, True)) self.lb_about = QLabel("关于") # about self.lb_about_text = QLabel() self.lb_about_text.setText(about) self.lb_about_text.setOpenExternalLinks(True) self.lb_author = QLabel("作者") # author self.lb_author_mail = QLabel( "<a href='mailto:[email protected]'>rachpt</a>") self.lb_author_mail.setOpenExternalLinks(True) self.lb_update = QLabel("项目") # 更新 self.lb_update_url = QLabel(project_url) self.lb_update_url.setOpenExternalLinks(True) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.StandardButton.Close) self.buttonBox.button( QDialogButtonBox.StandardButton.Close).setText("关闭") self.buttonBox.rejected.connect(self.reject) self.buttonBox.setStyleSheet(btn_style) self.recommend = QLabel( "<br />大文件推荐使用 <a href='https://github.com/Aruelius/cloud189'>cloud189-cli</a>" ) self.recommend.setOpenExternalLinks(True) self.line = QLine(QPoint(), QPoint(550, 0)) self.lb_line = QLabel('<html><hr /></html>') vbox = QVBoxLayout() vbox.addWidget(self.logo) vbox.addStretch(1) self.form = QFormLayout() self.form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) self.form.setFormAlignment(Qt.AlignmentFlag.AlignLeft) self.form.setHorizontalSpacing(40) self.form.setVerticalSpacing(15) self.form.addRow(self.lb_qt_ver, self.lb_qt_text) self.form.addRow(self.lb_name, self.lb_name_text) self.form.addRow(self.lb_update, self.lb_update_url) self.form.addRow(self.lb_author, self.lb_author_mail) self.form.addRow(self.lb_about, self.lb_about_text) self.form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy. AllNonFixedFieldsGrow) # 覆盖MacOS的默认样式 vbox.addLayout(self.form) vbox.addStretch(1) vbox.addWidget(self.recommend) vbox.addWidget(self.lb_line) donate = QLabel() donate.setText("<b>捐助我</b> 如果你愿意") donate.setAlignment(Qt.AlignmentFlag.AlignCenter) hbox = QHBoxLayout() hbox.addStretch(2) for it in ["wechat", "alipay", "qqpay"]: lb = QLabel() lb.setPixmap(QPixmap(SRC_DIR + f"{it}.jpg")) hbox.addWidget(lb) hbox.addStretch(1) hbox.addWidget(self.buttonBox) vbox.addWidget(donate) vbox.addLayout(hbox) self.setLayout(vbox) self.setMinimumWidth(720) def paintEvent(self, event): QDialog.paintEvent(self, event) if not self.line.isNull(): painter = QPainter(self) pen = QPen(Qt.GlobalColor.red, 3) painter.setPen(pen) painter.drawLine(self.line)
class InfoDialog(QDialog): """文件信息对话框""" get_dl_link = pyqtSignal(str, str) closed = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.infos = None self._short_link_flag = True # 防止多次重试 self.initUI() self.setStyleSheet(dialog_qss_style) def update_ui(self): self.tx_dl_link.setPlaceholderText("单击获取") self.tx_name.setText(self.infos.name) if self.infos.is_file: self.setWindowTitle("文件信息") self.lb_name.setText("文件名:") self.lb_desc.setText("文件描述:") self.tx_dl_link.setText("") # 清空旧的信息 self.lb_dl_link.setVisible(True) self.tx_dl_link.setVisible(True) else: self.setWindowTitle("文件夹信息") self.lb_name.setText("文件夹名:") self.lb_desc.setText("文件夹描述:") self.lb_dl_link.setVisible(False) self.tx_dl_link.setVisible(False) if self.infos.size: self.tx_size.setText(self.infos.size) self.lb_size.setVisible(True) self.tx_size.setVisible(True) else: self.tx_size.setVisible(False) self.lb_size.setVisible(False) if self.infos.time: self.lb_time.setVisible(True) self.tx_time.setVisible(True) self.tx_time.setText(self.infos.time) else: self.lb_time.setVisible(False) self.tx_time.setVisible(False) if self.infos.downs: self.lb_dl_count.setVisible(True) self.tx_dl_count.setVisible(True) self.tx_dl_count.setText(str(self.infos.downs)) else: self.tx_dl_count.setVisible(False) self.lb_dl_count.setVisible(False) if self.infos.pwd: self.tx_pwd.setText(self.infos.pwd) self.tx_pwd.setPlaceholderText("") else: self.tx_pwd.setText("") self.tx_pwd.setPlaceholderText("无") if self.infos.desc: self.tx_desc.setText(self.infos.desc) self.tx_desc.setPlaceholderText("") else: self.tx_desc.setText("") self.tx_desc.setPlaceholderText("无") self.tx_share_url.setText(self.infos.url) self.adjustSize() def set_values(self, infos): self.infos = infos self.update_ui() def set_dl_link_tx(self, text): self.tx_dl_link.setText(text) self.adjustSize() def call_get_dl_link(self): url = self.tx_share_url.text() pwd = self.tx_pwd.text() self.get_dl_link.emit(url, pwd) self.tx_dl_link.setPlaceholderText("后台获取中,请稍候!") def call_get_short_url(self): if self._short_link_flag: self._short_link_flag = False self.tx_short.setPlaceholderText("后台获取中,请稍候!") url = self.tx_share_url.text() from lanzou.api.extra import get_short_url short_url = get_short_url(url) if short_url: self.tx_short.setText(short_url) self.tx_short.setPlaceholderText("") self._short_link_flag = True else: self.tx_short.setText("") self.tx_short.setPlaceholderText("生成失败!api 可能已经失效") def clean(self): self._short_link_flag = True self.tx_short.setText("") self.tx_short.setPlaceholderText("单击获取") def initUI(self): self.setWindowIcon(QIcon(SRC_DIR + "share.ico")) self.setWindowTitle("文件信息") self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Close) self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setText("关闭") self.buttonBox.rejected.connect(self.reject) self.buttonBox.rejected.connect(self.clean) self.buttonBox.rejected.connect(self.closed.emit) self.logo = QLabel() self.logo.setPixmap(QPixmap(SRC_DIR + "q9.gif")) self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter) self.logo.setStyleSheet("background-color:rgb(255,204,51);") self.lb_name = QLabel() self.lb_name.setText("文件名:") self.tx_name = AutoResizingTextEdit() self.tx_name.setReadOnly(True) self.tx_name.setMinimumLines(1) self.lb_size = QLabel() self.lb_size.setText("文件大小:") self.tx_size = QLabel() self.lb_time = QLabel() self.lb_time.setText("上传时间:") self.tx_time = QLabel() self.lb_dl_count = QLabel() self.lb_dl_count.setText("下载次数:") self.tx_dl_count = QLabel() self.lb_share_url = QLabel() self.lb_share_url.setText("分享链接:") self.tx_share_url = QLineEdit() self.tx_share_url.setReadOnly(True) self.lb_pwd = QLabel() self.lb_pwd.setText("提取码:") self.tx_pwd = QLineEdit() self.tx_pwd.setReadOnly(True) self.lb_short = QLabel() self.lb_short.setText("短链接:") self.tx_short = AutoResizingTextEdit(self) self.tx_short.setPlaceholderText("单击获取") self.tx_short.clicked.connect(self.call_get_short_url) self.tx_short.setReadOnly(True) self.tx_short.setMinimumLines(1) self.lb_desc = QLabel() self.lb_desc.setText("文件描述:") self.tx_desc = AutoResizingTextEdit() self.tx_desc.setReadOnly(True) self.tx_desc.setMinimumLines(1) self.lb_dl_link = QLabel() self.lb_dl_link.setText("下载直链:") self.tx_dl_link = AutoResizingTextEdit(self) self.tx_dl_link.setPlaceholderText("单击获取") self.tx_dl_link.clicked.connect(self.call_get_dl_link) self.tx_dl_link.setReadOnly(True) self.tx_dl_link.setMinimumLines(1) vbox = QVBoxLayout() vbox.addWidget(self.logo) vbox.addStretch(1) form = QFormLayout() form.setLabelAlignment(Qt.AlignmentFlag.AlignRight) form.addRow(self.lb_name, self.tx_name) form.addRow(self.lb_size, self.tx_size) form.addRow(self.lb_time, self.tx_time) form.addRow(self.lb_dl_count, self.tx_dl_count) form.addRow(self.lb_share_url, self.tx_share_url) form.addRow(self.lb_pwd, self.tx_pwd) form.addRow(self.lb_short, self.tx_short) form.addRow(self.lb_desc, self.tx_desc) form.addRow(self.lb_dl_link, self.tx_dl_link) form.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow) # 覆盖MacOS的默认样式 vbox.addLayout(form) vbox.addStretch(1) vbox.addWidget(self.buttonBox) self.setLayout(vbox) self.setMinimumWidth(500)
class UploadDialog(QDialog): """文件上传对话框""" new_infos = pyqtSignal(object) def __init__(self, user_home): super().__init__() self.cwd = user_home self._folder_id = -1 self._folder_name = "LanZouCloud" self.set_pwd = False self.set_desc = False self.pwd = '' self.desc = '' self.allow_big_file = False self.max_size = 100 self.selected = [] self.initUI() self.set_size() self.setStyleSheet(dialog_qss_style) def set_pwd_desc_bigfile(self, settings): self.set_pwd = settings["set_pwd"] self.set_desc = settings["set_desc"] self.pwd = settings["pwd"] self.desc = settings["desc"] self.allow_big_file = settings["allow_big_file"] self.max_size = settings["max_size"] if self.allow_big_file: self.btn_chooseMultiFile.setToolTip("") else: self.btn_chooseMultiFile.setToolTip(f"文件大小上限 {self.max_size}MB") def set_values(self, folder_name, folder_id, files): self.setWindowTitle("上传文件至 ➩ " + str(folder_name)) self._folder_id = folder_id self._folder_name = folder_name if files: self.selected = files self.show_selected() self.exec() def initUI(self): self.setWindowTitle("上传文件") self.setWindowIcon(QIcon(SRC_DIR + "upload.ico")) self.logo = QLabel() self.logo.setPixmap(QPixmap(SRC_DIR + "logo3.gif")) self.logo.setStyleSheet("background-color:rgb(0,153,255);") self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter) # btn 1 self.btn_chooseDir = QPushButton("选择文件夹", self) self.btn_chooseDir.setObjectName("btn_chooseDir") self.btn_chooseDir.setObjectName("btn_chooseDir") self.btn_chooseDir.setIcon(QIcon(SRC_DIR + "folder.gif")) # btn 2 self.btn_chooseMultiFile = QPushButton("选择多文件", self) self.btn_chooseDir.setObjectName("btn_chooseMultiFile") self.btn_chooseMultiFile.setObjectName("btn_chooseMultiFile") self.btn_chooseMultiFile.setIcon(QIcon(SRC_DIR + "file.ico")) # btn 3 self.btn_deleteSelect = QPushButton("移除", self) self.btn_deleteSelect.setObjectName("btn_deleteSelect") self.btn_deleteSelect.setIcon(QIcon(SRC_DIR + "delete.ico")) self.btn_deleteSelect.setToolTip("按 Delete 移除选中文件") # 列表 self.list_view = MyListView() self.list_view.drop_files.connect(self.add_drop_files) self.list_view.setViewMode(QListView.ViewMode.ListMode) self.slm = QStandardItem() self.model = QStandardItemModel() self.list_view.setModel(self.model) self.model.removeRows(0, self.model.rowCount()) # 清除旧的选择 self.list_view.setEditTriggers( QAbstractItemView.EditTrigger.NoEditTriggers) self.list_view.setSelectionBehavior( QAbstractItemView.SelectionBehavior.SelectRows) self.list_view.setSelectionMode( QAbstractItemView.SelectionMode.ExtendedSelection) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Orientation.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("确定") self.buttonBox.button( QDialogButtonBox.StandardButton.Cancel).setText("取消") vbox = QVBoxLayout() hbox_head = QHBoxLayout() hbox_button = QHBoxLayout() hbox_head.addWidget(self.btn_chooseDir) hbox_head.addStretch(1) hbox_head.addWidget(self.btn_chooseMultiFile) hbox_button.addWidget(self.btn_deleteSelect) hbox_button.addStretch(1) hbox_button.addWidget(self.buttonBox) vbox.addWidget(self.logo) vbox.addLayout(hbox_head) vbox.addWidget(self.list_view) vbox.addLayout(hbox_button) self.setLayout(vbox) self.setMinimumWidth(350) # 设置信号 self.btn_chooseDir.clicked.connect(self.slot_btn_chooseDir) self.btn_chooseMultiFile.clicked.connect(self.slot_btn_chooseMultiFile) self.btn_deleteSelect.clicked.connect(self.slot_btn_deleteSelect) self.buttonBox.accepted.connect(self.slot_btn_ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.clear_old) self.buttonBox.rejected.connect(self.reject) def set_size(self): if self.selected: h = 18 if len(self.selected) > 18 else 10 w = 40 for i in self.selected: i_len = len(i) if i_len > 100: w = 100 break if i_len > w: w = i_len self.resize(120 + w * 7, h * 30) else: self.resize(400, 300) def clear_old(self): self.selected = [] self.model.removeRows(0, self.model.rowCount()) self.set_size() def show_selected(self): self.model.removeRows(0, self.model.rowCount()) for item in self.selected: if os.path.isfile(item): self.model.appendRow( QStandardItem(QIcon(SRC_DIR + "file.ico"), item)) else: self.model.appendRow( QStandardItem(QIcon(SRC_DIR + "folder.gif"), item)) self.set_size() def backslash(self): """Windows backslash""" tasks = {} for item in self.selected: url = os.path.normpath(item) total_size = 0 total_file = 0 if os.path.isfile(url): total_size = os.path.getsize(url) if not total_size: continue # 空文件无法上传 total_file += 1 else: for filename in os.listdir(url): file_path = os.path.join(url, filename) if not os.path.isfile(file_path): continue # 跳过子文件夹 total_size += os.path.getsize(file_path) total_file += 1 tasks[url] = UpJob(url=url, fid=self._folder_id, folder=self._folder_name, pwd=self.pwd if self.set_pwd else None, desc=self.desc if self.set_desc else None, total_size=total_size, total_file=total_file) return tasks def slot_btn_ok(self): tasks = self.backslash() if self.selected: self.new_infos.emit(tasks) self.clear_old() def slot_btn_deleteSelect(self): _indexes = self.list_view.selectionModel().selection().indexes() if not _indexes: return indexes = [] for i in _indexes: # 获取所选行号 indexes.append(i.row()) indexes = set(indexes) for i in sorted(indexes, reverse=True): self.selected.remove(self.model.item(i, 0).text()) self.model.removeRow(i) self.set_size() def add_drop_files(self, files): for item in files: if item not in self.selected: self.selected.append(item) self.show_selected() def slot_btn_chooseDir(self): dir_choose = QFileDialog.getExistingDirectory(self, "选择文件夹", self.cwd) # 起始路径 if dir_choose == "": return if dir_choose not in self.selected: self.selected.append(dir_choose) self.cwd = os.path.dirname(dir_choose) self.show_selected() def slot_btn_chooseMultiFile(self): files, _ = QFileDialog.getOpenFileNames(self, "选择多文件", self.cwd, "All Files (*)") if len(files) == 0: return for _file in files: if _file not in self.selected: if os.path.getsize(_file) <= self.max_size * 1048576: self.selected.append(_file) elif self.allow_big_file: self.selected.append(_file) self.show_selected() def keyPressEvent(self, e): if e.key() == Qt.Key.Key_Delete: # delete self.slot_btn_deleteSelect()