class HLabels(QHBoxLayout): labelSheet = """QLabel { font-size: 12pt; font-weight: bold; padding-right: 5px; color: #9eeeee;}""" valSheet = """QLabel { font-size: 12pt; font-weight: bold; color: #eece9e;}""" def __init__(self, label1=None, label2=None): super().__init__() self.label = QLabel(label1) self.label.setStyleSheet(self.labelSheet) self.value = QLabel(label2) self.value.setStyleSheet(self.valSheet) self.addWidget(self.label) self.addWidget(self.value) self.label.setAlignment(Qt.AlignmentFlag.AlignRight) self.value.setAlignment(Qt.AlignmentFlag.AlignLeft) def update_value(self, text): self.value.setText(str(text)) def update_percent(self, var): text = str(round(var * 100, 4)) self.value.setText(text + "%")
def show_setting(self, conf: dict, layout: QGridLayout): groups = list() x = 0 y = 0 shape = 3 for key in conf.keys(): if type(conf[key]) == bool or type(conf[key]) == str: continue conf_title = conf[key]["title"] conf_enabled = conf[key]["enabled"] conf_times = conf[key]["times"] group = QGroupBox(conf_title) group.setStyleSheet("QGroupBox{border-radius:5px;}") group.setToolTip(conf_title + " 的设置") enabled = QCheckBox("启用") enabled.setObjectName(key) enabled.setToolTip("单击切换开关状态") enabled.setChecked(conf_enabled) enabled.setStyleSheet( "QCheckBox::indicator{width:10px;height:10px;border:none;border-radius:5px;background:#9BE3DE;}QCheckBox::indicator:unchecked{background:#BEEBE9;}QCheckBox::indicator:unchecked:hover{background:#9AD3BC;}QCheckBox::indicator:checked{background:#95E1D3;}QCheckBox::indicator:checked:hover{background:#98DED9;}" ) times = QHBoxLayout() times_label = QLabel("次数:") times_label.setStyleSheet( "QLabel{background:transparent;border:none;border-radius:5px;}" ) times_input = EnhancedEdit() times_input.setObjectName(key) times_input.setText(str(conf_times)) times_input.setToolTip("仅限正整数") times_input.setValidator( QRegularExpressionValidator( QRegularExpression("^[1-9][0-9]{1,8}$"))) times_input.setStyleSheet( "QLineEdit{border:1px solid #F3EAC2;border-radius:5px;background:transparent;}QLineEdit:hover{border:1px solid #F5B461;}" ) times.addWidget(times_label) times.addWidget(times_input) group_layout = QVBoxLayout() group_layout.addWidget(enabled) group_layout.addLayout(times) group.setLayout(group_layout) group.setObjectName(key) groups.append(group) for group in groups: if y >= shape: x = x + 1 y = 0 layout.addWidget(group, x, y) y = y + 1 self.logger.debug("最后的元素位置:(%d,%d)" % (x, y)) return (x, y)
class FileHashCheckerApp(QWidget): def __init__(self): super().__init__() self.setWindowTitle("File Hash Checker") self.setGeometry(0, 0, 600, 300) self.labelFileDropper = FileDropper("Datei auswählen") #self.selectFileButton.clicked.connect(self.get_file_to_check) self.labelFileDropper.setAcceptDrops(True) self.labelFileDropper.setStyleSheet( "border: 1px solid black; border-radius: 15px; text-align: center; " "height: 200px") self.labelFileDropper.move(0, 0) self.labelFileDropper.resize(500, 150) self.labelMD5Hash = QLabel( "Bitte zu vergleichenden MD5 Hash eingeben:") self.textMD5Hash = QLineEdit() self.buttonCompareMD5 = QPushButton('MD5 Vergleichen') self.buttonCompareMD5.clicked.connect(self.compare_md5) self.labelIsSame = QLabel() layout = QVBoxLayout() layout.addWidget(self.labelFileDropper) layout.addWidget(self.labelMD5Hash) layout.addWidget(self.textMD5Hash) layout.addWidget(self.buttonCompareMD5) layout.addWidget(self.labelIsSame) self.setLayout(layout) def get_file_to_check(self): file_name, _ = QFileDialog.getOpenFileName(self, 'Datei auswählen') self.labelFilePath.setText(file_name) def compare_md5(self): given_hash = self.textMD5Hash.text() generated_hash = md5(self.selectFileButton.text()) if given_hash.upper() == generated_hash.upper(): self.labelIsSame.setText('True') self.labelIsSame.setStyleSheet("background-color: green") else: self.labelIsSame.setText('False: ' + generated_hash.upper()) self.labelIsSame.setStyleSheet("background-color: red")
class StatsFrame(QWidget): """Calculate statistics for each turn of the players.""" label_ssheet = """QLabel { font-size: 15pt; font-weight: bold; padding-right: 5px; color: #fca018;}""" def __init__(self, parent=None, window=None): super().__init__(parent=parent) self.window = window self.hlayout = QHBoxLayout() self.setLayout(self.hlayout) self.vbox1 = QVBoxLayout() self.vbox2 = QVBoxLayout() self.cardCount = HLabels("Cards in Deck: ", "0") self.deckCount = HLabels("Number of Decks: ", "0") self.playerCount = HLabels("Number of Players: ", "0") self.breaking21 = HLabels("Breaking 21: ", " 0") self.exactly = HLabels("Exactly 21: ", " 0") self.under21 = HLabels("Under 21: ", " 0") self.probabilities = QLabel("Probabilities", parent=self) self.quantities = QLabel("Quantities", parent=self) self.vbox2.addWidget(self.quantities) self.vbox2.addLayout(self.cardCount) self.vbox2.addLayout(self.deckCount) self.vbox2.addLayout(self.playerCount) self.vbox1.addWidget(self.probabilities) self.vbox1.addLayout(self.breaking21) self.vbox1.addLayout(self.exactly) self.vbox1.addLayout(self.under21) self.hlayout.addLayout(self.vbox1) self.hlayout.addLayout(self.vbox2) self.quantities.setStyleSheet(self.label_ssheet) self.probabilities.setStyleSheet(self.label_ssheet) self.probabilities.setAlignment(Qt.AlignmentFlag(4)) self.quantities.setAlignment(Qt.AlignmentFlag(4)) self.labels = { "cards": self.cardCount, "decks": self.deckCount, "players": self.playerCount, "breaking": self.breaking21, "exactly": self.exactly, "under": self.under21, } self.window.driver.hook(self.labels)
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() label = QLabel("This is PyQt6 Label") vbox.addWidget(label) label2 = QLabel("This is PyQt6 GUI Application Development") label2.setFont(QtGui.QFont("Sanserif ", 20)) label2.setStyleSheet('color:red') vbox.addWidget(label2) self.setLayout(vbox) self.show()
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()
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 FixedSlider(QSlider): def __init__(self): super(FixedSlider,self).__init__() self.setTickInterval(1) self.value_label=QLabel(self) self.value_label.setFixedSize(40,20) self.value_label.setAutoFillBackground(True) self.value_label.setStyleSheet("QLabel{background:transparent;font:8px}") self.value_label.setAlignment(Qt.Alignment.AlignCenter) self.value_label.setVisible(False) self.value_label.move(0,-5) def mousePressEvent(self,event:QMouseEvent): super(FixedSlider,self).mousePressEvent(event) if self.value_label.isVisible()==False: self.value_label.setVisible(True) self.value_label.setText(str(self.value()/10)) def mouseMoveEvent(self,event:QMouseEvent): super(FixedSlider,self).mouseMoveEvent(event) self.value_label.setText(str(self.value()/10)) self.value_label.move(int((self.width()-self.value_label.width())*self.value()/(self.maximum()-self.minimum())),-5) def mouseReleaseEvent(self,event:QMouseEvent): super(FixedSlider,self).mouseReleaseEvent(event) if self.value_label.isVisible()==True: self.value_label.setVisible(False)
class UI(QWidget): update_signal = pyqtSignal(str) show_qr_signal = pyqtSignal(bytes) finish_signal = pyqtSignal() close_qr_signal = pyqtSignal() def __init__(self): super().__init__() self.logger = logging.getLogger(__name__) formatter = logging.Formatter( fmt="%(asctime)s-%(levelname)s-%(message)s", datefmt="%Y-%m-%d %H:%M:%S") filehandler = logging.FileHandler(filename="logs.log", mode="w", encoding="utf-8") handler = QLogger(update_signal=self.update_signal) handler.setLevel(logging.INFO) filehandler.setLevel(logging.INFO) self.logger.setLevel(logging.INFO) if os.path.exists("config.json") == False: self.gen_conf() with open(file="config.json", mode="r", encoding="utf-8") as conf_reader: conf = json.loads(conf_reader.read()) debug = bool(conf["debug"]) if debug == True: handler.setLevel(logging.DEBUG) filehandler.setLevel(logging.DEBUG) self.logger.setLevel(logging.DEBUG) handler.setFormatter(formatter) filehandler.setFormatter(formatter) self.logger.addHandler(handler) self.logger.addHandler(filehandler) self.logger.debug("当前调试状态:%s" % debug) self.resize(1024, 768) self.setWindowOpacity(0.9) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.setWindowFlag(Qt.WindowFlags.FramelessWindowHint) self.setAutoFillBackground(True) self.work = Work(show_qr_signal=self.show_qr_signal, finish_signal=self.finish_signal, close_qr_signal=self.close_qr_signal) self.work_thread = QThread() self.work.moveToThread(self.work_thread) self.main_layout = QGridLayout() self.setLayout(self.main_layout) self.title = QLabel("ChinaUniOnlineGUI") self.title.setStyleSheet( "QLabel{border:none;border-radius:5px;background:transparent;color:#9AD3BC;font-size:60px;}" ) self.title.setAlignment(Qt.Alignment.AlignCenter) handler.widget.setStyleSheet( "QPlainTextEdit{font-family:Microsoft YaHei;background:#F3EAC2;border:none;border-radius:5px;}QScrollBar:vertical,QScrollBar::handle:vertical{background:#F3EAC2;border:none;border-radius:8px;width:16px;}QScrollBar::handle:vertical:hover{background:#F5B461;}QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical{background:#FFFDF9;border:none;border-radius:8px;width:16px;}QScrollBar::down-arrow:vertical,QScrollBar::up-arrow:vertical{background:#F5B461;border:none;border-radius:8px;width:16px;height:16px;}QScrollBar::sub-line:vertical,QScrollBar::add-line:vertical{background:transparent;border:none;}" ) self.control = QVBoxLayout() self.control_close = QPushButton() self.control_close.setToolTip("关闭") self.control_close.setStyleSheet( "QPushButton{background:#FFE3ED;border-radius:5px;border:none;}QPushButton:hover{background:#EC524B;}" ) self.contron_max = QPushButton() if self.isMaximized() == False: self.contron_max.setToolTip("最大化") else: self.contron_max.setToolTip("还原") self.contron_max.setStyleSheet( "QPushButton{background:#FFFDF9;border-radius:5px;border:none;}QPushButton:hover{background:#F5B461;}" ) self.control_min = QPushButton() self.control_min.setToolTip("最小化") self.control_min.setStyleSheet( "QPushButton{background:#BEEBE9;border-radius:5px;border:none;}QPushButton:hover{background:#F3EAC2;}" ) self.start_button = QPushButton("开始(&S)") self.start_button.setStyleSheet( "QPushButton{background:#9BE3DE;border:none;border-radius:5px;font-size:20px;font-family:DengXian;}QPushButton:hover{background:#9AD3BC;}" ) self.start_button.setToolTip("开始") self.start_button.setFixedSize(120, 60) self.start_button.setDefault(True) setting_button = QPushButton("设置") setting_button.setToolTip("设置") setting_button.setFixedSize(60, 60) setting_button.setStyleSheet( "QPushButton{background:#9BE3DE;border:none;border-radius:5px;font-size:20px;font-family:DengXian;}QPushButton:hover{background:#9AD3BC;}" ) setting_button.clicked.connect(self.setting_callback) start = QHBoxLayout() start.addWidget(self.start_button, 2) start.addWidget(setting_button, 1) self.control_close.clicked.connect(self.close) self.control_min.clicked.connect(self.min_callback) self.contron_max.clicked.connect(self.max_callback) self.start_button.clicked.connect(self.start_callback) self.work_thread.started.connect(self.work.start) self.finish_signal.connect(self.finish_callback) self.close_qr_signal.connect(self.close_qr) self.control.addWidget(self.control_min) self.control.addWidget(self.contron_max) self.control.addWidget(self.control_close) self.main_layout.addLayout(self.control, 0, 0) self.main_layout.addWidget(self.title, 0, 1) self.main_layout.addLayout(start, 0, 2) self.main_layout.addWidget(handler.widget, 1, 1) self.update_signal.connect(handler.widget.appendPlainText) handler.widget.textChanged.connect(handler.scroll_widget_to_bottom) self.show_qr_signal.connect(self.show_qr) self.logger.debug("已初始化UI") def min_callback(self): if self.isMinimized() == False: self.showMinimized() def max_callback(self): if self.isMaximized() == False: self.showMaximized() self.contron_max.setToolTip("还原") else: self.showNormal() self.contron_max.setToolTip("最大化") def start_callback(self): self.start_time = time.time() self.work_thread.start() self.start_button.setEnabled(False) self.start_button.setText("执行中...") def finish_callback(self): self.start_button.setEnabled(True) self.start_button.setText("开始") passed_time = time.time() - self.start_time mins, secs = divmod(passed_time, 60) hours, mins = divmod(mins, 60) self.logger.info("执行完成,共计用时 {:0>2d}:{:0>2d}:{:0>2d}".format( int(hours), int(mins), int(secs))) 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() def close_qr(self): self.qr_dialog.close() def setting_callback(self): setting = SettingWindow(parent=self) setting.setStyleSheet( "QDialog{border:none;border-radius:5px;background:#F3EAC2;}") setting.show() def gen_conf(self): default_conf = { "debug": False, "hero": { "title": "英雄篇", "enabled": True, "times": 1 }, "revival": { "title": "复兴篇", "enabled": True, "times": 1 }, "creation": { "title": "创新篇", "enabled": True, "times": 1 }, "belief": { "title": "信念篇", "enabled": True, "times": 1 }, "limit_time": { "title": "限时赛", "enabled": True, "times": 1 }, "rob": { "title": "抢十赛", "enabled": True, "times": 1 } } with open(file="config.json", mode="w", encoding="utf-8") as conf_writer: conf_writer.write( json.dumps(default_conf, indent=4, sort_keys=True, ensure_ascii=False)) self.logger.info("已生成默认配置文件") def mousePressEvent(self, event: QMouseEvent): self.logger.debug("触发鼠标按压事件") super().mousePressEvent(event) self.setFocus() self.m_flag = True if event.button() == Qt.MouseButtons.LeftButton and self.isMaximized( ) == False and self.hasFocus() == True: self.old_pos = event.globalPosition() #获取鼠标相对窗口的位置 self.logger.debug("已获取鼠标位置") self.setCursor(QtGui.QCursor( Qt.CursorShape.SizeAllCursor)) #更改鼠标图标 def mouseMoveEvent(self, event: QMouseEvent): self.logger.debug("触发鼠标移动事件") super().mouseMoveEvent(event) if self.m_flag == True: delta_x = int(event.globalPosition().x() - self.old_pos.x()) delta_y = int(event.globalPosition().y() - self.old_pos.y()) self.move(self.x() + delta_x, self.y() + delta_y) #更改窗口位置 self.logger.debug("已更改窗口位置") self.old_pos = event.globalPosition() def mouseReleaseEvent(self, event: QMouseEvent): self.logger.debug("触发鼠标释放事件") super().mouseReleaseEvent(event) self.m_flag = False self.setCursor(QtGui.QCursor(Qt.CursorShape.ArrowCursor))
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)
def __init__(self, parent: QWidget): super().__init__() self.logger = logging.getLogger(__name__) with open(file="config.json", mode="r", encoding="utf-8") as conf_reader: self.conf = json.loads(conf_reader.read()) self.logger.debug("初始化设置界面。设置内容:%s" % self.conf) layout = QGridLayout() self.setLayout(layout) self.setModal(True) self.setParent(parent) self.resize(400, 300) title = QLabel("设置") title.setStyleSheet( "QLabel{border:none;border-radius:5px;background:transparent;color:#9AD3BC;font-size:20px;}" ) title.setAlignment(Qt.Alignment.AlignCenter) layout.addWidget(title, 0, 1, Qt.Alignment.AlignCenter) control_close = QPushButton() control_close.setStyleSheet( "QPushButton{background:#FFE3ED;border-radius:5px;border:none;}QPushButton:hover{background:#EC524B;}" ) control_close.setToolTip("关闭") control_close.setFixedHeight(20) control_close.clicked.connect(self.close_callback) layout.addWidget(control_close, 0, 0) debug_check = QCheckBox("调试模式") debug_check.setChecked(self.conf["debug"]) debug_check.setToolTip("单击切换开关状态") debug_check.setStyleSheet( "QCheckBox::indicator{width:10px;height:10px;border:none;border-radius:5px;background:#9BE3DE;}QCheckBox::indicator:unchecked{background:#BEEBE9;}QCheckBox::indicator:unchecked:hover{background:#9AD3BC;}QCheckBox::indicator:checked{background:#95E1D3;}QCheckBox::indicator:checked:hover{background:#98DED9;}" ) self.content = QGridLayout() (x, y) = self.show_setting(conf=self.conf, layout=self.content) # 返回content的最后一个元素的x,y proxy = QGroupBox() proxy.setObjectName("proxy") proxy_layout = QVBoxLayout() proxy_label = QLabel("代理地址:") proxy_label.setStyleSheet( "QLabel{background:transparent;border:none;}") proxy_input = EnhancedEdit() proxy_input.setText(self.conf["proxy"]) proxy_input.setToolTip("格式为协议://IP:端口,留空保持直连") proxy_input.setStyleSheet( "QLineEdit{border:1px solid #F3EAC2;border-radius:5px;background:transparent;}QLineEdit:hover{border:1px solid #F5B461;}" ) proxy_layout.addWidget(proxy_label) proxy_layout.addWidget(proxy_input) proxy.setLayout(proxy_layout) proxy.setStyleSheet("QGroupBox{border-radius:5px;}") proxy.setToolTip("代理设置") if y + 1 >= 3: y_ = 0 x_ = x + 1 else: y_ = y + 1 x_ = x self.content.addWidget(proxy, x_, y_) self.content.addWidget(debug_check) layout.addLayout(self.content, 1, 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 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(QWidget): def reload(self): if len(self.file_list): return self.webEngineView.reload() def on_downloadRequestState(self,event): if not len(self.file_list): self.label.setText('Статус:...') self.reload() try: for b in self.buttons: b.setEnabled(True) except: pass return if event.value>1: if event.value==2: if len(self.file_list[-1])>5: h0=self.file_list[-1][6] h1=gethash(self.file_list[-1][-1]) if h0==h1: try: tm=mktime(datetime.strptime(self.file_list[-1][4],"%d.%m.%Y %H:%M").timetuple()) tm+=(int(self.file_list[-1][5])%60) utime(self.file_list[-1][-1],(tm,tm)) except: pass else: print('Error. ("%s") - %i'%(self.file_list[-1][-1],event.value)) self.label.setText('Статус: ошибка скачивания файла ("'+self.file_list[-1][-1]+'")') self.file_list.pop() self.download_files() else: print('Error. ("%s") - %i'%(self.file_list[-1][-1],event.value)) self.label.setText('Статус: ошибка скачивания файла ("%s", код ошибки: %i)!'%(self.file_list[-1][-1],event.value)) self.reload() try: for b in self.buttons: b.setEnabled(True) except: pass def check_canceled(self,event): if event: self.canceled_callback() def on_downloadRequested(self,event): self.webEngineView.page().runJavaScript(\ """ function check_canceled(){ if(window.process_canceled) return 1; else return 0; }; check_canceled(); """,self.check_canceled) if event.state().value==0: event.stateChanged.connect(lambda event: self.on_downloadRequestState(event)) event.accept() def download_files(self): if len(self.file_list): if not self.file_number: self.file_number=len(self.file_list) tmp=self.file_list[-1] if exists(tmp[-1]): try: remove(tmp[-1]) except: pass self.webEngineView.page().profile().downloadRequested.connect(lambda event:\ self.on_downloadRequested(event)) self.label.setText('Статус: скачивание файла "%s" ( %i из %i )'%\ (tmp[2],self.file_number-len(self.file_list)+1,self.file_number)) self.webEngineView.page().download(QUrl(tmp[3]),tmp[-1]) else: self.webEngineView.page().profile().downloadRequested.disconnect() self.label.setText('Статус:...') self.reload() try: for b in self.buttons: b.setEnabled(True) except: pass def canceled_callback(self): self.label.setText('Статус: операция отменена!') self.file_list=[] self.file_number=0 self.reload() try: for b in self.buttons: b.setEnabled(True) except: pass def getfilelist_error_callback(self): self.label.setText('Статус: ошибка при получении списка файлов для скачивания!') self.file_list=[] self.file_number=0 self.reload() try: for b in self.buttons: b.setEnabled(True) except: pass def getfilelist_success_callback(self,links): for link in links: path=join(self.basepath,link[0],link[1]) try: makedirs(path,exist_ok=True) except: return link.append(realpath(join(path,link[2]))) self.file_list.append(link) if len(self.file_list): self.download_files() def progress_callback(self,counter): self.label.setText('Статус: получение списка файлов для скачивания (получено %d ссылок)...'%counter) def getfileslist(self): if len(self.file_list): return self.url=self.webEngineView.url().toString() if not('Files/prt0' in self.url): return self.file_list=[] self.file_number=0 try: for b in self.buttons: b.setEnabled(False) except: pass self.label.setText('Статус: получение списка файлов для скачивания...') self.webEngineView.page().runJavaScript(\ self.qwebchannel_js+self.busy+""" function get_sig_links_details_parse(file_links,details_links,partitions,partition,section,data){ if(window.process_canceled){ cancel(); return; }; var parser=new DOMParser(); var doc=parser.parseFromString(data,'text/html'); var req=doc.querySelectorAll('a[href*=\"GetFile\"]'); for(const e of req){ if(e.text.includes('.sig')){ file_links.push([pd_replace(partition),sec_replace(section),e.text,e.href]); progress(file_links); }; }; if(details_links.length) get_sig_links_details(file_links,details_links,partitions); else success(file_links); }; function start_parser(){ var file_links=[]; var details_links=[]; var url=window.location.href.toString(); var path=''; if(url.includes('ProjectDocuments')) path='ProjectDocuments'; else if(url.includes('RIIDocuments')) path='RIIDocuments'; else if(url.includes('SmetaDocuments')) path='SmetaDocuments'; else if(url.includes('IrdDocuments')) path='IrdDocuments'; var href=''.concat('https://lk.spbexp.ru/Grid/',path,'FilesRead/own', window.location.href.toString().split('\/').pop()); var v=document.querySelector('span[style=\"font-weight:600; color:darkblue\"]');\ if(v==null) return; var section=v.textContent; var links=[[path,section,href]]; new QWebChannel(qt.webChannelTransport,(channel)=>{ window.qtwebchannel=channel.objects.backend; }); get_pd_links_details(links,file_links,details_links,[]); }; start_parser(); """,lambda x: None) def getallfileslist(self): if len(self.file_list): return self.url=self.webEngineView.url().toString() if not('lk.spbexp.ru/Zeg/Zegmain' in self.url or\ ('lk.spbexp.ru/SF/' in self.url and '/prt0/' in self.url)): return self.file_list=[] self.file_number=0 try: for b in self.buttons: b.setEnabled(False) except: pass self.label.setText('Статус: получение списка файлов для скачивания...') self.webEngineView.page().runJavaScript(\ self.qwebchannel_js+self.busy+""" function get_sig_links_details_parse(file_links,details_links,partitions,partition,section,data){ if(window.process_canceled){ cancel(); return; }; var parser=new DOMParser(); var doc=parser.parseFromString(data,'text/html'); var req=doc.querySelectorAll('a[href*=\"GetFile\"]'); for(const e of req){ if(e.text.includes('.sig')){ file_links.push([pd_replace(partition),sec_replace(section),e.text,e.href]); progress(file_links); }; }; if(details_links.length) get_sig_links_details(file_links,details_links,partitions); else get_file_links(file_links,details_links,partitions); }; function get_pd_links_parse(path,file_links,details_links,partitions,data){ if(window.process_canceled){ cancel(); return; }; var links=[]; for(const d of data.Data){ if(d['NumberOfFiles']){ var href=''.concat('https://lk.spbexp.ru/Grid/',path,'FilesRead/own',d['IDRow']); if(path.includes('IrdDocuments')) links.push([path,d['Content'],href]); else links.push([path,d['Nazvanie'],href]); }; }; if(links.length) get_pd_links_details(links,file_links,details_links,partitions); }; async function get_pd_links(path,file_links,details_links,partitions){ if(window.process_canceled){ cancel(); return; }; var req=document.querySelector('a[href*="/Zeg/Zegmain1"]'); if(req){ var href=''.concat('https://lk.spbexp.ru/Grid/',path,'Read/own',req.pathname.split('\/').pop()); return await fetch(href) .then(async (response) => { const j=await response.json(); get_pd_links_parse(path,file_links,details_links,partitions,j); }).catch((error) => { errlog(error); }); }; }; function get_file_links(file_links,details_links,partitions){ if(window.process_canceled){ cancel(); return; }; if(partitions.length) get_pd_links(partitions.pop(),file_links,details_links,partitions); else success(file_links); }; function start_parser(){ var file_links=[]; var details_links=[]; var partitions=['ProjectDocuments','RIIDocuments','SmetaDocuments','IrdDocuments']; new QWebChannel(qt.webChannelTransport,(channel)=>{ window.qtwebchannel=channel.objects.backend; }); get_file_links(file_links,details_links,partitions); }; start_parser(); """,lambda x: None) def __init__(self): super().__init__() self.setWindowTitle('ExpGet') self.busy=""" window.process_canceled=false; function errlog(error){ window.qtwebchannel.backrun_error(error); }; function success(file_links){ window.qtwebchannel.backrun_success(file_links); }; function progress(file_links){ window.qtwebchannel.backrun_progress(file_links.length); }; async function cancel(){ window.qtwebchannel.backrun_cancel(); }; function pd_replace(partition){ switch(partition){ case 'ProjectDocuments': return 'ПД'; case 'RIIDocuments': return 'РИИ'; case 'SmetaDocuments': return 'СД'; case 'IrdDocuments': return 'ИРД'; }; }; function sec_replace(section){ return section.trim().replaceAll('"','_').slice(0,64).trim(); }; async function get_sig_links_details(file_links,details_links,partitions){ if(window.process_canceled){ cancel(); return; }; if(details_links.length){ var d=details_links.pop(); return await fetch(d[2]) .then(async (response) => { const j=await response.text(); get_sig_links_details_parse(file_links,details_links,partitions,d[0],d[1],j); }).catch((error) => { errlog(error); }); }; }; function get_pd_links_details_parse(links,file_links,details_links,partitions,partition,section,data){ if(window.process_canceled){ cancel(); return; }; for(const d of data.Data){ file_links.push([pd_replace(partition),sec_replace(section),d['Nazvanie'], ''.concat('https://lk.spbexp.ru/File/GetFile/',d['IDRow']),d['sDateTo'],d['Number'],d['sMD5']]); progress(file_links); details_links.push([partition,section,''.concat('https://lk.spbexp.ru/SF/FileCspViewer/',d['IDRow'])]); }; if(links.length) get_pd_links_details(links,file_links,details_links,partitions); else get_sig_links_details(file_links,details_links,partitions); }; async function get_pd_links_details(links,file_links,details_links,partitions){ if(window.process_canceled){ cancel(); return; }; if(links.length){ var d=links.pop(); return await fetch(d[2]) .then(async (response) => { const j=await response.json(); get_pd_links_details_parse(links,file_links,details_links,partitions,d[0],d[1],j); }).catch((error) => { errlog(error); }); }; }; function button_click(){ window.process_canceled=true; }; class ProgressRing extends HTMLElement{ constructor(){ super(); const stroke=this.getAttribute('stroke'); const radius=this.getAttribute('radius'); const normalizedRadius=radius-stroke*2; this._circumference=normalizedRadius*2*Math.PI; this._root=this.attachShadow({mode:'open'}); this._root.innerHTML=` <svg height="${radius*2}" width="${radius*2}"> <circle class="ring" stroke="#3c3b3a" stroke-width="${stroke*2}" stroke-opacity="0.5" fill="transparent" r="${normalizedRadius}" cx="${radius}" cy="${radius}" shape-rendering="geometricPrecision" /> <circle class="ring" stroke="#c8c5c3" stroke-dasharray="${this._circumference} ${this._circumference}" style="stroke-dashoffset:${this._circumference}" stroke-width="${stroke}" fill="transparent" r="${normalizedRadius}" cx="${radius}" cy="${radius}" shape-rendering="geometricPrecision" /> <circle class="button" stroke="#191919" stroke-width="1" fill="#f44336" r="${normalizedRadius-stroke}" cx="${radius}" cy="${radius}" shape-rendering="geometricPrecision" onclick="button_click()" /> <text class="txt" x="50%" y="52%" text-rendering="geometricPrecision">STOP</text> <circle class="ring" stroke="#000000" stroke-width="1" stroke-opacity="0.5" fill="transparent" r="${normalizedRadius+8}" cx="${radius}" cy="${radius}" shape-rendering="geometricPrecision" /> </svg> <style> .ring { transition: stroke-dashoffset 0.35s; transform: rotate(-90deg); transform-origin: 50% 50%; pointer-events: none; } .button:hover { fill: #ce000f; opacity: 1; } .txt { font: bold 40px sans-serif; fill: #323232; stroke: #4c4c4c; stroke-width: 1px; text-anchor: middle; dominant-baseline: middle; pointer-events: none; } </style> `; } setProgress(percent){ const offset=this._circumference-(percent/100*this._circumference); const circle=this._root.querySelectorAll('circle')[1]; circle.style.strokeDashoffset=offset; } setTransition(value){ const circle=this._root.querySelectorAll('circle')[1]; circle.style.transition='stroke-dashoffset '+value+'s'; } static get observedAttributes(){ return ['progress','transition']; } attributeChangedCallback(name,oldValue,newValue){ if(name==='progress') this.setProgress(newValue); if(name==='transition') this.setTransition(newValue); } } function overlay() { [].forEach.call(document.querySelectorAll('nav'), function (el) { el.style.visibility = 'hidden'; }); [].forEach.call(document.querySelectorAll('*[class*=popover]'), function (el) { el.style.visibility = 'hidden'; }); var overlay=document.createElement("div"); overlay.style.opacity=0.9; overlay.style.width='100%'; overlay.style.height='100%'; overlay.style.top='0px'; overlay.style.left='0px'; overlay.style.backgroundColor='#666666'; overlay.style.zIndex='1000'; overlay.style.position = 'absolute'; document.getElementsByTagName('body')[0].appendChild(overlay); window.customElements.define('progress-ring',ProgressRing); const circle=document.createElement("div"); circle.innerHTML=`<progress-ring stroke="8" radius="88" progress="0"></progress-ring>` circle.style.top='50%'; circle.style.left='50%'; circle.style.marginTop='-88px' circle.style.marginLeft='-88px' circle.style.zIndex='2000'; circle.style.position='absolute'; window.cprogress=0; document.body.appendChild(circle); document.body.style.overflow='hidden'; }; overlay(); const interval=setInterval(()=>{ const el=document.querySelector('progress-ring'); if(window.cprogress==200){ el.setAttribute('transition','0'); window.cprogress=0; el.setAttribute('progress',window.cprogress); } else{ if(window.cprogress==0) el.setAttribute('transition','0.35'); window.cprogress+=2; el.setAttribute('progress',window.cprogress); }; },100); """ self.webEngineView=QWebEngineView() ws=self.webEngineView.page().profile().defaultProfile() ws.setHttpCacheMaximumSize(0) ws=self.webEngineView.settings() ws.setAttribute(ws.WebAttribute.AutoLoadImages,True) ws.setAttribute(ws.WebAttribute.PluginsEnabled,False) qwebchannel_js=QFile(':/qtwebchannel/qwebchannel.js') if not qwebchannel_js.open(QIODeviceBase.OpenModeFlag.ReadOnly): raise SystemExit( 'Failed to load qwebchannel.js with error: %s' % qwebchannel_js.errorString()) self.qwebchannel_js=bytes(qwebchannel_js.readAll()).decode('utf-8') self.channel=QWebChannel() self.handler=CallHandler() self.handler.set_parent(self) self.channel.registerObject('backend',self.handler) self.webEngineView.page().setWebChannel(self.channel) self.grid=QGridLayout() self.grid.setContentsMargins(0,0,0,0) self.grid.setVerticalSpacing(0) self.grid.setHorizontalSpacing(0) self.hbox=QHBoxLayout() self.hbox.setSpacing(0) self.reload_button=QPushButton('Обновить страницу') self.get_data_button=QPushButton('Получить данные') self.get_all_data_button=QPushButton('Скачать данные проекта') self.buttons=[self.reload_button,self.get_data_button,self.get_all_data_button] self.hbox.addWidget(self.reload_button,1) self.hbox.addWidget(self.get_data_button,1) self.hbox.addWidget(self.get_all_data_button,1) self.grid.addLayout(self.hbox,0,0) self.grid.addWidget(self.webEngineView,1,0) self.label=QLabel('Статус:...') self.label.setStyleSheet("QLabel {background-color:gray;color:black;}") self.grid.addWidget(self.label,2,0) self.grid.setRowStretch(0,0) self.grid.setRowStretch(1,1) self.grid.setRowStretch(2,0) self.setLayout(self.grid) self.webEngineView.load(QUrl('https://lk.spbexp.ru')) self.webEngineView.loadFinished.connect(self.login) self.file_list=[] self.file_number=0 self.lock=False self.basepath=realpath('out') try: makedirs(self.basepath,exist_ok=True) except: pass self.reload_button.clicked.connect(self.reload) self.get_data_button.clicked.connect(self.getfileslist) self.get_all_data_button.clicked.connect(self.getallfileslist) def goto_projects(self): self.webEngineView.loadFinished.disconnect() self.webEngineView.load(QUrl('https://lk.spbexp.ru/SF/Zayava/')) def login_callback(self,result): if len(result): self.webEngineView.loadFinished.connect(self.goto_projects) def login(self): l=p='' try: f=open('login','r') d=f.readlines() f.close() l=d[0].strip() p=d[1].strip() except: self.webEngineView.loadFinished.disconnect() return self.webEngineView.page().runJavaScript(""" function find_login_form(){ var v=document.querySelector('input[id=Login]'); if(v!=null){ v.value=`%s`; v=document.querySelector('input[id=Password]'); if(v!=null){ v.value=`%s`; v=document.querySelector('button[type=submit]'); if(v!=null){ v.click(); return 'ok'; }; }; }; return ''; }; find_login_form();"""%(l,p),self.login_callback)
class PlayerBox(QGroupBox): """PlayerBox Subclass of QGroupBox. Returns GroupBoxWidget data and cards for each player. """ offsheet = """QGroupBox { font-size: 14pt; padding: 4px; margin: 5px; color: #efeefe; border: 9px solid #dfa;}""" onsheet = """QGroupBox { color: red; padding: 4px; margin: 5px; border: 5px solid red; border-radius: 2px;}""" labelsheet = """QLabel { color: #efeefe; margin-bottom: 8px; font-weight: bold; font-size: 14pt; font-style: italic;}""" scoresheet = """QLabel { border: 1px solid #efeefe; padding: 3px; margin-bottom: 8px; color: #efeefe; font-weight: bold; font-size: 16pt; font-style: italic;}""" def __init__(self, title, parent=None, player=None): """Construct a PlayerBox Widget. Args: parent (QWidget, optional) Parent widget object. Defaults to None. player (Player) The player this box will be assigned to. """ super().__init__(title, parent=parent) self.player = player self._turn = False self.player.set_box(self) self.setStyleSheet(self.offsheet) self._setupUi() def _setupUi(self): """Create UI elements.""" self.vbox = QVBoxLayout() self.vbox.setObjectName(self.player.title + "BoxVertLayout") self.grid = QGridLayout() self.grid.setObjectName(self.player.title + "BoxCardPicsLayout") self.hbox = QHBoxLayout() self.hbox.setObjectName(self.player.title + "BoxHorizLayout") self.vbox.addLayout(self.hbox) self.vbox.addLayout(self.grid) self.setLayout(self.vbox) self._setupLabels() self._setupCards() def _setupLabels(self): """Set up window labels.""" expolicy = QSizePolicy.Policy.MinimumExpanding minpolicy = QSizePolicy.Policy.Minimum self.label = QLabel("Score: ") self.label.setObjectName("ScoreLabel") self.label.setStyleSheet(self.labelsheet) self.hbox.addWidget(self.label) self.hbox.addSpacerItem(QSpacerItem(10, 0, minpolicy, minpolicy)) self.scorelabel = QLabel("0") self.scorelabel.setObjectName("ScoreValue") self.scorelabel.setStyleSheet(self.scoresheet) self.hbox.addWidget(self.scorelabel) self.hbox.addSpacerItem(QSpacerItem(50, 0, expolicy, minpolicy)) def _setupCards(self): """Create card covers.""" card = CardWidget(parent=self) card.setObjectName("Card1") self.addCard(card) self.grid.addWidget(card, 0, 0, -1, -1) card2 = CardWidget(parent=self) card2.setObjectName(self.player.title + "Card2") self.addCard(card2) self.grid.addWidget(card2, 0, 1, -1, -1) @property def cardCount(self): """Count cards in players hand. Returns: int: Total number of cards in players hand. """ return len(self.player.cards) @property def cards(self): """Shortcut method accessing players cards property. Returns: list: List of card widgets. """ return self.player.cards def addCard(self, card): """Shortcut for adding card widget to players list of cards. Args: card (obj): CardWidget object. """ self.player.cards.append(card) def deleteCard(self, card): """Remove card from Players list of cards property.""" if card in self.player.cards: self.player.cards.remove(card) def reset(self): """Clear PlayerBox of all widgets. Called when current round ends and new deal begins. """ while len(self.cards) > 0: card = self.cards[0] self.grid.removeWidget(card) card.setVisible(False) card.hide() card.destroy(True, True) self.deleteCard(card) del card self.repaint() self.update() def addWidget(self, card): """Add another card to Window. Args: card (obj): Card object from deck. Returns: bool: True if successful else None """ if self.cardCount <= 2: for widget in self.cards: if not widget.has_card(): return widget.setCard(card) widget = CardWidget(card=card) widget.setObjectName("Card" + str(self.cardCount)) self.grid.addWidget(widget, 0, self.cardCount, -1, -1) return self.addCard(widget) def isTurn(self): """Return True if currently players turn. Returns: bool: True if it's players turn else false. """ return self._turn def turn(self): """Flip `self.turn` property False or True. Changes the style of PlayerBox to indicate if it is or isn't currently players turn. """ if self.isTurn(): self._turn = False self.setStyleSheet(self.offsheet) else: self._turn = True self.setStyleSheet(self.onsheet)
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 Window(QMainWindow): def __init__(self): super().__init__() self.width = 500 self.height = 500 self.xPos = 600 self.yPos = 400 self.initUI() def initUI(self): self.setGeometry(self.xPos, self.yPos, self.width, self.height) self.vBoxLayout = QVBoxLayout() self.slider = Slider( direction=Qt.Orientation.Horizontal, duration=750, animationType=QEasingCurve.Type.OutQuad, wrap=False, ) self.label1 = QLabel() self.label1.setText('First Slide') self.label1.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label1.setStyleSheet( 'QLabel{background-color: rgb(245, 177, 66); color: rgb(21, 21, 21); font: 25pt;}' ) self.slider.addWidget(self.label1) self.label2 = QLabel() self.label2.setText('Second Slide') self.label2.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label2.setStyleSheet( 'QLabel{background-color: rgb(21, 21, 21); color: rgb(245, 177, 66); font: 25pt;}' ) self.slider.addWidget(self.label2) self.label3 = QLabel() self.label3.setText('Third Slide') self.label3.setAlignment(Qt.AlignmentFlag.AlignCenter) self.label3.setStyleSheet( 'QLabel{background-color: rgb(93, 132, 48); color: rgb(245, 177, 66); font: 25pt;}' ) self.slider.addWidget(self.label3) self.buttonPrevious = QPushButton() self.buttonPrevious.setText('Previous Slide') self.buttonPrevious.clicked.connect(self.slider.slidePrevious) self.buttonNext = QPushButton() self.buttonNext.setText('Next Slide') self.buttonNext.clicked.connect(self.slider.slideNext) self.buttonLayout = QHBoxLayout() self.buttonLayout.addWidget(self.buttonPrevious) self.buttonLayout.addWidget(self.buttonNext) self.vBoxLayout.addWidget(self.slider) self.vBoxLayout.addLayout(self.buttonLayout) self.centralWidget = QWidget(self) self.centralWidget.setLayout(self.vBoxLayout) self.setCentralWidget(self.centralWidget) self.show()
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()