123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- import inspect
- import shutil
- import sys
- import os
- from typing import Optional
- from PyQt5.QtCore import Qt, QUrl,QSize
- from PyQt5.QtGui import QIcon
- from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QMenu, QFileDialog, \
- QMessageBox, QInputDialog, QLineEdit,QPushButton,QHBoxLayout
- from PyQt5.QtWebEngineWidgets import *
- import ftplib
- from FtpUitl import FtpOper
- import FtpConfig
- class FtpManage(QWidget):
- def __init__(self, parent=None):
- super().__init__(parent)
- # 连接到FTP服务器
- self.ftpOper = FtpOper()
- self.ftpOper.ftp.connect(FtpConfig.ftpHost, FtpConfig.ftpPort)
- self.ftpOper.ftp.login(FtpConfig.ftpUsername, FtpConfig.ftpPassword)
- self.fileItem = None
- self.ftpItem = None
- self.setupUi()
- #self.setContextMenuPolicy(Qt.CustomContextMenu) # 右键菜单
- #self.customContextMenuRequested.connect(self.rightClickMenu)
- self.createTempDir()
- def setupUi(self):
- current_directory = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) #获取当前路径
- # 构建布局
- self.mainLayout = QVBoxLayout()
- self.setLayout(self.mainLayout)
- self.layoutH = QHBoxLayout() # 按钮布局
- self.layoutV = QVBoxLayout() # 树结构的布局
- self.mainLayout.addLayout(self.layoutH)
- self.mainLayout.addLayout(self.layoutV) # 为窗体添加布局
- self.resize(600, 500)
- self.setWindowTitle("文件服务器")
- self.setWindowIcon(QIcon(os.path.join(current_directory + "\\icon", "logo.png")))
- # 功能按钮区域
- 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, str(self.ftpOper.ftp.host)) # 给根节点增加文本
- self.treeWidget.addTopLevelItem(self.rootItem) # 增加根节点
- self.layoutV.addWidget(self.treeWidget) # 将组件添加到布局中
- self.treeWidget.setColumnHidden(2, True) # 隐藏路径列
- #self.treeWidget.setStyleSheet("QTreeView::item {border:0.5px solid gray; border-left-color: transparent; border-right-color: transparent;}")
- # 右键菜单
- # def rightClickMenu(self, pos):
- # try:
- # self.currentItem = self.treeWidget.currentItem()
- # self.contexMenu = QMenu()
- # if (self.currentItem.text(1) == '文件' or self.currentItem.text(1) == '文件夹'):
- # self.actionFileUpload = self.contexMenu.addAction('上传文件')
- # self.actionFileUpload.triggered.connect(self.actionUploadHandler)
- # self.actionDirUpload = self.contexMenu.addAction('上传文件夹')
- # self.actionDirUpload.triggered.connect(self.actionUploadDirHandler)
- # self.actionDownload = self.contexMenu.addAction('下载')
- # self.actionDownload.triggered.connect(self.actionDownloadHandler)
- # self.actionDirCreate = self.contexMenu.addAction('新建目录')
- # self.actionDirCreate.triggered.connect(self.actionCreateHandler)
- # self.actionRename = self.contexMenu.addAction('重命名')
- # self.actionRename.triggered.connect(self.actionRenameHandler)
- # self.actionDelete = self.contexMenu.addAction('删除')
- # self.actionDelete.triggered.connect(self.actionDeleteHandler)
- # else:
- # self.actionDirCreate = self.contexMenu.addAction('新建目录')
- # self.actionDirCreate.triggered.connect(self.actionCreateHandler)
- # if (self.currentItem.text(1) == '文件'):
- # self.action3D = self.contexMenu.addAction('查看模型')
- # self.action3D.triggered.connect(self.action3DHandler)
- # self.actionFileOpen = self.contexMenu.addAction('查看文件')
- # self.actionFileOpen.triggered.connect(self.actionFileOpenHandler)
- # self.contexMenu.exec_(self.mapToGlobal(pos))
- # self.contexMenu.show()
- #
- # except Exception as e:
- # print(e)
- # 文件上传操作
- 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)
- QMessageBox.information(self, "提示信息", "文件上传完成")
- 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)
- QMessageBox.information(self, "提示信息", "文件夹上传完成")
- 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)
- QMessageBox.information(self, "提示信息", "下载完成")
- 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 = FtpConfig.modelViewerUri + FtpConfig.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(QWebEngineView):
- def __init__(
- self,
- weburi: str,
- title: Optional[str] = "自定义窗口",
- windowW: Optional[int] = 1200,
- windowH: Optional[int] = 800,
- ):
- super().__init__()
- self.uri = weburi
- self.resize(windowW, windowH)
- self.setWindowTitle(title)
- self.page = QWebEnginePage()
- self.page.load(QUrl(self.uri))
- self.setPage(self.page)
- if __name__ == '__main__':
- app = QApplication(sys.argv)
- ftpMain = FtpManage()
- ftpMain.show()
- ftpMain.traverseFTPDirectory(ftpMain.ftpOper.ftp, '/', ftpMain.rootItem) # 遍历构建树
- sys.exit(app.exec_())
|