前言
一直在做游戏服务器的开发工作,很多时候都要与数据打交道,很多数据都是手动修改,费时间也不易维护。一直想写一个工具一键导出游戏数据,不知从何写起;恰好现在新开发游戏,开发新游戏的同时,也写一个简简单单的数据导出工具,方便策划和程序查看或修改数据,协助新项目的进行。
本次使用的主要语言是Python,导出语言是后端的Lua表,以及前端的Json数据。为了方便策划规划、修改数据,数据来源主要是Excel,后期可能会做成数据库,但从策划的效率上来看,还是Excel方便些。
开始
开发环境
首先安装Python,我在做项目时已经更新到了3.9,由于很多库(例如pandas)都不支持2.0,因此3.0是一个不错的选择。
Python的集成开发环境我推荐使用PyCharm,社区版完全足够个人学习或开发,没有安装的童鞋可以前往这里学习并安装PyCharm和Python。
接下来需要安装此次必要的开发包,需要用到Python的pip安装工具,pip会到https://pythonhosted.org/下载指定包,但外网访问速度过慢,可使用参数-i指定下载地址,具体网址如下:
清华 | https://pypi.tuna.tsinghua.edu.cn/simple/ |
阿里云 | http://mirrors.aliyun.com/pypi/simple/ |
中国科技大学 | https://pypi.mirrors.ustc.edu.cn/simple/ |
华中理工大学 | http://pypi.hustunique.com/ |
山东理工大学 | http://pypi.sdutlinux.org/ |
豆瓣 | http://pypi.douban.com/simple/ |
也可配置pip,永久性指定pip安装源:
- Linux下,修改 ~/.pip/pip.conf (没有就创建一个文件夹及文件。文件夹要加“.”,表示是隐藏文件夹),填写以下内容:
-
-
[global]
-
index-url = http://mirrors.aliyun.com/pypi/simple/
-
[install]
-
trusted-host=mirrors.aliyun.com
- windows下,在User目录中创建一个pip目录,如:C:\Users\xx\pip,新建文件pip.ini。内容同上。
pip安装完成之后,在WIndows PowerShell即可安装必要库,pyqt5,pyqt5-tools,pandas,xlrd等包。命令如下:
pip3 install pyqt5, pyqt5-tools, pandas, xlrd
其中, PyQt5是一套Python绑定Digia QT5应用的框架,Pandas是一个强大的分析结构化数据的工具集, xlrd则是读取Excel的必要包。
创建项目
1、打开PyCharm,选择新建项目,并正确的选择Python版本,并勾选inhert global site-packages(使用系统的库),漏选可在项目中重新构建项目解释器,如下图:
新建项目:
项目解释器:
2、为PyCharm添加工具
打开PyCharm的Settings,选择Tools -> External Tools,点'+'添加工具。
(1. 配置QtDesigner:
-
name:QtDesigner
-
Program:选择Qt的designer.exe的路径
-
Working
directory:$ProjectFileDir$
(2. 配置PyUIC
-
name: PyUIC
-
Program:选择python.exe的路径
-
Arguments:-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
-
Working
directory:$FileDir$
(3. 配置PyRCC
-
Name: PyRCC
-
Program: 选择pyrcc5.exe的路径
-
Arguments: $FileName$ -o $FileNameWithoutExtension$_rc.py
-
Working
directory: $FileDir$
3、制作界面
在工程目录下新建文件夹ui,用于存放工程所需的界面文件、图标文件。
在工程目录下新建文件夹excel,用于存放数据编辑器开发阶段用的开发用例。
在工程目录下新建文件夹db,用于存放数据编辑器所产生的个人数据(也可用xml、conf等格式),例如界面状态,界面比例等等。
打开designer.exe,可以直接通过我们开篇设置的工具打开,有以下三种方式:
- 在Project中选择任意文件或文件夹,右键选择External Tools -> QtDesigner。
- 在菜单栏中,点击Tools -> External Tools -> QtDesigner。
- 在编辑窗口中,右键选择External Tools -> QtDesigner。
打开designer之后,如下所示:
选择创建一个MainWindow,保存到项目目录下的ui文件夹中,保存名为mainwindow_main.ui。
再次打开designer,开始设计主界面布局,重点添加菜单栏、工具栏、状态栏,以及工作区,同时为了美观,加入一些小图标,小图标必须放在ui或ui目录下的文件夹中,如图所示:
选择MainWindow,在右侧的属性编辑器中设置MainWindow的属性,如名称等等,这里我们需要设置程序的初始大小以及最小大小,防止在使用过程中将程序拉缩过小引起不必要的BUG,如下图所示:
设置完成之后添加菜单栏,工具栏,在窗口右键选择添加工具栏,具体如下:
添加基本的菜单,其中,一级菜单栏后面添加(&字母)可以使用Alt+快捷键,例如文件菜单可使用文件(&F)(使用搜狗输入法会默认占用Alt+F等快捷键,可在设置中关闭该功能,并且其他应用也可能占用该快捷键,望知晓),编辑菜单可使用编辑(&E)。
在资源管理器窗口中添加小图标资源(误关闭在视图中勾选打开),选择铅笔按钮,添加图片,具体如下:
在动作编辑窗口中添加action,并为action添加图标、名称、快捷键等信息,具体如下:
建立完成后,可以拖拽action到菜单栏或工具栏中,添加action,具体如下:
按照上述方法,建立完整的菜单栏,工具栏,效果如下(使用Ctrl+R即可预览):
在工作区中左侧添加一个Tree View,右侧添加一个Table Widget,如下图所示:
在空白处右键点击布局 - 在窗体布局中布局,如下图所示:
也可以使用一个Frame包裹Table Widget,用于后续的扩展,此时Table Widget要在Frame中,并且使用栅格布局,如下图所示:
在对象查看器中选择MainWIndow,右键添加状态栏。
使用Ctrl+R预览,要保证窗口不能拉缩过小,Tree Widget的宽度不变,高度占满程序高度,Table Widget宽高占用剩余部分,最终效果如下:
再次保存文件至ui中的mainwindow_main.ui,退出designer,开始开发逻辑部分。
这里我添加了一个登陆Dialog,暂时没有用到,留着后续使用:
(1.选择文件,新建一个Dialog,不需要按钮。
(2.设置窗口的最小大小和最大大小,登陆窗口操作者不能改变大小,同时设置这两个参数便可阻止操作者做不可预料的事情:
(3.为窗口添加控件,并且使用占位控件,设置控件之间的距离,效果如下:
(4.设置设置账号输入框和密码输入框QLineEdit属性中的maxLength为16个字符,密码输入框的QLineEdit属性中的echoMode为password,用于隐藏输入信息,效果如下:
保存并取名为dialog_login.ui,登录界面制作完成。
4、编写响应逻辑
打开PyCharm,要在Python中使用designer制作的界面文件,需要把ui文件和qrc文件转换成py文件,这里就需要我们开始设置的另外两个工具PyUIC和PyRCC了。
选中Project窗口中的ui.qrc(必须要选中),使用PyRCC生成ui_rc.py,即可生成二级制的Python资源。
选中Project窗口中的mainwindow_main.ui(必须要选中),使用PyUIC生成mainwindow_main.py,即可生成我们编写的界面Python文件。
注意:由于我们没有在main.py文件所在目录下创建界面,因此要手动删除mainwindow_main.py文件中引用ui_rc的代码,在main.py中再引用
修改main.py文件,删除原有内容,写入以下内容:
-
# coding:utf-8
-
-
import sys
-
import ui.mainwindow_main
-
import ui.ui_rc
-
from PyQt5.QtWidgets
import QApplication, QMainWindow
-
-
class Application(QMainWindow):
-
def __init__(self, parent=None):
-
super(QMainWindow, self).__init__(parent)
-
-
self.ui = ui.mainwindow_main.Ui_MainWindow()
-
self.ui.setupUi(self)
-
-
if __name__ ==
'__main__':
-
app = QApplication(sys.argv)
-
main = Application()
-
main.show()
-
sys.exit(app.exec_())
在代码中设置默认最大化显示。并为动作添加信号和槽。绑定局部按钮、菜单的响应函数。
-
class Application(QMainWindow):
-
def __init__(self, parent=None):
-
super(QMainWindow, self).__init__(parent)
-
-
self.ui = ui.mainwindow_main.Ui_MainWindow()
-
self.ui.setupUi(self)
-
# 最大化显示
-
self.showMaximized()
-
-
self.initHandle()
-
-
def initHandle(self):
-
self.ui.action_exit.triggered.connect(qApp.quit)
-
self.ui.action_import.triggered.connect(self.importData)
-
self.ui.action_export.triggered.connect(self.exportData)
-
-
def importData(self):
-
pass
-
-
def exportData(self):
-
pass
在主目录中新建文件夹excel, 添加excel文档, 并按照以下规则添加内容:
Id | 数据类型(extra) | 数据类型(special) | 数据类型(table) | 数据类型(int) | 数据类型(float) | 数据类型(bool) | 数据类型(string) | 数据类型(vector) |
id | time | condition | data | num | cd | isEntity | name | arr |
int | extra | special | table | int | float | bool | string | vector |
TimeData | TimeData | |||||||
1 | TimeData,1 | 1,2 | 1.10 | 1 | 游戏玩家 | 1 | ||
2 | 2 | ExtraData,1 | 2,3 | 2 | 1.20 | 0 | 游戏玩家 | 1,2 |
3 | 3 | TimeData,2 | 1,3 | 3 | 1.30 | TRUE | 游戏玩家 | 1.0,3.1 |
4 | 4 | ExtraData,2 | 2,4 | 4 | 1.40 | FALSE | 游戏玩家 | true,false |
5 | 5 | TimeData,1 | 1,4 | 5 | 1.50 | true | 游戏玩家 | "你好","世界" |
6 | 6 | ExtraData,1 | 2,5 | 9999999 | 1.60 | false | 游戏玩家 | "50%","100%" |
ExampleData的数据:
TimeData的数据:
其中, 第一行为说明文字,便于理解,第二行是字段名,第三行是字段类型,由于有可能涉及到跨表,所以第四行是跨表的表名。并且规定table是列表式的扩展;extra是单数据的扩展;special是指定表名的扩展。
在主类中添加读取Excel模块, 添加对Excel的解析类, 具体内容如下:
-
# coding:utf-8
-
-
import sys
-
import os
-
import re
-
import ui.mainwindow_main
-
import ui.ui_rc
-
from PyQt5.QtWidgets
import QApplication, QMainWindow, QMessageBox, QTreeWidgetItem, qApp, QTableWidgetItem
-
from PyQt5.QtCore
import Qt
-
from PyQt5
import QtCore, QtGui
-
import pandas
as pd
-
import threading
-
import numpy
as np
-
-
class ExcelData:
-
def __init__(self, filepath):
-
self.filepath = filepath
-
-
self.sheets = {}
-
self.finish =
False
-
# self.readSheet()
-
self.read_thread = threading.Thread(target=self.readSheet, args={
0})
-
self.read_thread.start()
-
-
def readSheet(self, name=0):
-
if name
in self.sheets:
-
return self.sheets[name]
-
sheet = pd.read_excel(self.filepath, sheet_name=name)
-
data = {}
-
data[
'row'] = sheet.shape[
0] -
3
-
data[
'col'] = sheet.shape[
1]
-
data[
'title'] = sheet.columns.values.tolist()
-
-
row_values_header = np.array(sheet.iloc[[
0]]).tolist()[
0]
-
row_values_type = np.array(sheet.iloc[[
1]]).tolist()[
0]
-
row_values_table = np.array(sheet.iloc[[
2]]).tolist()[
0]
-
-
data[
'header'] = []
-
data[
'type'] = []
-
data[
'table'] = []
-
for i
in range(data[
'col']):
-
data[
'header'].append(row_values_header[i])
-
data[
'type'].append(row_values_type[i])
-
data[
'table'].append(row_values_table[i])
-
pass
-
-
for t
in data[
'table']:
-
if t == t:
-
self.readSheet(t)
-
pass
-
-
data[
'record'] = {}
-
for i
in range(
3, data[
'row']+
3):
-
row_values_list = np.array(sheet.iloc[[i]]).tolist()[
0]
-
for j
in range(data[
'col']):
-
data[
'record'][(i
-3, j)] = row_values_list[j]
-
pass
-
-
self.sheets[name] = data
-
self.finish =
True
-
-
class Application(QMainWindow):
-
def __init__(self, parent=None):
-
super(QMainWindow, self).__init__(parent)
-
-
self.global_data = {}
-
-
self.ui = ui.mainwindow_main.Ui_MainWindow()
-
self.ui.setupUi(self)
-
self.showMaximized()
-
-
self.readExcel()
-
-
self.initHandle()
-
-
def initHandle(self):
-
self.ui.action_exit.triggered.connect(qApp.quit)
-
self.ui.action_import.triggered.connect(self.importData)
-
self.ui.action_export.triggered.connect(self.exportData)
-
-
def setTableWidgetData(self, qmodeLindex):
-
name = self.ui.treeWidget.currentItem().text(
0)
-
item = self.global_data[name]
-
if
not item.finish:
-
QMessageBox.critical(self, (
'提示'), (
'正在加载中, 请稍后......'), QMessageBox.Ok)
-
return
-
data = item.sheets[
0]
-
-
self.ui.tableWidget.setRowCount(data[
'row'])
-
self.ui.tableWidget.setColumnCount(data[
'col'])
-
self.ui.tableWidget.setHorizontalHeaderLabels(data[
'title'])
-
-
for i
in range(data[
'row']):
-
for j
in range(data[
'col']):
-
item = QTableWidgetItem(str(data[
'record'][(i, j)]))
-
item.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
-
self.ui.tableWidget.setItem(i, j, item)
-
pass
-
-
def readExcel(self):
-
self.ui.treeWidget.doubleClicked.connect(self.setTableWidgetData)
-
-
self.ui.treeWidget.setColumnCount(
1)
-
self.ui.treeWidget.setHeaderLabels([
'Key'])
-
for root, dirs, files
in os.walk(
'excel'):
-
for file
in files:
-
res = re.findall(
'\\~\\$', file)
-
if len(res) >
0:
-
continue
-
name = file.split(
'.')[
0]
-
root = QTreeWidgetItem(self.ui.treeWidget)
-
root.setText(
0, name)
-
-
self.global_data[name] = ExcelData(
'excel/' + file)
-
pass
-
-
def importData(self):
-
pass
-
-
def exportData(self):
-
pass
-
-
if __name__ ==
'__main__':
-
app = QApplication(sys.argv)
-
main = Application()
-
main.show()
-
sys.exit(app.exec_())
从Excel导出的数据中, 很多都与我们设计的初衷有出路, 比如Excel中的小数导入到程序中会变成很多的有效位的数字, 再比如在填表中错误的将字符串填入数字类型中, 简而言之我们需要程序有纠错机制, 也就是对数据的检查, 并及时的通知程序员某些数据有问题需要修正. 因此, 需要对数据进行修正, 具体如下:
-
TYPE_INT = [
'int',
'sint',
'cint',
'dint']
-
TYPE_FLO = [
'float',
'sfloat',
'cfloat',
'dfloat']
-
TYPE_STR = [
'string',
'sstring',
'cstring',
'dstring']
-
TYPE_BOL = [
'bool',
'sbool',
'cbool',
'dbool']
-
TYPE_VEC = [
'vector',
'svector',
'cvector',
'dvector']
-
TYPE_EXT = [
'extra',
'sextra',
'cextra',
'iextra',
'ciextra',
'dextra']
-
TYPE_TAB = [
'table',
'stable',
'ctable',
'itable',
'citable',
'dtable']
-
TYPE_SPE = [
'special',
'sspecial',
'cspecial',
'dspecial']
-
-
TYPE_S = [
'int',
'sint',
'float',
'sfloat',
'string',
'sstring',
'bool',
'sbool',
'vector',
'svector',
'extra',
'sextra',
'iextra',
'table',
'stable',
'itable',
'special',
'sspecial']
-
-
TYPE_S_INT = [
'int',
'sint']
-
TYPE_S_FLO = [
'float',
'sfloat']
-
TYPE_S_STR = [
'string',
'sstring']
-
TYPE_S_BOL = [
'bool',
'sbool']
-
TYPE_S_VEC = [
'vector',
'svector']
-
TYPE_S_EXT = [
'extra',
'sextra',
'iextra']
-
TYPE_S_TAB = [
'table',
'stable',
'itable']
-
TYPE_S_SPE = [
'special',
'sspecial']
-
-
TYPE_C = [
'int',
'cint',
'float',
'cfloat',
'string',
'cstring',
'bool',
'cbool',
'vector',
'cvector',
'extra',
'cextra',
'iextra',
'ciextra',
'table',
'ctable',
'itable',
'citable',
'special',
'cspecial']
-
-
TYPE_C_INT = [
'int',
'cint']
-
TYPE_C_FLO = [
'float',
'cfloat']
-
TYPE_C_STR = [
'string',
'cstring']
-
TYPE_C_BOL = [
'bool',
'cbool']
-
TYPE_C_VEC = [
'vector',
'cvector']
-
TYPE_C_EXT = [
'extra',
'cextra',
'iextra',
'ciextra']
-
TYPE_C_EXI = [
'iextra',
'ciextra']
-
TYPE_C_TAB = [
'table',
'ctable',
'itable',
'citable']
-
TYPE_C_TAI = [
'itable',
'citable']
-
TYPE_C_SPE = [
'special',
'cspecial']
-
for i
in range(
3, data[
'row']+
3):
-
row_values_list = np.array(sheet.iloc[[i]]).tolist()[
0]
-
for j
in range(data[
'col']):
-
data[
'record'][(i
-3, j)] = self.serialize(row_values_list[j], data[
'type'][j])
-
pass
-
def serialize(self, data, type=None):
-
if data != data:
-
data =
''
-
value = str(data)
-
state =
0
-
try:
-
if type
in TYPE_INT:
-
value = int(value)
-
elif type
in TYPE_FLO:
-
value = float(
'%.3f' % float(value))
-
elif type
in TYPE_STR:
-
value = str(value)
-
elif type
in TYPE_BOL:
-
if data
in [
'false',
'False',
'FALSE',
0,
0.0]:
-
value =
False
-
elif data
in [
'true',
'True',
'TRUE',
1,
1.0]:
-
value =
True
-
else:
-
value =
1
-
state =
1
-
elif type
in TYPE_VEC:
-
if
not re.match(
'^((true|false|-?\d+|".+"|-?\d+\.\d*),)*(true|false|-?\d+|".+"|-?\d+\.\d*)?$', value):
-
state =
2
-
elif type
in TYPE_TAB:
-
pass
-
elif type
in TYPE_EXT:
-
pass
-
elif type
in TYPE_SPE:
-
pass
-
else:
-
state =
2
-
except ValueError:
-
state =
2
-
info = {
'data':value,
'state':state}
-
return info
-
for i
in range(data[
'row']):
-
for j
in range(data[
'col']):
-
table = data[
'record'][(i, j)]
-
item = QTableWidgetItem(str(table[
'data']))
-
item.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
-
item.setBackground(self.setItemColor(table[
'state']))
-
self.ui.tableWidget.setItem(i, j, item)
-
pass
-
-
def setItemColor(self, state):
-
if state ==
1:
-
return QtGui.QColor(
255,
255,
0)
-
elif state ==
2:
-
return QtGui.QColor(
255,
0,
0)
-
return QtGui.QColor(
255,
255,
255)
然后就是导出数据相关的设置了,在导出的过程中,首先将数据格式化成字典,然后将字典转化成所需的格式。这里可以加载外部函数,做到导出逻辑可分离模式;也可直接在程序中指定规则,不过在发布成程序后,定制性功能不好做而已。具体按照需求定制,其中,导出Json,可以使用Python的Json库,Lua则可以仿照Json书写。
将在下一篇文章中制作,这里就不做演示了。
本篇文章的最终完整代码如下:
-
# coding:utf-8
-
-
import sys
-
import os
-
import re
-
import ui.dialog_login
-
import ui.mainwindow_main
-
import ui.ui_rc
-
from PyQt5.QtWidgets
import QApplication, QDialog, QMainWindow, QMessageBox, QTreeWidgetItem, qApp, QTableWidgetItem
-
from PyQt5.QtCore
import Qt
-
from PyQt5
import QtCore, QtGui
-
from PyQt5.QtSql
import QSqlDatabase, QSqlQuery
-
import pandas
as pd
-
import threading
-
import numpy
as np
-
-
TYPE_INT = [
'int',
'sint',
'cint',
'dint']
-
TYPE_FLO = [
'float',
'sfloat',
'cfloat',
'dfloat']
-
TYPE_STR = [
'string',
'sstring',
'cstring',
'dstring']
-
TYPE_BOL = [
'bool',
'sbool',
'cbool',
'dbool']
-
TYPE_VEC = [
'vector',
'svector',
'cvector',
'dvector']
-
TYPE_EXT = [
'extra',
'sextra',
'cextra',
'iextra',
'ciextra',
'dextra']
-
TYPE_TAB = [
'table',
'stable',
'ctable',
'itable',
'citable',
'dtable']
-
TYPE_SPE = [
'special',
'sspecial',
'cspecial',
'dspecial']
-
-
TYPE_S = [
'int',
'sint',
'float',
'sfloat',
'string',
'sstring',
'bool',
'sbool',
'vector',
'svector',
'extra',
'sextra',
'iextra',
'table',
'stable',
'itable',
'special',
'sspecial']
-
-
TYPE_S_INT = [
'int',
'sint']
-
TYPE_S_FLO = [
'float',
'sfloat']
-
TYPE_S_STR = [
'string',
'sstring']
-
TYPE_S_BOL = [
'bool',
'sbool']
-
TYPE_S_VEC = [
'vector',
'svector']
-
TYPE_S_EXT = [
'extra',
'sextra',
'iextra']
-
TYPE_S_TAB = [
'table',
'stable',
'itable']
-
TYPE_S_SPE = [
'special',
'sspecial']
-
-
TYPE_C = [
'int',
'cint',
'float',
'cfloat',
'string',
'cstring',
'bool',
'cbool',
'vector',
'cvector',
'extra',
'cextra',
'iextra',
'ciextra',
'table',
'ctable',
'itable',
'citable',
'special',
'cspecial']
-
-
TYPE_C_INT = [
'int',
'cint']
-
TYPE_C_FLO = [
'float',
'cfloat']
-
TYPE_C_STR = [
'string',
'cstring']
-
TYPE_C_BOL = [
'bool',
'cbool']
-
TYPE_C_VEC = [
'vector',
'cvector']
-
TYPE_C_EXT = [
'extra',
'cextra',
'iextra',
'ciextra']
-
TYPE_C_EXI = [
'iextra',
'ciextra']
-
TYPE_C_TAB = [
'table',
'ctable',
'itable',
'citable']
-
TYPE_C_TAI = [
'itable',
'citable']
-
TYPE_C_SPE = [
'special',
'cspecial']
-
-
class DBManager():
-
def __init__(self):
-
self.db = QSqlDatabase.addDatabase(
'QSQLITE')
-
self.db.setDatabaseName(
'./db/DataEditor.db')
-
if
not self.db.open():
-
QMessageBox.critical(
None, (
'无法打开数据库'), (
'提示'), QMessageBox.Cancel)
-
return
-
-
self.query = QSqlQuery()
-
self.query.exec_(
'create table users (name varchar(30), password varchar(30))')
-
-
def exec(self, sql):
-
res = self.query.exec_(sql)
-
pass
-
-
class LoginDialog(QDialog):
-
ok_signal = QtCore.pyqtSignal(str, str)
-
cancel_signal = QtCore.pyqtSignal()
-
def __init__(self, parent=None):
-
super(QDialog, self).__init__(parent)
-
self.ui = ui.dialog_login.Ui_dlg_login()
-
self.ui.setupUi(self)
-
-
self.setWindowFlags(Qt.FramelessWindowHint)
-
self.setWindowModality(Qt.WindowModal)
-
-
self.ui.btn_login.clicked.connect(self.ok)
-
self.ui.btn_cancel.clicked.connect(qApp.quit)
-
-
self.ui.btn_login.setDefault(
True)
-
-
def ok(self):
-
account = self.ui.edt_account.text()
-
password = self.ui.edt_password.text()
-
if len(account) <
2
or len(password) <
6:
-
QMessageBox.critical(self, (
'提示'), (
'请正确输入用户名或密码'), QMessageBox.Ok)
-
return
-
-
self.ok_signal.emit(self.ui.edt_account.text(), self.ui.edt_password.text())
-
self.close()
-
-
class ExcelData:
-
def __init__(self, filepath):
-
self.filepath = filepath
-
-
self.sheets = {}
-
self.finish =
False
-
# self.readSheet()
-
self.read_thread = threading.Thread(target=self.readSheet, args={
0})
-
self.read_thread.start()
-
-
def readSheet(self, name=0):
-
if name
in self.sheets:
-
return self.sheets[name]
-
sheet = pd.read_excel(self.filepath, sheet_name=name)
-
data = {}
-
data[
'row'] = sheet.shape[
0] -
3
-
data[
'col'] = sheet.shape[
1]
-
data[
'title'] = sheet.columns.values.tolist()
-
-
row_values_header = np.array(sheet.iloc[[
0]]).tolist()[
0]
-
row_values_type = np.array(sheet.iloc[[
1]]).tolist()[
0]
-
row_values_table = np.array(sheet.iloc[[
2]]).tolist()[
0]
-
-
data[
'header'] = []
-
data[
'type'] = []
-
data[
'table'] = []
-
for i
in range(data[
'col']):
-
data[
'header'].append(row_values_header[i])
-
data[
'type'].append(row_values_type[i])
-
data[
'table'].append(row_values_table[i])
-
pass
-
-
for t
in data[
'table']:
-
if t == t:
-
self.readSheet(t)
-
pass
-
-
data[
'record'] = {}
-
for i
in range(
3, data[
'row']+
3):
-
row_values_list = np.array(sheet.iloc[[i]]).tolist()[
0]
-
for j
in range(data[
'col']):
-
data[
'record'][(i
-3, j)] = self.serialize(row_values_list[j], data[
'type'][j])
-
pass
-
-
self.sheets[name] = data
-
self.finish =
True
-
-
def dispalyint(self, str):
-
pass
-
-
def serialize(self, data, type=None):
-
if data != data:
-
data =
''
-
value = str(data)
-
state =
0
-
try:
-
if type
in TYPE_INT:
-
value = int(value)
-
elif type
in TYPE_FLO:
-
value = float(
'%.3f' % float(value))
-
elif type
in TYPE_STR:
-
value = str(value)
-
elif type
in TYPE_BOL:
-
if data
in [
'false',
'False',
'FALSE',
0,
0.0]:
-
value =
False
-
elif data
in [
'true',
'True',
'TRUE',
1,
1.0]:
-
value =
True
-
else:
-
value =
1
-
state =
1
-
elif type
in TYPE_VEC:
-
if
not re.match(
'^((true|false|-?\d+|".+"|-?\d+\.\d*),)*(true|false|-?\d+|".+"|-?\d+\.\d*)?$', value):
-
state =
2
-
elif type
in TYPE_TAB:
-
pass
-
elif type
in TYPE_EXT:
-
pass
-
elif type
in TYPE_SPE:
-
pass
-
else:
-
state =
2
-
except ValueError:
-
state =
2
-
info = {
'data':value,
'state':state}
-
return info
-
-
class Application(QMainWindow):
-
def __init__(self, parent=None):
-
super(QMainWindow, self).__init__(parent)
-
-
self.global_data = {}
-
-
self.ui = ui.mainwindow_main.Ui_MainWindow()
-
self.ui.setupUi(self)
-
self.showMaximized()
-
-
self.db = DBManager()
-
-
# self.doLogin()
-
-
self.readExcel()
-
-
self.initHandle()
-
-
def initHandle(self):
-
self.ui.action_exit.triggered.connect(qApp.quit)
-
self.ui.action_import.triggered.connect(self.importData)
-
self.ui.action_export.triggered.connect(self.exportData)
-
-
def exit(self):
-
self.close()
-
-
def doLogin(self):
-
self.dlg_login = LoginDialog(self)
-
self.dlg_login.show()
-
-
self.dlg_login.ok_signal.connect(self.login)
-
self.dlg_login.cancel_signal.connect(self.exit)
-
-
def login(self, account, password):
-
self.db.exec(
"insert into users values('"+account+
"','"+password+
"')")
-
-
def setTableWidgetData(self, qmodeLindex):
-
name = self.ui.treeWidget.currentItem().text(
0)
-
item = self.global_data[name]
-
if
not item.finish:
-
QMessageBox.critical(self, (
'提示'), (
'正在加载中, 请稍后......'), QMessageBox.Ok)
-
return
-
data = item.sheets[
0]
-
-
self.ui.tableWidget.setRowCount(data[
'row'])
-
self.ui.tableWidget.setColumnCount(data[
'col'])
-
self.ui.tableWidget.setHorizontalHeaderLabels(data[
'title'])
-
-
for i
in range(data[
'row']):
-
for j
in range(data[
'col']):
-
table = data[
'record'][(i, j)]
-
item = QTableWidgetItem(str(table[
'data']))
-
item.setTextAlignment(Qt.AlignVCenter | Qt.AlignHCenter)
-
item.setBackground(self.setItemColor(table[
'state']))
-
self.ui.tableWidget.setItem(i, j, item)
-
pass
-
-
def setItemColor(self, state):
-
if state ==
1:
-
return QtGui.QColor(
255,
255,
0)
-
elif state ==
2:
-
return QtGui.QColor(
255,
0,
0)
-
return QtGui.QColor(
255,
255,
255)
-
-
def readExcel(self):
-
self.ui.treeWidget.doubleClicked.connect(self.setTableWidgetData)
-
-
self.ui.treeWidget.setColumnCount(
1)
-
self.ui.treeWidget.setHeaderLabels([
'Key'])
-
for root, dirs, files
in os.walk(
'excel'):
-
for file
in files:
-
res = re.findall(
'\\~\\$', file)
-
if len(res) >
0:
-
continue
-
name = file.split(
'.')[
0]
-
root = QTreeWidgetItem(self.ui.treeWidget)
-
root.setText(
0, name)
-
-
self.global_data[name] = ExcelData(
'excel/' + file)
-
pass
-
-
def importData(self):
-
name = self.ui.treeWidget.currentItem().text(
0)
-
item = self.global_data[name]
-
if
not item.finish:
-
QMessageBox.critical(self, (
'提示'), (
'文件未加载完成, 请稍后......'), QMessageBox.Ok)
-
return
-
self.global_data[name] = ExcelData(item.filepath)
-
pass
-
-
def exportData(self):
-
name = self.ui.treeWidget.currentItem().text(
0)
-
item = self.global_data[name]
-
if
not item.finish:
-
QMessageBox.critical(self, (
'提示'), (
'正在加载中, 请稍后......'), QMessageBox.Ok)
-
return
-
pass
-
-
if __name__ ==
'__main__':
-
app = QApplication(sys.argv)
-
main = Application()
-
main.show()
-
sys.exit(app.exec_())
运行效果如下:
转载:https://blog.csdn.net/ww13055188466/article/details/115003202