def _update_checkboxes(self, item: QTreeWidgetItem, column: int): if column != 0: return new_check_state = item.checkState(0) self._set_check_state_to_tree(item, new_check_state) while True: item = item.parent() if item is None: break has_checked_children = False has_partially_checked_children = False has_unchecked_children = False for i in range(item.childCount()): state = item.child(i).checkState(0) if state == Qt.Checked: has_checked_children = True elif state == Qt.PartiallyChecked: has_partially_checked_children = True else: has_unchecked_children = True if not has_partially_checked_children and not has_unchecked_children: new_state = Qt.Checked elif has_checked_children or has_partially_checked_children: new_state = Qt.PartiallyChecked else: new_state = Qt.Unchecked item.setCheckState(0, new_state) self._update_selection_label()
def update(self, *, network: Network, servers: dict, use_tor: bool): self.clear() # connected servers connected_servers_item = QTreeWidgetItem([_("Connected nodes"), '']) connected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL) chains = network.get_blockchains() n_chains = len(chains) for chain_id, interfaces in chains.items(): b = blockchain.blockchains.get(chain_id) if b is None: continue name = b.get_name() if n_chains > 1: x = QTreeWidgetItem( [name + '@%d' % b.get_max_forkpoint(), '%d' % b.height()]) x.setData(0, self.ITEMTYPE_ROLE, self.ItemType.CHAIN) x.setData(0, self.CHAIN_ID_ROLE, b.get_id()) else: x = connected_servers_item for i in interfaces: star = ' *' if i == network.interface else '' item = QTreeWidgetItem( [f"{i.server.net_addr_str()}" + star, '%d' % i.tip]) item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.CONNECTED_SERVER) item.setData(0, self.SERVER_ADDR_ROLE, i.server) item.setToolTip(0, str(i.server)) x.addChild(item) if n_chains > 1: connected_servers_item.addChild(x) # disconnected servers disconnected_servers_item = QTreeWidgetItem( [_("Other known servers"), ""]) disconnected_servers_item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.TOPLEVEL) connected_hosts = set( [iface.host for ifaces in chains.values() for iface in ifaces]) protocol = PREFERRED_NETWORK_PROTOCOL for _host, d in sorted(servers.items()): if _host in connected_hosts: continue if _host.endswith('.onion') and not use_tor: continue port = d.get(protocol) if port: server = ServerAddr(_host, port, protocol=protocol) item = QTreeWidgetItem([server.net_addr_str(), ""]) item.setData(0, self.ITEMTYPE_ROLE, self.ItemType.DISCONNECTED_SERVER) item.setData(0, self.SERVER_ADDR_ROLE, server) disconnected_servers_item.addChild(item) self.addTopLevelItem(connected_servers_item) self.addTopLevelItem(disconnected_servers_item) connected_servers_item.setExpanded(True) for i in range(connected_servers_item.childCount()): connected_servers_item.child(i).setExpanded(True) disconnected_servers_item.setExpanded(True) # headers h = self.header() h.setStretchLastSection(False) h.setSectionResizeMode(0, QHeaderView.Stretch) h.setSectionResizeMode(1, QHeaderView.ResizeToContents) super().update()
def addOverlaysToTreeWidget(self, overlayDict, forbiddenOverlays, preSelectedOverlays, singleOverlaySelection): self.singleOverlaySelection = singleOverlaySelection testItem = QTreeWidgetItem("a") for keys in list(overlayDict.keys()): if overlayDict[keys] in forbiddenOverlays: continue else: boolStat = False split = keys.split("/") for i in range(len(split)): if len(split) == 1: newItemsChild = OverlayTreeWidgetItem( overlayDict[keys], keys) self.addTopLevelItem(newItemsChild) boolStat = False if overlayDict[keys] in preSelectedOverlays: newItemsChild.setCheckState(0, Qt.Checked) else: newItemsChild.setCheckState(0, Qt.Unchecked) elif i + 1 == len(split) and len(split) > 1: newItemsChild = OverlayTreeWidgetItem( overlayDict[keys], keys) testItem.addChild(newItemsChild) if overlayDict[keys] in preSelectedOverlays: newItemsChild.setCheckState(0, Qt.Checked) else: newItemsChild.setCheckState(0, Qt.Unchecked) elif self.topLevelItemCount() == 0 and i + 1 < len(split): newItem = QTreeWidgetItem([split[i]]) self.addTopLevelItem(newItem) testItem = newItem boolStat = True elif self.topLevelItemCount() != 0 and i + 1 < len(split): if boolStat == False: for n in range(self.topLevelItemCount()): if self.topLevelItem(n).text(0) == split[i]: testItem = self.topLevelItem(n) boolStat = True break elif n + 1 == self.topLevelItemCount(): newItem = QTreeWidgetItem([split[i]]) self.addTopLevelItem(newItem) testItem = newItem boolStat = True elif testItem.childCount() == 0: newItem = QTreeWidgetItem([split[i]]) testItem.addChild(newItem) testItem = newItem boolStat = True else: for x in range(testItem.childCount()): if testItem.child(x).text(0) == split[i]: testItem = testItem.child(x) boolStat = True break elif x + 1 == testItem.childCount(): newItem = QTreeWidgetItem([split[i]]) testItem.addChild(newItem) testItem = newItem boolStat = True
class ClientUI(QWidget): def __init__(self): super().__init__() # self.window = QWidget() self.resize(1000, 600) self.setWindowTitle("拍一拍聊天室") # 聊天窗口 self.messageBrowser = QTextBrowser() # 字体选择 self.fontComboBox = QFontComboBox() self.fontComboBox.setObjectName("fontComboBox") self.fontComboBox.currentFontChanged.connect( self.on_fontComboBox_currentFontChanged) # 字体大小 self.sizeComboBox = QComboBox() self.sizeComboBox.setObjectName("SizeComboBox") self.sizeComboBox.setCurrentIndex(0) for i in range(31): self.sizeComboBox.addItem("") _translate = QCoreApplication.translate self.sizeComboBox.setCurrentText(_translate("Widget", "10")) for i in range(31): self.sizeComboBox.setItemText(i, _translate("Widget", str(i + 10))) self.sizeComboBox.currentIndexChanged.connect( self.on_SizeComboBox_currentIndexChanged) # 加粗 self.boldToolBtn = QToolButton() self.boldToolBtn.setContextMenuPolicy(Qt.DefaultContextMenu) icon = QIcon() root = QFileInfo(__file__).absolutePath() # 获取根目录 icon.addPixmap(QPixmap(root + "/image/bold.png"), QIcon.Normal, QIcon.Off) self.boldToolBtn.setIcon(icon) self.boldToolBtn.setIconSize(QSize(22, 22)) self.boldToolBtn.setCheckable(True) self.boldToolBtn.setAutoRaise(True) self.boldToolBtn.setObjectName("boldToolBtn") self.boldToolBtn.setToolTip(_translate("Widget", "加粗")) self.boldToolBtn.setText(_translate("Widget", "...")) self.boldToolBtn.clicked.connect(self.on_boldToolBtn_clicked) # 斜体 self.italicToolBtn = QToolButton() icon1 = QIcon() icon1.addPixmap(QPixmap(root + "/image/italic.png"), QIcon.Normal, QIcon.Off) self.italicToolBtn.setIcon(icon1) self.italicToolBtn.setIconSize(QSize(22, 22)) self.italicToolBtn.setCheckable(True) self.italicToolBtn.setAutoRaise(True) self.italicToolBtn.setObjectName("italicToolBtn") self.italicToolBtn.setToolTip(_translate("Widget", "倾斜")) self.italicToolBtn.setText(_translate("Widget", "...")) self.italicToolBtn.clicked.connect(self.on_italicToolBtn_clicked) # 下划线 self.underlineToolBtn = QToolButton() icon2 = QIcon() icon2.addPixmap(QPixmap(root + "/image/under.png"), QIcon.Normal, QIcon.Off) self.underlineToolBtn.setIcon(icon2) self.underlineToolBtn.setIconSize(QSize(22, 22)) self.underlineToolBtn.setCheckable(True) self.underlineToolBtn.setAutoRaise(True) self.underlineToolBtn.setObjectName("underlineToolBtn") self.underlineToolBtn.setToolTip(_translate("Widget", "下划线")) self.underlineToolBtn.setText(_translate("Widget", "...")) self.underlineToolBtn.clicked.connect(self.on_underlineToolBtn_clicked) # 颜色 self.colorToolBtn = QToolButton() icon3 = QIcon() icon3.addPixmap(QPixmap(root + "/image/color.png"), QIcon.Normal, QIcon.Off) self.colorToolBtn.setIcon(icon3) self.colorToolBtn.setIconSize(QSize(22, 22)) self.colorToolBtn.setAutoRaise(True) self.colorToolBtn.setObjectName("colorToolBtn") self.colorToolBtn.setToolTip(_translate("Widget", "更改字体颜色")) self.colorToolBtn.setText(_translate("Widget", "...")) self.colorToolBtn.clicked.connect(self.on_colorToolBtn_clicked) # 聊天记录 self.saveToolBtn = QToolButton() icon4 = QIcon() icon4.addPixmap(QPixmap(root + "/image/save.png"), QIcon.Normal, QIcon.Off) self.saveToolBtn.setIcon(icon4) self.saveToolBtn.setIconSize(QSize(22, 22)) self.saveToolBtn.setAutoRaise(True) self.saveToolBtn.setObjectName("saveToolBtn") self.saveToolBtn.setToolTip(_translate("Widget", "保存聊天记录")) self.saveToolBtn.setText(_translate("Widget", "...")) self.saveToolBtn.clicked.connect(self.on_saveToolBtn_clicked) self.clearToolBtn = QToolButton() icon5 = QIcon() icon5.addPixmap(QPixmap(root + "/image/clear.png"), QIcon.Normal, QIcon.Off) self.clearToolBtn.setIcon(icon5) self.clearToolBtn.setIconSize(QSize(22, 22)) self.clearToolBtn.setAutoRaise(True) self.clearToolBtn.setObjectName("clearToolBtn") self.clearToolBtn.setToolTip(_translate("Widget", "清空聊天记录")) self.clearToolBtn.setText(_translate("Widget", "...")) self.clearToolBtn.clicked.connect(self.on_clearToolBtn_clicked) # 发送按钮 self.sendButton = QPushButton('发送') self.sendButton.clicked.connect(self.sendChatMsg) # 发送功能横向布局 functionboxLayout = QHBoxLayout() functionboxLayout.addWidget(self.fontComboBox) functionboxLayout.addWidget(self.sizeComboBox) functionboxLayout.addWidget(self.boldToolBtn) functionboxLayout.addWidget(self.italicToolBtn) functionboxLayout.addWidget(self.underlineToolBtn) functionboxLayout.addWidget(self.colorToolBtn) functionboxLayout.addWidget(self.saveToolBtn) functionboxLayout.addWidget(self.clearToolBtn) functionboxLayout.addStretch(1) functionboxLayout.addWidget(self.sendButton) # 输入框 self.messageEdit = QTextEdit() # 左侧竖向布局,三行 vhoxLayout_left = QVBoxLayout() vhoxLayout_left.addWidget(self.messageBrowser) vhoxLayout_left.addLayout(functionboxLayout) vhoxLayout_left.addWidget(self.messageEdit) vhoxLayout_left.setStretch(0, 4) vhoxLayout_left.setStretch(1, 1) vhoxLayout_left.setStretch(2, 2) # 在线用户列表 self.userView = QTreeWidget() self.userView.setHeaderLabels(["用户列表"]) self.userView_online_node = QTreeWidgetItem(self.userView) self.userView_online_node.setText(0, "在线用户") self.userView_all_node = QTreeWidgetItem(self.userView) self.userView_all_node.setText(0, "所有用户") self.userView.setContextMenuPolicy(Qt.CustomContextMenu) self.userView.customContextMenuRequested.connect(self.userView_menu) # 公共栏Label self.announcement_label = QLabel() self.announcement_label.setText("公告栏") # 公告栏 self.announcement_edit = QTextEdit() self.announcement_edit.setEnabled(False) # 左侧竖向布局,一整块 vhoxLayout_right = QVBoxLayout() vhoxLayout_right.addWidget(self.userView) vhoxLayout_right.addWidget(self.announcement_label) vhoxLayout_right.addWidget(self.announcement_edit) # 最大布局,横向两列 hboxLayout = QHBoxLayout(self) hboxLayout.addLayout(vhoxLayout_left) hboxLayout.addLayout(vhoxLayout_right) hboxLayout.setStretch(0, 3) hboxLayout.setStretch(1, 1) self.show() def update_admin_UI(self): self.announcement_edit.setEnabled(True) self.announcement_edit.setContextMenuPolicy(Qt.CustomContextMenu) self.announcement_edit.customContextMenuRequested.connect( lambda: handler.announcement(self.announcement_edit.toPlainText())) def userView_menu(self): item = self.userView.currentItem() menu = QMenu(self.userView) if item.parent().text(0) == '在线用户': menu.addAction('拍一拍').triggered.connect( lambda: handler.pai_yi_pai(myname, item.text(0))) elif admin: menu.addAction('删除').triggered.connect( lambda: handler.delete_user(item.text(0))) menu.exec_(QCursor.pos()) def mergeFormatDocumentOrSelection(self, format): cursor = self.messageEdit.textCursor() if not cursor.hasSelection(): cursor.select(QTextCursor.Document) cursor.mergeCharFormat(format) self.messageEdit.mergeCurrentCharFormat(format) def on_fontComboBox_currentFontChanged(self, p0): fmt = QTextCharFormat() fmt.setFont(p0) # fmt.setFontFamily(p0) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() # 字体大小 def on_SizeComboBox_currentIndexChanged(self, p0): fmt = QTextCharFormat() p0 += 10 fmt.setFontPointSize(p0) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() def on_boldToolBtn_clicked(self, checked): fmt = QTextCharFormat() fmt.setFontWeight(checked and QFont.Bold or QFont.Normal) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() def on_italicToolBtn_clicked(self, checked): fmt = QTextCharFormat() fmt.setFontItalic(checked) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() def on_underlineToolBtn_clicked(self, checked): fmt = QTextCharFormat() fmt.setFontUnderline(checked) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() def on_colorToolBtn_clicked(self): col = QColorDialog.getColor(self.messageEdit.textColor(), self) if not col.isValid(): return fmt = QTextCharFormat() fmt.setForeground(col) self.mergeFormatDocumentOrSelection(fmt) self.messageEdit.setFocus() def on_saveToolBtn_clicked(self): print(self.messageBrowser.document().isEmpty()) if self.messageBrowser.document().isEmpty(): QMessageBox.warning(self, "警告", "聊天记录为空,无法保存!", QMessageBox.Ok) else: fileName = QFileDialog.getSaveFileName(self, "保存聊天记录", "./聊天记录", ("HTML-Files (*.html)")) if fileName[0]: if self.saveFile(fileName[0]): QMessageBox.information(self, "聊天记录保存", "保存成功!") def saveFile(self, fileName): SuffixFileName = fileName.split(".")[1] if SuffixFileName in ("html"): content = self.messageBrowser.toHtml() else: content = self.messageBrowser.toPlainText() try: with codecs.open(fileName, 'w', encoding="utf-8") as f: f.write(content) return True except IOError: QMessageBox.critical(self, "保存错误", "聊天记录保存失败!") return False def on_clearToolBtn_clicked(self): self.messageBrowser.clear() def getMessage(self): msg = self.messageEdit.toHtml() self.messageEdit.clear() self.messageEdit.setFocus() return msg def sendChatMsg(self): sendMsg(ALL_MSG, self.getMessage(), myname) self.messageEdit.clear() def showMsg(self, msg, name): time = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss") if name == '拍拍': self.messageBrowser.setTextColor(Qt.darkGreen) self.messageBrowser.append("[" + name + "] " + time) self.messageBrowser.append(msg) elif name != myname: self.messageBrowser.setTextColor(Qt.blue) self.messageBrowser.append("[" + name + "] " + time) self.messageBrowser.append(msg) else: self.messageBrowser.setTextColor(Qt.red) self.messageBrowser.append("[我] " + time) self.messageBrowser.append(msg) def addUserNode(self, group, name): self.userView.expandAll() newUser = QTreeWidgetItem(group) newUser.setText(0, name) newUser.setIcon(0, QIcon('image/user.jpg')) if (name == myname): newUser.setBackground(0, QColor(255, 0, 0, 100)) if name == '拍拍': newUser.setBackground(0, QColor(0, 255, 0, 100)) def removeUserNode(self, name): n = self.userView_online_node.childCount() for i in range(n): if self.userView_online_node.child(i).text(0) == name: self.userView_online_node.removeChild( self.userView_online_node.child(i)) break def removeAllNode(self, name): n = self.userView_all_node.childCount() for i in range(n): if self.userView_all_node.child(i).text(0) == name: self.userView_all_node.removeChild( self.userView_all_node.child(i)) break def userJoin(self, name): self.messageBrowser.setTextColor(Qt.gray) self.messageBrowser.append('系统消息:' + name + '上线了') self.addUserNode(self.userView_online_node, name) def userLeft(self, name): self.messageBrowser.setTextColor(Qt.gray) self.messageBrowser.append('系统消息:' + name + '离开了') self.removeUserNode(name) def pai_yi_pai(self, name, toname): self.messageBrowser.setTextColor(Qt.gray) self.messageBrowser.append(name + ' 拍了拍 ' + toname) if toname == myname: app.alert(self, 2000) def setHandler(self, handler): self.handler = handler def handler(self, msg): global admin type = msg[0] if type == ALL_MSG: self.showMsg(msg[1], msg[2]) elif type == USR_JOIN: self.userJoin(msg[1]) elif type == USR_LEFT: self.userLeft(msg[1]) elif type == USR_LOGIN: admin = msg[2] if admin: self.update_admin_UI() elif type == ALL_ONLINE_USR: for i in msg[1]: self.userJoin(i) self.announcement_edit.setText(msg[2]) for i in msg[3]: self.addUserNode(self.userView_all_node, i) elif type == PAI_YI_PAI: self.pai_yi_pai(msg[1], msg[2]) elif type == ANNOUNCEMENT: self.announcement_edit.setText(msg[1]) elif type == DELETE_USR: self.removeUserNode(msg[1]) self.removeAllNode(msg[1])
class myGUI(QMainWindow): signal_loginSuccess = QtCore.pyqtSignal() signal_startShowTree = QtCore.pyqtSignal() signal_setStartBackupBtn = QtCore.pyqtSignal(str, bool) signal_showUserOptionWindow = QtCore.pyqtSignal() signal_showDevOptionWindow = QtCore.pyqtSignal() signal_close = QtCore.pyqtSignal() signal_appendDownloadList = QtCore.pyqtSignal(dict) signal_processbar_value = QtCore.pyqtSignal(int) signal_startUpdate = QtCore.pyqtSignal(str) def __init__(self): super().__init__() self.config = ConfigParser() self.pool = threadpool.ThreadPool(4) self.DownloadPool = threadpool.ThreadPool(1) self.readSetting() string.setLanguage(self.config['User']['language']) self.version = 1.21 self.host = 'https://ilearn2.fcu.edu.tw' self.statusbar = self.statusBar() self.initUI() self.web = iLearnManager(self.host) self.init_iLearn() self.FileTree = {} self.success = 0 self.failed = 0 self.fileList = [] self.retryList = [] self.failedList = [] self.retryTimes = 0 self.nowLoad = 0 self.retryAfter = 0 self.initCheckUpdate = False self.retryTimer = QtCore.QTimer() self.retryTimer.timeout.connect(self.startRetry) self.signal_loginSuccess.connect(self.ShowResource) self.signal_appendDownloadList.connect(self.appendItemToDownloadList) self.signal_processbar_value.connect(self.setProcessBarValue) self.signal_startShowTree.connect(self.startShowTree) self.signal_setStartBackupBtn.connect(self.setStartBackupBtn) self.timer_checkUpdate = QtCore.QTimer() self.timer_checkUpdate.timeout.connect(self.checkUpdate) self.timer_checkUpdate.start(1000) if self.config['dev'].getboolean('autologin') == True: self.btn_login.click() def closeEvent(self, event): self.signal_close.emit() self.close() def setStatusBarText(self, str): self.statusbar.showMessage(str) def init_iLearn(self): self.web.signal_finishDownload.connect(self.startDownload) self.web.signal_setStatusProcessBar.connect(self.setStatusProcessBar) self.web.signal_Log.connect(self.print) self.web.signal_setStatusBarText.connect(self.setStatusBarText) t = Thread(target=self.TestiLearnConnection) t.run() def setStartBackupBtn(self, text, enabled): self.btn_StartBackup.setEnabled(enabled) self.btn_StartBackup.setText(text) def setStatusProcessBar(self, idx, value): if value == -1: ProcessBar = QProgressBar() self.StatusTable.setCellWidget(idx, 3, ProcessBar) elif value == -2: if self.retryTimes == 0: self.failed += 1 self.failedList.append(idx) self.StatusTable.removeCellWidget(idx, 3) # 移除進度條之控件 ErrorIcon = QIcon(":img/DownloadFailed.png") # 開啟下載失敗之圖案 item = QTableWidgetItem(ErrorIcon, string._('Download Falied')) # 新增顯示失敗的元件 self.StatusTable.setItem(idx, 3, item) # 將新元件設定到表格內 if idx == len(self.fileList) - 1: self.signal_processbar_value.emit(idx + 1) self.finishDownloadCheck(idx) elif value == 101: if self.retryTimes != 0: self.failed -= 1 self.success += 1 self.print(string._('Download file %d finish!') % (idx + 1)) self.StatusTable.removeCellWidget(idx, 3) OkIcon = QIcon(":img/FinishDownload.png") # 開啟下載完成之圖案 item = QTableWidgetItem(OkIcon, "OK") # 新增顯示OK的元件 self.StatusTable.setItem(idx, 3, item) # 將新元件設定到表格內 self.finishDownloadCheck(idx) else: ProcessBar = self.StatusTable.cellWidget(idx, 3) if ProcessBar == None: ProcessBar = QProgressBar() self.StatusTable.setCellWidget(idx, 3, ProcessBar) ProcessBar.setValue(value) def finishDownloadCheck(self, idx): def checkIsEndElement(idx): if self.retryTimes == 0: return idx == len(self.fileList) - 1 else: return idx == self.retryList[-1] def backupFinish(): self.signal_processbar_value.emit(self.statusProcessBar.maximum()) self.signal_setStartBackupBtn.emit(string._('Start Backup'), True) QMessageBox.information( self, string._("Download finish!"), string._("Success:%d\nFailed:%d") % (self.success, self.failed)) if checkIsEndElement(idx): if self.retryTimes == self.config['User'].getint('retrytimes'): backupFinish() else: self.signal_processbar_value.emit( self.statusProcessBar.maximum()) if self.failed == 0: backupFinish() else: self.retryAfter = self.config['User'].getint( 'secondbetweenretry') self.retryTimer.start(1000) def checkUpdate(self): try: self.timer_checkUpdate.stop() except: pass with open('version.ini', mode='w') as f: f.write(str(self.version)) s = requests.Session() versionFile = s.get( 'https://raw.githubusercontent.com/fcu-d0441320/iLearnBackupTool/master/version.ini' ) version = float(versionFile.text) if version > self.version: reply = QMessageBox.question( self, string._('Find New version'), string. _('Find New Version:%.1f\nNow Vsrsion:%.1f\nDo you want to update?' ) % (version, self.version), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.setVisible(False) self.signal_startUpdate.emit(self.config['User']['language']) else: if self.initCheckUpdate == False: self.initCheckUpdate = True else: QMessageBox.information(self, string._('This is the latest version'), string._('This is the latest version')) # QMessageBox().information(self,"有更新版本!","發現有新版本,請前往官網更新,或檢查是否與Updater_GUI.exe放置於相同資料夾!") def moveToCenter(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def initUI(self): self.resize(800, 600) self.setWindowIcon(QIcon(':img/Main_Icon.png')) self.moveToCenter() self.setWindowTitle(string._('iLearn Backup Tool')) self.statusbar.showMessage(string._('Starting Backup Tool...')) self.createMenu() self.statusProcessBar = QProgressBar() self.statusProcessBar.setValue(0) self.statusProcessBar.setFormat(string._("Ready...")) self.statusbar.addPermanentWidget(self.statusProcessBar) self.grid = QGridLayout() widget = QWidget() self.setCentralWidget(widget) widget.setLayout(self.grid) self.grid.addWidget(self.createLoginGroup(), 0, 0) self.grid.addWidget(self.createSaveGroup(), 1, 0) self.grid.addWidget(self.createCourseGroup(), 0, 1, 2, 1) self.grid.addWidget(self.createStatusView(), 2, 0, 1, 2) self.grid.setColumnStretch(0, 10) self.grid.setColumnStretch(1, 20) self.grid.setRowStretch(0, 6) self.grid.setRowStretch(1, 6) self.grid.setRowStretch(2, 10) self.web = iLearnManager() self.show() def createStatusTable(self): self.StatusTable = QTableWidget() self.StatusTable.setColumnCount(4) horizontal_header = [ string._('File name'), string._('Path'), string._('iLearn mod'), string._('Download status') ] self.StatusTable.setHorizontalHeaderLabels(horizontal_header) self.StatusTable.setEditTriggers(QTableWidget.NoEditTriggers) self.StatusTable.setColumnWidth(0, 140) self.StatusTable.setColumnWidth(1, 120) self.StatusTable.setColumnWidth(2, 120) self.StatusTable.setColumnWidth(3, 380) return self.StatusTable def createLogSpace(self): self.LogSpace = QPlainTextEdit() self.LogSpace.setReadOnly(True) return self.LogSpace def print(self, msg): self.LogSpace.appendPlainText( time.strftime("[%H:%M:%S] ", time.localtime()) + msg) def createStatusView(self): tabs = QTabWidget() tabs.addTab(self.createStatusTable(), string._('Backup status')) tabs.addTab(self.createLogSpace(), string._('Log')) return tabs def createLoginGroup(self): groupBox = QGroupBox(string._('Step 1: Login')) form = QGridLayout() label_NID = QLabel(string._('NID:')) self.input_NID = QLineEdit() self.input_NID.setText(self.config['dev']['nid']) label_Pass = QLabel(string._('Password:'******'dev']['pass']) self.input_Pass.setEchoMode(QLineEdit.Password) form.addWidget(label_NID, 0, 0) form.addWidget(self.input_NID, 0, 1, 1, 2) form.addWidget(label_Pass, 1, 0) form.addWidget(self.input_Pass, 1, 1, 1, 2) self.btn_clean = QPushButton(string._('Clean'), self) self.btn_clean.clicked[bool].connect(self.cleanLogin) form.addWidget(self.btn_clean, 2, 1) self.btn_login = QPushButton(string._('Login'), self) self.btn_login.clicked[bool].connect(self.Login) form.addWidget(self.btn_login, 2, 2) label_iLearnStatus = QLabel(string._('iLearn status:')) form.addWidget(label_iLearnStatus, 3, 0) self.label_iLearn = QLabel() form.addWidget(self.label_iLearn, 3, 1, 1, 2) vbox = QVBoxLayout() vbox.addLayout(form) vbox.addStretch(1) groupBox.setLayout(vbox) return groupBox def cleanLogin(self): self.input_NID.setText("") self.input_Pass.setText("") def createSaveGroup(self): groupBox = QGroupBox(string._('Step 3:Select save option')) radio1 = QRadioButton(string._('Save as file')) radio1.setChecked(True) radio2 = QRadioButton(string._('Save as web page')) radio2.setEnabled(False) self.btn_StartBackup = QPushButton(string._('Start Backup'), self) self.btn_StartBackup.setEnabled(False) self.btn_StartBackup.clicked[bool].connect(self.StartBackup) self.btn_OpenFolder = QPushButton(string._('Open Folder'), self) self.btn_OpenFolder.clicked[bool].connect(self.OpenFolder) vbox = QVBoxLayout() vbox.addWidget(radio1) vbox.addWidget(radio2) vbox.addWidget(self.btn_StartBackup) vbox.addWidget(self.btn_OpenFolder) vbox.addStretch(1) groupBox.setLayout(vbox) if not exists('iLearn'): makedirs('iLearn') return groupBox def OpenFolder(self): subprocess.Popen(['explorer', "iLearn"]) def createCourseGroup(self): groupBox = QGroupBox( string._('Step 2:Select sourse resource to backup')) self.CourseListBox = QVBoxLayout() self.CourseListBox.addStretch(1) self.CourseTreeList = QTreeWidget() self.CourseTreeList.setHeaderHidden(True) self.CourseTreeList.setEnabled(False) self.CourseTreeList.itemExpanded.connect(self.ExpandCourse) self.CourseTreeListRoot = QTreeWidgetItem(self.CourseTreeList) self.CourseTreeListRoot.setText(0, string._("All course")) self.CourseTreeListRoot.setFlags(self.CourseTreeListRoot.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) self.CourseTreeListRoot.setCheckState(0, QtCore.Qt.Unchecked) HLayout = QHBoxLayout() HLayout.addWidget(self.CourseTreeList) groupBox.setLayout(HLayout) return groupBox def createMenu(self): menubar = self.menuBar() fileMenu = menubar.addMenu(string._('File')) closeAct = QAction(QIcon(':img/Close_Icon.png'), string._('Quit'), self) closeAct.triggered.connect(qApp.quit) fileMenu.addAction(closeAct) optMenu = menubar.addMenu(string._('Option')) DevOptAct = QAction(QIcon(':img/Settings_Icon.png'), string._('Developer options'), self) DevOptAct.triggered.connect(self.showDevOption) UserOptionAction = QAction(QIcon(':img/Settings_Icon.png'), string._('Preferences'), self) UserOptionAction.triggered.connect(self.showUserOption) optMenu.addAction(UserOptionAction) optMenu.addAction(DevOptAct) helpMenu = menubar.addMenu(string._('Help')) helpAct = QAction(QIcon(':img/Help_Icon.png'), string._('Help'), self) helpAct.triggered.connect(self.showHelp) aboutAct = QAction(QIcon(':img/About_Icon.png'), string._('About'), self) aboutAct.triggered.connect(self.showInformation) checkUpdateAct = QAction(QIcon(':img/Update_Icon.png'), string._('Check update'), self) checkUpdateAct.triggered.connect(self.checkUpdate) helpMenu.addAction(helpAct) helpMenu.addAction(checkUpdateAct) helpMenu.addAction(aboutAct) def showHelp(self): subprocess.Popen( ["explorer", "http://github.com/fcu-d0441320/iLearnBackupTool"]) def showUserOption(self): self.signal_showUserOptionWindow.emit() def showDevOption(self): self.signal_showDevOptionWindow.emit() def Login(self): t = Thread(target=self.__Login) t.run() def __Login(self): self.web.setUser(self.input_NID.text(), self.input_Pass.text()) self.label_iLearn.setText( string._('User %s is signing in...') % self.input_NID.text()) self.print( string._('User %s is signing in...') % self.input_NID.text()) status, UserName = self.web.Login() if status: # == True self.statusbar.showMessage(string._('Sign in success')) self.label_iLearn.setText( string._('%s sign in sucess') % (UserName)) self.print(string._('%s sign in sucess') % (UserName)) self.input_Pass.setEnabled(False) self.input_NID.setEnabled(False) self.btn_login.setEnabled(False) self.btn_clean.setEnabled(False) self.signal_loginSuccess.emit() else: self.statusbar.showMessage(string._('Sign in failed')) self.label_iLearn.setText(string._('Sign in failed')) self.print(UserName + string._('Sign in failed')) self.nowLoad = 0 def ShowResource(self): self.CourseTreeList.setEnabled(True) self.courseList = self.web.getCourseList() for course in self.courseList: courseItem = QTreeWidgetItem(self.CourseTreeListRoot) self.CourseTreeListRoot.setExpanded(True) courseItem.setFlags(courseItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) courseItem.setText(0, course['title']) courseItem.setCheckState(0, QtCore.Qt.Unchecked) courseItem.setIcon(0, QIcon(':img/mod.course.jpg')) child = QTreeWidgetItem(courseItem) child.setFlags(courseItem.flags() | QtCore.Qt.ItemIsUserCheckable) child.setText(0, string._('Loading...')) child.setCheckState(0, QtCore.Qt.Unchecked) self.startBackgroundLoad() def ExpandCourse(self, courseItem): if courseItem.child(0).text(0) == string._('Loading...'): i = 0 for ele in self.courseList: if ele['title'] == courseItem.text(0): break else: i += 1 self.timer = QtCore.QTimer() # 计时器 self.timer.timeout.connect( partial(self.appedResourceToTree, i, courseItem)) self.timer.start(10) def startBackgroundLoad(self): if self.nowLoad < len(self.courseList): self.statusProcessBar.setMaximum(len(self.courseList)) self.statusProcessBar.setFormat( string._('Loding course resource') + '(%v/' + '%d)' % (len(self.courseList))) reqs = threadpool.makeRequests(self.loadFileTreeBackground, range(len(self.courseList))) for req in reqs: self.pool.putRequest(req) def setProcessBarValue(self, value): if value == self.statusProcessBar.maximum(): self.statusProcessBar.setFormat(string._('Ready...')) self.statusProcessBar.setMaximum(100) self.statusProcessBar.setValue(0) else: self.statusProcessBar.setValue(value) def startShowTree(self): for i, courseItem in [ (i, self.CourseTreeListRoot.child(i)) for i in range(self.CourseTreeListRoot.childCount()) ]: if courseItem.child(0).text(0) == string._('Loading...'): self.appedResourceToTree(i, courseItem) def loadFileTreeBackground(self, index): self.signal_processbar_value.emit(index) if self.courseList[index]['title'] not in self.FileTree: FileList = self.web.getCourseFileList( self.courseList[index], useRealFileName=self.config['User'].getboolean( 'userealfilename'), showTime=self.config['dev'].getboolean('showloadtime')) if self.courseList[index]['title'] not in self.FileTree: self.FileTree[self.courseList[index]['title']] = FileList if index == len(self.courseList) - 1: self.signal_processbar_value.emit(len(self.courseList)) self.signal_setStartBackupBtn.emit(string._('Start Backup'), True) self.signal_startShowTree.emit() def appedResourceToTree(self, i, courseItem): course = self.courseList[i] try: self.timer.stop() except: pass tStart = time.time() if course['title'] not in self.FileTree: courseFileList = self.web.getCourseFileList( course, useRealFileName=self.config['User'].getboolean( 'userealfilename'), showTime=self.config['dev'].getboolean('showloadtime')) if course['title'] not in self.FileTree: self.FileTree[course['title']] = courseFileList self.nowLoad += 1 else: courseFileList = self.FileTree[course['title']] checkStatus = courseItem.checkState(0) totalFiles = 0 if len(courseFileList) == 0: sectionItem = QTreeWidgetItem(courseItem) sectionItem.setFlags(sectionItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) sectionItem.setText(0, string._('There has no resource to download.')) sectionItem.setCheckState(0, checkStatus) for section in courseFileList: sectionItem = QTreeWidgetItem(courseItem) sectionItem.setFlags(sectionItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) sectionItem.setText(0, section['section']) sectionItem.setCheckState(0, checkStatus) sectionItem.setIcon(0, QIcon(":img/mod.folder.svg")) for recource in section['mods']: if recource['mod'] == 'forum': forumItem = QTreeWidgetItem(sectionItem) forumItem.setFlags(forumItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) forumItem.setText(0, recource['name']) forumItem.setCheckState(0, checkStatus) forumItem.setIcon(0, QIcon(":img/mod.discuss.svg")) for topic in recource['data']: topicItem = QTreeWidgetItem(forumItem) topicItem.setFlags(topicItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) topicItem.setText(0, topic['name']) topicItem.setCheckState(0, checkStatus) topicItem.setIcon(0, QIcon(":img/mod.discuss.svg")) totalFiles += 1 elif recource['mod'] in [ 'url', 'resource', 'assign', 'page', 'videos' ]: recourceItem = QTreeWidgetItem(sectionItem) recourceItem.setFlags(recourceItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) recourceItem.setText(0, recource['name']) recourceItem.setCheckState(0, checkStatus) recourceItem.setIcon( 0, QIcon(":img/mod." + recource['mod'] + ".svg")) totalFiles += 1 elif recource['mod'] == 'folder': folderItem = QTreeWidgetItem(sectionItem) folderItem.setFlags(folderItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) folderItem.setText(0, recource['name']) folderItem.setCheckState(0, checkStatus) folderItem.setIcon(0, QIcon(":img/mod.folder.svg")) for file in recource['data']: fileItem = QTreeWidgetItem(folderItem) fileItem.setFlags(fileItem.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) fileItem.setText(0, file['name']) fileItem.setCheckState(0, checkStatus) fileItem.setIcon(0, QIcon(":img/mod.resource.svg")) totalFiles += 1 courseItem.removeChild(courseItem.child(0)) tStop = time.time() if self.config['dev'].getboolean('showloadtime'): self.print( string._( 'Load course %s in %.3f sec, total has %d resource(s)') % (courseItem.text(0), tStop - tStart, totalFiles)) def showFileList(self): self.btn_StartBackup.setEnabled(False) courseIndex = 0 for courseItem in [ self.CourseTreeListRoot.child(i) for i in range(self.CourseTreeListRoot.childCount()) ]: courseIndex += 1 self.signal_processbar_value.emit(courseIndex) courseData = self.FileTree[courseItem.text(0)] if courseItem.checkState(0) != QtCore.Qt.Unchecked: for sectionItem in [ courseItem.child(i) for i in range(courseItem.childCount()) ]: sectionItemName = sectionItem.text(0) if sectionItemName == string._( 'There has no resource to download.'): continue sectionData = \ [courseData[i] for i in range(len(courseData)) if courseData[i]['section'] == sectionItemName][0] if sectionItem.checkState(0) != QtCore.Qt.Unchecked: for modItem in [ sectionItem.child(i) for i in range(sectionItem.childCount()) ]: modItemName = modItem.text(0) modData = [ sectionData['mods'][i] for i in range(len(sectionData['mods'])) if sectionData['mods'][i]['name'] == modItemName ][0] if modItem.checkState(0) != QtCore.Qt.Unchecked: if modData['mod'] == 'forum': for topicItem in [ modItem.child(i) for i in range( modItem.childCount()) ]: if topicItem.checkState( 0) == QtCore.Qt.Checked: topicName = topicItem.text(0) resource = [ modData['data'][i] for i in range( len(modData['data'])) if modData['data'][i]['name'] == topicName ][0] self.signal_appendDownloadList.emit( resource) elif modData['mod'] in [ 'resource', 'url', 'assign', 'page', 'videos' ]: if modItem.checkState( 0) == QtCore.Qt.Checked: self.signal_appendDownloadList.emit( modData) elif modData['mod'] == 'folder': for fileItem in [ modItem.child(i) for i in range( modItem.childCount()) ]: if fileItem.checkState( 0) == QtCore.Qt.Checked: fileName = fileItem.text(0) resource = [ modData['data'][i] for i in range( len(modData['data'])) if modData['data'][i]['name'] == fileName ][0] self.signal_appendDownloadList.emit( resource) self.retryTimes = 0 self.failedList = [] self.retryList = [] self.success = 0 self.failed = 0 time.sleep(0.5) reqs = threadpool.makeRequests(self.startDownload, range(len(self.fileList))) for req in reqs: self.DownloadPool.putRequest(req) if len(self.fileList) == 0: self.btn_StartBackup.setEnabled(True) else: self.statusProcessBar.setFormat( string._('Downloading...') + "(%v/" + "%d)" % len(self.fileList)) self.statusProcessBar.setMaximum(len(self.fileList)) def startRetry(self): if self.retryAfter == 0: self.retryTimes += 1 self.retryTimer.stop() self.retryList = self.failedList self.failedList = [] reqs = threadpool.makeRequests(self.startDownload, self.retryList) for req in reqs: self.DownloadPool.putRequest(req) self.statusProcessBar.setFormat( string._('Downloading...') + "(%v/" + "%d)" % len(self.retryList)) self.statusProcessBar.setMaximum(len(self.retryList)) else: self.retryAfter -= 1 self.signal_setStartBackupBtn.emit( string._('Download will retry after %d sec.') % self.retryAfter, False) def appendItemToDownloadList(self, Item): self.fileList.append(Item) mod = Item['mod'] if '/' in mod: mod = mod.split('/')[1] row_count = self.StatusTable.rowCount() self.StatusTable.insertRow(row_count) self.StatusTable.setItem(row_count, 0, QTableWidgetItem(Item['name'])) self.StatusTable.setItem(row_count, 1, QTableWidgetItem(Item['path'])) self.StatusTable.setItem( row_count, 2, QTableWidgetItem(QIcon(':img/mod.%s.svg' % mod), Item['mod'])) self.StatusTable.setItem(row_count, 3, QTableWidgetItem('等待中...')) def startDownload(self, idx): if idx < len(self.fileList): self.btn_StartBackup.setText( string._('Downloading...(%d/%d)') % (idx, len(self.fileList))) if self.retryTimes != 0: indexInRetryList = 0 for ele in self.retryList: indexInRetryList += 1 if ele == idx: break self.btn_StartBackup.setText( string._('Downloading...(%d/%d)') % (indexInRetryList, len(self.retryList))) self.signal_processbar_value.emit(idx) self.print(string._('Start to download %dth file') % (idx + 1)) self.web.DownloadFile(idx, self.fileList[idx]) time.sleep(0.5) def StartBackup(self): self.fileList = [] self.StatusTable.setRowCount(0) self.statusProcessBar.setFormat( string._('Loading file list') + "(%v" + "/%d)" % self.CourseTreeListRoot.childCount()) self.statusProcessBar.setMaximum(self.CourseTreeListRoot.childCount()) self.showFileList() def showInformation(self): QMessageBox.about(self, string._('About'), string._('tool Information') % self.version) def TestiLearnConnection(self): self.statusbar.showMessage( string._('Testing connection with iLearn2...')) self.label_iLearn.setText(string._('Connecting...')) if self.web.TestConnection(): # ==Ture self.statusbar.showMessage(string._('Connect to iLearn2 success!')) self.label_iLearn.setText(string._('Connect success!')) else: self.statusbar.showMessage(string._('Can not connect to iLearn2!')) self.label_iLearn.setText(string._('Connect failed!')) def readSetting(self): try: self.config.read('setting.ini', encoding='utf-8') OPTION = self.config.get('User', 'userealfilename') OPTION = self.config.get('User', 'language') OPTION = self.config.get('User', 'retrytimes') OPTION = self.config.get('User', 'secondbetweenretry') OPTION = self.config.get('dev', 'nid') OPTION = self.config.get('dev', 'pass') OPTION = self.config.get('dev', 'showloadtime') except: self.config['User'] = {} self.config['User']['userealfilename'] = 'False' self.config['User']['language'] = '繁體中文' self.config['dev'] = {} self.config['dev']['nid'] = '' self.config['dev']['pass'] = '' self.config['dev']['autologin'] = '******' self.config['dev']['showloadtime'] = 'False' self.config['User']['retrytimes'] = '3' self.config['User']['secondbetweenretry'] = '5' with open('setting.ini', 'w', encoding='utf-8') as configfile: self.config.write(configfile) def restart(self): subprocess.Popen("iLearnBackupTool") self.close()
def _set_check_state_to_tree(self, item: QTreeWidgetItem, check_state: Qt.CheckState): for i in range(item.childCount()): child = item.child(i) child.setCheckState(0, check_state) self._set_check_state_to_tree(child, check_state)
class OutTab(QWidget): def __init__(self, parent=None): super(OutTab, self).__init__(parent) lableFilePath = QLabel("物料文件:") self.editFilePath = QLineEdit() self.buttonImport = QPushButton("导入") self.buttonLocate = QPushButton("定位") self.buttonOut = QPushButton("出库") lableSapIDList = QLabel("物料列表:") self.tree = QTreeWidget() self.tree.setColumnCount(3) self.tree.setHeaderLabels(['物料ID','标签ID','状态']) self.buttonImport.clicked.connect(self.importSapIDs) self.buttonLocate.clicked.connect(self.Locate) self.buttonOut.clicked.connect(self.removeID) mainLayout = QGridLayout() mainLayout.addWidget(lableFilePath, 0, 0) mainLayout.addWidget(self.editFilePath, 0, 1) mainLayout.addWidget(self.buttonImport, 0, 2) mainLayout.addWidget(lableSapIDList, 1, 0, Qt.AlignTop) mainLayout.addWidget(self.buttonLocate, 1, 2, Qt.AlignTop) mainLayout.addWidget(self.buttonOut, 2, 2, Qt.AlignTop) mainLayout.addWidget(self.tree, 1, 1, 9, 1 ) self.setLayout(mainLayout) #self.finder = Controler.Controler() #从指定的物料清单文件中解析出物料编码列表 def GetSapIDs(self,strFilePath): listID = [] with open(strFilePath, 'r') as f: strAll = f.read() listTemp = strAll.split('\n')[3:-2] for x in listTemp: strID = re.split(r'\s+', x)[2] # strID = "".join(list(filter(str.isdigit, strID))) listID.append(strID) return listID #打开物料清单文件,解析后显示在树形控件中 def importSapIDs(self): options = QFileDialog.Options() fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", self.editFilePath.text(), "Text Files (*.txt)", options=options) if not fileName: return self.editFilePath.setText(fileName) listID = self.GetSapIDs(fileName) iLength = len(listID) feeders = Feeders.Feeders() self.tree.clear() self.root = QTreeWidgetItem(self.tree) self.root.setText(0, fileName.split('/')[-1]) for i in range(iLength): SapID = listID[i] RfID = feeders.findFeeder(SapID) id = QTreeWidgetItem(self.root) id.setText(0, SapID) id.setText(1, RfID) id.setText(2, 'N/A') id.setCheckState(0, 0) self.tree.expandAll() self.tree.resizeColumnToContents(0) self.root.sortChildren(1, 0) self.AutoSelect() def AutoSelect(self): for i in range(self.root.childCount()): child = self.root.child(i) if child.text(1) != 'N/A': child.setCheckState(0, 2) def Locate(self): for i in range(self.root.childCount()): child = self.root.child(i) if child.checkState(0): if child.text(1) == 'N/A': child.setText(2, '无法定位') else: child.setText(2, '已定位') child.setSelected(True) QApplication.processEvents() strRfID = child.text(1) #self.finder.FindID(strRfID) time.sleep(0.2) child.setSelected(False) child.setText(2, '待定位') def removeID(self): flag = True while flag == True: flag = False for i in range(self.root.childCount()): child = self.root.child(i) if child.checkState(0): feeders = Feeders.Feeders() strRfID = child.text(1) #self.finder.FindID(strRfID, False) self.root.removeChild(child) feeders.remove(strRfID) flag = True break self.AutoSelect()
def add_children(self, top_lvl_wid: QTreeWidgetItem): frmMain = self.frmMain model = self.enh_model idx_NAME = self.get_header_index(HEADER_NAME) idx_GEAR_TYPE = self.get_header_index(HEADER_GEAR_TYPE) idx_BASE_ITEM_COST = self.get_header_index(HEADER_BASE_ITEM_COST) idx_TARGET = self.get_header_index(HEADER_TARGET) master_gw = self.itemWidget(top_lvl_wid, idx_NAME) this_gear = master_gw.gear these_lvls = this_gear.guess_target_lvls(intersect=None, excludes=None) prunes = [] for i in range(0, top_lvl_wid.childCount()): child = top_lvl_wid.child(0) child_gw: GearWidget = self.itemWidget(child, idx_NAME) top_lvl_wid.takeChild(0) if not child_gw.chkInclude.isChecked(): prunes.append(child_gw.gear.enhance_lvl) try: child_gw.chkInclude.disconnect() except TypeError: pass def chk_click(state): spinner = self.sender() lvl = spinner.__dict__['lvl'] if state == Qt.Unchecked: try: this_gear.target_lvls.remove(lvl) except ValueError: pass else: if lvl not in this_gear.target_lvls: this_gear.target_lvls.append(lvl) #self.model.invalidate_enahce_list() for lvl in these_lvls: twi = QTreeWidgetItem(top_lvl_wid, [''] * self.columnCount()) _gear = this_gear.duplicate() _gear.set_enhance_lvl(lvl) this_check_state = Qt.Unchecked if lvl in prunes or lvl not in this_gear.target_lvls else Qt.Checked this_gw = GearWidget(_gear, model, edit_able=False, display_full_name=False, check_state=this_check_state) this_gw.sig_error.connect(self.frmMain.sig_show_message) this_gw.sig_layout_changed.connect( lambda: self.resizeColumnToContents(0)) this_gw.chkInclude.__dict__['lvl'] = lvl this_gw.chkInclude.stateChanged.connect(chk_click) self.setItemWidget(twi, idx_NAME, this_gw) top_lvl_wid.addChild(twi) gt_txt = master_gw.cmbType.currentText() twi.setText(idx_GEAR_TYPE, gt_txt) twi.setText(idx_BASE_ITEM_COST, top_lvl_wid.text(2)) twi.setText(idx_TARGET, _gear.enhance_lvl) twi.setForeground(idx_GEAR_TYPE, Qt.black) twi.setBackground(idx_GEAR_TYPE, gt_str_to_q_color(gt_txt).lighter())
class DeviceDialog(QDialog, Ui_DeviceDialog): """ Function and Event handling class for the Ui_DeviceDialog. """ qtcb_enumerate = pyqtSignal(str, str, str, type((0,)), type((0,)), int, int) qtcb_connected = pyqtSignal(int) def __init__(self, parent): QDialog.__init__(self, parent, get_modeless_dialog_flags()) self._logger_window = parent self.device_name_by_device_identifier = {} for display_name, device_spec in device_specs.items(): self.device_name_by_device_identifier[device_spec['class'].DEVICE_IDENTIFIER] = display_name self.qtcb_enumerate.connect(self.cb_enumerate) self.qtcb_connected.connect(self.cb_connected) self.host = None self.port = None self.secret = None self.ipcon = IPConnection() self.ipcon.register_callback(IPConnection.CALLBACK_CONNECTED, self.qtcb_connected.emit) self.ipcon.register_callback(IPConnection.CALLBACK_ENUMERATE, self.qtcb_enumerate.emit) self.setupUi(self) self.btn_add_device.clicked.connect(self.btn_add_device_clicked) self.btn_refresh.clicked.connect(self.btn_refresh_clicked) self.btn_close.clicked.connect(self.btn_close_clicked) self.tree_widget.itemActivated.connect(self.add_item) self.connected_uids = [] self.available_item = QTreeWidgetItem(['No devices available']) self.supported_item = QTreeWidgetItem(['Supported devices']) self.tree_widget.addTopLevelItem(self.available_item) self.tree_widget.addTopLevelItem(self.supported_item) for device_name in device_specs: self.supported_item.addChild(QTreeWidgetItem([device_name])) self.supported_item.sortChildren(0, Qt.AscendingOrder) self.supported_item.setExpanded(True) def cb_connected(self, connect_reason): self.tree_widget.clearSelection() self.available_item.takeChildren() self.available_item.setExpanded(True) self.available_item.setText(0, 'No devices available at {0}:{1}'.format(self.host, self.port)) self.connected_uids = [] if self.secret != None: self.ipcon.set_auto_reconnect(False) # don't auto-reconnect on authentication error try: self.ipcon.authenticate(self.secret) except: try: self.ipcon.disconnect() except: pass if connect_reason == IPConnection.CONNECT_REASON_AUTO_RECONNECT: extra = ' after auto-reconnect' else: extra = '' self.available_item.setText(0, 'Could not authenticate' + extra) return self.ipcon.set_auto_reconnect(True) try: self.ipcon.enumerate() except: pass def cb_enumerate(self, uid, connected_uid, position, hardware_version, firmware_version, device_identifier, enumeration_type): if enumeration_type in [IPConnection.ENUMERATION_TYPE_AVAILABLE, IPConnection.ENUMERATION_TYPE_CONNECTED]: if uid not in self.connected_uids: display_name = self.device_name_by_device_identifier.get(device_identifier) if display_name != None: self.connected_uids.append(uid) self.available_item.addChild(QTreeWidgetItem(['{0} [{1}]'.format(display_name, uid)])) self.available_item.setText(0, 'Devices available at {0}:{1}'.format(self.host, self.port)) self.available_item.sortChildren(0, Qt.AscendingOrder) else: if uid in self.connected_uids: self.connected_uids.remove(uid) for i in range(self.available_item.childCount()): child = self.available_item.child(i) if '[{0}]'.format(uid) in child.text(0): self.available_item.takeChild(i) break if self.available_item.childCount() == 0: self.available_item.setText(0, 'No devices available at {0}:{1}'.format(self.host, self.port)) def btn_add_device_clicked(self): for item in self.tree_widget.selectedItems(): if item == self.available_item or item == self.supported_item: continue self._logger_window.add_device_to_tree(self.create_device_config(item.text(0))) def btn_refresh_clicked(self): try: self.ipcon.disconnect() except: pass self.tree_widget.clearSelection() self.available_item.takeChildren() self.available_item.setExpanded(True) self.connected_uids = [] self.host = self._logger_window.combo_host.currentText() self.port = self._logger_window.spin_port.value() if self._logger_window.check_authentication.isChecked(): self.secret = self._logger_window.edit_secret.text() try: self.secret.encode('ascii') except: self.secret = None self.available_item.setText(0, 'Authentication secret cannot contain non-ASCII characters') return else: self.secret = None try: self.ipcon.connect(self.host, self.port) self.available_item.setText(0, 'No devices available at {0}:{1}'.format(self.host, self.port)) except: self.available_item.setText(0, 'Could not connect to {0}:{1}'.format(self.host, self.port)) def btn_close_clicked(self): self.close() def add_item(self, item): if item == self.available_item or item == self.supported_item: return self._logger_window.add_device_to_tree(self.create_device_config(item.text(0))) def create_device_config(self, item_text): name, uid = Utilities.parse_device_name(item_text) # FIXME device_spec = device_specs[name] if uid == None: # FIXME uid = '' device = { 'host': 'default', 'name': name, 'uid': uid, 'values': {}, 'options': {} } for value_spec in device_spec['values']: device['values'][value_spec['name']] = {'interval': 0} if value_spec['subvalues'] != None: device['values'][value_spec['name']]['subvalues'] = {} for subvalue_name in value_spec['subvalues']: device['values'][value_spec['name']]['subvalues'][subvalue_name] = True if device_spec['options'] != None: for option_spec in device_spec['options']: device['options'][option_spec['name']] = {'value': option_spec['default']} return device
class MainDialog(QDialog, FORM_CLASS): """The main dialog of QGIS2Web plugin.""" items = {} def __init__(self, iface, parent=None): super(MainDialog, self).__init__(parent) QDialog.__init__(self) self.setupUi(self) self.iface = iface self.previewUrl = None self.layer_search_combo = None self.exporter_combo = None self.feedback = FeedbackDialog(self) self.feedback.setModal(True) stgs = QSettings() self.restoreGeometry(stgs.value("qgis2web/MainDialogGeometry", QByteArray(), type=QByteArray)) self.verticalLayout_2.addStretch() self.horizontalLayout_6.addStretch() if stgs.value("qgis2web/previewOnStartup", Qt.Checked) == Qt.Checked: self.previewOnStartup.setCheckState(Qt.Checked) else: self.previewOnStartup.setCheckState(Qt.Unchecked) if (stgs.value("qgis2web/closeFeedbackOnSuccess", Qt.Checked) == Qt.Checked): self.closeFeedbackOnSuccess.setCheckState(Qt.Checked) else: self.closeFeedbackOnSuccess.setCheckState(Qt.Unchecked) self.previewFeatureLimit.setText( stgs.value("qgis2web/previewFeatureLimit", "1000")) self.appearanceParams.setSelectionMode( QAbstractItemView.SingleSelection) self.preview = None if webkit_available: widget = QWebView() self.preview = widget try: # if os.environ["TRAVIS"]: self.preview.setPage(WebPage()) except: print("Failed to set custom webpage") webview = self.preview.page() webview.setNetworkAccessManager(QgsNetworkAccessManager.instance()) self.preview.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) self.preview.settings().setAttribute( QWebSettings.DnsPrefetchEnabled, True) else: widget = QTextBrowser() widget.setText(self.tr('Preview is not available since QtWebKit ' 'dependency is missing on your system')) self.right_layout.insertWidget(0, widget) self.populateConfigParams(self) self.populate_layers_and_groups(self) self.populateLayerSearch() writer = WRITER_REGISTRY.createWriterFromProject() self.setStateToWriter(writer) self.exporter = EXPORTER_REGISTRY.createFromProject() self.exporter_combo.setCurrentIndex( self.exporter_combo.findText(self.exporter.name())) self.exporter_combo.currentIndexChanged.connect( self.exporterTypeChanged) self.toggleOptions() if webkit_available: if self.previewOnStartup.checkState() == Qt.Checked: self.autoUpdatePreview() self.buttonPreview.clicked.connect(self.previewMap) else: self.buttonPreview.setDisabled(True) QgsProject.instance().cleared.connect(self.reject) self.layersTree.model().dataChanged.connect(self.populateLayerSearch) self.ol3.clicked.connect(self.changeFormat) self.leaflet.clicked.connect(self.changeFormat) self.buttonExport.clicked.connect(self.saveMap) helpText = os.path.join(os.path.dirname(os.path.realpath(__file__)), "helpFile.md") self.helpField.setSource(QUrl.fromLocalFile(helpText)) if webkit_available: self.devConsole = QWebInspector(self.preview) self.devConsole.setFixedHeight(0) self.devConsole.setObjectName("devConsole") self.devConsole.setPage(self.preview.page()) self.devConsole.hide() self.right_layout.insertWidget(1, self.devConsole) self.filter = devToggleFilter() self.filter.devToggle.connect(self.showHideDevConsole) self.installEventFilter(self.filter) self.setModal(False) @pyqtSlot(bool) def showHideDevConsole(self, visible): self.devConsole.setVisible(visible) def changeFormat(self): self.autoUpdatePreview() self.toggleOptions() def exporterTypeChanged(self): new_exporter_name = self.exporter_combo.currentText() try: self.exporter = [ e for e in EXPORTER_REGISTRY.getExporters() if e.name() == new_exporter_name][0]() except: pass def currentMapFormat(self): """ Returns the currently selected map writer type """ return self.getWriterFactory().type() def getWriterFactory(self): """ Returns a factory to create the currently selected map writer """ if self.mapFormat.checkedButton() == self.ol3: return OpenLayersWriter elif self.mapFormat.checkedButton() == self.leaflet: return LeafletWriter def createWriter(self): """ Creates a writer object reflecting the current settings in the dialog """ writer = self.getWriterFactory()() (writer.layers, writer.groups, writer.popup, writer.visible, writer.json, writer.cluster, writer.getFeatureInfo) = self.getLayersAndGroups() writer.params = self.getParameters() return writer def showErrorMessage(self, error): """ Shows an error message in the preview window """ html = "<html>" html += "<head></head>" html += "<style>body {font-family: sans-serif;}</style>" html += "<body><h1>Error</h1>" html += "<p>qgis2web produced an error:</p><code>" html += error html += "</code></body></html>" if self.preview: self.preview.setHtml(html) def showFeedbackMessage(self, title, message): """ Shows a feedback message in the preview window """ html = "<html>" html += "<head></head>" html += "<style>body {font-family: sans-serif;}</style>" html += "<body><h1>{}</h1>".format(title) html += "<p>{}</p>".format(message) html += "</body></html>" if self.preview: self.preview.setHtml(html) def toggleOptions(self): currentWriter = self.getWriterFactory() for param, value in specificParams.items(): treeParam = self.appearanceParams.findItems(param, (Qt.MatchExactly | Qt.MatchRecursive))[0] if currentWriter == OpenLayersWriter: if value == "OL3": treeParam.setDisabled(False) else: treeParam.setDisabled(True) else: if value == "OL3": treeParam.setDisabled(True) else: treeParam.setDisabled(False) for option, value in specificOptions.items(): treeOptions = self.layersTree.findItems(option, (Qt.MatchExactly | Qt.MatchRecursive)) for treeOption in treeOptions: if currentWriter == OpenLayersWriter: if value == "OL3": treeOption.setDisabled(False) else: treeOption.setDisabled(True) else: if value == "OL3": treeOption.setDisabled(True) else: treeOption.setDisabled(False) def createPreview(self): writer = self.createWriter() return writer.write(self.iface, dest_folder=utils.tempFolder()).index_file def shouldAutoPreview(self): """ Returns a tuple, with a bool for whether the preview should automatically be generated, and a string for explanations as to why the preview cannot be automatically generated """ writer = self.createWriter() total_features = 0 for layer in writer.layers: if isinstance(layer, QgsVectorLayer): total_features += layer.featureCount() if total_features > int(self.previewFeatureLimit.text()): # Too many features => too slow! return (False, self.tr('<p>A large number of features are ' 'present in the map. Generating the ' 'preview may take some time.</p>' '<p>Click Update Preview to generate the ' 'preview anyway.</p>')) return (True, None) def autoUpdatePreview(self): """ Triggered when a preview will be automatically generated, i.e. not as a result of the user manually clicking the Update Preview button. """ (auto_preview, message) = self.shouldAutoPreview() if not auto_preview: self.showFeedbackMessage(self.tr('Preview Map'), message) else: self.previewMap() def previewMap(self): preview_file = self.createPreview() self.loadPreviewFile(preview_file) def saveMap(self): writer = self.createWriter() write_folder = self.exporter.exportDirectory() if not write_folder: return self.feedback.reset() self.feedback.show() results = writer.write(self.iface, dest_folder=write_folder, feedback=self.feedback) self.feedback.showFeedback('Success') if self.closeFeedbackOnSuccess.checkState() == Qt.Checked: self.feedback.close() result = self.exporter.postProcess(results, feedback=self.feedback) if result and (not os.environ.get('CI') and not os.environ.get('TRAVIS')): webbrowser.open_new_tab(self.exporter.destinationUrl()) def populate_layers_and_groups(self, dlg): """Populate layers on QGIS into our layers and group tree view.""" root_node = QgsProject.instance().layerTreeRoot() tree_groups = [] tree_layers = root_node.findLayers() self.layers_item = QTreeWidgetItem() self.layers_item.setText(0, "Layers and Groups") self.layersTree.setColumnCount(3) for tree_layer in tree_layers: layer = tree_layer.layer() if (layer.type() != QgsMapLayer.PluginLayer and layer.customProperty("ol_layer_type") is None): try: if layer.type() == QgsMapLayer.VectorLayer: testDump = layer.renderer().dump() layer_parent = tree_layer.parent() if layer_parent.parent() is None: item = TreeLayerItem(self.iface, layer, self.layersTree, dlg) self.layers_item.addChild(item) else: if layer_parent not in tree_groups: tree_groups.append(layer_parent) except: QgsMessageLog.logMessage(traceback.format_exc(), "qgis2web", level=Qgis.Critical) for tree_group in tree_groups: group_name = tree_group.name() group_layers = [ tree_layer.layer() for tree_layer in tree_group.findLayers()] item = TreeGroupItem(group_name, group_layers, self.layersTree) self.layers_item.addChild(item) self.layersTree.addTopLevelItem(self.layers_item) self.layersTree.expandAll() self.layersTree.resizeColumnToContents(0) self.layersTree.resizeColumnToContents(1) for i in range(self.layers_item.childCount()): item = self.layers_item.child(i) if item.checkState(0) != Qt.Checked: item.setExpanded(False) def populateLayerSearch(self): self.layer_search_combo.clear() self.layer_search_combo.addItem("None") (layers, groups, popup, visible, json, cluster, getFeatureInfo) = self.getLayersAndGroups() for count, layer in enumerate(layers): if layer.type() == layer.VectorLayer: options = [] fields = layer.fields() for f in fields: fieldIndex = fields.indexFromName(unicode(f.name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() if editorWidget == 'Hidden': continue options.append(unicode(f.name())) for option in options: displayStr = unicode(layer.name() + ": " + option) self.layer_search_combo.insertItem(0, displayStr) sln = utils.safeName(layer.name()) self.layer_search_combo.setItemData( self.layer_search_combo.findText(displayStr), sln + "_" + unicode(count)) def configureExporter(self): self.exporter.configure() def populateConfigParams(self, dlg): """ Populates the dialog with option items and widgets """ self.items = defaultdict(dict) tree = dlg.appearanceParams configure_export_action = QAction('...', self) configure_export_action.triggered.connect(self.configureExporter) params = getParams(configure_exporter_action=configure_export_action) for group, settings in params.items(): if group != "Data export": item = QTreeWidgetItem() item.setText(0, group) for param, value in settings.items(): subitem = self.createOptionItem(tree_widget=tree, parent_item=item, parameter=param, default_value=value) item.addChild(subitem) self.items[group][param] = subitem self.appearanceParams.addTopLevelItem(item) item.sortChildren(0, Qt.AscendingOrder) self.appearanceParams.expandAll() self.appearanceParams.resizeColumnToContents(0) self.appearanceParams.resizeColumnToContents(1) self.layer_search_combo.removeItem(1) # configure export params in separate tab exportTree = dlg.exportParams for group, settings in params.items(): if group == "Data export": item = QTreeWidgetItem() item.setText(0, group) for param, value in settings.items(): subitem = self.createOptionItem(tree_widget=exportTree, parent_item=item, parameter=param, default_value=value) item.addChild(subitem) self.items[group][param] = subitem self.exportParams.addTopLevelItem(item) item.sortChildren(0, Qt.AscendingOrder) self.exportParams.expandAll() self.exportParams.resizeColumnToContents(0) self.exportParams.resizeColumnToContents(1) def createOptionItem(self, tree_widget, parent_item, parameter, default_value): """create the tree item corresponding to an option parameter""" action = None if isinstance(default_value, dict): action = default_value['action'] default_value = default_value['option'] subitem = TreeSettingItem(parent_item, tree_widget, parameter, default_value, action) if parameter == 'Layer search': self.layer_search_combo = subitem.combo elif parameter == 'Exporter': self.exporter_combo = subitem.combo return subitem def setStateToWriter(self, writer): """ Sets the dialog state to match the specified writer """ self.selectMapFormat(writer) self.setStateToParams(writer.params) def setStateToParams(self, params): """ Sets the dialog state to match the specified parameters """ for group, settings in self.items.items(): for param, item in settings.items(): value = params[group][param] item.setValue(value) def selectMapFormat(self, writer): """ Updates dialog state to match the specified writer format """ self.ol3.setChecked(isinstance(writer, OpenLayersWriter)) self.leaflet.setChecked(isinstance(writer, LeafletWriter)) def loadPreviewFile(self, file): """ Loads a web based preview from a local file path """ self.previewUrl = QUrl.fromLocalFile(file) if self.preview: self.preview.settings().clearMemoryCaches() self.preview.setUrl(self.previewUrl) def getParameters(self): parameters = defaultdict(dict) for group, settings in self.items.items(): for param, item in settings.items(): if param in ('Widget Icon', 'Widget Background'): parameters[group][param] = item._value.color().name() else: parameters[group][param] = item.value() if param == "Layer search": parameters["Appearance"]["Search layer"] = ( self.layer_search_combo.itemData( self.layer_search_combo.currentIndex())) return parameters def saveParameters(self): """ Saves current dialog state to project """ WRITER_REGISTRY.saveWriterToProject(self.createWriter()) EXPORTER_REGISTRY.writeToProject(self.exporter) def getLayersAndGroups(self): layers = [] groups = {} popup = [] visible = [] json = [] cluster = [] getFeatureInfo = [] for i in range(self.layers_item.childCount()): item = self.layers_item.child(i) if isinstance(item, TreeLayerItem): if item.checkState(0) == Qt.Checked: layers.append(item.layer) popup.append(item.popup) visible.append(item.visible) json.append(item.json) cluster.append(item.cluster) getFeatureInfo.append(item.getFeatureInfo) else: group = item.name groupLayers = [] if item.checkState(0) != Qt.Checked: continue for layer in item.layers: groupLayers.append(layer) layers.append(layer) popup.append({}) if item.visible: visible.append(True) else: visible.append(False) if hasattr(item, "json") and item.json: json.append(True) else: json.append(False) if hasattr(item, "cluster") and item.cluster: cluster.append(True) else: cluster.append(False) if hasattr(item, "getFeatureInfo") and item.getFeatureInfo: getFeatureInfo.append(True) else: getFeatureInfo.append(False) groups[group] = groupLayers[::-1] return (layers[::-1], groups, popup[::-1], visible[::-1], json[::-1], cluster[::-1], getFeatureInfo[::-1]) def reject(self): self.saveParameters() (layers, groups, popup, visible, json, cluster, getFeatureInfo) = self.getLayersAndGroups() try: for layer, pop, vis in zip(layers, popup, visible): attrDict = {} for attr in pop: attrDict['attr'] = pop[attr] layer.setCustomProperty("qgis2web/popup/" + attr, pop[attr]) layer.setCustomProperty("qgis2web/Visible", vis) except: pass QSettings().setValue( "qgis2web/MainDialogGeometry", self.saveGeometry()) QSettings().setValue("qgis2web/previewOnStartup", self.previewOnStartup.checkState()) QSettings().setValue("qgis2web/closeFeedbackOnSuccess", self.closeFeedbackOnSuccess.checkState()) QSettings().setValue("qgis2web/previewFeatureLimit", self.previewFeatureLimit.text()) QDialog.close(self) def closeEvent(self, event): if self.devConsole or self.devConsole.isVisible() and self.preview: del self.devConsole del self.preview self.reject() event.accept()
def __init__(self, dialog): super(Shortcuts, self).__init__(dialog) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.scheme = SchemeSelector(self) layout.addWidget(self.scheme) self.searchEntry = LineEdit() self.searchEntry.setPlaceholderText(_("Search...")) layout.addWidget(self.searchEntry) self.tree = QTreeWidget(self) self.tree.setHeaderLabels([_("Command"), _("Shortcut")]) self.tree.setRootIsDecorated(False) self.tree.setColumnCount(2) self.tree.setAllColumnsShowFocus(True) self.tree.setAnimated(True) layout.addWidget(self.tree) self.edit = QPushButton(icons.get("preferences-desktop-keyboard-shortcuts"), '') layout.addWidget(self.edit) # signals self.searchEntry.textChanged.connect(self.updateFilter) self.scheme.currentChanged.connect(self.slotSchemeChanged) self.scheme.changed.connect(self.changed) self.tree.currentItemChanged.connect(self.slotCurrentItemChanged) self.tree.itemDoubleClicked.connect(self.editCurrentItem) self.edit.clicked.connect(self.editCurrentItem) # make a dict of all actions with the actions as key and the names as # value, with the collection prepended (for loading/saving) win = dialog.parent() allactions = {} for collection in actioncollectionmanager.manager(win).actionCollections(): for name, action in collection.actions().items(): allactions[action] = (collection, name) # keep a list of actions not in the menu structure left = list(allactions.keys()) def add_actions(menuitem, actions): """Add actions to a QTreeWidgetItem.""" for a in actions: if a.menu(): item = build_menu_item(a) if item.childCount(): menuitem.addChild(item) elif a in left: left.remove(a) menuitem.addChild(ShortcutItem(a, *allactions[a])) menuitem.setFlags(Qt.ItemIsEnabled) # disable selection def build_menu_item(action): """Return a QTreeWidgetItem with children for all the actions in the submenu.""" menuitem = QTreeWidgetItem() text = qutil.removeAccelerator(action.text()) menuitem.setText(0, _("Menu {name}").format(name=text)) add_actions(menuitem, action.menu().actions()) return menuitem # present the actions nicely ordered as in the menus for a in win.menuBar().actions(): menuitem = build_menu_item(a) if menuitem.childCount(): self.tree.addTopLevelItem(menuitem) # sort leftover actions left.sort(key=lambda i: i.text()) # show actions that are left, grouped by collection titlegroups = {} for a in left[:]: # copy collection, name = allactions[a] if collection.title(): titlegroups.setdefault(collection.title(), []).append(a) left.remove(a) for title in sorted(titlegroups): item = QTreeWidgetItem(["{0}:".format(title)]) for a in titlegroups[title]: item.addChild(ShortcutItem(a, *allactions[a])) self.tree.addTopLevelItem(item) item.setFlags(Qt.ItemIsEnabled) # disable selection # show other actions that were not in the menus item = QTreeWidgetItem([_("Other commands:")]) for a in left: if a.text() and not a.menu(): item.addChild(ShortcutItem(a, *allactions[a])) if item.childCount(): self.tree.addTopLevelItem(item) item.setFlags(Qt.ItemIsEnabled) # disable selection self.tree.expandAll() item = self.tree.topLevelItem(0).child(0) if _lastaction: # find the previously selected item for i in self.items(): if i.name == _lastaction: item = i break self.tree.setCurrentItem(item) self.tree.resizeColumnToContents(0)
def __init__(self, dialog): super(Shortcuts, self).__init__(dialog) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) self.scheme = SchemeSelector(self) layout.addWidget(self.scheme) self.tree = QTreeWidget(self) self.tree.setHeaderLabels([_("Command"), _("Shortcut")]) self.tree.setRootIsDecorated(False) self.tree.setColumnCount(2) self.tree.setAllColumnsShowFocus(True) self.tree.setAnimated(True) layout.addWidget(self.tree) self.edit = QPushButton(icons.get("preferences-desktop-keyboard-shortcuts"), '') layout.addWidget(self.edit) # signals self.scheme.currentChanged.connect(self.slotSchemeChanged) self.scheme.changed.connect(self.changed) self.tree.currentItemChanged.connect(self.slotCurrentItemChanged) self.tree.itemDoubleClicked.connect(self.editCurrentItem) self.edit.clicked.connect(self.editCurrentItem) # make a dict of all actions with the actions as key and the names as # value, with the collection prepended (for loading/saving) win = dialog.parent() allactions = {} for collection in actioncollectionmanager.manager(win).actionCollections(): for name, action in collection.actions().items(): allactions[action] = (collection, name) # keep a list of actions not in the menu structure left = list(allactions.keys()) def add_actions(menuitem, actions): """Add actions to a QTreeWidgetItem.""" for a in actions: if a.menu(): item = build_menu_item(a) if item.childCount(): menuitem.addChild(item) elif a in left: left.remove(a) menuitem.addChild(ShortcutItem(a, *allactions[a])) menuitem.setFlags(Qt.ItemIsEnabled) # disable selection def build_menu_item(action): """Return a QTreeWidgetItem with children for all the actions in the submenu.""" menuitem = QTreeWidgetItem() text = qutil.removeAccelerator(action.text()) menuitem.setText(0, _("Menu {name}").format(name=text)) add_actions(menuitem, action.menu().actions()) return menuitem # present the actions nicely ordered as in the menus for a in win.menuBar().actions(): menuitem = build_menu_item(a) if menuitem.childCount(): self.tree.addTopLevelItem(menuitem) # sort leftover actions left.sort(key=lambda i: i.text()) # show actions that are left, grouped by collection titlegroups = {} for a in left[:]: # copy collection, name = allactions[a] if collection.title(): titlegroups.setdefault(collection.title(), []).append(a) left.remove(a) for title in sorted(titlegroups): item = QTreeWidgetItem(["{0}:".format(title)]) for a in titlegroups[title]: item.addChild(ShortcutItem(a, *allactions[a])) self.tree.addTopLevelItem(item) item.setFlags(Qt.ItemIsEnabled) # disable selection # show other actions that were not in the menus item = QTreeWidgetItem([_("Other commands:")]) for a in left: if a.text() and not a.menu(): item.addChild(ShortcutItem(a, *allactions[a])) if item.childCount(): self.tree.addTopLevelItem(item) item.setFlags(Qt.ItemIsEnabled) # disable selection self.tree.expandAll() item = self.tree.topLevelItem(0).child(0) if _lastaction: # find the previously selected item for i in self.items(): if i.name == _lastaction: item = i break self.tree.setCurrentItem(item) self.tree.resizeColumnToContents(0)
def childrens(self, item: QTreeWidgetItem): """Iterates through toplevelitems and returns them.""" for i in range(item.childCount()): yield item.child(i)
class MainDialog(QDialog, Ui_MainDialog): """The main dialog of QGIS2Web plugin.""" items = {} def __init__(self, iface): QDialog.__init__(self) self.setupUi(self) self.iface = iface self.previewUrl = None self.layer_search_combo = None self.exporter_combo = None self.feedback = FeedbackDialog(self) self.feedback.setModal(True) stgs = QSettings() self.restoreGeometry(stgs.value("qgis2web/MainDialogGeometry", QByteArray(), type=QByteArray)) if stgs.value("qgis2web/previewOnStartup", Qt.Checked) == Qt.Checked: self.previewOnStartup.setCheckState(Qt.Checked) else: self.previewOnStartup.setCheckState(Qt.Unchecked) if (stgs.value("qgis2web/closeFeedbackOnSuccess", Qt.Checked) == Qt.Checked): self.closeFeedbackOnSuccess.setCheckState(Qt.Checked) else: self.closeFeedbackOnSuccess.setCheckState(Qt.Unchecked) self.previewFeatureLimit.setText( stgs.value("qgis2web/previewFeatureLimit", "1000")) self.paramsTreeOL.setSelectionMode(QAbstractItemView.SingleSelection) self.preview = None if webkit_available: widget = QWebView() self.preview = widget try: # if os.environ["TRAVIS"]: self.preview.setPage(WebPage()) except: print("Failed to set custom webpage") webview = self.preview.page() webview.setNetworkAccessManager(QgsNetworkAccessManager.instance()) self.preview.settings().setAttribute( QWebSettings.DeveloperExtrasEnabled, True) else: widget = QTextBrowser() widget.setText(self.tr('Preview is not available since QtWebKit ' 'dependency is missing on your system')) self.right_layout.insertWidget(0, widget) self.populateConfigParams(self) self.populate_layers_and_groups(self) self.populateLayerSearch() writer = WRITER_REGISTRY.createWriterFromProject() self.setStateToWriter(writer) self.exporter = EXPORTER_REGISTRY.createFromProject() self.exporter_combo.setCurrentIndex( self.exporter_combo.findText(self.exporter.name())) self.exporter_combo.currentIndexChanged.connect( self.exporterTypeChanged) self.toggleOptions() if webkit_available: if self.previewOnStartup.checkState() == Qt.Checked: self.autoUpdatePreview() self.buttonPreview.clicked.connect(self.previewMap) else: self.buttonPreview.setDisabled(True) self.layersTree.model().dataChanged.connect(self.populateLayerSearch) self.ol3.clicked.connect(self.changeFormat) self.leaflet.clicked.connect(self.changeFormat) self.buttonExport.clicked.connect(self.saveMap) helpText = os.path.join(os.path.dirname(os.path.realpath(__file__)), "helpFile.md") self.helpField.setSource(QUrl.fromLocalFile(helpText)) if webkit_available: self.devConsole = QWebInspector(self.verticalLayoutWidget_2) self.devConsole.setFixedHeight(0) self.devConsole.setObjectName("devConsole") self.devConsole.setPage(self.preview.page()) self.devConsole.hide() self.right_layout.insertWidget(1, self.devConsole) self.filter = devToggleFilter() self.filter.devToggle.connect(self.showHideDevConsole) self.installEventFilter(self.filter) self.setModal(False) @pyqtSlot(bool) def showHideDevConsole(self, visible): self.devConsole.setVisible(visible) def changeFormat(self): self.autoUpdatePreview() self.toggleOptions() def exporterTypeChanged(self): new_exporter_name = self.exporter_combo.currentText() try: self.exporter = [ e for e in EXPORTER_REGISTRY.getExporters() if e.name() == new_exporter_name][0]() except: pass def currentMapFormat(self): """ Returns the currently selected map writer type """ return self.getWriterFactory().type() def getWriterFactory(self): """ Returns a factory to create the currently selected map writer """ if self.mapFormat.checkedButton() == self.ol3: return OpenLayersWriter elif self.mapFormat.checkedButton() == self.leaflet: return LeafletWriter def createWriter(self): """ Creates a writer object reflecting the current settings in the dialog """ writer = self.getWriterFactory()() (writer.layers, writer.groups, writer.popup, writer.visible, writer.json, writer.cluster, writer.getFeatureInfo) = self.getLayersAndGroups() writer.params = self.getParameters() return writer def showErrorMessage(self, error): """ Shows an error message in the preview window """ html = "<html>" html += "<head></head>" html += "<style>body {font-family: sans-serif;}</style>" html += "<body><h1>Error</h1>" html += "<p>qgis2web produced an error:</p><code>" html += error html += "</code></body></html>" if self.preview: self.preview.setHtml(html) def showFeedbackMessage(self, title, message): """ Shows a feedback message in the preview window """ html = "<html>" html += "<head></head>" html += "<style>body {font-family: sans-serif;}</style>" html += "<body><h1>{}</h1>".format(title) html += "<p>{}</p>".format(message) html += "</body></html>" if self.preview: self.preview.setHtml(html) def toggleOptions(self): currentWriter = self.getWriterFactory() for param, value in specificParams.items(): treeParam = self.paramsTreeOL.findItems(param, (Qt.MatchExactly | Qt.MatchRecursive))[0] if currentWriter == OpenLayersWriter: if value == "OL3": treeParam.setDisabled(False) else: treeParam.setDisabled(True) else: if value == "OL3": treeParam.setDisabled(True) else: treeParam.setDisabled(False) for option, value in specificOptions.items(): treeOptions = self.layersTree.findItems(option, (Qt.MatchExactly | Qt.MatchRecursive)) for treeOption in treeOptions: if currentWriter == OpenLayersWriter: if value == "OL3": treeOption.setDisabled(False) else: treeOption.setDisabled(True) else: if value == "OL3": treeOption.setDisabled(True) else: treeOption.setDisabled(False) def createPreview(self): writer = self.createWriter() return writer.write(self.iface, dest_folder=utils.tempFolder()).index_file def shouldAutoPreview(self): """ Returns a tuple, with a bool for whether the preview should automatically be generated, and a string for explanations as to why the preview cannot be automatically generated """ writer = self.createWriter() total_features = 0 for layer in writer.layers: if isinstance(layer, QgsVectorLayer): total_features += layer.featureCount() if total_features > int(self.previewFeatureLimit.text()): # Too many features => too slow! return (False, self.tr('<p>A large number of features are ' 'present in the map. Generating the ' 'preview may take some time.</p>' '<p>Click Update Preview to generate the ' 'preview anyway.</p>')) return (True, None) def autoUpdatePreview(self): """ Triggered when a preview will be automatically generated, i.e. not as a result of the user manually clicking the Update Preview button. """ (auto_preview, message) = self.shouldAutoPreview() if not auto_preview: self.showFeedbackMessage(self.tr('Preview Map'), message) else: self.previewMap() def previewMap(self): preview_file = self.createPreview() self.loadPreviewFile(preview_file) def saveMap(self): writer = self.createWriter() write_folder = self.exporter.exportDirectory() if not write_folder: return self.feedback.reset() self.feedback.show() results = writer.write(self.iface, dest_folder=write_folder, feedback=self.feedback) self.feedback.showFeedback('Success') if self.closeFeedbackOnSuccess.checkState() == Qt.Checked: self.feedback.close() result = self.exporter.postProcess(results, feedback=self.feedback) if result and (not os.environ.get('CI') and not os.environ.get('TRAVIS')): webbrowser.open_new_tab(self.exporter.destinationUrl()) def populate_layers_and_groups(self, dlg): """Populate layers on QGIS into our layers and group tree view.""" root_node = QgsProject.instance().layerTreeRoot() tree_groups = [] tree_layers = root_node.findLayers() self.layers_item = QTreeWidgetItem() self.layers_item.setText(0, "Layers and Groups") self.layersTree.setColumnCount(3) for tree_layer in tree_layers: layer = tree_layer.layer() if (layer.type() != QgsMapLayer.PluginLayer and layer.customProperty("ol_layer_type") is None): try: if layer.type() == QgsMapLayer.VectorLayer: testDump = layer.renderer().dump() layer_parent = tree_layer.parent() if layer_parent.parent() is None: item = TreeLayerItem(self.iface, layer, self.layersTree, dlg) self.layers_item.addChild(item) else: if layer_parent not in tree_groups: tree_groups.append(layer_parent) except: QgsMessageLog.logMessage(traceback.format_exc(), "qgis2web", level=Qgis.Critical) for tree_group in tree_groups: group_name = tree_group.name() group_layers = [ tree_layer.layer() for tree_layer in tree_group.findLayers()] item = TreeGroupItem(group_name, group_layers, self.layersTree) self.layers_item.addChild(item) self.layersTree.addTopLevelItem(self.layers_item) self.layersTree.expandAll() self.layersTree.resizeColumnToContents(0) self.layersTree.resizeColumnToContents(1) for i in range(self.layers_item.childCount()): item = self.layers_item.child(i) if item.checkState(0) != Qt.Checked: item.setExpanded(False) def populateLayerSearch(self): self.layer_search_combo.clear() self.layer_search_combo.addItem("None") (layers, groups, popup, visible, json, cluster, getFeatureInfo) = self.getLayersAndGroups() for count, layer in enumerate(layers): if layer.type() == layer.VectorLayer: options = [] fields = layer.fields() for f in fields: fieldIndex = fields.indexFromName(unicode(f.name())) editorWidget = layer.editorWidgetSetup(fieldIndex).type() if editorWidget == 'Hidden': continue options.append(unicode(f.name())) for option in options: displayStr = unicode(layer.name() + ": " + option) self.layer_search_combo.insertItem(0, displayStr) sln = utils.safeName(layer.name()) self.layer_search_combo.setItemData( self.layer_search_combo.findText(displayStr), sln + "_" + unicode(count)) def configureExporter(self): self.exporter.configure() def populateConfigParams(self, dlg): """ Populates the dialog with option items and widgets """ self.items = defaultdict(dict) tree = dlg.paramsTreeOL configure_export_action = QAction('...', self) configure_export_action.triggered.connect(self.configureExporter) params = getParams(configure_exporter_action=configure_export_action) for group, settings in params.items(): item = QTreeWidgetItem() item.setText(0, group) for param, value in settings.items(): subitem = self.createOptionItem(tree_widget=tree, parent_item=item, parameter=param, default_value=value) item.addChild(subitem) self.items[group][param] = subitem self.paramsTreeOL.addTopLevelItem(item) item.sortChildren(0, Qt.AscendingOrder) self.paramsTreeOL.expandAll() self.paramsTreeOL.resizeColumnToContents(0) self.paramsTreeOL.resizeColumnToContents(1) self.layer_search_combo.removeItem(1) def createOptionItem(self, tree_widget, parent_item, parameter, default_value): """create the tree item corresponding to an option parameter""" action = None if isinstance(default_value, dict): action = default_value['action'] default_value = default_value['option'] subitem = TreeSettingItem(parent_item, tree_widget, parameter, default_value, action) if parameter == 'Layer search': self.layer_search_combo = subitem.combo elif parameter == 'Exporter': self.exporter_combo = subitem.combo return subitem def setStateToWriter(self, writer): """ Sets the dialog state to match the specified writer """ self.selectMapFormat(writer) self.setStateToParams(writer.params) def setStateToParams(self, params): """ Sets the dialog state to match the specified parameters """ for group, settings in self.items.items(): for param, item in settings.items(): value = params[group][param] item.setValue(value) def selectMapFormat(self, writer): """ Updates dialog state to match the specified writer format """ self.ol3.setChecked(isinstance(writer, OpenLayersWriter)) self.leaflet.setChecked(isinstance(writer, LeafletWriter)) def loadPreviewFile(self, file): """ Loads a web based preview from a local file path """ self.previewUrl = QUrl.fromLocalFile(file) if self.preview: self.preview.settings().clearMemoryCaches() self.preview.setUrl(self.previewUrl) def getParameters(self): parameters = defaultdict(dict) for group, settings in self.items.items(): for param, item in settings.items(): parameters[group][param] = item.value() if param == "Layer search": parameters["Appearance"]["Search layer"] = ( self.layer_search_combo.itemData( self.layer_search_combo.currentIndex())) return parameters def saveParameters(self): """ Saves current dialog state to project """ WRITER_REGISTRY.saveWriterToProject(self.createWriter()) EXPORTER_REGISTRY.writeToProject(self.exporter) def getLayersAndGroups(self): layers = [] groups = {} popup = [] visible = [] json = [] cluster = [] getFeatureInfo = [] for i in range(self.layers_item.childCount()): item = self.layers_item.child(i) if isinstance(item, TreeLayerItem): if item.checkState(0) == Qt.Checked: layers.append(item.layer) popup.append(item.popup) visible.append(item.visible) json.append(item.json) cluster.append(item.cluster) getFeatureInfo.append(item.getFeatureInfo) else: group = item.name groupLayers = [] if item.checkState(0) != Qt.Checked: continue for layer in item.layers: groupLayers.append(layer) layers.append(layer) popup.append({}) if item.visible: visible.append(True) else: visible.append(False) if hasattr(item, "json") and item.json: json.append(True) else: json.append(False) if hasattr(item, "cluster") and item.cluster: cluster.append(True) else: cluster.append(False) if hasattr(item, "getFeatureInfo") and item.getFeatureInfo: getFeatureInfo.append(True) else: getFeatureInfo.append(False) groups[group] = groupLayers[::-1] return (layers[::-1], groups, popup[::-1], visible[::-1], json[::-1], cluster[::-1], getFeatureInfo[::-1]) def reject(self): self.saveParameters() (layers, groups, popup, visible, json, cluster, getFeatureInfo) = self.getLayersAndGroups() for layer, pop, vis in zip(layers, popup, visible): attrDict = {} for attr in pop: attrDict['attr'] = pop[attr] layer.setCustomProperty("qgis2web/popup/" + attr, pop[attr]) layer.setCustomProperty("qgis2web/Visible", vis) QSettings().setValue( "qgis2web/MainDialogGeometry", self.saveGeometry()) QSettings().setValue("qgis2web/previewOnStartup", self.previewOnStartup.checkState()) QSettings().setValue("qgis2web/closeFeedbackOnSuccess", self.closeFeedbackOnSuccess.checkState()) QSettings().setValue("qgis2web/previewFeatureLimit", self.previewFeatureLimit.text()) QDialog.close(self) def closeEvent(self, event): if self.devConsole or self.devConsole.isVisible() and self.preview: del self.devConsole del self.preview self.reject() event.accept()
def _remove_all_children(self, parent: QTreeWidgetItem): while parent.childCount() > 0: parent.removeChild(parent.child(0))
class RestoreDialog(widgets.dialog.Dialog): def __init__(self, parent=None): super(RestoreDialog, self).__init__(parent) self.messageLabel().setWordWrap(True) userguide.addButton(self.buttonBox(), "snippets") self.tree = QTreeWidget(headerHidden=True, rootIsDecorated=False) self.setMainWidget(self.tree) self.deletedItem = QTreeWidgetItem(self.tree) self.deletedItem.setFlags(Qt.ItemIsUserCheckable) self.changedItem = QTreeWidgetItem(self.tree) self.changedItem.setFlags(Qt.ItemIsUserCheckable) self.tree.itemChanged.connect(self.slotItemChanged) app.translateUI(self) app.languageChanged.connect(self.populate) self.accepted.connect(self.updateSnippets) qutil.saveDialogSize(self, "snippettool/restoredialog/size") def translateUI(self): self.setWindowTitle( app.caption(_("dialog title", "Restore Built-in Snippets"))) self.setMessage(_( "This dialog allows you to recover built-in snippets that have " "been changed or deleted. Check the snippets you want to recover " "and click the button \"Restore Checked Snippets.\"")) self.button("ok").setText(_("Restore Checked Snippets")) self.deletedItem.setText(0, _("Deleted Snippets")) self.changedItem.setText(0, _("Changed Snippets")) def populate(self): """Puts the deleted/changed snippets in the tree.""" self.deletedItem.takeChildren() self.deletedItem.setExpanded(True) self.deletedItem.setCheckState(0, Qt.Unchecked) self.changedItem.takeChildren() self.changedItem.setExpanded(True) self.changedItem.setCheckState(0, Qt.Unchecked) builtins = list(builtin.builtin_snippets) builtins.sort(key = snippets.title) names = frozenset(snippets.names()) for name in builtins: if name in names: if snippets.isoriginal(name): continue parent = self.changedItem else: parent = self.deletedItem item = QTreeWidgetItem(parent) item.name = name item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(0, Qt.Unchecked) item.setText(0, snippets.title(name)) self.deletedItem.setDisabled(self.deletedItem.childCount() == 0) self.changedItem.setDisabled(self.changedItem.childCount() == 0) self.checkOkButton() def slotItemChanged(self, item): if item in (self.deletedItem, self.changedItem): for i in range(item.childCount()): item.child(i).setCheckState(0, item.checkState(0)) self.checkOkButton() def checkedSnippets(self): """Yields the names of the checked snippets.""" for parent in (self.deletedItem, self.changedItem): for i in range(parent.childCount()): child = parent.child(i) if child.checkState(0) == Qt.Checked: yield child.name def updateSnippets(self): """Restores the checked snippets.""" collection = self.parent().parent().snippetActions for name in self.checkedSnippets(): collection.restoreDefaultShortcuts(name) model.model().saveSnippet(name, None, None) def checkOkButton(self): """Enables the OK button if there are selected snippets.""" self.button("ok").setEnabled(any(self.checkedSnippets()))
def addOverlaysToTreeWidget(self, overlayDict, forbiddenOverlays, preSelectedOverlays, singleOverlaySelection): self.singleOverlaySelection = singleOverlaySelection testItem = QTreeWidgetItem("a") for keys in list(overlayDict.keys()): if overlayDict[keys] in forbiddenOverlays: continue else: boolStat = False split = keys.split("/") for i in range(len(split)): if len(split) == 1: newItemsChild = OverlayTreeWidgetItem(overlayDict[keys], keys) self.addTopLevelItem(newItemsChild) boolStat = False if overlayDict[keys] in preSelectedOverlays: newItemsChild.setCheckState(0, Qt.Checked) else: newItemsChild.setCheckState(0, Qt.Unchecked) elif i+1 == len(split) and len(split) > 1: newItemsChild = OverlayTreeWidgetItem(overlayDict[keys], keys) testItem.addChild(newItemsChild) if overlayDict[keys] in preSelectedOverlays: newItemsChild.setCheckState(0, Qt.Checked) else: newItemsChild.setCheckState(0, Qt.Unchecked) elif self.topLevelItemCount() == 0 and i+1 < len(split): newItem = QTreeWidgetItem([split[i]]) self.addTopLevelItem(newItem) testItem = newItem boolStat = True elif self.topLevelItemCount() != 0 and i+1 < len(split): if boolStat == False: for n in range(self.topLevelItemCount()): if self.topLevelItem(n).text(0) == split[i]: testItem = self.topLevelItem(n) boolStat = True break elif n+1 == self.topLevelItemCount(): newItem = QTreeWidgetItem([split[i]]) self.addTopLevelItem(newItem) testItem = newItem boolStat = True elif testItem.childCount() == 0: newItem = QTreeWidgetItem([split[i]]) testItem.addChild(newItem) testItem = newItem boolStat = True else: for x in range(testItem.childCount()): if testItem.child(x).text(0) == split[i]: testItem = testItem.child(x) boolStat = True break elif x+1 == testItem.childCount(): newItem = QTreeWidgetItem([split[i]]) testItem.addChild(newItem) testItem = newItem boolStat = True