# -*- coding: utf-8 -*- import ftplib import inspect import os import shutil from typing import Optional from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QSize, QUrl from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QPushButton, QTreeWidget, QTreeWidgetItem, QMessageBox, \ QInputDialog, QLineEdit, QWidget, QFileDialog from .FtpUitl import FtpOper from .FtpConfig import * from qgis.utils import iface current_directory = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # 获取当前路径 class Ui_SearchDockWidget(object): def setupUi(self, SearchDockWidget): # 设置主窗口的标题栏文字 # iface.mainWindow().setWindowTitle("新标题 - QGIS") iface.mainWindow().setWindowTitle("新标题 - 四维数码") self.ftpOper = FtpOper() self.ftpOper.ftp.connect(ftpHost, ftpPort) self.ftpOper.ftp.login(ftpUsername, ftpPassword) self.fileItem = None self.ftpItem = None SearchDockWidget.setObjectName("SearchDockWidget") SearchDockWidget.resize(422, 303) font = QtGui.QFont() font.setFamily("微软雅黑") SearchDockWidget.setFont(font) self.dockWidgetContents = QtWidgets.QWidget() self.dockWidgetContents.setObjectName("dockWidgetContents") # 添加FTP管理界面 self.mainLayout = QVBoxLayout(self.dockWidgetContents) self.layoutH = QHBoxLayout() # 按钮布局 self.layoutV = QVBoxLayout() # 树结构的布局 # 功能按钮区域 self.uploadFileButton = QPushButton(self) self.uploadFileButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件上传.png"))) self.uploadFileButton.setIconSize(QSize(32, 32)) self.uploadFileButton.setFixedSize(36, 36) self.uploadFileButton.setToolTip("上传文件") self.uploadFileButton.clicked.connect(self.actionUploadHandler) self.layoutH.addWidget(self.uploadFileButton) self.uploadFolderButton = QPushButton(self) self.uploadFolderButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件夹上传.png"))) self.uploadFolderButton.setIconSize(QSize(32, 32)) self.uploadFolderButton.setFixedSize(36, 36) self.uploadFolderButton.setToolTip("上传文件夹") self.uploadFolderButton.clicked.connect(self.actionUploadDirHandler) self.layoutH.addWidget(self.uploadFolderButton) self.downloadButton = QPushButton(self) self.downloadButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件下载.png"))) self.downloadButton.setIconSize(QSize(32, 32)) self.downloadButton.setFixedSize(36, 36) self.downloadButton.setToolTip("下载") self.downloadButton.clicked.connect(self.actionDownloadHandler) self.layoutH.addWidget(self.downloadButton) self.createDirButton = QPushButton(self) self.createDirButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "新建文件夹.png"))) self.createDirButton.setIconSize(QSize(32, 32)) self.createDirButton.setFixedSize(36, 36) self.createDirButton.setToolTip("新建目录") self.createDirButton.clicked.connect(self.actionCreateHandler) self.layoutH.addWidget(self.createDirButton) self.renameButton = QPushButton(self) self.renameButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件重命名.png"))) self.renameButton.setIconSize(QSize(32, 32)) self.renameButton.setFixedSize(36, 36) self.renameButton.setToolTip("重命名") self.renameButton.clicked.connect(self.actionRenameHandler) self.layoutH.addWidget(self.renameButton) self.deleteButton = QPushButton(self) self.deleteButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件删除.png"))) self.deleteButton.setIconSize(QSize(32, 32)) self.deleteButton.setFixedSize(36, 36) self.deleteButton.setToolTip("删除") self.deleteButton.clicked.connect(self.actionDeleteHandler) self.layoutH.addWidget(self.deleteButton) self.modelViewButton = QPushButton(self) self.modelViewButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "模型查看.png"))) self.modelViewButton.setIconSize(QSize(32, 32)) self.modelViewButton.setFixedSize(36, 36) self.modelViewButton.setToolTip("查看模型") self.modelViewButton.clicked.connect(self.action3DHandler) self.layoutH.addWidget(self.modelViewButton) self.openFileButton = QPushButton(self) self.openFileButton.setIcon(QIcon(os.path.join(current_directory + "\\icon", "文件查看.png"))) self.openFileButton.setIconSize(QSize(32, 32)) self.openFileButton.setFixedSize(36, 36) self.openFileButton.setToolTip("查看文件") self.openFileButton.clicked.connect(self.actionFileOpenHandler) self.layoutH.addWidget(self.openFileButton) # 目录树结构区 self.treeWidget = QTreeWidget() # QTreeWidget组件定义 self.treeWidget.headerItem().setText(0, "列表") # FTP服务器节点树 self.treeWidget.headerItem().setText(1, "类型") # 节点对应的类型:文件,文件夹 self.treeWidget.headerItem().setText(2, "路径") # 当前节点在FTP服务器中的路径,方便文件操作时获取服务器中路径,为隐藏列 self.treeWidget.setColumnWidth(0, 375) # 给第1列设置列宽 self.rootItem = QTreeWidgetItem() self.rootItem.setText(0, "文件结构树") # 给根节点增加文本 self.treeWidget.addTopLevelItem(self.rootItem) # 增加根节点 self.layoutV.addWidget(self.treeWidget) # 将组件添加到布局中 self.treeWidget.setColumnHidden(2, True) # 隐藏路径列 self.mainLayout.addLayout(self.layoutH) self.mainLayout.addLayout(self.layoutV) # 为窗体添加布局 SearchDockWidget.setWidget(self.dockWidgetContents) self.retranslateUi(SearchDockWidget) QtCore.QMetaObject.connectSlotsByName(SearchDockWidget) self.traverseFTPDirectory(self.ftpOper.ftp, '/', self.rootItem) # 遍历构建树 def retranslateUi(self, SearchDockWidget): _translate = QtCore.QCoreApplication.translate SearchDockWidget.setWindowTitle(_translate("SearchDockWidget", "文件服务器")) def actionUploadHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() if self.currentItem.text(1) == '文件': # 如果选中文件节点则获取此节点的父节点 self.ftp_path = self.currentItem.parent().text(2) self.ftpItem = self.currentItem.parent() elif self.currentItem.text(1) == '文件夹': self.ftp_path = self.currentItem.text(2) self.ftpItem = self.currentItem file_paths, _ = QFileDialog.getOpenFileNames(None, '选择文件', '', 'All Files (*)') if len(file_paths) > 0: for file_path in file_paths: self.file_name = os.path.basename(file_path) self.ftpOper.uploadfile(file_path, self.ftp_path) self.fileItem = QTreeWidgetItem() self.fileItem.setText(0, self.file_name) self.fileItem.setText(1, '文件') self.fileItem.setText(2, self.ftp_path + '/' + self.file_name) # 已有同名节点不再增加此名称的节点 havesame = self.findTreeSameNode(self.ftpItem, self.file_name) if not havesame: self.ftpItem.addChild(self.fileItem) else: QMessageBox.information(self, "提示信息", "请选择树节点") # 文件上传时查找目录树此节点的子节点中是否已有此名称 def findTreeSameNode(self, thisitem, filename): for i in range(thisitem.childCount()): child = thisitem.child(i) if child.text(0) == filename: return True else: return False # 文件夹上传操作 def actionUploadDirHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() if self.currentItem.text(1) == '文件': # 如果选中文件节点则获取此节点的父节点 self.ftp_path = self.currentItem.parent().text(2) self.ftpItem = self.currentItem.parent() else: self.ftp_path = self.currentItem.text(2) self.ftpItem = self.currentItem dir_path = QFileDialog.getExistingDirectory(None, "选择文件夹", "") if dir_path: self.ftpOper.uploaddir(dir_path, self.ftp_path) dirname = os.path.split(dir_path)[-1] # 文件夹名称 rootchildItem = QTreeWidgetItem() rootchildItem.setText(0, dirname) rootchildItem.setText(1, '文件夹') rootchildItem.setText(2, self.ftp_path + '/' + dirname) self.ftpItem.addChild(rootchildItem) self.traverseLocalDirectory(dir_path, rootchildItem, self.ftp_path + '/' + dirname) else: QMessageBox.information(self, "提示信息", "请选择树节点") # 遍历本地目录,构建可视化树结构 def traverseLocalDirectory(self, dirpath, item, ftppath): for file in os.listdir(dirpath): src = os.path.join(dirpath, file) childItem = QTreeWidgetItem() childItem.setText(0, file) if os.path.isfile(src): childItem.setText(1, '文件') childItem.setText(2, ftppath + '/' + file) item.addChild(childItem) elif os.path.isdir(src): childItem.setText(1, '文件夹') childItem.setText(2, ftppath + '/' + file) item.addChild(childItem) # 递归调用自身 self.traverseLocalDirectory(src, childItem, ftppath + '/' + file) # 文件下载操作 def actionDownloadHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() dir_path = QFileDialog.getExistingDirectory(None, "选择文件夹", "") if dir_path: if self.currentItem.text(1) == '文件': localfilepath = dir_path + '/' + self.currentItem.text(0) reomtefilepath = self.currentItem.text(2) self.ftpOper.downloadfile(localfilepath, reomtefilepath) else: localdirpath = dir_path + '/' + self.currentItem.text(0) remotedirpath = self.currentItem.text(2) self.ftpOper.downloaddir(localdirpath, remotedirpath) else: QMessageBox.information(self, "提示信息", "请选择树节点") # 文件删除操作 def actionDeleteHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() questiontext = '是否要删除所选中的:【' + self.currentItem.text(0) + '】?' question = QMessageBox.question(None, '删除确认', questiontext) if question == QMessageBox.Yes: reomtefilepath = self.currentItem.text(2) if self.currentItem.text(1) == '文件': remotepath, remotefile_name = os.path.split(reomtefilepath) self.ftpOper.deletfile(remotefile_name, remotepath) # 删除FTP服务端文件 self.currentItem.parent().removeChild(self.currentItem) # 删除目录树对应的节点 else: self.ftpOper.deletedir(reomtefilepath) self.currentItem.parent().removeChild(self.currentItem) # 删除目录树对应的节点 else: QMessageBox.information(self, "提示信息", "请选择树节点") # 创建文件夹操作 def actionCreateHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() dirname, ok = QInputDialog.getText(QWidget(), '新建文件夹', '请输入文件夹名称:') if ok: if self.currentItem.text(1): if self.currentItem.text(1) == '文件': # 如果选中文件节点则获取此节点的父节点 self.dirpath = self.currentItem.parent().text(2) self.ftpItem = self.currentItem.parent() else: self.dirpath = self.currentItem.text(2) self.ftpItem = self.currentItem else: self.dirpath = '' self.ftpItem = self.currentItem self.newdirpath = self.dirpath + '/' + dirname self.rusulttext = self.ftpOper.makedir(dirname, self.dirpath, self.newdirpath) if self.rusulttext == '文件夹创建成功': self.fileItem = QTreeWidgetItem() self.fileItem.setText(0, dirname) self.fileItem.setText(1, '文件夹') self.fileItem.setText(2, self.newdirpath) self.ftpItem.addChild(self.fileItem) else: QMessageBox.information(self, "提示信息", "请选择树节点") # 重命名操作 def actionRenameHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() extension = None oldname = None oldpath = None if self.currentItem.text(1) == '文件': oldname, oldextension = os.path.splitext(self.currentItem.text(0)) elif self.currentItem.text(1) == '文件夹': oldname = self.currentItem.text(0) newname, ok = QInputDialog.getText(QWidget(), '重命名', '请输入新名称', QLineEdit.Normal, oldname) if ok: if self.currentItem.text(1) == '文件': oldpath = self.currentItem.text(2) pathparent, name1 = os.path.split(oldpath) name2, extension = os.path.splitext(name1) newpath = pathparent + '/' + newname + extension else: oldpath = self.currentItem.text(2) if not (self.currentItem.parent().text(1)): # 根目录 newpath = '/' + newname else: pathparent, name1 = os.path.split(oldpath) newpath = pathparent + '/' + newname result = self.ftpOper.rename(oldpath, newpath) if result: if extension: self.currentItem.setText(0, newname + extension) else: self.currentItem.setText(0, newname) self.currentItem.setText(2, newpath) else: QMessageBox.information(self, "提示信息", "请选择树节点") # 查看三维模型操作 def action3DHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() if (self.currentItem.text(1) == '文件'): modelUrl = modelViewerUri + modelBaseUri + self.currentItem.text(2) self.modelWebView = ModelWebView(weburi=modelUrl, title='数管系统三维模型浏览') self.modelWebView.show() else: QMessageBox.information(self, "提示信息", "请选择文件类型树节点") else: QMessageBox.information(self, "提示信息", "请选择树节点") # 打开文件操作 def actionFileOpenHandler(self): if self.treeWidget.currentItem(): self.currentItem = self.treeWidget.currentItem() if (self.currentItem.text(1) == '文件'): localdir = os.path.abspath(os.curdir) localtempdir = localdir + '/temp' if os.path.exists(localtempdir): if self.currentItem.text(1) == '文件': localfilepath = localtempdir + '/' + self.currentItem.text(0) reomtefilepath = self.currentItem.text(2) if os.path.exists(localfilepath): try: os.startfile(localfilepath) # 打开本地文件 except Exception as e: print(e) else: self.ftpOper.downloadfile(localfilepath, reomtefilepath) # 下载到本地临时文件夹 try: os.startfile(localfilepath) # 打开本地文件 except Exception as e: print(e) else: QMessageBox.information(self, "提示信息", "请选择文件类型树节点") else: QMessageBox.information(self, "提示信息", "请选择树节点") # 创建本地文件临时文件夹 def createTempDir(self): localdir = os.path.abspath(os.curdir) localtempdir = localdir + '/temp' if not (os.path.exists(localtempdir)): # 如不存在,创建本地临时文件夹 os.makedirs(localtempdir) else: # 如存在,删除后重新创建本地临时文件夹 shutil.rmtree(localtempdir) os.mkdir(localtempdir) # 遍历FTP服务端目录,构建可视化树结构 def traverseFTPDirectory(self, ftp, path, item): ftp.cwd(path) file_list = ftp.nlst() for file in file_list: try: ftp.cwd(path + '/' + file) # 尝试切换到子目录 # 添加子节点 childItem = QTreeWidgetItem() childItem.setText(0, file) childItem.setText(1, '文件夹') childItem.setText(2, (path + '/' + file).replace('//', '/')) item.addChild(childItem) # 递归调用自身 self.traverseFTPDirectory(ftp, (path + '/' + file).replace('//', '/'), childItem) ftp.cwd('..') # 返回到上一级目录 except ftplib.error_perm: # 添加子节点 childItem = QTreeWidgetItem() childItem.setText(0, file) childItem.setText(1, '文件') childItem.setText(2, (path + '/' + file).replace('//', '/')) item.addChild(childItem) class ModelWebView(): def __init__( self, weburi: str, title: Optional[str] = "自定义窗口", windowW: Optional[int] = 1200, windowH: Optional[int] = 800, ): print("")