def on_about_item(self, event): """ Onselect action for 'About' """ dialog = AboutDialog(self.main_window) dialog.run()
def on_about_item_activate(self, widget): dialog = AboutDialog( "PyGWSvcCtl", "A tool to remotely manage the services on a computer.\n Based on Jelmer Vernooij's original Samba-GTK", self.icon_pixbuf) dialog.run() dialog.hide()
def on_about_item_activate(self, widget): dialog = AboutDialog( "Main", "A tool to display other utilities in a simple, unified window.", None ) dialog.run() dialog.hide()
def on_about_item_activate(self, widget): dialog = AboutDialog( "PyGWSAM", "A tool to manage accounts on a SAM server.\n Based on Jelmer Vernooij's original Samba-GTK", self.icon_pixbuf ) dialog.run() dialog.hide()
def on_about_item_activate(self, widget): dialog = AboutDialog( "PyGWCronTab", "A tool to remotely manage scheduled tasks.\n Based on Jelmer Vernooij's original Samba-GTK", self.icon_pixbuf ) dialog.run() dialog.hide()
def on_about_menu_item_activate(self, menuitem, data=None): ''' Called when the user clicks the 'About' menu. We create a GtkAboutDialog. This dialog will NOT be modal but will be on top of the main application window.''' if self.about_dialog: self.about_dialog.present() return about_dialog=AboutDialog(self) self.about_dialog = about_dialog about_dialog.show()
def init_variable(self): self._disk = LanZouCloud() self._config = "./config.pkl" self._folder_list = {} self._file_list = {} self._path_list = {} self._path_list_old = {} self._locs = {} self._parent_id = -1 # --> .. self._work_name = "" # share disk rec, not use now self._work_id = -1 # disk folder id self._old_work_id = self._work_id # 用于上传完成后判断是否需要更新disk界面 self.load_settings() if os.name == 'nt': self._disk.set_rar_tool("./rar.exe") else: self._disk.set_rar_tool("/usr/bin/rar") # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) # 下载器 self.download_manager = DownloadManager(self._disk) self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 7000)) # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述更新器 self.desc_fetcher = DescFetcher(self._disk) self.desc_fetcher.desc.connect(self.call_update_desc) # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_label.setObjectName("msg_label") self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.rename_set_desc_and_mkdir) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__)
def about(self): dialog = AboutDialog(self) dialog.open()
class MainWindow(QMainWindow, Ui_MainWindow): __version__ = 'v0.0.6' def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.init_variable() self.init_workers() self.init_menu() self.setWindowTitle("蓝奏云客户端 - {}".format(self.__version__)) self.setWindowIcon(QIcon("./icon/lanzou-logo2.png")) self.set_window_at_center() self.init_extract_share_ui() self.init_disk_ui() self.call_login_luncher() self.create_left_menus() self.setObjectName("MainWindow") self.setStyleSheet(qssStyle) # self.disk_tab.setStyleSheet("* {background-color: rgba(255, 255, 255, 150);}") self.tabWidget.setStyleSheet("QTabBar{ background-color: #AEEEEE; }") def init_menu(self): self.login.triggered.connect(self.show_login_dialog) # 登录 self.login.setIcon(QIcon("./icon/login.ico")) self.login.setShortcut("Ctrl+L") self.toolbar.addAction(self.login) self.logout.triggered.connect( lambda: self.logout_worker.set_values(self._disk)) # 登出 self.logout.setIcon(QIcon("./icon/logout.ico")) self.logout.setShortcut("Ctrl+Q") # 登出快捷键 self.download.setShortcut("Ctrl+J") # 以下还未使用 self.download.setIcon(QIcon("./icon/download.ico")) self.download.setEnabled(False) # 暂时不用 self.delete.setShortcut("Ctrl+D") self.delete.setIcon(QIcon("./icon/delete.ico")) self.delete.setEnabled(False) # 暂时不用 # self.how.setShortcut("Ctrl+H") self.how.setIcon(QIcon("./icon/help.ico")) self.how.triggered.connect(self.open_wiki_url) # self.about.setShortcut("Ctrl+O") self.about.setIcon(QIcon("./icon/about.ico")) self.about.triggered.connect(self.about_dialog.exec) self.upload.setIcon(QIcon("./icon/upload.ico")) self.upload.setShortcut("Ctrl+U") # 上传快捷键 def init_variable(self): self._disk = LanZouCloud() self._config = "./config.pkl" self._folder_list = {} self._file_list = {} self._path_list = {} self._path_list_old = {} self._locs = {} self._parent_id = -1 # --> .. self._work_name = "" # share disk rec, not use now self._work_id = -1 # disk folder id self._old_work_id = self._work_id # 用于上传完成后判断是否需要更新disk界面 self.download_threads = 3 # 同时三个下载任务 self.load_settings() if os.name == 'nt': self._disk.set_rar_tool("./rar.exe") else: self._disk.set_rar_tool("/usr/bin/rar") def init_workers(self): # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) # 登出器 self.logout_worker = LogoutWorker() self.logout_worker.successed.connect(self.call_logout_update_ui) # 下载器 self.download_manager = DownloadManager() self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 7000)) # 获取更多信息,直链、下载次数等 self.more_info_worker = GetMoreInfoWorker() self.more_info_worker.msg.connect(self.show_status) self.more_info_worker.infos.connect(self.show_info_dialog) # 登录文件列表更新器 self.list_refresher = ListRefresher(self._disk) self.list_refresher.err_msg.connect(self.show_status) self.list_refresher.infos.connect(self.update_lists) # 获取所有文件夹fid,并移动 self.all_folders_worker = GetAllFoldersWorker() self.all_folders_worker.msg.connect(self.show_status) self.all_folders_worker.infos.connect(self.show_move_file_dialog) self.all_folders_worker.moved.connect( lambda: self.list_refresher.set_values(self._work_id, False, True, False)) # 更新文件列表 # 重命名、修改简介、新建文件夹 self.rename_mkdir_worker = RenameMkdirWorker() self.rename_mkdir_worker.msg.connect(self.show_status) self.rename_mkdir_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 设置文件(夹)提取码 self.set_pwd_worker = SetPwdWorker() self.set_pwd_worker.msg.connect(self.show_status) self.set_pwd_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 删除文件(夹) self.remove_files_worker = RemoveFilesWorker(self._disk) self.remove_files_worker.msg.connect(self.show_status) # 显示错误提示 self.remove_files_worker.finished.connect( lambda: self.list_refresher.set_values(self._work_id)) # 更新界面 # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述与提取码更新器 self.desc_pwd_fetcher = DescPwdFetcher() self.desc_pwd_fetcher.desc.connect(self.call_update_desc_pwd) self.desc_pwd_fetcher.tasks.connect( self.call_download_manager_thread) # 连接下载管理器线程 # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_label.setObjectName("msg_label") self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.call_rename_mkdir_worker) # 修改设置 提取码对话框 self.set_pwd_dialog = SetPwdDialog() self.set_pwd_dialog.new_infos.connect(self.set_passwd) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__) def show_login_dialog(self): """显示登录对话框""" login_dialog = LoginDialog(self._config) login_dialog.clicked_ok.connect(self.call_login_luncher) login_dialog.setWindowModality(Qt.ApplicationModal) login_dialog.exec() def show_upload_dialog(self): """显示上传文件对话框""" self.upload_dialog.set_values(list(self._path_list.keys())[-1]) self.upload_dialog.exec() def load_settings(self): try: with open(self._config, "rb") as _file: self.settings = load(_file) except Exception: dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" self.settings = {"user": "", "pwd": "", "path": dl_path} with open(self._config, "wb") as _file: dump(self.settings, _file) def call_download_manager_thread(self, tasks): self.download_manager.set_values(tasks, self.settings["path"], self.download_threads) self.download_manager.start() def call_downloader(self): tab_page = self.tabWidget.currentIndex() if tab_page == 0: listview = self.table_share model = self.model_share elif tab_page == 1: listview = self.table_disk model = self.model_disk else: return infos = [] _indexes = listview.selectionModel().selection().indexes() for i in _indexes: # 获取所选行号 info = model.item(i.row(), 0).data() if info and info not in infos: infos.append(info) if not infos: return self.desc_pwd_fetcher.set_values(self._disk, infos, download=True) def call_logout_update_ui(self): """菜单栏、工具栏登出""" self.toolbar.removeAction(self.logout) self.tabWidget.setCurrentIndex(0) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.toolbar.removeAction(self.logout) # 登出工具 self.logout.setEnabled(False) self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.upload.setEnabled(False) self.upload.triggered.disconnect(self.show_upload_dialog) def login_update_ui(self, success, msg, duration): """根据登录是否成功更新UI""" if success: self.show_status(msg, duration) self.tabWidget.insertTab(1, self.disk_tab, "我的蓝奏云") self.tabWidget.insertTab(2, self.rec_tab, "回收站") self.disk_tab.setEnabled(True) self.rec_tab.setEnabled(True) # 更新快捷键与工具栏 self.toolbar.addAction(self.logout) # 添加登出工具栏 self.toolbar.addAction(self.upload) # 添加上传文件工具栏 # 菜单栏槽 self.logout.setEnabled(True) self.upload.setEnabled(True) self.upload.triggered.connect(self.show_upload_dialog) # 设置当前显示 tab self.tabWidget.setCurrentIndex(1) QCoreApplication.processEvents() # 重绘界面 # 刷新文件列表 self.list_refresher.set_values(self._work_id) else: self.show_status(msg, duration) self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 更新快捷键与工具栏 self.toolbar.removeAction(self.logout) # 登出工具栏 self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.logout.setEnabled(False) self.upload.setEnabled(False) def call_login_luncher(self): """登录网盘""" self.load_settings() self.logout_worker.set_values(self._disk, update_ui=False) self.toolbar.removeAction(self.logout) try: username = self.settings["user"] password = self.settings["pwd"] cookie = self.settings["cookie"] self.login_luncher.set_values(username, password, cookie) self.login_luncher.start() except Exception as exp: print(exp) pass def set_file_icon(self, name): suffix = name.split(".")[-1] ico_path = "./icon/{}.gif".format(suffix) if os.path.isfile(ico_path): return QIcon(ico_path) else: return QIcon("./icon/file.ico") def show_file_and_folder_lists(self): """显示文件和文件夹列表""" self.model_disk.removeRows(0, self.model_disk.rowCount()) # 清理旧的内容 file_count = len(self._file_list.keys()) folder_count = len(self._folder_list.keys()) name_header = [ "文件夹{}个".format(folder_count), ] if folder_count else [] if file_count: name_header.append("文件{}个".format(file_count)) self.model_disk.setHorizontalHeaderLabels( ["/".join(name_header), "大小", "时间"]) folder_ico = QIcon("./icon/folder.gif") pwd_ico = QIcon("./icon/keys.ico") # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url),直链 if self._work_id != -1: _back = QStandardItem(folder_ico, "..") _back.setToolTip("双击返回上层文件夹,选中无效") self.model_disk.appendRow( [_back, QStandardItem(""), QStandardItem("")]) for infos in self._folder_list.values(): # 文件夹 name = QStandardItem(folder_ico, infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, "") if infos[5] else QStandardItem( "") # 提取码+size self.model_disk.appendRow([name, size_, QStandardItem("")]) for infos in self._file_list.values(): # 文件 name = QStandardItem(self.set_file_icon(infos[1]), infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, infos[2]) if infos[5] else QStandardItem( infos[2]) # 提取码+size self.model_disk.appendRow([name, size_, QStandardItem(infos[3])]) for row in range(self.model_disk.rowCount()): # 右对齐 self.model_disk.item(row, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_disk.item(row, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) def update_lists(self, infos): if not infos: return if infos['r']['files']: self._file_list = infos['file_list'] if infos['r']['folders']: self._folder_list = infos['folder_list'] self._path_list = infos['path_list'] current_folder = list(self._path_list.keys())[-1] self._work_id = self._path_list.get(current_folder, -1) if infos['r']['fid'] != -1: parent_folder_name = list(self._path_list.keys())[-2] self._parent_id = self._path_list.get(parent_folder_name, -1) self.show_file_and_folder_lists() if infos['r']['path']: self.show_full_path() def config_tableview(self, tab): if tab == "share": model = self.model_share table = self.table_share elif tab == "disk": model = self.model_disk table = self.table_disk model.setHorizontalHeaderLabels(["文件名", "大小", "时间"]) table.setModel(model) # 是否显示网格线 table.setShowGrid(False) # 禁止编辑表格 table.setEditTriggers(QAbstractItemView.NoEditTriggers) # 隐藏水平表头 table.verticalHeader().setVisible(False) # 设置表头可以自动排序 table.setSortingEnabled(True) table.setMouseTracking(False) # 设置表头的背景色为绿色 table.horizontalHeader().setStyleSheet( "QHeaderView::section{background:lightgray}") # 设置 不可选择单个单元格,只可选择一行。 table.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置第二三列的宽度 table.horizontalHeader().resizeSection(1, 90) table.horizontalHeader().resizeSection(2, 80) # 设置第一列宽度自动调整,充满屏幕 table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) table.setContextMenuPolicy(Qt.CustomContextMenu) # 允许右键产生子菜单 table.customContextMenuRequested.connect(self.generateMenu) # 右键菜单 def create_left_menus(self): self.left_menus = QMenu() self.left_menu_share_url = self.left_menus.addAction("外链分享地址等") self.left_menu_share_url.setIcon(QIcon("./icon/share.ico")) self.left_menu_rename_set_desc = self.left_menus.addAction("修改文件夹名与描述") self.left_menu_rename_set_desc.setIcon(QIcon("./icon/desc.ico")) self.left_menu_set_pwd = self.left_menus.addAction("设置访问密码") self.left_menu_set_pwd.setIcon(QIcon("./icon/password.ico")) self.left_menu_move = self.left_menus.addAction("移动(支持批量)") self.left_menu_move.setIcon(QIcon("./icon/move.ico")) def call_rename_mkdir_worker(self, infos): """重命名、修改简介与新建文件夹""" self.rename_mkdir_worker.set_values(self._disk, infos, self._work_id) def set_passwd(self, infos): """设置文件(夹)提取码""" self.set_pwd_worker.set_values(self._disk, infos, self._work_id) def call_mkdir(self): """弹出新建文件夹对话框""" self.rename_dialog.set_values(None) self.rename_dialog.exec() def call_remove_files(self): indexs = [] infos = [] _indexs = self.table_disk.selectionModel().selection().indexes() if not _indexs: return for i in _indexs: # 获取所选行号 indexs.append(i.row()) indexs = set(indexs) for index in indexs: info = self.model_disk.item(index, 0).data() # 用于提示删除的文件名 if info: infos.append(info[:3]) delete_dialog = DeleteDialog(infos) delete_dialog.new_infos.connect(self.remove_files_worker.set_values) delete_dialog.exec() def generateMenu(self, pos): """右键菜单""" row_nums = self.sender().selectionModel().selection().indexes() if not row_nums: # 如果没有选中行,什么也不做 return _model = self.sender().model() infos = [] # 多个选中的行,用于移动文件与... for one_row in row_nums: one_row_data = _model.item(one_row.row(), 0).data() if one_row_data and one_row_data not in infos: # 删掉 .. 行 infos.append(one_row_data) if not infos: return info = infos[0] # 取选中的第一行 # 通过是否有文件 ID 判断是登录界面还是提取界面 if info[0]: self.left_menu_rename_set_desc.setEnabled(True) self.left_menu_set_pwd.setEnabled(True) # 通过infos第3个字段 size 判断是否为文件夹,文件夹不能移动,设置不同的显示菜单名 if info[2]: self.left_menu_rename_set_desc.setText("修改文件描述") self.left_menu_move.setEnabled(True) else: self.left_menu_rename_set_desc.setText("修改文件夹名与描述") self.left_menu_move.setDisabled(True) else: self.left_menu_rename_set_desc.setDisabled(True) self.left_menu_move.setDisabled(True) self.left_menu_set_pwd.setDisabled(True) action = self.left_menus.exec_(self.sender().mapToGlobal(pos)) if action == self.left_menu_share_url: # 显示详细信息 # 后台跟新信息,并显示信息对话框 self.more_info_worker.set_values(info, self._disk) elif action == self.left_menu_move: # 移动文件 self.all_folders_worker.set_values(self._disk, infos) elif action == self.left_menu_set_pwd: # 修改提取码 self.desc_pwd_fetcher.set_values(self._disk, [ info, ]) # 兼容下载器,使用列表的列表 self.set_pwd_dialog.set_values(info) self.set_pwd_dialog.exec() elif action == self.left_menu_rename_set_desc: # 重命名与修改描述 self.desc_pwd_fetcher.set_values(self._disk, [ info, ]) # 兼容下载器,使用列表的列表 self.rename_dialog.set_values(info) self.rename_dialog.exec() def call_update_desc_pwd(self, desc, pwd, infos): '''更新 desc、pwd''' infos[6] = desc infos[5] = pwd self.rename_dialog.set_values(infos) self.set_pwd_dialog.set_values(infos) def show_move_file_dialog(self, infos, all_dirs_dict): '''显示移动文件对话框''' move_file_dialog = MoveFileDialog(infos, all_dirs_dict) move_file_dialog.new_infos.connect( self.all_folders_worker.move_file) # 调用移动线程 move_file_dialog.exec() def show_info_dialog(self, infos): '''显示更多信息对话框''' info_dialog = InfoDialog(infos) info_dialog.setWindowModality(Qt.ApplicationModal) # 窗口前置 info_dialog.exec() def call_change_dir(self, folder_id=-1): """按钮调用""" def callfunc(): self.list_refresher.set_values(folder_id) return callfunc def change_dir(self, dir_name): """双击切换工作目录""" dir_name = self.model_disk.item(dir_name.row(), 0).text() # 文件夹名 if dir_name == "..": # 返回上级路径 self.list_refresher.set_values(self._parent_id) elif dir_name in self._folder_list.keys(): folder_id = self._folder_list[dir_name][0] self.list_refresher.set_values(folder_id) def call_upload(self, infos): """上传文件(夹)""" self._old_work_id = self._work_id # 记录上传文件夹id self.upload_worker.set_values(self._disk, infos, self._old_work_id) self.upload_worker.start() def show_full_path(self): """路径框显示当前路径""" index = 1 for name in self._path_list_old.items(): self._locs[index].clicked.disconnect() self.disk_loc.removeWidget(self._locs[index]) self._locs[index].deleteLater() self._locs[index] = None del self._locs[index] index += 1 index = 1 for name, fid in self._path_list.items(): self._locs[index] = QPushButton(name, self.disk_tab) self._locs[index].setIcon(QIcon("./icon/folder.gif")) self._locs[index].setStyleSheet( "QPushButton {border: none; background:transparent;}") self.disk_loc.insertWidget(index, self._locs[index]) self._locs[index].clicked.connect(self.call_change_dir(fid)) index += 1 self._path_list_old = self._path_list def select_all_btn(self, action="reverse"): """默认反转按钮状态""" page = self.tabWidget.currentIndex() if page == 0: btn = self.btn_share_select_all table = self.table_share elif page == 1: btn = self.btn_disk_select_all table = self.table_disk elif page == 2: return else: return if btn.isEnabled(): if action == "reverse": if btn.text() == "全选": table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./icon/select-none.ico")) elif btn.text() == "取消": table.clearSelection() btn.setText("全选") btn.setIcon(QIcon("./icon/select-all.ico")) elif action == "cancel": # 点击列表其中一个就表示放弃全选 btn.setText("全选") btn.setIcon(QIcon("./icon/select-all.ico")) else: table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./icon/select-none.ico")) def finished_upload(self): """上传完成调用""" if self._old_work_id == self._work_id: self.list_refresher.set_values(self._work_id, True, True, False) else: self._old_work_id = self._work_id self.show_status("上传完成!", 7000) def init_disk_ui(self): self.model_disk = QStandardItemModel(1, 3) self.config_tableview("disk") self.btn_disk_delete.setIcon(QIcon("./icon/delete.ico")) self.btn_disk_dl.setIcon(QIcon("./icon/downloader.ico")) self.btn_disk_select_all.setIcon(QIcon("./icon/select-all.ico")) self.btn_disk_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_disk_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_disk.clicked.connect(lambda: self.select_all_btn("cancel")) self.btn_disk_dl.clicked.connect(self.call_downloader) self.btn_disk_mkdir.setIcon(QIcon("./icon/add-folder.ico")) self.btn_disk_mkdir.clicked.connect(self.call_mkdir) self.btn_disk_delete.clicked.connect(self.call_remove_files) self.table_disk.doubleClicked.connect(self.change_dir) # 上传器 self.upload_worker = UploadWorker() self.upload_worker.finished.connect(self.finished_upload) self.upload_worker.code.connect(self.show_status) def show_status(self, msg, duration=0): self._msg_label.setText(msg) # self.statusbar.showMessage(msg, duration) # QCoreApplication.processEvents() # 重绘界面,在弱网络情况导致程序闪退 if duration != 0: QTimer.singleShot(duration, lambda: self._msg_label.setText("")) # shared url def call_get_shared_info(self): if not self.get_shared_info_thread.isRunning(): # 防止快速多次调用 self.line_share_url.setEnabled(False) self.btn_extract.setEnabled(False) text = self.line_share_url.text().strip() self.get_shared_info_thread.set_values(text) def show_share_url_file_lists(self, infos): if infos["code"] == LanZouCloud.SUCCESS: file_count = len(infos["info"].keys()) self.model_share.setHorizontalHeaderLabels( ["文件{}个".format(file_count), "大小", "时间"]) for infos in infos["info"].values(): name = QStandardItem(self.set_file_icon(infos[1]), infos[1]) name.setData(infos) self.model_share.appendRow( [name, QStandardItem(infos[2]), QStandardItem(infos[3])]) for r in range(self.model_share.rowCount()): # 右对齐 self.model_share.item(r, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_share.item(r, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.table_share.setDisabled(False) self.btn_share_select_all.setDisabled(False) self.btn_share_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_share_dl.setDisabled(False) else: self.btn_share_select_all.setDisabled(True) self.btn_share_select_all.setToolTip("") self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) def set_download_path(self): """设置下载路径""" dl_path = QFileDialog.getExistingDirectory() dl_path = os.path.normpath(dl_path) # windows backslash if dl_path == self.settings["path"]: return if dl_path == "": dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" up_info = {"path": dl_path} else: up_info = {"path": dl_path} update_settings(self._config, up_info) self.load_settings() self.line_dl_path.setText(self.settings["path"]) def init_extract_share_ui(self): self.btn_share_select_all.setDisabled(True) self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) self.model_share = QStandardItemModel(1, 3) self.config_tableview("share") # 获取分享链接信息线程 self.get_shared_info_thread = GetSharedInfo() self.get_shared_info_thread.update.connect( lambda: self.model_share.removeRows(0, self.model_share.rowCount() )) # 清理旧的信息 self.get_shared_info_thread.msg.connect(self.show_status) # 提示信息 self.get_shared_info_thread.infos.connect( self.show_share_url_file_lists) # 内容信息 self.get_shared_info_thread.finished.connect( lambda: self.btn_extract.setEnabled(True)) self.get_shared_info_thread.finished.connect( lambda: self.line_share_url.setEnabled(True)) # 控件设置 self.line_share_url.setPlaceholderText( "蓝奏云链接,如有提取码,放后面,空格或汉字等分割,回车键提取") self.line_share_url.returnPressed.connect(self.call_get_shared_info) self.btn_extract.clicked.connect(self.call_get_shared_info) self.btn_share_dl.clicked.connect(self.call_downloader) self.btn_share_dl.setIcon(QIcon("./icon/downloader.ico")) self.btn_share_select_all.setIcon(QIcon("./icon/select-all.ico")) self.btn_share_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_share.clicked.connect( lambda: self.select_all_btn("cancel")) # 全选按钮 # 添加文件下载路径选择器 self.line_dl_path = MyLineEdit(self.share_tab) self.line_dl_path.setObjectName("line_dl_path") self.horizontalLayout_share_2.insertWidget(2, self.line_dl_path) self.line_dl_path.setText(self.settings["path"]) self.line_dl_path.clicked.connect(self.set_download_path) # QSS self.label_share_url.setStyleSheet( "#label_share_url {color: rgb(255,255,60);}") self.label_dl_path.setStyleSheet( "#label_dl_path {color: rgb(255,255,60);}") def keyPressEvent(self, e): if e.key() == Qt.Key_A: # Ctrl/Alt + A 全选 if e.modifiers() and Qt.ControlModifier: self.select_all_btn() def set_window_at_center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() new_left = int((screen.width() - size.width()) / 2) new_top = int((screen.height() - size.height()) / 2) self.move(new_left, new_top) def open_wiki_url(self): # 打开使用说明页面 url = QUrl('https://github.com/rachpt/lanzou-gui/wiki') if not QDesktopServices.openUrl(url): self.show_status('Could not open wiki page!', 5000)
def init_workers(self): # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) # 登出器 self.logout_worker = LogoutWorker() self.logout_worker.successed.connect(self.call_logout_update_ui) # 下载器 self.download_manager = DownloadManager() self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 7000)) # 获取更多信息,直链、下载次数等 self.more_info_worker = GetMoreInfoWorker() self.more_info_worker.msg.connect(self.show_status) self.more_info_worker.infos.connect(self.show_info_dialog) # 登录文件列表更新器 self.list_refresher = ListRefresher(self._disk) self.list_refresher.err_msg.connect(self.show_status) self.list_refresher.infos.connect(self.update_lists) # 获取所有文件夹fid,并移动 self.all_folders_worker = GetAllFoldersWorker() self.all_folders_worker.msg.connect(self.show_status) self.all_folders_worker.infos.connect(self.show_move_file_dialog) self.all_folders_worker.moved.connect( lambda: self.list_refresher.set_values(self._work_id, False, True, False)) # 更新文件列表 # 重命名、修改简介、新建文件夹 self.rename_mkdir_worker = RenameMkdirWorker() self.rename_mkdir_worker.msg.connect(self.show_status) self.rename_mkdir_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 设置文件(夹)提取码 self.set_pwd_worker = SetPwdWorker() self.set_pwd_worker.msg.connect(self.show_status) self.set_pwd_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 删除文件(夹) self.remove_files_worker = RemoveFilesWorker(self._disk) self.remove_files_worker.msg.connect(self.show_status) # 显示错误提示 self.remove_files_worker.finished.connect( lambda: self.list_refresher.set_values(self._work_id)) # 更新界面 # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述与提取码更新器 self.desc_pwd_fetcher = DescPwdFetcher() self.desc_pwd_fetcher.desc.connect(self.call_update_desc_pwd) self.desc_pwd_fetcher.tasks.connect( self.call_download_manager_thread) # 连接下载管理器线程 # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_label.setObjectName("msg_label") self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.call_rename_mkdir_worker) # 修改设置 提取码对话框 self.set_pwd_dialog = SetPwdDialog() self.set_pwd_dialog.new_infos.connect(self.set_passwd) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__)
class MainWindow(QMainWindow, Ui_MainWindow): __version__ = 'v0.1.0' if not os.path.isdir("./src") or not os.path.isfile("./src/file.ico"): from src import release_src os.makedirs("./src", exist_ok=True) release_src() def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.init_default_settings() self.init_variables() self.init_workers() self.init_menu() self.setWindowTitle("蓝奏云客户端 - {}".format(self.__version__)) self.set_window_at_center() self.init_extract_share_ui() self.init_disk_ui() self.init_rec_ui() self.call_login_luncher() self.create_left_menus() self.setStyleSheet(qssStyle) self.tabWidget.setStyleSheet("QTabBar{ background-color: #AEEEEE; }") self.check_update_worker.set_values(self.__version__, False) # 检测新版 # 系统托盘 self.tray = QSystemTrayIcon(QIcon('src/lanzou_logo2.png'), parent=self) show_action = QAction("显示窗口", self) hide_action = QAction("最小化到托盘", self) quit_action = QAction("退出程序", self) show_action.triggered.connect(self.show) hide_action.triggered.connect(self.hide) quit_action.triggered.connect(qApp.quit) show_action.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) hide_action.setIcon(self.style().standardIcon( QStyle.SP_TitleBarMinButton)) quit_action.setIcon(self.style().standardIcon( QStyle.SP_TitleBarCloseButton)) self.tray.activated.connect(self.show) #托盘点击事件 tray_menu = QMenu(QApplication.desktop()) tray_menu.addAction(show_action) tray_menu.addAction(hide_action) tray_menu.addAction(quit_action) self.tray.setContextMenu(tray_menu) self.tray.show() def init_menu(self): self.login.triggered.connect(self.show_login_dialog) # 登录 self.login.setIcon(QIcon("./src/login.ico")) self.login.setShortcut("Ctrl+L") self.toolbar.addAction(self.login) self.logout.triggered.connect( lambda: self.logout_worker.set_values(self._disk)) # 登出 self.logout.setIcon(QIcon("./src/logout.ico")) self.logout.setShortcut("Ctrl+Q") # 登出快捷键 self.download.setShortcut("Ctrl+J") self.download.setIcon(QIcon("./src/download.ico")) self.download.setEnabled(False) # 暂时不用 self.delete.setShortcut("Ctrl+D") self.delete.setIcon(QIcon("./src/delete.ico")) self.delete.setEnabled(False) # 暂时不用 self.how.setShortcut("F1") self.how.setIcon(QIcon("./src/help.ico")) self.how.triggered.connect(self.open_wiki_url) self.about.setShortcut("Ctrl+B") self.about.setIcon(QIcon("./src/about.ico")) self.about.triggered.connect(self.about_dialog.exec) self.upload.setIcon(QIcon("./src/upload.ico")) self.upload.setShortcut("Ctrl+U") # 上传快捷键 # 添加设置菜单,暂时放这里 self.setting_menu = QAction(self) # 设置菜单 self.setting_menu.setObjectName("setting_menu") self.setting_menu.setText("设置") self.files.addAction(self.setting_menu) self.setting_menu.setIcon(QIcon("./src/settings.ico")) self.setting_menu.triggered.connect(self.setting_dialog.open_dialog) self.setting_menu.setShortcut("Ctrl+P") # 设置快捷键 # tab 切换时更新 self.tabWidget.currentChanged.connect(self.call_change_tab) def init_default_settings(self): """初始化默认设置""" download_threads = 3 # 同时三个下载任务 max_size = 100 # 单个文件大小上限 MB timeout = 5 # 每个请求的超时 s(不包含下载响应体的用时) time_fmt = False # 是否使用年月日时间格式 to_tray = False # 关闭到系统托盘 dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" self._default_settings = { "download_threads": download_threads, "max_size": max_size, "to_tray": to_tray, "dl_path": dl_path, "timeout": timeout, "time_fmt": time_fmt } def init_variables(self): self._disk = LanZouCloud() self._config_file = "./config.pkl" self._folder_list = {} self._file_list = {} self._path_list = FolderList() self._path_list_old = FolderList() self._locs = {} self._parent_id = -1 # --> .. self._work_name = "" # share disk rec, not use now self._work_id = -1 # disk folder id self._old_work_id = self._work_id # 用于上传完成后判断是否需要更新disk界面 self.load_settings() def update_lanzoucloud_settings(self): """更新LanzouCloud实例设置""" self._disk.set_timeout(self.configs["settings"]["timeout"]) self._disk.set_max_size(self.configs["settings"]["max_size"]) self.download_threads = self.configs["settings"]["download_threads"] self.time_fmt = self.configs["settings"]["time_fmt"] # 时间显示格式 self.to_tray = self.configs["settings"][ "to_tray"] if "to_tray" in self.configs["settings"] else False def init_workers(self): # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) self.login_luncher.update_cookie.connect(self.call_update_cookie) # 登出器 self.logout_worker = LogoutWorker() self.logout_worker.successed.connect(self.call_logout_update_ui) # 下载器 self.download_manager = DownloadManager() self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 2999)) # 获取更多信息,直链、下载次数等 self.info_dialog = InfoDialog() # 对话框 self.info_dialog.setWindowModality(Qt.ApplicationModal) # 窗口前置 self.more_info_worker = GetMoreInfoWorker() # 后台更新线程 self.more_info_worker.msg.connect(self.show_status) self.more_info_worker.infos.connect(self.info_dialog.set_values) self.more_info_worker.dl_link.connect( self.info_dialog.tx_dl_link.setText) self.info_dialog.get_dl_link.connect(self.more_info_worker.get_dl_link) # 登录文件列表更新器 self.list_refresher = ListRefresher(self._disk) self.list_refresher.err_msg.connect(self.show_status) self.list_refresher.infos.connect(self.update_disk_lists) self.list_refresher.infos.connect(lambda: self.show_status("")) # 获取所有文件夹fid,并移动 self.all_folders_worker = GetAllFoldersWorker() self.all_folders_worker.msg.connect(self.show_status) self.all_folders_worker.infos.connect(self.show_move_file_dialog) self.all_folders_worker.moved.connect( lambda: self.list_refresher.set_values(self._work_id, True, False, False)) # 更新文件列表 # 重命名、修改简介、新建文件夹 self.rename_mkdir_worker = RenameMkdirWorker() self.rename_mkdir_worker.msg.connect(self.show_status) self.rename_mkdir_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 设置文件(夹)提取码 self.set_pwd_worker = SetPwdWorker() self.set_pwd_worker.msg.connect(self.show_status) self.set_pwd_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 删除文件(夹) self.remove_files_worker = RemoveFilesWorker(self._disk) self.remove_files_worker.msg.connect(self.show_status) # 显示错误提示 self.remove_files_worker.finished.connect( lambda: self.list_refresher.set_values(self._work_id)) # 更新界面 # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述与提取码更新器 self.desc_pwd_fetcher = DescPwdFetcher() self.desc_pwd_fetcher.desc.connect(self.call_update_desc_pwd) self.desc_pwd_fetcher.tasks.connect( self.call_download_manager_thread) # 连接下载管理器线程 # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_movie_lb = QLabel() self._msg_movie = QMovie("src/loading_more.gif") self._msg_movie.setScaledSize(QSize(24, 24)) self._msg_movie_lb.setMovie(self._msg_movie) self._msg_label.setObjectName("msg_label") self._msg_movie_lb.setObjectName("msg_movie_lb") self.statusbar.addWidget(self._msg_movie_lb) self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.call_rename_mkdir_worker) # 修改设置 提取码对话框 self.set_pwd_dialog = SetPwdDialog() self.set_pwd_dialog.new_infos.connect(self.set_passwd) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__) # 菜单栏设置 self.setting_dialog = SettingDialog(self._config_file, self._default_settings) self.setting_dialog.saved.connect( lambda: self.load_settings(ref_ui=True)) # 登录回收站信息更新器 self.get_rec_lists_worker = GetRecListsWorker(self._disk) self.get_rec_lists_worker.msg.connect(self.show_status) self.get_rec_lists_worker.infos.connect(self.update_rec_lists) self.get_rec_lists_worker.folders.connect( lambda: self.show_status('', 0)) self.get_rec_lists_worker.folders.connect( self.pop_up_rec_folder_dialog) # 回收站操作器 self.rec_manipulator = RecManipulator(self._disk) self.rec_manipulator.msg.connect(self.show_status) self.rec_manipulator.successed.connect(self.get_rec_lists_worker.start) # 检查软件版本 self.check_update_worker = CheckUpdateWorker() self.about_dialog.check_update.connect( self.check_update_worker.set_values) self.check_update_worker.infos.connect(self.about_dialog.show_update) self.check_update_worker.bg_update_infos.connect( self.show_new_version_msg) def show_login_dialog(self): """显示登录对话框""" login_dialog = LoginDialog(self._config_file) login_dialog.clicked_ok.connect(self.call_login_luncher) login_dialog.setWindowModality(Qt.ApplicationModal) login_dialog.exec() def show_upload_dialog(self, files): """显示上传文件对话框""" if len(self._path_list) > 0: self.upload_dialog.set_values(self._path_list[-1].name, files) else: self.show_status("等待文件列表更新...", 2000) def show_upload_dialog_menus(self): '''菜单栏显示上传对话框槽函数''' self.show_upload_dialog(None) def load_settings(self, ref_ui=False): """加载用户设置""" try: with open(self._config_file, "rb") as _file: self.configs = load(_file) except: self.configs = { "user": "", "pwd": "", "cookie": "", "settings": self._default_settings } with open(self._config_file, "wb") as _file: dump(self.configs, _file) # 兼容以前的平配置文件 if "settings" not in self.configs or not self.configs["settings"]: self.configs.update({"settings": self._default_settings}) update_settings(self._config_file, {"settings": self._default_settings}) self.update_lanzoucloud_settings() if ref_ui and self.tabWidget.currentIndex() == 1: # 更新文件界面的时间 self.show_file_and_folder_lists() def call_download_manager_thread(self, tasks): self.download_manager.set_values(tasks, self.configs["settings"]["dl_path"], self.download_threads) self.download_manager.start() def call_multi_manipulator(self, action): """批量操作器""" tab_page = self.tabWidget.currentIndex() if tab_page == 0: listview = self.table_share model = self.model_share elif tab_page == 1: listview = self.table_disk model = self.model_disk elif tab_page == 2: listview = self.table_rec model = self.model_rec else: return infos = [] _indexes = listview.selectionModel().selection().indexes() for i in _indexes: # 获取所选行号 info = model.item(i.row(), 0).data() if info and info not in infos: infos.append(info) if tab_page == 0 or tab_page == 1: if not infos: return self.desc_pwd_fetcher.set_values(self._disk, infos, download=True) elif tab_page == 2: if action == "recovery": title = "确定恢复选定文件(夹)?" elif action == "delete": title = "确定彻底删除选定文件(夹)?" elif action == "recovery_all": title = "确定还原全部文件(夹)?" msg = "提示: 恢复回收站中的文件将不可撤销,请确认。" elif action == "clean": title = "确定清除全部文件(夹)?" msg = "提示: 删除回收站中的文件将不可恢复,请确认。" if action == "recovery" or action == "delete": if not infos: self.show_status("请先选中需要操作的文件!", 2999) return msg = "\t\t列表:\n" for i in infos: msg += f"{i.time}\t{i.name}\t{i.size}\n" message_box = QMessageBox(self) message_box.setStyleSheet(btn_style) message_box.setWindowTitle(title) message_box.setText(msg) message_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) buttonY = message_box.button(QMessageBox.Yes) buttonY.setText('确定') buttonN = message_box.button(QMessageBox.No) buttonN.setText('取消') message_box.exec_() if message_box.clickedButton() == buttonY: self.rec_manipulator.set_values(infos, action) def call_logout_update_ui(self): """菜单栏、工具栏登出""" self.toolbar.removeAction(self.logout) self.tabWidget.setCurrentIndex(0) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.toolbar.removeAction(self.logout) # 登出工具 self.logout.setEnabled(False) self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.upload.setEnabled(False) self.upload.triggered.disconnect(self.show_upload_dialog_menus) def login_update_ui(self, success, msg, duration): """根据登录是否成功更新UI""" if success: self.show_status(msg, duration) self.tabWidget.insertTab(1, self.disk_tab, "我的蓝奏云") self.tabWidget.insertTab(2, self.rec_tab, "回收站") self.disk_tab.setEnabled(True) self.rec_tab.setEnabled(True) # 更新快捷键与工具栏 self.toolbar.addAction(self.logout) # 添加登出工具栏 self.toolbar.addAction(self.upload) # 添加上传文件工具栏 # 菜单栏槽 self.logout.setEnabled(True) self.upload.setEnabled(True) self.upload.triggered.connect(self.show_upload_dialog_menus) # 设置当前显示 tab self.tabWidget.setCurrentIndex(1) QCoreApplication.processEvents() # 重绘界面 # 刷新文件列表 # self.list_refresher.set_values(self._work_id) else: self.show_status(msg, duration) self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 更新快捷键与工具栏 self.toolbar.removeAction(self.logout) # 登出工具栏 self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.logout.setEnabled(False) self.upload.setEnabled(False) def call_login_luncher(self): """登录网盘""" self.load_settings() self.logout_worker.set_values(self._disk, update_ui=False) self.toolbar.removeAction(self.logout) try: username = self.configs["user"] password = self.configs["pwd"] cookie = self.configs["cookie"] self.login_luncher.set_values(username, password, cookie) self.login_luncher.start() except Exception as exp: print(exp) pass def call_update_cookie(self, cookie): """更新cookie至config文件""" up_info = {"cookie": cookie} update_settings(self._config_file, up_info) def show_file_and_folder_lists(self): """显示用户文件和文件夹列表""" self.model_disk.removeRows(0, self.model_disk.rowCount()) # 清理旧的内容 file_count = len(self._file_list.keys()) folder_count = len(self._folder_list.keys()) name_header = [ "文件夹{}个".format(folder_count), ] if folder_count else [] if file_count: name_header.append("文件{}个".format(file_count)) self.model_disk.setHorizontalHeaderLabels( ["/".join(name_header), "大小", "时间"]) folder_ico = QIcon("./src/folder.gif") pwd_ico = QIcon("./src/keys.ico") # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url),直链 if self._work_id != -1: _back = QStandardItem(folder_ico, "..") _back.setToolTip("双击返回上层文件夹,选中无效") self.model_disk.appendRow( [_back, QStandardItem(""), QStandardItem("")]) for infos in self._folder_list.values(): # 文件夹 name = QStandardItem(folder_ico, infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, "") if infos[5] else QStandardItem( "") # 提取码+size self.model_disk.appendRow([name, size_, QStandardItem("")]) for infos in self._file_list.values(): # 文件 name = QStandardItem(set_file_icon(infos[1]), infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, infos[2]) if infos[5] else QStandardItem( infos[2]) # 提取码+size time_ = QStandardItem(time_format( infos[3])) if self.time_fmt else QStandardItem(infos[3]) self.model_disk.appendRow([name, size_, time_]) for row in range(self.model_disk.rowCount()): # 右对齐 self.model_disk.item(row, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_disk.item(row, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) def update_disk_lists(self, infos): """更新用户文件列表""" if not infos: return if infos['r']['files']: self._file_list = infos['file_list'] if infos['r']['folders']: self._folder_list = infos['folder_list'] self._path_list = infos['path_list'] self._work_id = self._path_list[-1].id if infos['r']['fid'] != -1: self._parent_id = self._path_list[-2].id self.show_file_and_folder_lists() if infos['r']['path']: self.show_full_path() def config_tableview(self, tab): """Tab 初始化""" if tab == "share": model = self.model_share table = self.table_share elif tab == "disk": model = self.model_disk table = self.table_disk elif tab == "rec": model = self.model_rec table = self.table_rec model.setHorizontalHeaderLabels(["文件名", "大小", "时间"]) table.setModel(model) # 是否显示网格线 table.setShowGrid(False) # 禁止编辑表格 table.setEditTriggers(QAbstractItemView.NoEditTriggers) # 隐藏水平表头 table.verticalHeader().setVisible(False) # 设置表头可以自动排序 table.setSortingEnabled(True) table.setMouseTracking(False) # 设置表头的背景色为绿色 table.horizontalHeader().setStyleSheet( "QHeaderView::section{background:lightgray}") # 设置 不可选择单个单元格,只可选择一行。 table.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置第二三列的宽度 table.horizontalHeader().resizeSection(1, 90) table.horizontalHeader().resizeSection(2, 80) # 设置第一列宽度自动调整,充满屏幕 table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) if tab != "rec": table.setContextMenuPolicy(Qt.CustomContextMenu) # 允许右键产生子菜单 table.customContextMenuRequested.connect(self.generateMenu) # 右键菜单 def create_left_menus(self): self.left_menus = QMenu() self.left_menu_share_url = self.left_menus.addAction("外链分享地址等") self.left_menu_share_url.setIcon(QIcon("./src/share.ico")) self.left_menu_rename_set_desc = self.left_menus.addAction("修改文件夹名与描述") self.left_menu_rename_set_desc.setIcon(QIcon("./src/desc.ico")) self.left_menu_set_pwd = self.left_menus.addAction("设置访问密码") self.left_menu_set_pwd.setIcon(QIcon("./src/password.ico")) self.left_menu_move = self.left_menus.addAction("移动(支持批量)") self.left_menu_move.setIcon(QIcon("./src/move.ico")) def call_rename_mkdir_worker(self, infos): """重命名、修改简介与新建文件夹""" self.rename_mkdir_worker.set_values(self._disk, infos, self._work_id, self._folder_list) def set_passwd(self, infos): """设置文件(夹)提取码""" self.set_pwd_worker.set_values(self._disk, infos, self._work_id) def call_mkdir(self): """弹出新建文件夹对话框""" self.rename_dialog.set_values(None) self.rename_dialog.exec() def call_remove_files(self): indexs = [] infos = [] _indexs = self.table_disk.selectionModel().selection().indexes() if not _indexs: return for i in _indexs: # 获取所选行号 indexs.append(i.row()) indexs = set(indexs) for index in indexs: info = self.model_disk.item(index, 0).data() # 用于提示删除的文件名 if info: infos.append(info[:3]) delete_dialog = DeleteDialog(infos) delete_dialog.new_infos.connect(self.remove_files_worker.set_values) delete_dialog.exec() def generateMenu(self, pos): """右键菜单""" row_nums = self.sender().selectionModel().selection().indexes() if not row_nums: # 如果没有选中行,什么也不做 return _model = self.sender().model() infos = [] # 多个选中的行,用于移动文件与... for one_row in row_nums: one_row_data = _model.item(one_row.row(), 0).data() if one_row_data and one_row_data not in infos: # 删掉 .. 行 infos.append(one_row_data) if not infos: return info = infos[0] # 取选中的第一行 # 通过是否有文件 ID 判断是登录界面还是提取界面 if info[0]: self.left_menu_rename_set_desc.setEnabled(True) self.left_menu_set_pwd.setEnabled(True) # 通过infos第3个字段 size 判断是否为文件夹,文件夹不能移动,设置不同的显示菜单名 if info[2]: self.left_menu_rename_set_desc.setText("修改文件描述") self.left_menu_move.setEnabled(True) else: self.left_menu_rename_set_desc.setText("修改文件夹名与描述") self.left_menu_move.setDisabled(True) else: self.left_menu_rename_set_desc.setDisabled(True) self.left_menu_move.setDisabled(True) self.left_menu_set_pwd.setDisabled(True) action = self.left_menus.exec_(self.sender().mapToGlobal(pos)) if action == self.left_menu_share_url: # 显示详细信息 # 后台跟新信息,并显示信息对话框 self.more_info_worker.set_values(info, self._disk) elif action == self.left_menu_move: # 移动文件 self.all_folders_worker.set_values(self._disk, infos) elif action == self.left_menu_set_pwd: # 修改提取码 self.desc_pwd_fetcher.set_values(self._disk, [ info, ]) # 兼容下载器,使用列表的列表 self.set_pwd_dialog.set_values(info) self.set_pwd_dialog.exec() elif action == self.left_menu_rename_set_desc: # 重命名与修改描述 self.desc_pwd_fetcher.set_values(self._disk, [ info, ]) # 兼容下载器,使用列表的列表 self.rename_dialog.set_values(info) self.rename_dialog.exec() def call_update_desc_pwd(self, desc, pwd, infos): '''更新 desc、pwd''' infos[6] = desc infos[5] = pwd self.rename_dialog.set_values(infos) self.set_pwd_dialog.set_values(infos) def show_move_file_dialog(self, infos, all_dirs_dict): '''显示移动文件对话框''' move_file_dialog = MoveFileDialog(infos, all_dirs_dict) move_file_dialog.new_infos.connect( self.all_folders_worker.move_file) # 调用移动线程 move_file_dialog.exec() def call_change_dir(self, folder_id=-1): """顶部路径按钮调用""" def callfunc(): self.list_refresher.set_values(folder_id) return callfunc def change_dir(self, dir_name): """双击切换工作目录""" dir_name = self.model_disk.item(dir_name.row(), 0).text() # 文件夹名 if dir_name == "..": # 返回上级路径 self.list_refresher.set_values(self._parent_id) elif dir_name in self._folder_list.keys(): folder_id = self._folder_list[dir_name][0] self.list_refresher.set_values(folder_id) def call_upload(self, infos): """上传文件(夹)""" self._old_work_id = self._work_id # 记录上传文件夹id self.upload_worker.set_values(self._disk, infos, self._old_work_id) self.upload_worker.start() def show_full_path(self): """路径框显示当前路径""" index = 1 for _ in iter(self._path_list_old): self._locs[index].clicked.disconnect() self.disk_loc.removeWidget(self._locs[index]) self._locs[index].deleteLater() self._locs[index] = None del self._locs[index] index += 1 index = 1 for item in iter(self._path_list): self._locs[index] = QPushButton(item.name, self.disk_tab) self._locs[index].setToolTip(f"fid:{item.id}") self._locs[index].setIcon(QIcon("./src/folder.gif")) self._locs[index].setStyleSheet( "QPushButton {border:none; background:transparent;}") self.disk_loc.insertWidget(index, self._locs[index]) self._locs[index].clicked.connect(self.call_change_dir(item.id)) index += 1 self._path_list_old = self._path_list def select_all_btn(self, action="reverse"): """默认反转按钮状态""" page = self.tabWidget.currentIndex() if page == 0: btn = self.btn_share_select_all table = self.table_share elif page == 1: btn = self.btn_disk_select_all table = self.table_disk elif page == 2: btn = self.btn_rec_select_all table = self.table_rec else: return if btn.isEnabled(): if action == "reverse": if btn.text() == "全选": table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./src/select_none.ico")) elif btn.text() == "取消": table.clearSelection() btn.setText("全选") btn.setIcon(QIcon("./src/select_all.ico")) elif action == "cancel": # 点击列表其中一个就表示放弃全选 btn.setText("全选") btn.setIcon(QIcon("./src/select_all.ico")) else: table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./src/select_none.ico")) def finished_upload(self): """上传完成调用""" if self._old_work_id == self._work_id: self.list_refresher.set_values(self._work_id, True, True, False) else: self._old_work_id = self._work_id self.show_status("上传完成!", 7000) # disk tab def init_disk_ui(self): self.model_disk = QStandardItemModel(1, 3) self.config_tableview("disk") self.btn_disk_delete.setIcon(QIcon("./src/delete.ico")) self.btn_disk_dl.setIcon(QIcon("./src/downloader.ico")) self.btn_disk_select_all.setIcon(QIcon("./src/select_all.ico")) self.btn_disk_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_disk_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_disk.clicked.connect(lambda: self.select_all_btn("cancel")) self.btn_disk_dl.clicked.connect( lambda: self.call_multi_manipulator("download")) self.btn_disk_mkdir.setIcon(QIcon("./src/add_folder.ico")) self.btn_disk_mkdir.clicked.connect(self.call_mkdir) self.btn_disk_delete.clicked.connect(self.call_remove_files) # 文件拖拽上传 self.table_disk.drop_files.connect(self.show_upload_dialog) self.table_disk.doubleClicked.connect(self.change_dir) # 上传器 self.upload_worker = UploadWorker() self.upload_worker.finished.connect(self.finished_upload) self.upload_worker.code.connect(self.show_status) # rec tab def pop_up_rec_folder_dialog(self, files): # 弹出回收站文件夹内容对话框 if files: rec_file_dialog = RecFolderDialog(files) rec_file_dialog.exec() else: self.show_status("文件夹为空!", 2999) def call_rec_folder_dialog(self, dir_name): # 显示弹出对话框 dir_data = self.model_rec.item(dir_name.row(), 0).data() # 文件夹信息 if isinstance(dir_data, RecFolder): self.show_status(f"正在获取文件夹 {dir_data.name} 信息,稍后", 10000) self.get_rec_lists_worker.set_values(dir_data.id) def update_rec_lists(self, dir_lists, file_lists): """显示回收站文件和文件夹列表""" self.model_rec.removeRows(0, self.model_rec.rowCount()) # 清理旧的内容 file_count = len(file_lists) folder_count = len(dir_lists) if ((not dir_lists) and (not file_lists)) or (file_count == 0 and folder_count == 0): self.show_status("回收站为空!", 4000) return name_header = [ "文件夹{}个".format(folder_count), ] if folder_count else [] if file_count: name_header.append("文件{}个".format(file_count)) self.model_rec.setHorizontalHeaderLabels( ["/".join(name_header), "大小", "时间"]) folder_ico = QIcon("./src/folder.gif") for item in iter(dir_lists): # 文件夹 name = QStandardItem(folder_ico, item.name) name.setData(item) name.setToolTip("双击查看详情") size_ = QStandardItem(item.size) time_ = QStandardItem(item.time) self.model_rec.appendRow([name, size_, time_]) for item in iter(file_lists): # 文件 name = QStandardItem(set_file_icon(item.name), item.name) name.setData(item) size_ = QStandardItem(item.size) time_ = QStandardItem(item.time) self.model_rec.appendRow([name, size_, time_]) for row in range(self.model_rec.rowCount()): # 右对齐 self.model_rec.item(row, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_rec.item(row, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) def init_rec_ui(self): """回收站ui初始化""" self.model_rec = QStandardItemModel(1, 3) self.config_tableview("rec") self.table_rec.doubleClicked.connect(self.call_rec_folder_dialog) self.btn_rec_select_all.setIcon(QIcon("./src/select_all.ico")) self.btn_rec_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.btn_rec_delete.clicked.connect( lambda: self.call_multi_manipulator("delete")) self.btn_recovery.clicked.connect( lambda: self.call_multi_manipulator("recovery")) self.btn_rec_delete.setToolTip("彻底删除选中文件(夹)") self.btn_recovery.setToolTip("恢复选中文件(夹)") self.btn_recovery_all.clicked.connect( lambda: self.call_multi_manipulator("recovery_all")) self.btn_recovery_all.setToolTip("恢复全部") self.btn_rec_clean.clicked.connect( lambda: self.call_multi_manipulator("clean")) self.btn_rec_clean.setToolTip("清理回收站全部") # shared url def call_get_shared_info(self): if not self.get_shared_info_thread.isRunning(): # 防止快速多次调用 self.line_share_url.setEnabled(False) self.btn_extract.setEnabled(False) text = self.line_share_url.text().strip() self.get_shared_info_thread.set_values(text) def show_share_url_file_lists(self, infos): if infos["code"] == LanZouCloud.SUCCESS: file_count = len(infos["info"].keys()) self.model_share.setHorizontalHeaderLabels( ["文件{}个".format(file_count), "大小", "时间"]) for infos in infos["info"].values(): name = QStandardItem(set_file_icon(infos[1]), infos[1]) name.setData(infos) time = QStandardItem(time_format( infos[3])) if self.time_fmt else QStandardItem(infos[3]) self.model_share.appendRow( [name, QStandardItem(infos[2]), time]) for r in range(self.model_share.rowCount()): # 右对齐 self.model_share.item(r, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_share.item(r, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.table_share.setDisabled(False) self.btn_share_select_all.setDisabled(False) self.btn_share_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_share_dl.setDisabled(False) else: self.btn_share_select_all.setDisabled(True) self.btn_share_select_all.setToolTip("") self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) def set_download_path(self): """设置下载路径""" dl_path = QFileDialog.getExistingDirectory() dl_path = os.path.normpath(dl_path) # windows backslash if dl_path == self.configs["settings"]["dl_path"] or dl_path == ".": return if dl_path == "": dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" up_info = {"dl_path": dl_path} else: up_info = {"dl_path": dl_path} update_settings(self._config_file, up_info, is_settings=True) self.load_settings() self.line_dl_path.setText(self.configs["settings"]["dl_path"]) def init_extract_share_ui(self): self.btn_share_select_all.setDisabled(True) self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) self.model_share = QStandardItemModel(1, 3) self.config_tableview("share") # 获取分享链接信息线程 self.get_shared_info_thread = GetSharedInfo() self.get_shared_info_thread.update.connect( lambda: self.model_share.removeRows(0, self.model_share.rowCount() )) # 清理旧的信息 self.get_shared_info_thread.msg.connect(self.show_status) # 提示信息 self.get_shared_info_thread.infos.connect( self.show_share_url_file_lists) # 内容信息 self.get_shared_info_thread.finished.connect( lambda: self.btn_extract.setEnabled(True)) self.get_shared_info_thread.finished.connect( lambda: self.line_share_url.setEnabled(True)) # 控件设置 self.line_share_url.setPlaceholderText( "蓝奏云链接,如有提取码,放后面,空格或汉字等分割,回车键提取") self.line_share_url.returnPressed.connect(self.call_get_shared_info) self.btn_extract.clicked.connect(self.call_get_shared_info) self.btn_share_dl.clicked.connect( lambda: self.call_multi_manipulator("download")) self.btn_share_dl.setIcon(QIcon("./src/downloader.ico")) self.btn_share_select_all.setIcon(QIcon("./src/select_all.ico")) self.btn_share_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_share.clicked.connect( lambda: self.select_all_btn("cancel")) # 全选按钮 # 添加文件下载路径选择器 self.line_dl_path = MyLineEdit(self.share_tab) self.line_dl_path.setObjectName("line_dl_path") self.horizontalLayout_share_2.insertWidget(2, self.line_dl_path) self.line_dl_path.setText(self.configs["settings"]["dl_path"]) self.line_dl_path.clicked.connect(self.set_download_path) # QSS self.label_share_url.setStyleSheet( "#label_share_url {color: rgb(255,255,60);}") self.label_dl_path.setStyleSheet( "#label_dl_path {color: rgb(255,255,60);}") # others def clean_status(self): self._msg_label.setText("") self._msg_movie_lb.clear() self._msg_movie.stop() def show_status(self, msg, duration=0): self._msg_label.setText(msg) if msg and duration >= 3000: self._msg_movie_lb.setMovie(self._msg_movie) self._msg_movie.start() else: self._msg_movie_lb.clear() self._msg_movie.stop() if duration != 0: QTimer.singleShot(duration, self.clean_status) def keyPressEvent(self, e): if e.key() == Qt.Key_A: # Ctrl/Alt + A 全选 if e.modifiers() and Qt.ControlModifier: self.select_all_btn() elif e.key() == Qt.Key_F5: # 刷新 if self.tabWidget.currentIndex() == 1: # disk 界面 self.show_status("正在更新当前目录...", 10000) self.list_refresher.set_values(self._work_id) elif self.tabWidget.currentIndex() == 2: # rec 界面 self.show_status("正在更新回收站...", 10000) self.get_rec_lists_worker.start() def call_change_tab(self): """切换标签页 动作""" tab_index = self.tabWidget.currentIndex() if tab_index == 2: self.show_status("正在更新回收站...", 10000) self.get_rec_lists_worker.start() elif tab_index == 1: self.show_status("正在更新当前目录...", 10000) self.list_refresher.set_values(self._work_id) def set_window_at_center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() new_left = int((screen.width() - size.width()) / 2) new_top = int((screen.height() - size.height()) / 2) self.move(new_left, new_top) def open_wiki_url(self): # 打开使用说明页面 url = QUrl('https://github.com/rachpt/lanzou-gui/wiki') if not QDesktopServices.openUrl(url): self.show_status('Could not open wiki page!', 5000) def show_new_version_msg(self, ver, msg): message_box = QMessageBox(self) message_box.setStyleSheet(btn_style) message_box.setWindowTitle(f"检测到新版 {ver}") message_box.setText(msg) message_box.setStandardButtons(QMessageBox.Close) buttonC = message_box.button(QMessageBox.Close) buttonC.setText('关闭') message_box.exec() def closeEvent(self, event): if self.to_tray: event.ignore() self.hide() self.tray.showMessage("蓝奏云客户端", "应用已经最小化到托盘,如需退出请右击", QSystemTrayIcon.Information, 2500)
def init_workers(self): # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) self.login_luncher.update_cookie.connect(self.call_update_cookie) # 登出器 self.logout_worker = LogoutWorker() self.logout_worker.successed.connect(self.call_logout_update_ui) # 下载器 self.download_manager = DownloadManager() self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 2999)) # 获取更多信息,直链、下载次数等 self.info_dialog = InfoDialog() # 对话框 self.info_dialog.setWindowModality(Qt.ApplicationModal) # 窗口前置 self.more_info_worker = GetMoreInfoWorker() # 后台更新线程 self.more_info_worker.msg.connect(self.show_status) self.more_info_worker.infos.connect(self.info_dialog.set_values) self.more_info_worker.dl_link.connect( self.info_dialog.tx_dl_link.setText) self.info_dialog.get_dl_link.connect(self.more_info_worker.get_dl_link) # 登录文件列表更新器 self.list_refresher = ListRefresher(self._disk) self.list_refresher.err_msg.connect(self.show_status) self.list_refresher.infos.connect(self.update_disk_lists) self.list_refresher.infos.connect(lambda: self.show_status("")) # 获取所有文件夹fid,并移动 self.all_folders_worker = GetAllFoldersWorker() self.all_folders_worker.msg.connect(self.show_status) self.all_folders_worker.infos.connect(self.show_move_file_dialog) self.all_folders_worker.moved.connect( lambda: self.list_refresher.set_values(self._work_id, True, False, False)) # 更新文件列表 # 重命名、修改简介、新建文件夹 self.rename_mkdir_worker = RenameMkdirWorker() self.rename_mkdir_worker.msg.connect(self.show_status) self.rename_mkdir_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 设置文件(夹)提取码 self.set_pwd_worker = SetPwdWorker() self.set_pwd_worker.msg.connect(self.show_status) self.set_pwd_worker.update.connect( self.list_refresher.set_values) # 更新界面 # 删除文件(夹) self.remove_files_worker = RemoveFilesWorker(self._disk) self.remove_files_worker.msg.connect(self.show_status) # 显示错误提示 self.remove_files_worker.finished.connect( lambda: self.list_refresher.set_values(self._work_id)) # 更新界面 # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述与提取码更新器 self.desc_pwd_fetcher = DescPwdFetcher() self.desc_pwd_fetcher.desc.connect(self.call_update_desc_pwd) self.desc_pwd_fetcher.tasks.connect( self.call_download_manager_thread) # 连接下载管理器线程 # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_movie_lb = QLabel() self._msg_movie = QMovie("src/loading_more.gif") self._msg_movie.setScaledSize(QSize(24, 24)) self._msg_movie_lb.setMovie(self._msg_movie) self._msg_label.setObjectName("msg_label") self._msg_movie_lb.setObjectName("msg_movie_lb") self.statusbar.addWidget(self._msg_movie_lb) self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.call_rename_mkdir_worker) # 修改设置 提取码对话框 self.set_pwd_dialog = SetPwdDialog() self.set_pwd_dialog.new_infos.connect(self.set_passwd) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__) # 菜单栏设置 self.setting_dialog = SettingDialog(self._config_file, self._default_settings) self.setting_dialog.saved.connect( lambda: self.load_settings(ref_ui=True)) # 登录回收站信息更新器 self.get_rec_lists_worker = GetRecListsWorker(self._disk) self.get_rec_lists_worker.msg.connect(self.show_status) self.get_rec_lists_worker.infos.connect(self.update_rec_lists) self.get_rec_lists_worker.folders.connect( lambda: self.show_status('', 0)) self.get_rec_lists_worker.folders.connect( self.pop_up_rec_folder_dialog) # 回收站操作器 self.rec_manipulator = RecManipulator(self._disk) self.rec_manipulator.msg.connect(self.show_status) self.rec_manipulator.successed.connect(self.get_rec_lists_worker.start) # 检查软件版本 self.check_update_worker = CheckUpdateWorker() self.about_dialog.check_update.connect( self.check_update_worker.set_values) self.check_update_worker.infos.connect(self.about_dialog.show_update) self.check_update_worker.bg_update_infos.connect( self.show_new_version_msg)
def about(self, widget): if not self.aboutdialog: self.aboutdialog = AboutDialog(self.mainwin) self.aboutdialog.run()
def _about(self): """Open about dialog. """ about = AboutDialog(self.parent) # Opens dialog near main window about.exec_() del about
def ShowAboutDialog(self): dialog = AboutDialog(self._view) dialog.Center() dialog.ShowModal() dialog.Destroy()
class MainWindow(QMainWindow, Ui_MainWindow): __version__ = 'v0.0.5' def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.init_variable() self.init_menu() self.setWindowTitle("蓝奏云客户端 - {}".format(self.__version__)) self.setWindowIcon(QIcon("./icon/lanzou-logo2.png")) self.set_window_at_center() self.init_extract_share_ui() self.init_disk_ui() self.call_login_luncher() self.create_left_menus() # print(QApplication.style().objectName()) self.setObjectName("MainWindow") self.setStyleSheet(qssStyle) # self.disk_tab.setStyleSheet("* {background-color: rgba(255, 255, 255, 150);}") self.tabWidget.setStyleSheet("QTabBar{ background-color: #AEEEEE; }") def init_menu(self): self.login.triggered.connect(self.show_login_dialog) # 登录 self.login.setIcon(QIcon("./icon/login.ico")) self.login.setShortcut("Ctrl+L") self.toolbar.addAction(self.login) self.logout.triggered.connect(self.call_logout) # 登出 self.logout.setIcon(QIcon("./icon/logout.ico")) self.logout.setShortcut("Ctrl+Q") # 登出快捷键 self.download.setShortcut("Ctrl+J") # 以下还未使用 self.download.setIcon(QIcon("./icon/download.ico")) self.delete.setShortcut("Ctrl+D") self.delete.setIcon(QIcon("./icon/delete.ico")) # self.how.setShortcut("Ctrl+H") self.how.setIcon(QIcon("./icon/help.ico")) self.how.triggered.connect(self.open_wiki_url) # self.about.setShortcut("Ctrl+O") self.about.setIcon(QIcon("./icon/about.ico")) self.about.triggered.connect(self.about_dialog.exec) self.upload.setIcon(QIcon("./icon/upload.ico")) self.upload.setShortcut("Ctrl+U") # 上传快捷键 def init_variable(self): self._disk = LanZouCloud() self._config = "./config.pkl" self._folder_list = {} self._file_list = {} self._path_list = {} self._path_list_old = {} self._locs = {} self._parent_id = -1 # --> .. self._work_name = "" # share disk rec, not use now self._work_id = -1 # disk folder id self._old_work_id = self._work_id # 用于上传完成后判断是否需要更新disk界面 self.load_settings() if os.name == 'nt': self._disk.set_rar_tool("./rar.exe") else: self._disk.set_rar_tool("/usr/bin/rar") # 登录器 self.login_luncher = LoginLuncher(self._disk) self.login_luncher.code.connect(self.login_update_ui) # 下载器 self.download_manager = DownloadManager(self._disk) self.download_manager.downloaders_msg.connect(self.show_status) self.download_manager.download_mgr_msg.connect(self.show_status) self.download_manager.finished.connect( lambda: self.show_status("所有下载任务已完成!", 7000)) # 上传器,信号在登录更新界面设置 self.upload_dialog = UploadDialog() self.upload_dialog.new_infos.connect(self.call_upload) # 文件描述更新器 self.desc_fetcher = DescFetcher(self._disk) self.desc_fetcher.desc.connect(self.call_update_desc) # 设置 tab self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 状态栏 self._msg_label = QLabel() self._msg_label.setObjectName("msg_label") self.statusbar.addWidget(self._msg_label) # 重命名、修改简介与新建文件夹对话框 self.rename_dialog = RenameDialog() self.rename_dialog.out.connect(self.rename_set_desc_and_mkdir) # 菜单栏关于 self.about_dialog = AboutDialog() self.about_dialog.set_values(self.__version__) def show_login_dialog(self): """显示登录对话框""" login_dialog = LoginDialog(self._config) login_dialog.clicked_ok.connect(self.call_login_luncher) login_dialog.setWindowModality(Qt.ApplicationModal) login_dialog.exec() def show_upload_dialog(self): """显示上传文件对话框""" self.upload_dialog.set_values(list(self._path_list.keys())[-1]) self.upload_dialog.exec() def load_settings(self): try: with open(self._config, "rb") as _file: self.settings = load(_file) except Exception: dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" self.settings = {"user": "", "pwd": "", "path": dl_path} with open(self._config, "wb") as _file: dump(self.settings, _file) def call_downloader(self): tab_page = self.tabWidget.currentIndex() if tab_page == 0: listview = self.table_share model = self.model_share elif tab_page == 1: listview = self.table_disk model = self.model_disk indexes = [] tasks = [] _indexes = listview.selectionModel().selection().indexes() for i in _indexes: # 获取所选行号 indexes.append(i.row()) indexes = set(indexes) save_path = self.settings["path"] for index in indexes: infos = model.item(index, 0).data() if not infos: continue # 查询 分享链接 以及 提取码 if infos[0]: # 从 disk 运行 if infos[2]: # 文件 _info = self._disk.get_share_info(infos[0], is_file=True) else: # 文件夹 _info = self._disk.get_share_info(infos[0], is_file=False) infos[5] = _info['pwd'] # 将bool值改成 字符串 infos.append(_info['url']) tasks.append([infos[1], infos[7], infos[5], save_path]) self.download_manager.set_values(tasks, 3) self.download_manager.start() def call_logout(self): """菜单栏、工具栏登出""" self._disk.logout() self.toolbar.removeAction(self.logout) self.tabWidget.setCurrentIndex(0) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.toolbar.removeAction(self.logout) # 登出工具 self.logout.setEnabled(False) self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.upload.setEnabled(False) self.upload.triggered.disconnect(self.show_upload_dialog) self.statusbar.showMessage("已经退出登录!", 4000) def login_update_ui(self, success, msg, duration): """根据登录是否成功更新UI""" if success: self.show_status(msg, duration) self.tabWidget.insertTab(1, self.disk_tab, "我的蓝奏云") self.tabWidget.insertTab(2, self.rec_tab, "回收站") self.disk_tab.setEnabled(True) self.rec_tab.setEnabled(True) # 更新快捷键与工具栏 self.toolbar.addAction(self.logout) # 添加登出工具栏 self.toolbar.addAction(self.upload) # 添加上传文件工具栏 # 菜单栏槽 self.logout.setEnabled(True) self.upload.setEnabled(True) self.upload.triggered.connect(self.show_upload_dialog) # 设置当前显示 tab self.tabWidget.setCurrentIndex(1) QCoreApplication.processEvents() # 重绘界面 # 刷新文件列表 self.refresh_dir(self._work_id) else: self.show_status(msg, duration) self.tabWidget.setCurrentIndex(0) self.tabWidget.removeTab(2) self.tabWidget.removeTab(1) self.disk_tab.setEnabled(False) self.rec_tab.setEnabled(False) # 更新快捷键与工具栏 self.toolbar.removeAction(self.logout) # 登出工具栏 self.toolbar.removeAction(self.upload) # 上传文件工具栏 self.logout.setEnabled(False) self.upload.setEnabled(False) def call_login_luncher(self): """登录网盘""" self.load_settings() self._disk.logout() self.toolbar.removeAction(self.logout) try: username = self.settings["user"] password = self.settings["pwd"] cookie = self.settings["cookie"] self.login_luncher.set_values(username, password, cookie) self.login_luncher.start() except Exception as exp: print(exp) pass def set_file_icon(self, name): suffix = name.split(".")[-1] ico_path = "./icon/{}.gif".format(suffix) if os.path.isfile(ico_path): return QIcon(ico_path) else: return QIcon("./icon/file.ico") def show_file_and_folder_lists(self): """显示文件和文件夹列表""" self.model_disk.removeRows(0, self.model_disk.rowCount()) # 清理旧的内容 file_count = len(self._file_list.keys()) folder_count = len(self._folder_list.keys()) name_header = [ "文件夹{}个".format(folder_count), ] if folder_count else [] if file_count: name_header.append("文件{}个".format(file_count)) self.model_disk.setHorizontalHeaderLabels( ["/".join(name_header), "大小", "时间"]) folder_ico = QIcon("./icon/folder.gif") pwd_ico = QIcon("./icon/keys.ico") # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url),直链 if self._work_id != -1: _back = QStandardItem(folder_ico, "..") _back.setToolTip("双击返回上层文件夹,选中无效") self.model_disk.appendRow( [_back, QStandardItem(""), QStandardItem("")]) for infos in self._folder_list.values(): # 文件夹 name = QStandardItem(folder_ico, infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, "") if infos[5] else QStandardItem( "") # 提取码+size self.model_disk.appendRow([name, size_, QStandardItem("")]) for infos in self._file_list.values(): # 文件 name = QStandardItem(self.set_file_icon(infos[1]), infos[1]) name.setData(infos) tips = "" if infos[5] is not False: tips = "有提取码" if infos[6] is not False: tips = tips + ",描述:" + str(infos[6]) elif infos[6] is not False: tips = "描述:" + str(infos[6]) name.setToolTip(tips) size_ = QStandardItem(pwd_ico, infos[2]) if infos[5] else QStandardItem( infos[2]) # 提取码+size self.model_disk.appendRow([name, size_, QStandardItem(infos[3])]) for r in range(self.model_disk.rowCount()): # 右对齐 self.model_disk.item(r, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_disk.item(r, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) def refresh_dir(self, folder_id=-1, r_files=True, r_folders=True, r_path=True): """更新目录列表,包括列表和路径指示器""" if r_files: info = { i['name']: [ i['id'], i['name'], i['size'], i['time'], i['downs'], i['has_pwd'], i['has_des'] ] for i in self._disk.get_file_list(folder_id) } self._file_list = { key: info.get(key) for key in sorted(info.keys()) } # {name-[id,...]} if r_folders: info = { i['name']: [i['id'], i['name'], "", "", "", i['has_pwd'], i['desc']] for i in self._disk.get_dir_list(folder_id) } self._folder_list = { key: info.get(key) for key in sorted(info.keys()) } # {name-[id,...]} self._path_list = self._disk.get_full_path(folder_id) current_folder = list(self._path_list.keys())[-1] self._work_id = self._path_list.get(current_folder, -1) if folder_id != -1: parent_folder_name = list(self._path_list.keys())[-2] self._parent_id = self._path_list.get(parent_folder_name, -1) self.show_file_and_folder_lists() if r_path: self.show_full_path() def config_tableview(self, tab): if tab == "share": model = self.model_share table = self.table_share elif tab == "disk": model = self.model_disk table = self.table_disk model.setHorizontalHeaderLabels(["文件名", "大小", "时间"]) table.setModel(model) # 是否显示网格线 table.setShowGrid(False) # 禁止编辑表格 table.setEditTriggers(QAbstractItemView.NoEditTriggers) # 隐藏水平表头 table.verticalHeader().setVisible(False) # 设置表头可以自动排序 table.setSortingEnabled(True) table.setMouseTracking(False) # 设置表头的背景色为绿色 table.horizontalHeader().setStyleSheet( "QHeaderView::section{background:lightgray}") # 设置 不可选择单个单元格,只可选择一行。 table.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置第二三列的宽度 table.horizontalHeader().resizeSection(1, 90) table.horizontalHeader().resizeSection(2, 80) # 设置第一列宽度自动调整,充满屏幕 table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) table.setContextMenuPolicy(Qt.CustomContextMenu) # 允许右键产生子菜单 table.customContextMenuRequested.connect(self.generateMenu) # 右键菜单 def create_left_menus(self): self.left_menus = QMenu() self.left_menu_share_url = self.left_menus.addAction("外链分享地址") self.left_menu_share_url.setIcon(QIcon("./icon/share.ico")) self.left_menu_rename_set_desc = self.left_menus.addAction("修改文件夹名与描述") self.left_menu_rename_set_desc.setIcon(QIcon("./icon/desc.ico")) self.left_menu_set_pwd = self.left_menus.addAction("设置访问密码") self.left_menu_set_pwd.setIcon(QIcon("./icon/password.ico")) self.left_menu_move = self.left_menus.addAction("移动") self.left_menu_move.setIcon(QIcon("./icon/move.ico")) def rename_set_desc_and_mkdir(self, infos): """重命名、修改简介与新建文件夹""" action = infos[0] fid = infos[1] new_name = infos[2] new_desc = infos[3] if not fid: # 新建文件夹 fid = self._work_id if new_name in self._folder_list.keys(): self.statusbar.showMessage("文件夹已存在:{}".format(new_name), 7000) else: res = self._disk.mkdir(self._work_id, new_name, new_desc) if res == LanZouCloud.MKDIR_ERROR: self.statusbar.showMessage("创建文件夹失败:{}".format(new_name), 7000) else: sleep(1.5) # 暂停一下,否则无法获取新建的文件夹 self.statusbar.showMessage("成功创建文件夹:{}".format(new_name), 7000) # 此处仅更新文件夹,并显示 self.refresh_dir(self._work_id, False, True, False) else: # 重命名、修改简介 if action == "file": # 修改文件描述 res = self._disk.set_desc(fid, str(new_desc), is_file=True) else: # 修改文件夹,action == "folder" _res = self._disk.get_share_info(fid, is_file=False) if _res['code'] == LanZouCloud.SUCCESS: res = self._disk._set_dir_info(fid, str(new_name), str(new_desc)) else: res = _res['code'] if res == LanZouCloud.SUCCESS: self.statusbar.showMessage("修改成功!", 4000) elif res == LanZouCloud.FAILED: self.statusbar.showMessage("失败:发生错误!", 4000) if action == "file": # 只更新文件列表 self.refresh_dir(self._work_id, r_files=True, r_folders=False, r_path=False) else: # 只更新文件夹列表 self.refresh_dir(self._work_id, r_files=False, r_folders=True, r_path=False) def set_passwd(self, infos): """设置文件(夹)提取码""" fid = infos[0] if not fid: print("ERROR : 文件(夹)不存在:{}".format(infos[0])) return None new_pass = infos[1] if 2 <= len(new_pass) <= 6 or new_pass == "": if infos[2]: is_file = True else: is_file = False res = self._disk.set_passwd(fid, new_pass, is_file) if res == LanZouCloud.SUCCESS: self.statusbar.showMessage("提取码变更成功!♬", 3000) elif res == LanZouCloud.NETWORK_ERROR: self.statusbar.showMessage("网络错误,稍后重试!☒", 4000) else: self.statusbar.showMessage("提取码变更失败❀╳❀:{}".format(res), 4000) self.refresh_dir(self._work_id, r_files=is_file, r_folders=not is_file, r_path=False) else: self.statusbar.showMessage("提取码为2-6位字符,关闭请输入空!", 4000) def move_file(self, info): """移动文件至新的文件夹""" file_id = info[0] folder_id = info[1] if self._disk.move_file(file_id, folder_id) == LanZouCloud.SUCCESS: # 此处仅更新文件夹,并显示 self.refresh_dir(self._work_id, False, True, False) self.statusbar.showMessage("{} 移动成功!".format(info[2]), 4000) else: self.statusbar.showMessage("移动文件{}失败!".format(info[2]), 4000) def call_mkdir(self): """弹出新建文件夹对话框""" self.rename_dialog.set_values(None) self.rename_dialog.exec() def remove_files(self, infos): if not infos: return for i in infos: if i[1]: is_file = True else: is_file = False self._disk.delete(i[0], is_file) self.refresh_dir(self._work_id) def call_remove_files(self): indexs = [] infos = [] _indexs = self.table_disk.selectionModel().selection().indexes() if not _indexs: return for i in _indexs: # 获取所选行号 indexs.append(i.row()) indexs = set(indexs) for index in indexs: info = self.model_disk.item(index, 0).data() # 用于提示删除的文件名 if info: infos.append(info[:3]) delete_dialog = DeleteDialog(infos) delete_dialog.new_infos.connect(self.remove_files) delete_dialog.exec() def generateMenu(self, pos): """右键菜单""" row_num = self.sender().selectionModel().selection().indexes() if not row_num: # 如果没有选中行,什么也不做 return # row_num = row_num[0].row() _model = self.sender().model() infos = _model.item(row_num[0].row(), 0).data() if not infos: return # 通过是否有文件 ID 判断是登录界面还是提取界面 if infos[0]: self.left_menu_rename_set_desc.setEnabled(True) self.left_menu_set_pwd.setEnabled(True) # 通过infos第3个字段 size 判断是否为文件夹,文件夹不能移动,设置不同的显示菜单名 if infos[2]: self.left_menu_rename_set_desc.setText("修改文件描述") self.left_menu_move.setEnabled(True) else: self.left_menu_rename_set_desc.setText("修改文件夹名与描述") self.left_menu_move.setDisabled(True) else: self.left_menu_rename_set_desc.setDisabled(True) self.left_menu_move.setDisabled(True) self.left_menu_set_pwd.setDisabled(True) action = self.left_menus.exec_(self.sender().mapToGlobal(pos)) infos = self.get_more_infomation(infos) # 点击菜单项后更新信息 if action == self.left_menu_share_url: info_dialog = InfoDialog(infos) info_dialog.setWindowModality(Qt.ApplicationModal) info_dialog.exec() elif action == self.left_menu_move: all_dirs_dict = self._disk.get_folder_id_list() move_file_dialog = MoveFileDialog(infos, all_dirs_dict) move_file_dialog.new_infos.connect(self.move_file) move_file_dialog.exec() elif action == self.left_menu_set_pwd: set_pwd_dialog = SetPwdDialog(infos) set_pwd_dialog.new_infos.connect(self.set_passwd) set_pwd_dialog.exec() elif action == self.left_menu_rename_set_desc: self.desc_fetcher.set_values(infos) self.desc_fetcher.start() # 启动后台更新描述 self.rename_dialog.set_values(infos) self.rename_dialog.exec() def call_update_desc(self, desc, infos): infos[6] = desc # 更新 desc self.rename_dialog.set_values(infos) def get_more_infomation(self, infos): """获取文件直链、文件(夹)提取码描述""" if self._work_name == "Recovery": print("ERROR : 回收站模式下无法使用此操作") return None # infos: ID/None,文件名,大小,日期,下载次数(dl_count),提取码(pwd),描述(desc),|链接(share-url),直链 if infos[0]: # 从 disk 运行 if infos[2]: # 文件 _info = self._disk.get_share_info(infos[0], is_file=True) else: # 文件夹 _info = self._disk.get_share_info(infos[0], is_file=False) infos[5] = _info['pwd'] infos.append(_info['url']) if infos[2]: # 文件 res = self._disk.get_file_info_by_url(infos[-1], infos[5]) if res["code"] == LanZouCloud.SUCCESS: infos.append("{}".format(res["durl"] or "无")) # 下载直链 elif res["code"] == LanZouCloud.NETWORK_ERROR: infos.append("网络错误!获取失败") # 下载直链 else: infos.append("其它错误!") # 下载直链 else: infos.append("无") # 下载直链 infos[5] = infos[5] or "无" # 提取码 return infos def chang_dir(self, dir_name): """双击切换工作目录""" dir_name = self.model_disk.item(dir_name.row(), 0).text() # 文件夹名 if self._work_name == "Recovery" and dir_name not in [".", ".."]: return None if dir_name == "..": # 返回上级路径 self.refresh_dir(self._parent_id) elif dir_name in self._folder_list.keys(): folder_id = self._folder_list[dir_name][0] self.refresh_dir(folder_id) else: self.show_status("ERROR : 该文件夹不存在: {}".format(dir_name)) def call_change_dir(self, folder_id=-1): """按钮调用""" def callfunc(): self.refresh_dir(folder_id) return callfunc def call_upload(self, infos): """上传文件(夹)""" if self._work_name == 'Recovery': print('ERROR : 回收站模式下无法使用此操作') return None self._old_work_id = self._work_id # 记录上传文件夹id self.upload_worker.set_values(self._disk, infos, self._old_work_id) self.upload_worker.start() def show_full_path(self): """路径框显示当前路径""" index = 1 for name in self._path_list_old.items(): self._locs[index].clicked.disconnect() self.disk_loc.removeWidget(self._locs[index]) self._locs[index].deleteLater() self._locs[index] = None del self._locs[index] index += 1 index = 1 for name, id in self._path_list.items(): self._locs[index] = QPushButton(name, self.disk_tab) self._locs[index].setIcon(QIcon("./icon/folder.gif")) self._locs[index].setStyleSheet( "QPushButton {border: none; background:transparent;}") self.disk_loc.insertWidget(index, self._locs[index]) self._locs[index].clicked.connect(self.call_change_dir(id)) index += 1 self._path_list_old = self._path_list def select_all_btn(self, action="reverse"): """默认反转按钮状态""" page = self.tabWidget.currentIndex() if page == 0: btn = self.btn_share_select_all table = self.table_share elif page == 1: btn = self.btn_disk_select_all table = self.table_disk elif page == 2: return else: return if btn.isEnabled(): if action == "reverse": if btn.text() == "全选": table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./icon/select-none.ico")) elif btn.text() == "取消": table.clearSelection() btn.setText("全选") btn.setIcon(QIcon("./icon/select-all.ico")) elif action == "cancel": # 点击列表其中一个就表示放弃全选 btn.setText("全选") btn.setIcon(QIcon("./icon/select-all.ico")) else: table.selectAll() btn.setText("取消") btn.setIcon(QIcon("./icon/select-none.ico")) def finished_upload(self): """上传完成调用""" if self._old_work_id == self._work_id: self.refresh_dir(self._work_id, True, True, False) else: self._old_work_id = self._work_id self.show_status("上传完成!", 7000) def init_disk_ui(self): self.model_disk = QStandardItemModel(1, 3) self.config_tableview("disk") self.btn_disk_delete.setIcon(QIcon("./icon/delete.ico")) self.btn_disk_dl.setIcon(QIcon("./icon/downloader.ico")) self.btn_disk_select_all.setIcon(QIcon("./icon/select-all.ico")) self.btn_disk_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_disk_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_disk.clicked.connect(lambda: self.select_all_btn("cancel")) self.btn_disk_dl.clicked.connect(self.call_downloader) self.btn_disk_mkdir.setIcon(QIcon("./icon/add-folder.ico")) self.btn_disk_mkdir.clicked.connect(self.call_mkdir) self.btn_disk_delete.clicked.connect(self.call_remove_files) self.table_disk.doubleClicked.connect(self.chang_dir) # 上传器 self.upload_worker = UploadWorker() self.upload_worker.finished.connect(self.finished_upload) self.upload_worker.code.connect(self.show_status) def show_status(self, msg, duration=0): self._msg_label.setText(msg) # self.statusbar.showMessage(msg, duration) QCoreApplication.processEvents() # 重绘界面 if duration != 0: QTimer.singleShot(duration, lambda: self._msg_label.setText("")) # shared url def call_get_shared_info_worker(self): line_share_text = self.line_share_url.text().strip() pat = r"(https?://(www\.)?lanzous.com/[bi][a-z0-9]+)[^0-9a-z]*([a-z0-9]+)?" for share_url, _, pwd in re.findall(pat, line_share_text): pass if self._disk.is_file_url(share_url): # 链接为文件 is_file = True is_folder = False self.show_status("正在获取文件链接信息……") elif self._disk.is_folder_url(share_url): # 链接为文件夹 is_folder = True is_file = False self.show_status("正在获取文件夹链接信息,可能需要几秒钟,请稍后……") else: self.show_status("{} 为非法链接!".format(share_url)) self.btn_extract.setEnabled(True) self.line_share_url.setEnabled(True) return self.model_share.removeRows(0, self.model_share.rowCount()) QCoreApplication.processEvents() # 重绘界面 self.get_shared_info_thread.set_values(self._disk, share_url, pwd, is_file, is_folder) self.get_shared_info_thread.start() def call_get_shared_info(self): if not self.get_shared_info_thread.isRunning(): # 防止阻塞主进程 self.line_share_url.setEnabled(False) self.btn_extract.setEnabled(False) self.call_get_shared_info_worker() def show_share_url_file_lists(self, infos): if infos["code"] == LanZouCloud.SUCCESS: file_count = len(infos["info"].keys()) self.model_share.setHorizontalHeaderLabels( ["文件{}个".format(file_count), "大小", "时间"]) for infos in infos["info"].values(): name = QStandardItem(self.set_file_icon(infos[1]), infos[1]) name.setData(infos) self.model_share.appendRow( [name, QStandardItem(infos[2]), QStandardItem(infos[3])]) for r in range(self.model_share.rowCount()): # 右对齐 self.model_share.item(r, 1).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model_share.item(r, 2).setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.table_share.setDisabled(False) self.btn_share_select_all.setDisabled(False) self.btn_share_select_all.setToolTip("按下 Ctrl/Alt + A 全选或则取消全选") self.btn_share_dl.setDisabled(False) else: self.btn_share_select_all.setDisabled(True) self.btn_share_select_all.setToolTip("") self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) def set_download_path(self): """设置下载路径""" dl_path = QFileDialog.getExistingDirectory() if dl_path == self.settings["path"]: return if dl_path == "": dl_path = os.path.dirname( os.path.abspath(__file__)) + os.sep + "downloads" up_info = {"path": dl_path} else: up_info = {"path": dl_path} update_settings(self._config, up_info) self.load_settings() self.line_dl_path.setText(self.settings["path"]) def init_extract_share_ui(self): self.btn_share_select_all.setDisabled(True) self.btn_share_dl.setDisabled(True) self.table_share.setDisabled(True) self.model_share = QStandardItemModel(1, 3) self.config_tableview("share") self.get_shared_info_thread = GetSharedInfo() self.get_shared_info_thread.code.connect(self.show_status) # 状态码 self.get_shared_info_thread.infos.connect( self.show_share_url_file_lists) # 信息 self.get_shared_info_thread.finished.connect( lambda: self.btn_extract.setEnabled(True)) self.get_shared_info_thread.finished.connect( lambda: self.line_share_url.setEnabled(True)) self.line_share_url.setPlaceholderText( "蓝奏云链接,如有提取码,放后面,空格或汉字等分割,回车键提取") self.line_share_url.returnPressed.connect(self.call_get_shared_info) self.btn_extract.clicked.connect(self.call_get_shared_info) self.btn_share_dl.clicked.connect(self.call_downloader) self.btn_share_dl.setIcon(QIcon("./icon/downloader.ico")) self.btn_share_select_all.setIcon(QIcon("./icon/select-all.ico")) self.btn_share_select_all.clicked.connect( lambda: self.select_all_btn("reverse")) self.table_share.clicked.connect(lambda: self.select_all_btn("cancel")) # 添加文件下载路径选择器 self.line_dl_path = MyLineEdit(self.share_tab) self.line_dl_path.setObjectName("line_dl_path") self.horizontalLayout_share_2.insertWidget(2, self.line_dl_path) self.line_dl_path.setText(self.settings["path"]) self.line_dl_path.clicked.connect(self.set_download_path) # QSS self.label_share_url.setStyleSheet( "#label_share_url {color: rgb(255,255,60);}") self.label_dl_path.setStyleSheet( "#label_dl_path {color: rgb(255,255,60);}") def keyPressEvent(self, e): if e.key() == Qt.Key_A: # Ctrl/Alt + A 全选 if e.modifiers() and Qt.ControlModifier: self.select_all_btn() def set_window_at_center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() new_left = int((screen.width() - size.width()) / 2) new_top = int((screen.height() - size.height()) / 2) self.move(new_left, new_top) def open_wiki_url(self): # 打开使用说明页面 url = QUrl('https://github.com/rachpt/lanzou-gui/wiki') if not QDesktopServices.openUrl(url): self.show_status('Could not open wiki page!', 5000)
def __about_menu_item_activate(self, menu_item): """Show the about dialog.""" ad = AboutDialog() ad.run()
class UICallbacks: tmpconfpath = os.path.expanduser("~/.hatari/.tmp.cfg") def __init__(self): # Hatari and configuration self.hatari = Hatari() error = self.hatari.is_compatible() if error: ErrorDialog(None).run(error) sys.exit(-1) self.config = HatariConfigMapping(self.hatari) try: self.config.validate() except (KeyError, AttributeError): NoteDialog(None).run("Loading Hatari configuration failed!\nRetrying after saving Hatari configuration.") self.hatari.save_config() self.config = HatariConfigMapping(self.hatari) self.config.validate() # windows are created when needed self.mainwin = None self.hatariwin = None self.debugui = None self.panels = {} # dialogs are created when needed self.aboutdialog = None self.inputdialog = None self.tracedialog = None self.resetdialog = None self.quitdialog = None self.killdialog = None self.floppydialog = None self.harddiskdialog = None self.displaydialog = None self.joystickdialog = None self.machinedialog = None self.peripheraldialog = None self.sounddialog = None self.pathdialog = None # used by run() self.memstate = None self.floppy = None self.io_id = None # TODO: Hatari UI own configuration settings save/load self.tracepoints = None def _reset_config_dialogs(self): self.floppydialog = None self.harddiskdialog = None self.displaydialog = None self.joystickdialog = None self.machinedialog = None self.peripheraldialog = None self.sounddialog = None self.pathdialog = None # ---------- create UI ---------------- def create_ui(self, accelgroup, menu, toolbars, fullscreen, embed): "create_ui(menu, toolbars, fullscreen, embed)" # add horizontal elements hbox = gtk.HBox() if toolbars["left"]: hbox.pack_start(toolbars["left"], False, True) if embed: self._add_uisocket(hbox) if toolbars["right"]: hbox.pack_start(toolbars["right"], False, True) # add vertical elements vbox = gtk.VBox() if menu: vbox.add(menu) if toolbars["top"]: vbox.pack_start(toolbars["top"], False, True) vbox.add(hbox) if toolbars["bottom"]: vbox.pack_start(toolbars["bottom"], False, True) # put them to main window mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL) mainwin.set_title("%s %s" % (UInfo.name, UInfo.version)) mainwin.set_icon_from_file(UInfo.icon) if accelgroup: mainwin.add_accel_group(accelgroup) if fullscreen: mainwin.fullscreen() mainwin.add(vbox) mainwin.show_all() # for run and quit callbacks self.killdialog = KillDialog(mainwin) mainwin.connect("delete_event", self.quit) self.mainwin = mainwin def _add_uisocket(self, box): # add Hatari parent container to given box socket = gtk.Socket() # without this, closing Hatari would remove the socket widget socket.connect("plug-removed", lambda obj: True) socket.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) socket.set_events(gtk.gdk.ALL_EVENTS_MASK) socket.set_flags(gtk.CAN_FOCUS) # set max Hatari window size = desktop size self.config.set_desktop_size(gtk.gdk.screen_width(), gtk.gdk.screen_height()) # set initial embedded hatari size width, height = self.config.get_window_size() socket.set_size_request(width, height) # no resizing for the Hatari window box.pack_start(socket, False, False) self.hatariwin = socket # ------- run callback ----------- def _socket_cb(self, fd, event): if event != gobject.IO_IN: # hatari process died, make sure Hatari instance notices self.hatari.kill() return False width, height = self.hatari.get_embed_info() print("New size = %d x %d" % (width, height)) oldwidth, oldheight = self.hatariwin.get_size_request() self.hatariwin.set_size_request(width, height) if width < oldwidth or height < oldheight: # force also mainwin smaller (it automatically grows) self.mainwin.resize(width, height) return True def run(self, widget = None): if not self.killdialog.run(self.hatari): return if self.io_id: gobject.source_remove(self.io_id) args = ["--configfile"] # whether to use Hatari config or unsaved Hatari UI config? if self.config.is_changed(): args += [self.config.save_tmp(self.tmpconfpath)] else: args += [self.config.get_path()] if self.memstate: args += self.memstate # only way to change boot order is to specify disk on command line if self.floppy: args += self.floppy if self.hatariwin: size = self.hatariwin.window.get_size() self.hatari.run(args, self.hatariwin.window) # get notifications of Hatari window size changes self.hatari.enable_embed_info() socket = self.hatari.get_control_socket().fileno() events = gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR self.io_id = gobject.io_add_watch(socket, events, self._socket_cb) # all keyboard events should go to Hatari window self.hatariwin.grab_focus() else: self.hatari.run(args) def set_floppy(self, floppy): self.floppy = floppy # ------- quit callback ----------- def quit(self, widget, arg = None): # due to Gtk API, needs to return True when *not* quitting if not self.killdialog.run(self.hatari): return True if self.io_id: gobject.source_remove(self.io_id) if self.config.is_changed(): if not self.quitdialog: self.quitdialog = QuitSaveDialog(self.mainwin) if not self.quitdialog.run(self.config): return True gtk.main_quit() if os.path.exists(self.tmpconfpath): os.unlink(self.tmpconfpath) # continue to mainwin destroy if called by delete_event return False # ------- pause callback ----------- def pause(self, widget): if widget.get_active(): self.hatari.pause() else: self.hatari.unpause() # dialogs # ------- reset callback ----------- def reset(self, widget): if not self.resetdialog: self.resetdialog = ResetDialog(self.mainwin) self.resetdialog.run(self.hatari) # ------- about callback ----------- def about(self, widget): if not self.aboutdialog: self.aboutdialog = AboutDialog(self.mainwin) self.aboutdialog.run() # ------- input callback ----------- def inputs(self, widget): if not self.inputdialog: self.inputdialog = InputDialog(self.mainwin) self.inputdialog.run(self.hatari) # ------- floppydisk callback ----------- def floppydisk(self, widget): if not self.floppydialog: self.floppydialog = FloppyDialog(self.mainwin) self.floppydialog.run(self.config) # ------- harddisk callback ----------- def harddisk(self, widget): if not self.harddiskdialog: self.harddiskdialog = HardDiskDialog(self.mainwin) self.harddiskdialog.run(self.config) # ------- display callback ----------- def display(self, widget): if not self.displaydialog: self.displaydialog = DisplayDialog(self.mainwin) self.displaydialog.run(self.config) # ------- joystick callback ----------- def joystick(self, widget): if not self.joystickdialog: self.joystickdialog = JoystickDialog(self.mainwin) self.joystickdialog.run(self.config) # ------- machine callback ----------- def machine(self, widget): if not self.machinedialog: self.machinedialog = MachineDialog(self.mainwin) if self.machinedialog.run(self.config): self.hatari.trigger_shortcut("coldreset") # ------- peripheral callback ----------- def peripheral(self, widget): if not self.peripheraldialog: self.peripheraldialog = PeripheralDialog(self.mainwin) self.peripheraldialog.run(self.config) # ------- sound callback ----------- def sound(self, widget): if not self.sounddialog: self.sounddialog = SoundDialog(self.mainwin) self.sounddialog.run(self.config) # ------- path callback ----------- def path(self, widget): if not self.pathdialog: self.pathdialog = PathDialog(self.mainwin) self.pathdialog.run(self.config) # ------- debug callback ----------- def debugger(self, widget): if not self.debugui: self.debugui = HatariDebugUI(self.hatari) self.debugui.show() # ------- trace callback ----------- def trace(self, widget): if not self.tracedialog: self.tracedialog = TraceDialog(self.mainwin) self.tracepoints = self.tracedialog.run(self.hatari, self.tracepoints) # ------ snapshot load/save callbacks --------- def load(self, widget): path = os.path.expanduser("~/.hatari/hatari.sav") filename = get_open_filename("Select snapshot", self.mainwin, path) if filename: self.memstate = ["--memstate", filename] self.run() return True return False def save(self, widget): self.hatari.trigger_shortcut("savemem") # ------ config load/save callbacks --------- def config_load(self, widget): path = self.config.get_path() filename = get_open_filename("Select configuration file", self.mainwin, path) if filename: self.hatari.change_option("--configfile %s" % filename) self.config.load(filename) self._reset_config_dialogs() return True return False def config_save(self, widget): path = self.config.get_path() filename = get_save_filename("Save configuration as...", self.mainwin, path) if filename: self.config.save_as(filename) return True return False # ------- fast forward callback ----------- def set_fastforward(self, widget): self.config.set_fastforward(widget.get_active()) def get_fastforward(self): return self.config.get_fastforward() # ------- fullscreen callback ----------- def set_fullscreen(self, widget): # if user can select this, Hatari isn't in fullscreen self.hatari.change_option("--fullscreen") # ------- screenshot callback ----------- def screenshot(self, widget): self.hatari.trigger_shortcut("screenshot") # ------- record callbacks ----------- def recanim(self, widget): self.hatari.trigger_shortcut("recanim") def recsound(self, widget): self.hatari.trigger_shortcut("recsound") # ------- insert key special callback ----------- def keypress(self, widget, code): self.hatari.insert_event("keypress %s" % code) def textinsert(self, widget, text): HatariTextInsert(self.hatari, text) # ------- panel callback ----------- def panel(self, action, box): title = action.get_name() if title not in self.panels: window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_transient_for(self.mainwin) window.set_icon_from_file(UInfo.icon) window.set_title(title) window.add(box) window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) window.connect("delete_event", window_hide_cb) self.panels[title] = window else: window = self.panels[title] window.show_all() window.deiconify()
def on_about_button_press(self, instance): dlg = AboutDialog() dlg.open()