使用python和pyqt开发一款简易打包工具

前言

很多时候都会面临一个问题,将文件脚本图片等上传svn之后,那么需要将修改过的文件打包然后发给测试,假如有bug还要继续修改继续测试。几个回合下来,修改的文件不会只有几个而且假如

而且每次修改的针对问题及文件列表都不会一样,所以,拥有一个工具可以记录每次修改的bug,每次修改的文件,整合成为一个补丁包对于码农来说应该是一件好事了。于是该工具就诞生了。




思路:
    采用sqlite3作为数据库,设计三张表简单保存每个项目每次修改每个文件的信息,
    设计界面用于设定相关选项,写各种逻辑处理从svn提交以后的字符串解释成为文件列表及对应操作,
    然后每次打包都将相关文件复制或者删除(取决于你是删除或者修改添加该文件),然后保存到数据库中,
    最后在重新生成一份修改列表文档【该文档由数据库的信息获得】。



核心代码:


from PyQt5 import QtWidgets
import sys
import os
import re
from PyQt5 import QtGui
from modules.PackerFileAdditionDialog import *
from modules.ProjectEditionDialog import *
from modules.ProjectAdditionDialog import *
from DAL.ProjectDAL import *
from Base.InitDB import *
from modules.PackerFileAdditionDialog import *
from modules.PackerHistoryDialog import *
class ProjectMgrWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(ProjectMgrWindow,self).__init__()
        self.resize(900, 750)
        self.move(300, 300)
        self.uis={}
        self.setWindowTitle('自动打包工具')
        self._init_menus()
        #--主体相关工具。
        self.mainLayout=QtWidgets.QVBoxLayout()
        self.mainFrame= QtWidgets.QFrame()
        self.mainFrame.setLayout(self.mainLayout)
        #self.mainFrame.setSizePolicy(QtWidgets.QSizePolicy.Fixed,QtWidgets.QSizePolicy.Fixed)
        #self.setLayout(gridLayout)
        self.setCentralWidget(self.mainFrame)


        dal2=PackerDAL()
        rlist=dal2.getList()
        print(rlist)
        self._init_list_bar()
        self._init_main()
        self.initDatas()
    #初始化菜单。
    def _init_menus(self):
        self.addMenu = self.menuBar().addMenu("&添加")
        self.action_add_project=QtWidgets.QAction(QtGui.QIcon("resource/image/plus_16.ico"),"&添加项目",self,triggered=self.action_handler_add_project)
        self.addMenu.addAction(self.action_add_project)
        #self.addMenu.addAction(self.addAvatarAct)
        #self.addMenu.addAction(self.addAvatarSetAct)
        #self.addMenu.addAction(self.addAvatarDecorationAct)
        self.modifyMenu = self.menuBar().addMenu("&修改")
        #self.modifyMenu.addAction(self.modifyAvatarAct)
        #self.modifyMenu.addAction(self.modifyAvatarSetAct)
        self.settingMenu = self.menuBar().addMenu("&设置")

        self.action_softInfo=QtWidgets.QAction("&软件说明", self, triggered=self.action_handler_softInfo)
        self.action_update=QtWidgets.QAction("&检查更新", self, triggered=self.action_handler_update)
        self.action_aboutus = QtWidgets.QAction("&关于我们",self,triggered=self.action_handler_aboutus)
        self.action_initDB=QtWidgets.QAction("&数据初始化",self,triggered=self.action_handler_initdb)

        self.settingMenu.addAction(self.action_softInfo)
        self.settingMenu.addAction(self.action_aboutus)
        self.settingMenu.addAction(self.action_update)
        self.settingMenu.addAction(self.action_initDB)

        #self.settingMenu.addAction(self.settingAct)

        #工具栏
        self.toolbar = self.addToolBar('教程')

        self.toolbar.addAction(self.action_softInfo)
        #self.toolbar.addAction(self.homeAct)
        #self.toolbar = self.addToolBar('AddAvatar')
        #self.toolbar.addAction(self.addAvatarAct)
        #self.toolbar = self.addToolBar('AddAvatarDecoration')
        #self.toolbar.addAction(self.addAvatarDecorationAct)
        self.toolbar = self.addToolBar('其他')
        self.toolbar.addAction(self.action_aboutus)
        self.toolbar.addAction(self.action_update)



    #初始化列表工具。
    def _init_list_bar(self):

        vbox_opBar = QGridLayout()
        btn_history = QPushButton("查看打包历史")
        vbox_opBar.addWidget(btn_history, 0, 0)
        btn_packer = QPushButton("添加打包文件")
        vbox_opBar.addWidget(btn_packer, 0, 1)
        btn_edit = QPushButton("编辑信息")


        vbox_opBar.addWidget(btn_edit, 0, 2)

        btn_del = QPushButton("删除项目")
        vbox_opBar.addWidget(btn_del, 0, 3)

        btn_edit.clicked.connect(lambda: self.action_handler_edit_project(1))
        btn_del.clicked.connect(lambda: self.action_handler_del_project(1))
        btn_history.clicked.connect(lambda: self.action_handler_show_project_history(1))
        btn_packer.clicked.connect(lambda: self.action_handler_add_project_packer(1))

        vframe = QtWidgets.QGroupBox()
        vframe.setTitle("项目工具")
        vframe.setLayout(vbox_opBar)
        self.mainLayout.addWidget(vframe)


    def _init_main(self):

        self.detailTable = QTableWidget()
        self.detailTable.setColumnCount(4)
        self.detailTable.setHorizontalHeaderLabels(
                ['项目id', '项目名称','项目描述', '操作'])


        self.detailTable.setEditTriggers(QTableWidget.NoEditTriggers)
        self.detailTable.setSelectionBehavior(QTableWidget.SelectRows)
        self.detailTable.setSelectionMode(QTableWidget.SingleSelection)
        self.detailTable.setAlternatingRowColors(True)
        layout1=QHBoxLayout()
        layout1.addWidget(self.detailTable)
        vframe=QtWidgets.QGroupBox()

        vframe.setTitle("项目列表")
        vframe.setLayout(layout1)
        self.mainLayout.addWidget(vframe)


        pass

    #初始化相关数据,
    def initDatas(self):
        dal=ProjectDAL()
        lists=dal.getList()
        #self.detailTable.setRowCount(len(lists))
        #print(lists)



        for index in range(len(lists)):
            aitem=lists[index]

            self._add_record_row(str(aitem[0]),aitem[1],aitem[2])
            #自定义相关表格的控件。





    #处理点击检查更新时候的操作。。
    def action_handler_update(self):

        MESSAGE = "信息"
        reply = QtWidgets.QMessageBox.information(self, "系统通知", "亲,别逗了,内部工具不用自动更新的啊")
        if reply == QtWidgets.QMessageBox.Ok:
             pass
        else:
             pass
        pass

    def action_handler_initdb(self):

        MESSAGE = ""
        reply = QtWidgets.QMessageBox.warning(self, "警告", "系统将清空所有项目信息及项目打包记录【但不会删除硬盘上的任何文件】,是否继续?",QtWidgets.QMessageBox.Yes,QtWidgets.QMessageBox.No)
        if reply == QtWidgets.QMessageBox.Yes:
             initDB()
             self._clear_rows()
             pass
        else:
             pass
        pass
    def action_handler_aboutus(self):

        _logo_str="我们的标志是:\n"
        _logo_str+="上帝的骑宠,上古时期世界的霸主。\n"
        _logo_str+="┏┛┻━━━┛┻┓\n"
        _logo_str+="┃|||||||┃\n"
        _logo_str+="┃   ━   ┃\n"
        _logo_str+="┃ ┳┛  ┗┳  ┃\n"
        _logo_str+="┃       ┃\n"
        _logo_str+="┃   ┻   ┃\n"
        _logo_str+="┃       ┃\n"
        _logo_str+="┗━┓   ┏━┛\n"
        _logo_str+="  ┃ 史 ┃  \n"
        _logo_str+="  ┃ 诗 ┃  \n"
        _logo_str+="  ┃ 之 ┃  \n"
        _logo_str+="  ┃ 宠 ┃\n"
        _logo_str+="  ┃   ┗━━━┓\n"
        _logo_str+="  ┃经验与我同在 ┣┓\n"
        _logo_str+="  ┃攻楼专用宠物 ┃\n"
        _logo_str+="  ┗┓┓┏━┳┓┏┛\n"
        _logo_str+="   ┃┫┫ ┃┫┫\n"
        _logo_str+="   ┗┻┛ ┗┻┛\n"

        _intr_str="我们的口号是:\n"
        _intr_str+="搬别人的砖让别人无砖可搬\n"
        _intr_str+="亲,你还想加入我们吗?"


        reply = QtWidgets.QMessageBox.information(self, "关于我们", _logo_str+_intr_str)
        if reply == QtWidgets.QMessageBox.Ok:
             pass
        else:
             pass
        pass

    def action_handler_softInfo(self):

        _intr_str="该软件主要用于:\n"
        _intr_str+="从svn更新列表里面自动copy和替换掉旧有文件形成一个补丁包\n"
        _intr_str+="是网站及相关项目的简易打包工具。\n"
        _intr_str+="打包流程说明:\n"
        _intr_str+="1、请先建立一个打包项目,包括正确填写项目的根路径,项目的打包保存路径\n"
        _intr_str+="2、每次的打包都是从点击添加补丁文件按钮开始\n,将svn或者你要复制的目录路径字符串粘贴到相关区域,填写【可选】补丁说明,然后点击确定打包,\n"
        _intr_str+="系统将自动保存打包的历史并从源项目中取出相关文件融入到补丁包里面\n"
        _intr_str+="3、多次重复步骤二直到这个补丁包要上线。\n"
        _intr_str+="4、手动将项目补丁所在文件夹复制压缩发给测试或上线。"

        reply = QtWidgets.QMessageBox.information(self, "软件说明", _intr_str)
        if reply == QtWidgets.QMessageBox.Ok:
             pass
        else:
             pass
        pass

    def action_handler_select_project_folder(self):
        filedialog=QtWidgets.QFileDialog()
        filedialog.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
        filedialog.setViewMode(QtWidgets.QFileDialog.Detail)
        #filedialog.show()

        folders=filedialog.getExistingDirectory()
        if len(folders)>0:
            self.txt_project_path.setText(folders)

    def action_handler_select_save_path(self):
        filedialog=QtWidgets.QFileDialog()
        filedialog.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
        filedialog.setViewMode(QtWidgets.QFileDialog.Detail)
        #filedialog.show()

        folders=filedialog.getExistingDirectory()
        if len(folders)>0:
            self.txt_save_path.setText(folders)


    def action_handler_add_project(self):
        #addwin=ProjectEditionDialog()
        #addwin.exec()
        addwin=ProjectAdditionDialog()
        addwin.setCallBack(self._add_record_row)
        addwin.exec()
    def action_handler_addFiles(self):

        if self.detailTable.currentRow()==-1:
            QtWidgets.QMessageBox.warning(self,"警告","请先选择项目!")
            return
        selectIndex=self.detailTable.currentRow()
        rowItem=self.detailTable.rowAt(selectIndex)
        columnItem=self.detailTable.item(selectIndex,0)


        packerWin=PackerFileAdditionDialog(columnItem.text())
        #editWin.setCallBack(self._update_record_row)
        packerWin.exec()



    def action_handler_showLogs(self):

        valid_res=self._validate_settings()
        if valid_res:
            pass

    ##其他操作。
    def _validate_settings(self):
        if len(self.txt_project_path.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写项目根路径!")
            return False
        #检查跟目录是否有效。
        if os.path.exists(self.txt_project_path.text())==False:
            QtWidgets.QMessageBox.warning(self,"警告","项目路径无效,请确定这是准确的项目地址!")
            return False
        if len(self.txt_save_path.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","请确定补丁文件保存的目录路径!")
            return False
        if os.path.isfile(self.txt_save_path.text())==True:
            QtWidgets.QMessageBox.warning(self,"警告","补丁文件的路径必须是文件夹而非文件!")
            return False
        if os.path.exists(self.txt_save_path.text())==False:
            QtWidgets.QMessageBox.information(self,"文件夹不存在","补丁包的目录不存在,请先进行新建。")
            return False
        if len(self.txt_packName.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写补丁包的名称")
            return False
        #检查是否有效字符串。
        temp = self.txt_packName.text()
        xx="^([_\.\s\w\d\-\u4e00-\u9fa5]+)$"
        pattern = re.compile(xx)
        results =  pattern.findall(temp)
        if len(results)<=0:
            QtWidgets.QMessageBox.warning(self,"警告","补丁名称只能包含数字,中文,英文空格下划线,小数点,分割线,其他特殊符号都不允许!")
            return False
        return True

    def action_handler_edit_project(self,recordid):

        if self.detailTable.currentRow()==-1:
            QtWidgets.QMessageBox.warning(self,"警告","请先选择项目!")
            return
        selectIndex=self.detailTable.currentRow()
        rowItem=self.detailTable.rowAt(selectIndex)
        columnItem=self.detailTable.item(selectIndex,0)


        editWin=ProjectEditionDialog(columnItem.text())
        editWin.setCallBack(self._update_record_row)
        editWin.exec()

    def action_handler_del_project(self,recordid):

        if self.detailTable.currentRow()==-1:
            QtWidgets.QMessageBox.warning(self,"警告","请先选择项目!")
            return

        reply=QtWidgets.QMessageBox.warning(self,"警告","是否要删除该项目?【删除后数据库中项目的历史记录及信息将不复存在,但是硬盘上的任何文件都不会进行删除】",QtWidgets.QMessageBox.Yes,QtWidgets.QMessageBox.No)
        if reply==QtWidgets.QMessageBox.Yes:
            selectIndex=self.detailTable.currentRow()
            rowItem=self.detailTable.rowAt(selectIndex)
            columnItem=self.detailTable.item(selectIndex,0)

            dal=ProjectDAL()
            dal.delRecord(columnItem.text())
            self._del_record_row(columnItem.text())
        else:
            pass


    def action_handler_show_project_history(self,recordid):
        if self.detailTable.currentRow()==-1:
            QtWidgets.QMessageBox.warning(self,"警告","请先选择项目!")
            return
        selectIndex=self.detailTable.currentRow()
        rowItem=self.detailTable.rowAt(selectIndex)
        columnItem=self.detailTable.item(selectIndex,0)


        packerWin=PackerHistoryDialog(columnItem.text())
        #packerWin.setCallBack(self._update_record_row)
        packerWin.exec()

    def action_handler_add_project_packer(self,recordid):
        if self.detailTable.currentRow()==-1:
            QtWidgets.QMessageBox.warning(self,"警告","请先选择项目!")
            return
        selectIndex=self.detailTable.currentRow()
        rowItem=self.detailTable.rowAt(selectIndex)
        columnItem=self.detailTable.item(selectIndex,0)


        packerWin=PackerFileAdditionDialog(columnItem.text())
        #packerWin.setCallBack(self._update_record_row)
        packerWin.exec()

    def _clear_rows(self):
        self.detailTable.clearContents()

    def _add_record_row(self,recoredId,project_name,project_desc):
        rowcount=self.detailTable.rowCount()
        self.detailTable.insertRow(rowcount)
        self.detailTable.setRowHeight(rowcount, 50)

        self.detailTable.setColumnWidth(0, 125)
        self.detailTable.setColumnWidth(1, 150)
        self.detailTable.setColumnWidth(2, 200)
        newItem = QtWidgets.QTableWidgetItem(recoredId)

        self.detailTable.setItem(rowcount, 0, newItem)
        newItem = QtWidgets.QTableWidgetItem(project_name)
        self.detailTable.setItem(rowcount, 1, newItem)
        newItem = QtWidgets.QTableWidgetItem(project_desc)
        self.detailTable.setItem(rowcount, 2, newItem)

    def _update_record_row(self,recoredId,project_name,project_desc):
        rowIndex=-1
        for index in range(self.detailTable.rowCount()):

            columnItem=self.detailTable.item(index,0)

            if str(columnItem.text()) == str(recoredId):
                rowIndex=index
                break

        if rowIndex==-1:
            print("数据表中没有需要更新的数据项")
            return
        rowcount=rowIndex



        self.detailTable.setRowHeight(rowcount, 50)

        self.detailTable.setColumnWidth(0, 125)
        self.detailTable.setColumnWidth(1, 150)
        self.detailTable.setColumnWidth(2, 200)
        newItem = QtWidgets.QTableWidgetItem(recoredId)

        self.detailTable.setItem(rowcount, 0, newItem)
        newItem = QtWidgets.QTableWidgetItem(project_name)
        self.detailTable.setItem(rowcount, 1, newItem)
        newItem = QtWidgets.QTableWidgetItem(project_desc)
        self.detailTable.setItem(rowcount, 2, newItem)


    def _del_record_row(self,recoredId):
        rowIndex=-1
        for index in range(self.detailTable.rowCount()):

            columnItem=self.detailTable.item(index,0)

            if str(columnItem.text()) == str(recoredId):
                rowIndex=index
                break

        if rowIndex==-1:
            print("数据表中没有需要更新的数据项")
            return
        rowcount=rowIndex

        self.detailTable.removeRow(rowIndex)



import sys
from PyQt5 import QtWidgets

from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, pyqtSignal
import sqlite3
from DAL.ProjectDAL import *
import re
class ProjectAdditionDialog(QDialog):
    findNext = pyqtSignal(str, Qt.CaseSensitivity)
    findPrevious = pyqtSignal(str, Qt.CaseSensitivity)
    __recordId=""

    def __init__(self, parent = None):
        super().__init__(parent)
        self.setWindowTitle("添加打包项目")


        mainVBox = QtWidgets.QVBoxLayout()
        packerSettingBox=QtWidgets.QGroupBox()
        packerToolBar=QtWidgets.QGroupBox()
        packerToolBarLayout=QtWidgets.QHBoxLayout()
        packerToolBar.setLayout(packerToolBarLayout)

        packerSettingBox.setTitle("补丁包详细设置")
        packerToolBar.setTitle("补丁包操作")
        mainVBox.addWidget(packerSettingBox)
        #mainVBox.addWidget(packerToolBar)





        #补丁设置框
        gridLayout = QtWidgets.QGridLayout()


        rowIndex=0
        gridLayout.addWidget(QtWidgets.QLabel("项目名称:"),rowIndex,0)
        self.text_name=QtWidgets.QLineEdit()
        gridLayout.addWidget(self.text_name,rowIndex,1)


        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("项目根目录:"),rowIndex,0)
        self.txt_project_path=QtWidgets.QLineEdit()
        self.gridLayout=gridLayout
        gridLayout.addWidget(self.txt_project_path,rowIndex,1)
        btn_browserFolder0=QtWidgets.QPushButton("选择目录")
        gridLayout.addWidget(btn_browserFolder0,rowIndex,2)
        btn_browserFolder0.clicked.connect(self.action_handler_select_project_folder)

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("补丁包保存目录:"),rowIndex,0)
        self.txt_save_path=QtWidgets.QLineEdit()
        gridLayout.addWidget(self.txt_save_path,rowIndex,1)
        btn_browserFolder=QtWidgets.QPushButton("选择目录")
        btn_browserFolder.clicked.connect(self.action_handler_select_save_path)
        gridLayout.addWidget(btn_browserFolder,rowIndex,2)

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("补丁名称:"),rowIndex,0)
        self.txt_packName=QtWidgets.QLineEdit()
        gridLayout.addWidget(self.txt_packName,rowIndex,1)

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("补丁描述:"),rowIndex,0)
        self.txt_desc=QtWidgets.QTextEdit()
        gridLayout.addWidget(self.txt_desc,rowIndex,1)

        rowIndex=rowIndex+1
        #保存按钮。
        btn_save=QtWidgets.QPushButton("添加项目")
        gridLayout.addWidget(btn_save,rowIndex,0,1,3)
        btn_save.clicked.connect(self.action_handler_add_project)
        #packerSettingBox.setSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Fixed)
        #packerSettingBox.setFixedSize(800,180)



        packerSettingBox.setLayout(gridLayout)
        #补丁按钮区域。


        self.setLayout(mainVBox)

    def action_handler_select_project_folder(self):
        filedialog=QtWidgets.QFileDialog()
        filedialog.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
        filedialog.setViewMode(QtWidgets.QFileDialog.Detail)
        #filedialog.show()

        folders=filedialog.getExistingDirectory()
        if len(folders)>0:
            self.txt_project_path.setText(folders)

    def action_handler_select_save_path(self):
        filedialog=QtWidgets.QFileDialog()
        filedialog.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
        filedialog.setViewMode(QtWidgets.QFileDialog.Detail)
        #filedialog.show()

        folders=filedialog.getExistingDirectory()
        if len(folders)>0:
            self.txt_save_path.setText(folders)
    def action_handler_add_project(self):
        if self.validateForm()==False:
            return False

        dal=ProjectDAL()
        self.__recordId=dal.add(self.text_name.text(),self.txt_desc.toPlainText(),self.txt_project_path.text(),self.txt_save_path.text(),self.txt_packName.text());
        QtWidgets.QMessageBox.information(self,"系统消息","成功添加项目,请进入项目的打包页面进行文件的打包。")
        self.close()
        if self.callback:
            self.callback(str(self.__recordId),self.text_name.text(),self.txt_desc.toPlainText())
        pass


    def validateForm(self):
        res=False
        if len(self.text_name.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写项目的名称!")
            return False
        if len(self.txt_project_path.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写项目根路径!")
            return False
        #检查跟目录是否有效。
        if os.path.exists(self.txt_project_path.text())==False:
            QtWidgets.QMessageBox.warning(self,"警告","项目路径无效,请确定这是准确的项目地址!")
            return False
        if len(self.txt_save_path.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","请确定补丁文件保存的目录路径!")
            return False
        if os.path.isfile(self.txt_save_path.text())==True:
            QtWidgets.QMessageBox.warning(self,"警告","补丁文件的路径必须是文件夹而非文件!")
            return False
        if os.path.exists(self.txt_save_path.text())==False:
            QtWidgets.QMessageBox.information(self,"文件夹不存在","补丁包的目录不存在,请先进行新建。")
            return False
        if len(self.txt_packName.text())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写补丁包的名称")
            return False
        #检查是否有效字符串。
        temp = self.txt_packName.text()
        xx="^([_\.\s\w\d\-\u4e00-\u9fa5]+)$"
        pattern = re.compile(xx)
        results =  pattern.findall(temp)
        if len(results)<=0:
            QtWidgets.QMessageBox.warning(self,"警告","补丁名称只能包含数字,中文,英文空格下划线,小数点,分割线,其他特殊符号都不允许!")
            return False

        return True

    #设置回调函数。
    def setCallBack(self,callback):
        self.callback=callback



import sys
from PyQt5 import QtWidgets

from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, pyqtSignal
import sqlite3
from DAL.ProjectDAL import *
import re
from BLL.PackerHandler import *
from DAL.PackerDAL import *
class PackerFileAdditionDialog(QDialog):
    findNext = pyqtSignal(str, Qt.CaseSensitivity)
    findPrevious = pyqtSignal(str, Qt.CaseSensitivity)
    __recordId=""
    __project_root=""
    __save_root=""
    __pack_name=""


    def __init__(self,recodid, parent = None):
        super().__init__(parent)
        self.setWindowTitle("添加打包项目")
        self.__recordId=str(recodid)
        self.___init_datas()

        mainVBox = QtWidgets.QVBoxLayout()
        packerSettingBox=QtWidgets.QGroupBox()
        packerToolBar=QtWidgets.QGroupBox()
        packerToolBarLayout=QtWidgets.QHBoxLayout()
        packerToolBar.setLayout(packerToolBarLayout)

        packerSettingBox.setTitle("文件打包")
        packerToolBar.setTitle("补丁包操作")
        mainVBox.addWidget(packerSettingBox)
        #mainVBox.addWidget(packerToolBar)





        #补丁设置框
        gridLayout = QtWidgets.QGridLayout()


        rowIndex=0

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("补丁描述:"),rowIndex,0)
        self.txt_desc=QtWidgets.QTextEdit()
        gridLayout.addWidget(self.txt_desc,rowIndex,1)

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("修改的文件列表:"),rowIndex,0)
        self.txt_files=QtWidgets.QTextEdit()
        gridLayout.addWidget(self.txt_files,rowIndex,1)

        rowIndex=rowIndex+1
        gridLayout.addWidget(QtWidgets.QLabel("打包规则说明:"),rowIndex,0)
        self.txt_intro=QtWidgets.QLabel("")

        str1='''1、每条记录分为两部分,第一个是操作标志,有改动(m,M,modify,Modify),添加 (a,A,add,Add),删除(d,D,del,Del,delete,Delete)三种,
        后面就是影响的目录及路径了。\n'''
        str1+='''2、例如:\n'''
        str1+='a /WEB-INF/inc.js.jsp\n'
        str1+='m /WEB-INF/inc.js.tpl.jsp\n'
        str1+='a /module/p.lesson.js\n'
        str1+='a /module/p.lesson.min.js\n'
        str1+='d /public/images/logo_old.gif\n'
        self.txt_intro.setText(str1)

        gridLayout.addWidget(self.txt_intro,rowIndex,1)


        rowIndex=rowIndex+1
        #保存按钮。
        btn_save=QtWidgets.QPushButton("进行打包")
        gridLayout.addWidget(btn_save,rowIndex,0,1,3)
        btn_save.clicked.connect(self.action_handler_pack_files)
        #packerSettingBox.setSizePolicy(QtWidgets.QSizePolicy.Expanding,QtWidgets.QSizePolicy.Fixed)
        #packerSettingBox.setFixedSize(800,180)



        packerSettingBox.setLayout(gridLayout)
        #补丁按钮区域。


        self.setLayout(mainVBox)


    def action_handler_pack_files(self):
        res=self.validateForm()
        if res==False:
            return

        print("执行了action handler")
        _h=PackerHandler()
        items=_h.trans2FileItems(self.txt_files.toPlainText())
        print(items)
        #验证文件有效性。
        vres=_h.validateFiles(self.__project_root,items)

        #检查相关情况。
        _validateRes=True
        _validateResStr=""
        _needDelFolder=False
        for itemIndex in range(len(vres)):
            _item=vres[itemIndex]
            if _item["op_type"]=="del" and _item.get("filetype","")=="dir":
                _needDelFolder=True
            if _item["op_type"]=="add" or _item["op_type"]=="modify":
                if _item["exists"]==True:

                    pass
                else:
                    _validateResStr+="项目中,文件"+_item["path"]+"不存在。\n"
                    _validateRes=False

        if _validateRes==False:
            QtWidgets.QMessageBox.warning(self,"警告","项目"+self.__project_root+"中存在以下问题:\n"+_validateResStr+"无法通过文件校验.")
            return
        print("是否需要删除文件夹:"+str(_needDelFolder))
        if _needDelFolder==True:
            _reply=QtWidgets.QMessageBox.warning(self,"警告","此次更新中含有删除某个文件夹的命令,系统将会删除补丁包下对应的文件夹及文件夹下面所有内容,是否确定继续执行?",QtWidgets.QMessageBox.Yes,QtWidgets.QMessageBox.No)
            if _reply==QtWidgets.QMessageBox.Yes:
                _handler_res=_h.fileHandler(self.__project_root,self.__save_root,self.__pack_name,vres)
        else:
            _handler_res=_h.fileHandler(self.__project_root,self.__save_root,self.__pack_name,vres)

        dal=PackerDAL()
        dal.add(self.__recordId,self.txt_desc.toPlainText(),vres)
        self._gen_history_file()
        _reply=QtWidgets.QMessageBox.information(self,"通知","已经成功打包,且打包记录已经更新完毕。")
    def ___init_datas(self):
        dal=PackerDAL()
        item=dal.getRecord(self.__recordId)
        print(item)
        item=item[0]
        self.__pack_name=item[7]
        self.__project_root=item[3]
        self.__save_root=item[4]

    #设置回调函数。
    def setCallBack(self,callback):
        self.callback=callback

    def validateForm(self):
        res=False
        if len(self.txt_desc.toPlainText())<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写此次补丁的修改内容!")
            return False

        #检查跟目录是否有效。
        if os.path.exists(self.__project_root)==False:
            QtWidgets.QMessageBox.warning(self,"警告","项目路径无效,请确定这是准确的项目地址!")
            return False
        if len(self.__save_root)<=0:
            QtWidgets.QMessageBox.warning(self,"警告","请确定补丁文件保存的目录路径!")
            return False
        if os.path.isfile(self.__save_root)==True:
            QtWidgets.QMessageBox.warning(self,"警告","补丁文件的路径必须是文件夹而非文件!")
            return False
        if os.path.exists(self.__save_root)==False:
            QtWidgets.QMessageBox.information(self,"文件夹不存在","补丁包的目录不存在,请先进行新建。")
            return False
        if len(self.__pack_name)<=0:
            QtWidgets.QMessageBox.warning(self,"警告","必须填写补丁包的名称")
            return False
        #检查是否有效字符串。


        return True
    #生成打包日志文件。
    def _gen_history_file(self):
        dal=PackerDAL()
        _h=PackerHandler()
        infos=dal.getPackerHistory(self.__recordId)
        _text=_h.transHistory2Text(infos)
        _save_real_path=os.path.join(self.__save_root,self.__pack_name)
        _log_file_path=os.path.join(_save_real_path,"打包历史记录.txt")
        if os.path.exists(_log_file_path):
            os.remove(_log_file_path)
        file2 = open(_log_file_path,"w")
        file2.write(_text)
        file2.close()
        print(_text)
        pass



#python sqlite

#Author : Hongten
#MailTo : [email protected]
#QQ     : 648719819
#Blog   : http://www.cnblogs.com/hongten
#Create : 2013-08-09
#Version: 1.0

#DB-API 2.0 interface for SQLite databases

import sqlite3
import os
'''SQLite数据库是一款非常小巧的嵌入式开源数据库软件,也就是说
没有独立的维护进程,所有的维护都来自于程序本身。
在python中,使用sqlite3创建数据库的连接,当我们指定的数据库文件不存在的时候
连接对象会自动创建数据库文件;如果数据库文件已经存在,则连接对象不会再创建
数据库文件,而是直接打开该数据库文件。
    连接对象可以是硬盘上面的数据库文件,也可以是建立在内存中的,在内存中的数据库
    执行完任何操作后,都不需要提交事务的(commit)

    创建在硬盘上面: conn = sqlite3.connect('c:\\test\\test.db')
    创建在内存上面: conn = sqlite3.connect('"memory:')

    下面我们一硬盘上面创建数据库文件为例来具体说明:
    conn = sqlite3.connect('c:\\test\\hongten.db')
    其中conn对象是数据库链接对象,而对于数据库链接对象来说,具有以下操作:

        commit()            --事务提交
        rollback()          --事务回滚
        close()             --关闭一个数据库链接
        cursor()            --创建一个游标

    cu = conn.cursor()
    这样我们就创建了一个游标对象:cu
    在sqlite3中,所有sql语句的执行都要在游标对象的参与下完成
    对于游标对象cu,具有以下具体操作:

        execute()           --执行一条sql语句
        executemany()       --执行多条sql语句
        close()             --游标关闭
        fetchone()          --从结果中取出一条记录
        fetchmany()         --从结果中取出多条记录
        fetchall()          --从结果中取出所有记录
        scroll()            --游标滚动

'''


class SqliteHelper():
    #数据库文件绝句路径
    DB_FILE_PATH = ''
    #表名称
    TABLE_NAME = ''
    #是否打印sql
    SHOW_SQL = False


    def get_conn(self):
        '''获取到数据库的连接对象,参数为数据库文件的绝对路径
        如果传递的参数是存在,并且是文件,那么就返回硬盘上面改
        路径下的数据库文件的连接对象;否则,返回内存中的数据接
        连接对象'''
        path = os.path.join(os.path.dirname(__file__), "../resource/db/smart-packer.db")
        conn = sqlite3.connect(path)
        if os.path.exists(path) and os.path.isfile(path):
            print('硬盘上面:[{}]'.format(path))
            return conn
        else:
            conn = None
            print('内存上面:[:memory:]')
            return sqlite3.connect(':memory:')

    #弃用,现在先指定一个具体的path的数据库先。
    def ____get_conn(self,path):
        '''获取到数据库的连接对象,参数为数据库文件的绝对路径
        如果传递的参数是存在,并且是文件,那么就返回硬盘上面改
        路径下的数据库文件的连接对象;否则,返回内存中的数据接
        连接对象'''
        conn = sqlite3.connect(path)
        if os.path.exists(path) and os.path.isfile(path):
            print('硬盘上面:[{}]'.format(path))
            return conn
        else:
            conn = None
            print('内存上面:[:memory:]')
            return sqlite3.connect(':memory:')

    def get_cursor(self,conn):
        '''该方法是获取数据库的游标对象,参数为数据库的连接对象
        如果数据库的连接对象不为None,则返回数据库连接对象所创
        建的游标对象;否则返回一个游标对象,该对象是内存中数据
        库连接对象所创建的游标对象'''
        if conn is not None:

            return conn.cursor()
        else:
            return self.get_conn('').cursor()

    ###############################################################
    ####            创建|删除表操作     START
    ###############################################################
    def drop_table(self,conn, table):
        '''如果表存在,则删除表,如果表中存在数据的时候,使用该
        方法的时候要慎用!'''
        if table is not None and table != '':
            sql = 'DROP TABLE IF EXISTS ' + table
            if self.SHOW_SQL:
                print('执行sql:[{}]'.format(sql))
            cu = self.get_cursor(conn)
            cu.execute(sql)
            conn.commit()
            print('删除数据库表[{}]成功!'.format(table))
            self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(table))

    def create_table(self,conn, sql):
        '''创建数据库表:student'''
        if sql is not None and sql != '':
            cu = self.get_cursor(conn)
            if self.SHOW_SQL:
                print('执行sql:[{}]'.format(sql))
            cu.execute(sql)
            conn.commit()
            print('创建数据库表[student]成功!')
            self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))

    ###############################################################
    ####            创建|删除表操作     END
    ###############################################################

    def close_all(self,conn, cu):
        '''关闭数据库游标对象和数据库连接对象'''
        try:
            if cu is not None:
                cu.close()
        finally:
            if cu is not None:
                cu.close()

    ###############################################################
    ####            数据库操作CRUD     START
    ###############################################################

    def save(self,conn, sql, data):
        '''插入数据'''
        if sql is not None and sql != '':
            if data is not None:
                cu = self.get_cursor(conn)
                for d in data:
                    if self.SHOW_SQL:
                        print('执行sql:[{}],参数:[{}]'.format(sql, d))
                    cu.execute(sql, d)
                    conn.commit()
                self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))

    def fetchall(self,conn, sql):
        '''查询所有数据'''
        results=[]
        if sql is not None and sql != '':
            cu = self.get_cursor(conn)
            if self.SHOW_SQL:
                print('执行sql:[{}]'.format(sql))
            cu.execute(sql)
            r = cu.fetchall()
            if len(r) > 0:
                for e in range(len(r)):
                    print(r[e])
                    results.append(r[e])
            self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))

        return results

    #获取自增主键最后一个记录id。
    def getLastId(self,conn,tableName):
        sql_getId='Select  seq From sqlite_sequence  Where name = ? '
        conn=self.get_conn()
        data=tableName
        rec_=self.fetchone(conn,sql_getId,data)
        _recordId=rec_[0][0]
        return _recordId
    def fetchone(self,conn, sql, data):
        '''查询一条数据'''
        results=[]
        if sql is not None and sql != '':
            if data is not None:
                #Do this instead
                d = (data,)
                cu = self.get_cursor(conn)
                if self.SHOW_SQL:
                    print('执行sql:[{}],参数:[{}]'.format(sql, data))
                cu.execute(sql, d)
                r = cu.fetchall()
                if len(r) > 0:
                    for e in range(len(r)):
                        print(r[e])
                        results.append(r[e])
                self.close_all(conn, cu)
            else:

                cu = self.get_cursor(conn)
                if self.SHOW_SQL:
                    print('执行sql:[{}],参数:[{}]'.format(sql, data))
                cu.execute(sql)
                r = cu.fetchall()
                if len(r) > 0:
                    for e in range(len(r)):
                        print(r[e])
                        results.append(r[e])
                self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))

        return results

    def update(self,conn, sql, data):
        '''更新数据'''
        if sql is not None and sql != '':
            if data is not None:
                cu = self.get_cursor(conn)
                for d in data:
                    if self.SHOW_SQL:
                        print('执行sql:[{}],参数:[{}]'.format(sql, d))
                    cu.execute(sql, d)
                    conn.commit()
                self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))

    def delete(self,conn, sql, data):
        '''删除数据'''
        if sql is not None and sql != '':
            if data is not None:
                cu = self.get_cursor(conn)
                for d in data:
                    if self.SHOW_SQL:
                        print('执行sql:[{}],参数:[{}]'.format(sql, d))
                    cu.execute(sql, data)
                    conn.commit()
                self.close_all(conn, cu)
                pass
            else:
                cu = self.get_cursor(conn)
                cu.execute(sql)
                conn.commit()
                self.close_all(conn, cu)
        else:
            print('the [{}] is empty or equal None!'.format(sql))



__author__ = 'Administrator'
from Base.SqliteHelper import *


def initDB():
    sh=SqliteHelper()
    #第一步,删除project表并创建project表。
    conn = sh.get_conn()
    sh.drop_table(conn, "project")
    create_table_sql = '''CREATE TABLE `project` (
                          `id` int(20) NOT NULL,
                          `name` varchar(50) NOT NULL,
                          `desc` varchar(400) DEFAULT NULL,
                          `createtime` int(11) DEFAULT NULL,
                          `updatetime` int(11) DEFAULT NULL,
                          `project_root` varchar(400) DEFAULT NULL,
                          `packer_root` varchar(400) DEFAULT NULL,
                          `packer_name` varchar(50) DEFAULT NULL,
                           PRIMARY KEY (`id`)
                        )'''
    conn = sh.get_conn()
    sh.create_table(conn, create_table_sql)
    #第二步,创建project对应的打包表,将每次打包记录都存放起来。
    conn = sh.get_conn()
    sh.drop_table(conn, "project_packer")
    create_table_sql = '''CREATE TABLE `project_packer` (
                          `id` INTEGER PRIMARY KEY AUTOINCREMENT ,
                          `project_id` int(20) NOT NULL,

                          `desc` varchar(400) DEFAULT NULL,

                          `createtime` int(11) DEFAULT NULL,
                          `updatetime` int(11) DEFAULT NULL
                        )'''

    conn = sh.get_conn()
    sh.create_table(conn, create_table_sql)
    #第三步,创建每次打包对应的文件列表。
    conn = sh.get_conn()
    sh.drop_table(conn, "project_packer_item")
    create_table_sql = '''CREATE TABLE `project_packer_item` (
                          `id` INTEGER PRIMARY KEY AUTOINCREMENT ,
                          `packer_id` int(20) NOT NULL,

                          `file_path` varchar(400) DEFAULT NULL,
                          `op_type` varchar(10) DEFAULT NULL
                        )'''
    conn = sh.get_conn()
    sh.create_table(conn, create_table_sql)
#initDB()



__author__ = 'Administrator'
from Base.SqliteHelper import *
import time
#项目信息数据库操作类。
class ProjectDAL():
    def _getUIID(self):
        return (round(time.time()*100000))
    def add(self,name,desc,project_root,packer_root,packer_name):
        _recordId=self._getUIID()
        theTime=round(time.time())
        sh=SqliteHelper()
        save_sql = '''INSERT INTO project values (?, ?, ?, ?, ?, ?,?,?)'''
        data = [(_recordId, name, desc, project_root, packer_root, theTime,theTime,packer_name)]
        conn = sh.get_conn()
        sh.save(conn, save_sql, data)
        return _recordId
    def update(self,name,desc,project_root,packer_root,packer_name,recordId):
        theTime=round(time.time())
        save_sql = '''update project set name=?,desc=?,project_root=?,packer_root=?,packer_name=?,updatetime=? where id=?'''
        data = [(name,desc,project_root,packer_root,packer_name,theTime,recordId)]
        sh=SqliteHelper()
        conn = sh.get_conn()
        sh.update(conn, save_sql, data)
        return True
    def getList(self):
        fetchall_sql = '''SELECT * FROM project'''
        sh=SqliteHelper()
        conn = sh.get_conn()
        return sh.fetchall(conn, fetchall_sql)

    def getPager(self):
        pass
    def getRecord(self,id):
        sh=SqliteHelper()
        fetchone_sql = 'SELECT * FROM project WHERE id = ? '
        data = id
        conn = sh.get_conn()
        return sh.fetchone(conn, fetchone_sql, data)

    def delRecord(self,recordId):
        '''删除数据...'''

        sh=SqliteHelper()
        delete_sql = 'DELETE FROM project WHERE  ID = ? '
        data =(str(recordId),)
        conn = sh.get_conn()
        sh.delete(conn, delete_sql, data)
        return True
    def func1(self):
        pass


__author__ = 'Administrator'
import re
import os
import shutil
import time
class PackerHandler():
    #转换成为file items。
    def trans2FileItems(self,originStr):
        res=[]
        _str=originStr.replace("\r\n","\n")
        xx="\n+"

        pattern = re.compile(xx)
        results=re.split(pattern,_str)
        _blank="\s+"
        p_blank=re.compile(_blank)
        #获取到相关行数,然后继续处理操作方式以及路径,
        for itemIndex in range(len(results)):
            _s=results[itemIndex]
            _s=_s.strip()
            if len(_s) > 0:
                pass
            else:
                continue
            print(itemIndex)
            print(_s)
            _item_op_type="modify"
            _item_file_path=""
            _item_split=re.split(p_blank,_s)
            if len(_item_split) > 1:
                _item_op_type=_item_split[0]
                _item_file_path=_item_split[1]
            else:
                _item_file_path=_item_split[0]

            _item_op_type=_item_op_type.lower().strip()

            if _item_op_type=="m" or _item_op_type == "modify":
                _item_op_type="modify"
            elif _item_op_type=="a"  or _item_op_type=="add":
                _item_op_type="add"
            elif _item_op_type=="d" or _item_op_type=="del" or _item_op_type=="delete":
                _item_op_type="del"
            else:
                _item_op_type="modify"

            iteminfo={
                "op_type":_item_op_type,
                "path":_item_file_path
            }
            res.append(iteminfo)


        #results =  pattern.findall(originStr)

        return res

    #打包文件的有效性验证。。。通常只验证add modify 相关文件在原始目录是否存在。

    def validateFiles(self,project_root,files):
        listFiles=[]
        for itemIndex in range(len(files)):
            fileItem=files[itemIndex]
            print(fileItem)
            _fileRealPath=os.path.join(project_root, fileItem["path"])
            #print(_fileRealPath)
            fileNewItem={"op_type":fileItem["op_type"],"path":fileItem["path"]}
            if fileItem["op_type"]=="add" or fileItem["op_type"]=="modify":
                if os.path.exists(_fileRealPath):
                     fileNewItem["exists"]=True
                     if os.path.isfile(_fileRealPath):
                        fileNewItem["filetype"]="file"
                     else:
                         fileNewItem["filetype"]="dir"
                else:
                    fileNewItem["exists"]=False
            else:
                pass

            listFiles.append(fileNewItem)


        return  listFiles
    #经过了解释字符串,验证文件列表,然后就到了复制或者删除文件的地步了。
    def fileHandler(self,project_root,save_root,pack_name,files):
        _save_real_path=os.path.join(save_root,pack_name)
        if os.path.exists(_save_real_path)==False:
            os.makedirs(_save_real_path)

        for itemIndex in range(len(files)):
            fileItem=files[itemIndex]
            _originPath=os.path.join(project_root, fileItem["path"])
            _newPath=os.path.join(_save_real_path,fileItem["path"])


            #假如是文件,那么进行何种操作。假如是目录,那么进行何种操作。
            if os.path.isfile(_originPath):
                if os.path.exists(_newPath):
                    os.remove(_newPath)
                else:
                    #判断父亲文件夹是否存在。
                    _paths=os.path.split(_newPath)
                    if os.path.exists(_paths[0])==False:
                        os.makedirs(_paths[0])


                if fileItem["op_type"] == "add" or fileItem["op_type"]=="modify":
                    shutil.copyfile(_originPath,_newPath)
                pass
            elif os.path.isdir(_originPath):
                if fileItem["op_type"]=="del":
                    if os.path.exists(_newPath):
                        shutil.rmtree(_newPath)
                elif fileItem["op_type"]=="add":
                    if os.path.exists(_newPath)==False:
                        os.makedirs(_newPath)
                elif fileItem["op_type"]=="modify":
                    if os.path.exists(_newPath)==False:
                        os.makedirs(_newPath)

                pass




        pass


    #将相关历史记录转换成为成为人类可以看见的字符串形式。
    def transHistory2Text(self, historyInfos):
        _str = ''''''
        _str += '======================================================================================================\n'
        _str += '===============================================打包记录================================================\n'
        _str += '======================================================================================================\n'
        for itemIndex in range(len(historyInfos)):
            item = historyInfos[itemIndex]
            print("现在 item时候:")
            print(item)
            _timeStamp = int(item["createtime"])
            timeArray = time.localtime(_timeStamp)
            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)

            _str += '------------------------------------------------------------------------------------------------------\n'
            _str += "打包时间:\n"
            _str += otherStyleTime
            _str += "\n"
            _str += "打包描述:\n"
            _str += item["desc"]
            _str += "\n"
            _str += "文件列表:\n"
            for index2 in range(len(item["items"])):
                item2 = item["items"][index2]
                op_type = ""
                if item2["op_type"] == "add":
                    op_type = "添加(a)"
                elif item2["op_type"] == "modify":
                    op_type = "编辑(m)"
                elif item2["op_type"] == "del":
                    op_type = "删除(d)"
                _str += op_type + " " + item2["path"] + "\n"
                pass

            _str += "\n"
            _str += '------------------------------------------------------------------------------------------------------\n'
            _str += "\n"
            _str += "\n"
            _str += "\n"

        return _str



界面。。。程序员的审美就别吐槽了



使用python和pyqt开发一款简易打包工具_第1张图片



使用python和pyqt开发一款简易打包工具_第2张图片





使用python和pyqt开发一款简易打包工具_第3张图片

你可能感兴趣的:(使用python和pyqt开发一款简易打包工具)