コード例 #1
0
ファイル: gui.py プロジェクト: JunguangJiang/FTP
class ClientWindow(mainwindow.Ui_MainWindow):
    '''客户端窗口类'''
    def __init__(self):
        MainWindow = QMainWindow()
        self.setupUi(MainWindow)

        self.client = Client()
        self.task_manager = None  #传输任务工厂
        self.timer = QtCore.QTimer()  #计时器,用于不断刷新数据传输进度
        self.interval = 100  #刷新间隔,以ms为单位
        self.timer.timeout.connect(self.update_tasks_progress)

        self.status_label = QLabel(self.statusbar) #由于显示数据传输信息

        self.has_connected = False  #是否已经建立连接
        self.establish_signals_ans_slots()

        self.menubar.setEnabled(False)
        self.statusbar.setEnabled(False)

        MainWindow.closeEvent = self.closeEvent
        MainWindow.show()
        sys.exit(app.exec_())

    def establish_signals_ans_slots(self):
        '''建立窗体中的信号与槽'''
        # 文件浏览窗体
        self.folderWidget.customContextMenuRequested[QtCore.QPoint].connect(self.folder_right_menu_show)  #鼠标右键单击
        self.folderWidget.itemDoubleClicked.connect(self.on_go_action)  #鼠标左键双击
        self.folderWidget.setMouseTracking(True)
        self.folderWidget.itemEntered.connect(lambda item: self.on_look_action(item, self.folderWidget))  #鼠标悬浮

        # 任务浏览窗体
        self.tasksWidget.setMouseTracking(True)
        self.tasksWidget.itemEntered.connect(lambda item: self.on_look_action(item, self.tasksWidget))  #鼠标悬浮

        self.connectButton.clicked.connect(self.on_connect_button_clicked)  # 点击"连接"按钮
        self.goButton.clicked.connect(self.on_go_button)  # 点击"跳转"按钮

        self.menuSetting.setToolTipsVisible(True)

        self.actionNew_folder.triggered.connect(self.on_mkdir_action)
        self.actionNew_folder.setText("创建文件夹")
        self.actionPut_file.triggered.connect(lambda: self.on_put_action(is_folder=False))
        self.actionPut_file.setText("上传文件")
        self.actionPut_folder.triggered.connect(lambda: self.on_put_action(is_folder=True))
        self.actionPut_folder.setText("上传文件夹")

        self.actionMax_Occurs.triggered.connect(self.on_action_max_occurs_triggered)
        self.actionMax_Occurs.setText("设置同时传输最大任务数")
        self.actionPort.triggered.connect(self.on_action_port_triggered)
        self.actionPassive.triggered.connect(self.on_action_passive_triggered)

        self.actionSystem.triggered.connect(self.on_action_system_triggered)
        self.actionType.triggered.connect(self.on_action_type_triggered)


    def on_connect_button_clicked(self):
        '''当连接按钮按下时'''
        if self.has_connected == False:  #尚未建立连接
            # 则根据输入的ip、port、user、password连接到服务器
            ip = self.ipInput.text()
            port = self.portInput.text()
            user = self.userInput.text()
            password = self.passwordInput.text()
            if not self.client.open(ip, port):  #连接失败
                QMessageBox.information(self.centralwidget, "连接错误","ip或者port错误",QMessageBox.Yes)
                return
            self.client.user(user)
            reply = self.client.password(password)
            if reply.startswith("5"):  #若登录失败
                QMessageBox.information(self.centralwidget, "连接错误", "user或者password错误", QMessageBox.Yes)
            else:  #登录成功
                self.update_folder()  #更新文件浏览窗体
                self.connectButton.setText("disconnect")
                self.has_connected = True

                self.task_manager = TaskManager(ip, port, user, password)  #创建任务工厂
                self.timer.start(self.interval)  #开始刷新数据传输进度

                self.menubar.setEnabled(True)
                self.statusbar.setEnabled(True)
        else:  #如果已经建立连接
            self.timer.stop() #停止数据传输进度的刷新
            # 则客户端断开连接
            self.client.bye()
            self.client.close()
            self.connectButton.setText("connect")
            self.has_connected = False
            del self.task_manager
            self.task_manager = None

            self.menubar.setEnabled(False)
            self.statusbar.setEnabled(False)

    def closeEvent(self, event):
        '''单击退出时响应'''
        self.timer.stop()
        if self.has_connected:
            self.client.bye()
            self.client.close()
        del self.task_manager

    def update_navigation_bar(self):
        '''更新导航栏'''
        reply = self.client.pwd()  # 获取当前路径
        if reply.startswith("257"):
            self.navigationBar.setText(reply[5:-2])

    def update_folder(self):
        '''更新文件浏览窗口'''
        ls_file = "ls.txt"
        reply = self.client.ls(local_file=ls_file) #获取当前文件夹下的所有文件信息
        if reply == True:
            self.folderWidget.clearContents()
            self.folderWidget.setRowCount(len(open(ls_file,'r').readlines()))
            i = 0
            with open(ls_file, "r") as f:
                for line in f:
                    list = line.split()
                    #文件名
                    name_item = QTableWidgetItem(list[8])
                    name_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.folderWidget.setItem(i, 0, name_item)
                    #文件类型
                    type = get_file_type(file_name=list[8], access_string=list[0])
                    type_item = QTableWidgetItem(type)
                    type_item.setFlags(Qt.NoItemFlags)
                    self.folderWidget.setItem(i, 1, type_item)
                    #文件大小
                    if type == "folder":
                        size = "--"
                    else:
                        size = human_readable_size(list[4])
                    size_item = QTableWidgetItem(size)
                    size_item.setFlags(Qt.NoItemFlags)
                    self.folderWidget.setItem(i, 2, size_item)
                    #文件的最近修改时间
                    time_item = QTableWidgetItem(" ".join(list[5:8]))
                    time_item.setFlags(Qt.NoItemFlags)
                    self.folderWidget.setItem(i, 3, time_item)
                    i += 1

    def folder_right_menu_show(self, point):
        '''当右键点击文件浏览窗口时,根据选中项弹出相关操作'''
        if not self.has_connected:
            return
        popMenu = QMenu()
        index = self.folderWidget.indexAt(point)
        row = index.row()
        column = index.column()
        if row >= 0 and column == 0: #如果右键文件名一列
            file_name = self.folderWidget.item(row, 0).text()
            file_type = self.folderWidget.item(row, 1).text()
            # 无论是否为文件夹
            popMenu.addAction(QAction(text="rename 重命名", parent=popMenu, triggered=lambda: self.on_rename_action(old_name=file_name)))
            popMenu.addAction(QAction(text="get 下载", parent=popMenu, triggered=lambda: self.on_get_action(file_name)))
            if file_type == "folder": #如果该文件是文件夹
                popMenu.addAction(QAction(text="rmdir 删除文件夹", parent=popMenu, triggered=lambda: self.on_rmdir_action(file_name)))
                popMenu.addAction(QAction(text='zip 压缩', parent=popMenu, triggered=lambda :self.on_zip_action(file_name)))
            else: #如果该文件不是文件夹
                popMenu.addAction(QAction(text="reget 断点下载", parent=popMenu, triggered=lambda: self.on_reget_action(file_name)))
                popMenu.addAction(QAction(text="reput 断点上传", parent=popMenu, triggered=lambda: self.on_reput_action(file_name)))
                popMenu.addAction(QAction(text="append 追加内容", parent=popMenu, triggered=lambda: self.on_append_action(file_name)))
                popMenu.addAction(QAction(text="delete 删除文件", parent=popMenu, triggered=lambda: self.on_delete_action(file_name)))
                if file_type == 'zip': #如果是压缩文件
                    popMenu.addAction(QAction(text='unzip 解压', parent=popMenu, triggered=lambda :self.on_unzip_action(file_name)))
        else: #其余情况
            popMenu.addAction(QAction(text="mkdir 创建文件夹", parent=popMenu, triggered=lambda: self.on_mkdir_action()))
            popMenu.addAction(QAction(text="put 上传文件", parent=popMenu, triggered=lambda: self.on_put_action(is_folder=False)))
            popMenu.addAction(QAction(text="put(folder) 上传文件夹", parent=popMenu, triggered=lambda: self.on_put_action(is_folder=True)))
        popMenu.exec_(QtGui.QCursor.pos())

    def on_get_action(self, file_name):
        '''使用get下载文件file_name'''
        path = QFileDialog.getExistingDirectory(self.centralwidget, '下载文件', './')
        if path:
            indexes = self.folderWidget.selectedIndexes()
            for r in indexes:
                file_name = self.folderWidget.item(r.row(), 0).text()
                file_type = self.folderWidget.item(r.row(), 1).text()
                if file_type != "folder":
                    self.create_transfer_tasks(type="GET", local_file=path+"/"+file_name, remote_file=file_name)
                else:
                    self.create_transfer_tasks(type="GET_FOLDER", local_file=path, remote_file=file_name)

    def on_reget_action(self, file_name):
        '''使用reget重新下载文件'''
        local_file, ftype = QFileDialog.getOpenFileName(self.centralwidget, "下载文件 断点续传", './')
        if local_file:
            self.create_transfer_tasks(type="REGET", local_file=local_file, remote_file=file_name)

    def on_append_action(self, file_name):
        '''使用append上传文件(追加模式)'''
        local_file, ftype = QFileDialog.getOpenFileName(self.centralwidget, "上传文件 追加内容", './')
        if local_file:
            self.create_transfer_tasks(type="APPEND", local_file=local_file, remote_file=file_name)

    def on_reput_action(self, file_name):
        local_file, ftype = QFileDialog.getOpenFileName(self.centralwidget, "上传文件 断点续传", './')
        if local_file:
            self.create_transfer_tasks(type="REPUT", local_file=local_file, remote_file=file_name)

    def on_put_action(self, is_folder=False):
        '''使用put下载文件'''
        if is_folder:
            local_folder = QFileDialog.getExistingDirectory(self.centralwidget, "上传文件夹","./")
            self.create_transfer_tasks(type="PUT_FOLDER", local_file=local_folder, remote_file='.')
        else:
            local_files, ftype = QFileDialog.getOpenFileNames(self.centralwidget, "上传文件", './')
            for f in local_files:
                if not f:
                    continue
                self.create_transfer_tasks(type="PUT", local_file=f, remote_file=f.split('/')[-1])
        self.update_folder()


    def on_mkdir_action(self):
        '''使用mkdir创建文件夹'''
        dir, ok = QInputDialog.getText(self.centralwidget, "新建文件夹","新文件夹名")
        if ok:
            reply = self.client.mkdir(dir)
            if reply.startswith("550"):
                QMessageBox.information(self.centralwidget, "错误", "创建文件夹{}失败".format(dir), QMessageBox.Yes)
            else:
                self.update_folder()

    def on_rmdir_action(self, file_name):
        '''使用rmdir删除文件夹file_name,目前只支持删除里面没有内容的文件夹'''
        reply = self.client.rmdir(file_name)
        if reply.startswith("550"):
            QMessageBox.information(self.centralwidget, "错误", "删除文件夹{}失败".format(file_name), QMessageBox.Yes)
        else:
            self.update_folder()

    def on_delete_action(self,file_name):
        '''使用delete删除文件file_name'''
        reply = self.client.delete(file_name)
        if not reply.startswith("250"):
            QMessageBox.information(self.centralwidget, "错误", "删除文件{}失败".format(file_name), QMessageBox.Yes)
        else:
            self.update_folder()

    def on_rename_action(self,old_name):
        '''使用rename修改文件名'''
        new_name, ok = QInputDialog.getText(self.centralwidget, "修改文件名", "新文件名")
        if ok:
            reply = self.client.rename(from_name=old_name, to_name=new_name)
            if not reply.startswith("250"):
                QMessageBox.information(self.centralwidget, "错误", "修改文件{}的名字失败".format(old_name), QMessageBox.Yes)
            else:
                self.update_folder()

    def on_zip_action(self, folder_name):
        '''使用zip压缩文件'''
        self.client.zip(folder_name)
        self.update_folder()

    def on_unzip_action(self, zip_file_name):
        '''使用unzip解压文件'''
        self.client.unzip(zip_file_name)
        self.update_folder()

    def on_look_action(self, item, widget):
        '''当鼠标经过时响应'''
        widget.setToolTip(item.text())

    def on_go_action(self, item):
        '''当双击文件夹时响应'''
        if not self.has_connected:
            return
        dir = item.text()
        type = self.folderWidget.item(item.row(), 1).text()
        if type == "folder":
            reply = self.client.cd(dir)
            if not reply.startswith("550"): #改变路径成功
                self.update_navigation_bar()
                self.update_folder()

    def on_go_button(self):
        '''当点击go button时响应'''
        if not self.has_connected:
            return
        dir = self.navigationBar.text()
        reply = self.client.cd(dir)
        if reply.startswith("550"): #改变路径失败
            QMessageBox.information(self.centralwidget, "错误", "不存在该路径{}".format(dir), QMessageBox.Yes)
        else:  #改变路径成功
            self.update_navigation_bar()
            self.update_folder()

    def create_transfer_tasks(self,type, local_file, remote_file):
        '''创建数据传输任务'''
        self.timer.stop()  #在创建数据传输的过程中,不进行任务进度的刷新
        remote_path = self.client.pwd()[5:-2]  #获取当前路径
        taskInfo = self.task_manager.create_task(type, local_file, remote_file, remote_path)  #创建一个新的任务

        # 将新的任务显示在GUI界面上
        self.tasksWidget.setRowCount(self.tasksWidget.rowCount()+1)
        contents = [str(taskInfo.id), local_file, remote_file, type]
        for j in range(len(contents)):
            item = QTableWidgetItem(contents[j])
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.tasksWidget.setItem(self.tasksWidget.rowCount()-1,j,item)

        if taskInfo.status == "Failed":
            item = QTableWidgetItem("任务失败")
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.tasksWidget.setItem(self.tasksWidget.rowCount() - 1, 4, item)
        elif taskInfo.status == "Invalid":
            QMessageBox.information(self.centralwidget, "错误", "与正在运行或者等待的任务冲突", QMessageBox.Yes)
            item = QTableWidgetItem("任务冲突")
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.tasksWidget.setItem(self.tasksWidget.rowCount() - 1, 4, item)
        else:
            progressBar = QProgressBar(self.tasksWidget)
            progressBar.setRange(0, 100)
            progressBar.setValue(0)
            progressBar.setToolTip("任务进度")
            progressBar.setStyleSheet(
                                    "QProgressBar {\
                                        border: 2px solid grey;\
                                        border-radius: 5px; \
                                        text-align:center; \
                                    }\
                                    QProgressBar::chunk {\
                                        background-color: #05B8CC;\
                                        width: 2px; \
                                    }")
            self.tasksWidget.setCellWidget(self.tasksWidget.rowCount() - 1, 4, progressBar)

        self.timer.start()

    def update_tasks_progress(self):
        '''刷新任务的进度'''
        if not self.has_connected:
            return

        #  在状态栏展示下载速度、上传速度、正在进行的任务数和等待任务数
        download_speed, upload_speed = self.task_manager.calculate_data_tranfer_rate(self.interval)
        going_on_tasks_size = len(self.task_manager.going_on_tasks)
        waiting_tasks_size = len(self.task_manager.waiting_tasks)

        status_inforation = "正在进行的任务数:{:2d}   等待任务数: {:2d}          下载速率:{:9.1f}Mb/s      上传速率:{:9.1f}Mb/s".format(
            going_on_tasks_size, waiting_tasks_size, download_speed, upload_speed)
        self.status_label.setText(status_inforation)
        self.status_label.adjustSize()

        for i in range(self.tasksWidget.rowCount()):
            id = self.tasksWidget.item(i,0).text()
            taskInfo = self.task_manager.get_task_by_id(id)
            if taskInfo.status == "Going on":  #如果该任务正在进行中
                task = taskInfo.task
                status, msg = task.query_status(self.client)
                if status == "Not started":
                    print("此处有bug!!!")
                elif status == "Fail": # 任务失败
                    item = QTableWidgetItem("任务失败")
                    item.setToolTip(msg)
                    item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                    self.tasksWidget.removeCellWidget(i, 4)
                    self.tasksWidget.setItem(i, 4, item)
                    self.task_manager.recycle_task(taskInfo)  #回收该任务
                    continue
                elif status == "Success":  #完成传输
                    self.task_manager.recycle_task(taskInfo)  #则回收该任务
                    self.update_folder()
                    progress = 1.0
                else: #正在进行传输
                    try:
                        progress = float(msg)
                    except:
                        progress = 0.0
                # 刷新进度条的显示
                progressBar = self.tasksWidget.cellWidget(i, 4)
                progressBar.setValue(100.0 * progress)
                progressBar.setToolTip(str(msg))
                progressBar.update()
        self.tasksWidget.scrollToBottom()


    def on_action_max_occurs_triggered(self):
        '''设置同时数据传输的最大并发个数'''
        if not self.has_connected:
            return
        number, ok = QInputDialog.getInt(self.centralwidget, "输入","请输入同时进行的数据传输的最大个数",
                                         self.task_manager.max_occurs, 1, 10)
        if ok:
            self.task_manager.max_occurs = number

    def on_action_port_triggered(self):
        '''设置port模式'''
        if not self.has_connected:
            return
        self.client.sendport()

    def on_action_passive_triggered(self):
        '''设置Passive模式'''
        if not self.has_connected:
            return
        self.client.passive()

    def on_action_system_triggered(self):
        '''查询system'''
        if not self.has_connected:
            return
        info = self.client.system()
        QMessageBox.information(self.centralwidget, "查询system信息", info, QMessageBox.Yes)

    def on_action_type_triggered(self):
        '''查询type信息'''
        if not self.has_connected:
            return
        info = self.client.type()
        QMessageBox.information(self.centralwidget, "查询type信息", info, QMessageBox.Yes)