def quit(cls): """退出线程 :param cls: """ if hasattr(cls, '_thread'): cls._thread.quit() AppLog.info('login thread quit')
def listSubDir(self, pitem, path): """遍历子目录 :param item: 上级Item :param path: 目录 """ paths = os.listdir(path) files = [] for name in paths: spath = os.path.join(path, name) if not os.path.isfile(spath): continue spath = os.path.splitext(spath) if len(spath) == 0: continue if spath[1] == '.py' and spath[0].endswith('__init__') == False: files.append(name) # 已经存在的item existsItems = [pitem.child(i).text() for i in range(pitem.rowCount())] for name in files: if name in existsItems: continue file = os.path.join(path, name).replace('\\', '/') item = QStandardItem(name) # 添加自定义的数据 item.setData(False, Constants.RoleRoot) # 不是根目录 item.setData(file, Constants.RolePath) try: item.setData(open(file, 'rb').read().decode( errors='ignore'), Constants.RoleCode) except Exception as e: AppLog.warn( 'read file({}) code error: {}'.format(file, str(e))) pitem.appendRow(item)
def run(self): try: req = requests.get( UrlGetAppsByCategory.format(category=self.category, pageno=1, count=20, time=time())) content = req.json() errno = content.get('errno', 0) AppLog.debug('errno: %s', errno) AppLog.debug('msg: %s', content.get('msg', '')) if errno != 0: return content = content.get('data', {}) AppLog.debug('total_count: %s', content.get('total_count', '')) AppLog.debug('total_page: %s', content.get('total_page', '')) items = content.get('list', []) for i, item in enumerate(items): title = item.get('title', '') url = item.get('image', None) if not url: continue self.download(i, title, url) QThread.msleep(200) QThread.yieldCurrentThread() except Exception as e: AppLog.exception(e) Signals.pictureDownFinished.emit(self.widget)
def loadCursor(cls, widget, name='default.png'): # 加载光标 path = cls.cursorPath(name) AppLog.info('cursorPath: {}'.format(path)) if os.path.exists(path): # 设置自定义鼠标样式,并以0,0为原点 widget.setCursor(QCursor(QPixmap(path), 0, 0))
def loadColourfulTheme(cls, color, widget=None, replaces={}): """基于当前设置主题颜色 :param cls: :param color: 背景颜色 :param widget: 指定控件 """ # 加载主题取样式 path = cls.stylePath('Default') AppLog.info('stylePath: {}'.format(path)) try: styleSheet = open(path, 'rb').read().decode( 'utf-8', errors='ignore') # 需要替换部分样式 colorstr = GradientUtils.styleSheetCode(color) if isinstance(color, QLinearGradient) or isinstance(color, QRadialGradient) or isinstance(color, QConicalGradient): color = color.stops()[0][1] # 替换name templates = StyleGradientTemplate for name, value in replaces.items(): templates = templates.replace(name, value) styleSheet += templates.format( color.red(), color.green(), color.blue(), colorstr) widget = widget or QApplication.instance() widget.setStyleSheet(styleSheet) except Exception as e: AppLog.exception(e)
def renderReadme(self, path=''): """加载README.md并显示 """ path = path.replace('\\', '/') if not path: path = os.path.join(Constants.DirProjects, 'README.md') Constants.CurrentReadme = '' elif path.count('/') == 0: path = os.path.join(Constants.DirCurrent, path, 'README.md') Constants.CurrentReadme = path elif not path.endswith('README.md'): path = path + '/README.md' Constants.CurrentReadme = path if not os.path.exists(path): AppLog.debug('{} not exists'.format(path)) self._runJs('updateText("");') return if not os.path.isfile(path): AppLog.warn('file {} not exists'.format(path)) return Constants.DirCurrent = os.path.dirname(path).replace('\\', '/') AppLog.debug('DirCurrent change to: {}'.format(Constants.DirCurrent)) AppLog.debug('render: {}'.format(path)) Constants.CurrentReadme = path # 记录打开的路径防止重复加载 AppLog.debug('readme dir: {}'.format(Constants.DirCurrent)) content = repr(open(path, 'rb').read().decode()) self._runJs("updateText({});".format(content))
def _showWebMenu(self, pos): """显示网页右键菜单 :param pos: 点击位置 """ hit = self.webViewContent.page().currentFrame().hitTestContent(pos) url = hit.linkUrl() if url.isValid(): path = url.toLocalFile().strip().replace('\\', '/') names = path.split('Markdown/') if len(names) == 1: return path = os.path.abspath(os.path.join( Constants.DirCurrent, names[1])) AppLog.debug('path: {}'.format(path)) AppLog.debug('isdir: {}'.format(os.path.isdir(path))) self._webviewactRun.setData(path) self._webviewactView.setData(path) self._webviewactFolder.setData(path) if os.path.exists(path) and os.path.isdir(path): self._webviewactRun.setVisible(False) self._webviewactView.setVisible(False) self._webviewactFolder.setVisible(True) elif os.path.exists(path) and os.path.isfile(path): self._webviewactRun.setVisible(True) self._webviewactView.setVisible(True) self._webviewactFolder.setVisible(True) self._webviewMenu.exec_(QCursor.pos())
def initCatalog(self): """初始化本地仓库结构树 """ AppLog.debug('') if not os.path.exists(Constants.DirProjects): return pitem = self._dmodel.invisibleRootItem() # 只遍历根目录 for name in os.listdir(Constants.DirProjects): file = os.path.join(Constants.DirProjects, name).replace('\\', '/') if os.path.isfile(file): # 跳过文件 continue if name.startswith( '.') or name == 'Donate' or name == 'Test': # 不显示.开头的文件夹 continue items = self.findItems(name) if items: item = items[0] else: item = QStandardItem(name) # 添加自定义的数据 # 用于绘制进度条的item标识 item.setData(True, Constants.RoleRoot) # 目录或者文件的绝对路径 item.setData( os.path.abspath(os.path.join(Constants.DirProjects, name)), Constants.RolePath) pitem.appendRow(item) # 遍历子目录 self.listSubDir(item, file) # 排序 self._fmodel.sort(0, Qt.AscendingOrder)
def onLoginErrored(self, message): AppLog.debug('onLoginErrored') self._isLogin = False self.buttonLogin.showWaiting(False) self.setEnabled(True) AppLog.error(message) if message: self.labelNotice.setText(message)
def run(self): AppLog.info('start get all colourful') # 午夜巴黎 mcolor = QLinearGradient(0, 0, self.width, self.height) mcolor.ex = 1 mcolor.ey = 1 mcolor.startColor = QColor(20, 179, 255, 255) mcolor.endColor = QColor(226, 14, 255, 255) mcolor.setColorAt(0, mcolor.startColor) mcolor.setColorAt(1, mcolor.endColor) # 樱草青葱 pcolor = QLinearGradient(0, 0, self.width, self.height) pcolor.ex = 1 pcolor.ey = 1 pcolor.startColor = QColor(0, 173, 246, 255) pcolor.endColor = QColor(0, 234, 155, 255) pcolor.setColorAt(0, pcolor.startColor) pcolor.setColorAt(1, pcolor.endColor) # 秋日暖阳 acolor = QLinearGradient(0, 0, self.width, self.height) acolor.ex = 1 acolor.ey = 1 acolor.startColor = QColor(255, 128, 27, 255) acolor.endColor = QColor(255, 0, 14, 255) acolor.setColorAt(0, acolor.startColor) acolor.setColorAt(1, acolor.endColor) defaults = splistList( [ [self.tr('MidnightParis'), mcolor], # 午夜巴黎 [self.tr('PrimroseGreenOnion'), pcolor], # 樱草青葱 [self.tr('AutumnSun'), acolor], # 秋日暖阳 [self.tr('LightGray'), QColor(236, 236, 236)], # 淡灰色 [self.tr('DarkBlack'), QColor(33, 33, 33)], # 深黑色 [self.tr('BlueGreen'), QColor(0, 190, 172)], # 蓝绿色 [self.tr('Orange'), QColor(255, 152, 0)], # 橙色 [self.tr('Brown'), QColor(140, 100, 80)], # 咖啡色 [self.tr('Green'), QColor(121, 190, 60)], # 绿色 [self.tr('Pink'), QColor(236, 98, 161)], # 粉色 [self.tr('Purple'), QColor(103, 58, 183)], # 紫色 [self.tr('Blue'), QColor(0, 188, 212)], # 蓝色 [self.tr('GreyBlue'), QColor(80, 126, 164)], # 蓝灰色 [self.tr('Red'), QColor(244, 94, 99)], # 红色 ], 5) for row, default in enumerate(defaults): for col, (name, color) in enumerate(default): Signals.colourfulItemAdded.emit(row, col, name, color) QThread.msleep(100) QThread.yieldCurrentThread() Signals.colourfulItemAddFinished.emit() AppLog.info('colourful thread end')
def _initLanguage(self): """加载国际化翻译 """ if QLocale.system().language() in (QLocale.China, QLocale.Chinese, QLocale.Taiwan, QLocale.HongKong): # 加载中文 translator = QTranslator(self) translator.load('Resources/pyqtclient_zh_CN.qm') QApplication.instance().installTranslator(translator) AppLog.info('install local language')
def loadFont(cls): """加载字体 """ ThemeManager.ThemeName = Setting.value('theme', 'Default', str) # 加载主题中的字体 path = cls.fontPath() AppLog.info('fontPath: {}'.format(path)) if os.path.isfile(path): QFontDatabase.addApplicationFont(path)
def _doActView(self): """右键菜单查看代码 """ path = self.sender().data() try: code = open(path, 'rb').read().decode(errors='ignore') Signals.showCoded.emit(code) except Exception as e: AppLog.warn(str(e))
def onReadChannelFinished(self): process = self.sender() message = process.readAllStandardError().data().decode() if process.exitCode() != 0 and len(message.strip()) > 0: file = str(process.property('file')) reqfile = os.path.abspath(os.path.join( os.path.dirname(file), 'requirements.txt')) AppLog.debug('reqfile: {}'.format(reqfile)) dialog = ErrorDialog(message, self, reqfile=reqfile) dialog.exec_()
def createRequest(self, op, originalReq, outgoingData): """创建请求 :param op: 操作类型见http://doc.qt.io/qt-5/qnetworkaccessmanager.html#Operation-enum :param originalReq: 原始请求 :param outgoingData: 输出数据 """ url = originalReq.url() surl = url.toString() AppLog.debug('access url: {}'.format(surl)) if surl.endswith('Donate'): # 点击了打赏 originalReq.setUrl(QUrl()) return super(NetworkAccessManager, self).createRequest(op, originalReq, outgoingData) elif surl.endswith('k=5QVVEdF'): # 点击了QQ群链接 webbrowser.open(Constants.UrlGroup) originalReq.setUrl(QUrl()) return super(NetworkAccessManager, self).createRequest(op, originalReq, outgoingData) if url.scheme() == 'tencent': # 调用tx的app webbrowser.open(surl) originalReq.setUrl(QUrl()) elif url.scheme() == 'file': # 本地文件,比如一些图片文件等 names = surl.split('Markdown/') if len(names) > 1: rname = names[1] path = os.path.join(Constants.DirCurrent, rname).replace('\\', '/') if os.path.exists(path) and os.path.isfile(path): if rname[-3:] == '.py': originalReq.setUrl(QUrl()) # 运行py文件 Signals.runExampled.emit(path) else: originalReq.setUrl(QUrl.fromLocalFile(path)) elif os.path.exists(path) and os.path.isdir(path): if rname.count('/') == 0: # 跳转到左侧目录树 originalReq.setUrl(QUrl()) Signals.itemJumped.emit(rname) else: # 只加载文件,不加载其它网页 if not mimetypes.guess_type(url.fileName())[0]: originalReq.setUrl(QUrl()) # 调用系统打开网页 webbrowser.open_new_tab(surl) return super(NetworkAccessManager, self).createRequest(op, originalReq, outgoingData)
def onDoubleClicked(self, modelIndex): """Item双击 :param modelIndex: 此处是代理模型中的QModelIndex, 并不是真实的 """ root = modelIndex.data(Constants.RoleRoot) path = modelIndex.data(Constants.RolePath) AppLog.debug('is root: {}'.format(root)) AppLog.debug('path: {}'.format(path)) if not root and os.path.isfile(path): # 运行代码 Signals.runExampled.emit(path)
def start(cls, parent=None): """启动自动更新线程 :param cls: """ cls._thread = QThread(parent) cls._worker = UpgradeThread() cls._worker.moveToThread(cls._thread) cls._thread.started.connect(cls._worker.run) cls._thread.finished.connect(cls._worker.deleteLater) cls._thread.start() AppLog.info('update thread started')
def loadCursor(cls, widget, name='default.png'): # 加载光标 path = cls.cursorPath(name) if path in ThemeManager.Cursors: widget.setCursor(ThemeManager.Cursors[path]) return AppLog.info('cursorPath: {}'.format(path)) if os.path.exists(path): # 设置自定义鼠标样式,并以0,0为原点 cur = QCursor(QPixmap(path), 0, 0) ThemeManager.Cursors[path] = cur widget.setCursor(cur)
def start(cls, account, password, parent=None): """启动登录线程 :param cls: :param account: 账号 :param password: 密码 """ cls._thread = QThread(parent) cls._worker = LoginThread(account, password) cls._worker.moveToThread(cls._thread) cls._thread.started.connect(cls._worker.run) cls._thread.finished.connect(cls._worker.deleteLater) cls._thread.start() AppLog.info('login thread started')
def run(self): show = True try: req = requests.get(self.Url) AppLog.info(req.text) if req.status_code != 200: AppLog.info('update thread end') UpgradeThread.quit() return content = req.json() for version, text in content: if Version.version < version: if show: Signals.updateDialogShowed.emit() QThread.msleep(1000) show = False Signals.updateTextChanged.emit( str(Version.version), str(version), text) self.download(Constants.UpgradeFile.format( version), self.ZipUrl.format(version)) Signals.updateFinished.emit(self.tr('update completed')) except Exception as e: Signals.updateFinished.emit( self.tr('update failed: {}').format(str(e))) AppLog.exception(e) AppLog.info('update thread end') UpgradeThread.quit()
def start(cls, width, height, parent=None): """启动线程 :param cls: :param width: :param height: :param parent: """ cls._thread = QThread(parent) cls._worker = ColourfulThread(width, height) cls._worker.moveToThread(cls._thread) cls._thread.started.connect(cls._worker.run) cls._thread.finished.connect(cls._worker.deleteLater) cls._thread.start() AppLog.info('colourful thread started')
def download(self, index, title, url): try: dirPath = os.path.join(DirWallpaper, self.category) os.makedirs(dirPath, exist_ok=True) path = os.path.join(dirPath, os.path.basename(url)) if os.path.exists(path) and os.path.isfile(path): Signals.pictureItemAdded.emit(self.widget, index, title, path) return req = requests.get(url, headers=Headers) if req.status_code == 200: with open(path, 'wb') as fp: fp.write(req.content) Signals.pictureItemAdded.emit(self.widget, index, title, path) except Exception as e: AppLog.exception(e)
def run(self): AppLog.info('start get all theme') defaults = [[p.parent.name, str(p)] for p in Path(DirThemes).rglob('style.qss')] defaults = splistList(defaults, 5) for row, default in enumerate(defaults): for col, (name, path) in enumerate(default): Signals.themeItemAdded.emit(row, col, name, path) QThread.msleep(100) QThread.yieldCurrentThread() Signals.themeItemAddFinished.emit() AppLog.info('theme thread end')
def on_lineEditAccount_textChanged(self, account): """输入框编辑完成信号,寻找头像文件是否存在 """ if account not in self._accounts: # 不存在 return # 填充密码 try: self.lineEditPassword.setText(base64.b85decode( self._accounts[account][1].encode()).decode()) except Exception as e: self.lineEditPassword.setText('') AppLog.exception(e) # 更新头像 path = os.path.join(Constants.ImageDir, self._accounts[account][0]).replace( '\\', '/') + '.jpg' if os.path.exists(path) and self.buttonHead.image != path: # 更换头像 self.buttonHead.image = path
def _initUi(self): """初始UI""" self.setupUi(self) # 隐藏还原按钮 self.buttonNormal.setVisible(False) # 隐藏目录树的滑动条 self.treeViewCatalogs.verticalScrollBar().setVisible(False) # 加载鼠标样式 ThemeManager.loadCursor(self.widgetMain) ThemeManager.setPointerCursors([ self.buttonHead, # 主界面头像 self.buttonClear, # 主界面清空按钮 self.buttonGithub, # Github按钮 self.buttonQQ, # QQ按钮 self.buttonGroup, # 群按钮 self.buttonBackToUp, # 返回顶部按钮 self.buttonHome # 显示主页readme ]) # 安装事件过滤器用于还原鼠标样式 self.widgetMain.installEventFilter(self) # 绑定返回顶部提示框 ToolTip.bind(self.buttonBackToUp) ToolTip.bind(self.buttonHome) # 头像提示控件 ToolTip.bind(self.buttonHead) # 加载主题 colourful = Setting.value('colourful') picture = Setting.value('picture', '', str) AppLog.debug('colourful: %s', str(colourful)) AppLog.debug('picture: %s', picture) if picture: ThemeManager.loadFont() ThemeManager.loadPictureTheme(picture) elif colourful: ThemeManager.loadFont() if isinstance(picture, QColor): ThemeManager.loadColourfulTheme(colourful) else: # json数据转渐变 ThemeManager.loadColourfulTheme( GradientUtils.toGradient(colourful)) else: ThemeManager.loadTheme()
def onClicked(self, modelIndex): """Item单击 :param modelIndex: 此处是代理模型中的QModelIndex, 并不是真实的 """ root = modelIndex.data(Constants.RoleRoot) path = modelIndex.data(Constants.RolePath) code = modelIndex.data(Constants.RoleCode) AppLog.debug('is root: {}'.format(root)) AppLog.debug('path: {}'.format(path)) if not root and os.path.isfile(path) and code: # 右侧显示代码 Signals.showCoded.emit(code) if root and os.path.isdir(path): if self.isExpanded(modelIndex): self.collapse(modelIndex) else: self.expand(modelIndex) # 显示readme Signals.showReadmed.emit(os.path.join(path, 'README.md'))
def onLoginSuccessed(self, uid, name): AppLog.debug('onLoginSuccessed') self._isLogin = False self.buttonLogin.showWaiting(False) self.setEnabled(True) # 用账号密码实例化github访问对象 account = self.lineEditAccount.text().strip() password = self.lineEditPassword.text().strip() Constants._Account = account Constants._Password = password Constants._Username = name # 储存账号密码 Setting.setValue('account', account) if account not in self._accounts: # 更新账号数组 self._accounts[account] = [ uid, base64.b85encode(password.encode()).decode()] Setting.setValue('accounts', self._accounts) self.accept()
def loadPictureTheme(cls, image=None, widget=None, replaces={}): """设置图片背景的主题 :param cls: :param image: 背景图片 :param widget: 指定控件 """ # 加载主题取样式 path = cls.stylePath('Default') AppLog.info('stylePath: {}'.format(path)) try: styleSheet = open(path, 'rb').read().decode( 'utf-8', errors='ignore') # 需要替换部分样式 if image and os.path.isfile(image): # 获取图片主色调 color_thief = ColorThief(image) color = color_thief.get_color() AppLog.info('dominant color: {}'.format(str(color))) # 替换name templates = StylePictureTemplate for name, value in replaces.items(): templates = templates.replace(name, value) styleSheet += templates.format(os.path.abspath( image).replace('\\', '/')) + StyleColorTemplate.format(*color) widget = widget or QApplication.instance() widget.setStyleSheet(styleSheet) except Exception as e: AppLog.exception(e)
def loadTheme(cls): """根据配置加载主题 :param cls: :param parent: """ cls.ThemeName = Setting.value('theme', 'Default', str) # 加载主题中的字体 path = cls.fontPath() AppLog.info('fontPath: {}'.format(path)) if os.path.exists(path): QFontDatabase.addApplicationFont(path) # 加载主题取样式 path = cls.stylePath() AppLog.info('stylePath: {}'.format(path)) try: QApplication.instance().setStyleSheet( open(path, 'rb').read().decode('utf-8', errors='ignore')) except Exception as e: AppLog.error(str(e))
def download(self, file, url): AppLog.debug('start download {}'.format(url)) with closing(requests.get(url, stream=True)) as response: # 单次请求最大值 chunk_size = 1024 # 内容体总大小 content_size = int(response.headers['content-length']) data_count = 0 Signals.updateProgressChanged.emit(0, 0, content_size) AppLog.debug('content_size: {}'.format(content_size)) with open(file, 'wb') as fp: for data in response.iter_content(chunk_size=chunk_size): fp.write(data) data_count = data_count + len(data) if content_size > 0: Signals.updateProgressChanged.emit( data_count, 0, content_size) # 解压 self.unzip(file) AppLog.debug('download {} end'.format(file))