feat: 创建数据处理UI工程
This commit is contained in:
commit
62900edd85
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.exe
|
||||||
|
*.pyc
|
||||||
|
*.pyd
|
||||||
|
*.log
|
||||||
|
*.temp
|
||||||
|
*.dat
|
||||||
|
*.DAT
|
||||||
|
|
||||||
|
/other
|
||||||
|
/pyinstaller/dist
|
||||||
|
/pyinstaller/build
|
||||||
410
app/color.py
Normal file
410
app/color.py
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
import math
|
||||||
|
from PyQt6.QtGui import QColor
|
||||||
|
|
||||||
|
color_normal_table = [
|
||||||
|
QColor(85,87,83),
|
||||||
|
QColor(239,41,41),
|
||||||
|
QColor(138,226,52),
|
||||||
|
QColor(252,233,79),
|
||||||
|
QColor(114,159,207),
|
||||||
|
QColor(173,127,168),
|
||||||
|
QColor(52,226,226),
|
||||||
|
QColor(238,238,236),
|
||||||
|
]
|
||||||
|
|
||||||
|
color_bold_table = [
|
||||||
|
QColor(46,52,54),
|
||||||
|
QColor(204,0,0),
|
||||||
|
QColor(78,154,6),
|
||||||
|
QColor(196,160,0),
|
||||||
|
QColor(52,101,164),
|
||||||
|
QColor(117,80,123),
|
||||||
|
QColor(6,152,154),
|
||||||
|
QColor(211,215,207),
|
||||||
|
]
|
||||||
|
|
||||||
|
color4_table = [
|
||||||
|
QColor(12, 12, 12),
|
||||||
|
QColor(197, 15, 31),
|
||||||
|
QColor(19, 161, 14),
|
||||||
|
QColor(193, 156, 0),
|
||||||
|
QColor(0, 55, 218),
|
||||||
|
QColor(136, 23, 218),
|
||||||
|
QColor(58, 150, 221),
|
||||||
|
QColor(204, 204, 204),
|
||||||
|
QColor(118, 118, 118),
|
||||||
|
QColor(231, 72, 86),
|
||||||
|
QColor(22, 198, 12),
|
||||||
|
QColor(249, 241, 165),
|
||||||
|
QColor(59, 120, 255),
|
||||||
|
QColor(180, 0, 158),
|
||||||
|
QColor(97, 214, 214),
|
||||||
|
QColor(242, 242, 242),
|
||||||
|
]
|
||||||
|
# color4_table = [
|
||||||
|
# QColor(1, 1, 1),
|
||||||
|
# QColor(222, 56, 43),
|
||||||
|
# QColor(57, 181, 74),
|
||||||
|
# QColor(255, 199, 6),
|
||||||
|
# QColor(0, 111, 184),
|
||||||
|
# QColor(118, 38, 113),
|
||||||
|
# QColor(44, 181, 233),
|
||||||
|
# QColor(204, 204, 204),
|
||||||
|
# QColor(128, 128, 128),
|
||||||
|
# QColor(255, 0, 0),
|
||||||
|
# QColor(0, 255, 0),
|
||||||
|
# QColor(255, 255, 0),
|
||||||
|
# QColor(0, 0, 255),
|
||||||
|
# QColor(255, 0, 255),
|
||||||
|
# QColor(0, 255, 255),
|
||||||
|
# QColor(255, 255, 255),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
color8_table = [
|
||||||
|
QColor(0x00, 0x00, 0x00),
|
||||||
|
QColor(0x80, 0x00, 0x00),
|
||||||
|
QColor(0x00, 0x80, 0x00),
|
||||||
|
QColor(0x80, 0x80, 0x00),
|
||||||
|
QColor(0x00, 0x00, 0x80),
|
||||||
|
QColor(0x80, 0x00, 0x80),
|
||||||
|
QColor(0x00, 0x80, 0x80),
|
||||||
|
QColor(0xc0, 0xc0, 0xc0),
|
||||||
|
QColor(0x80, 0x80, 0x80),
|
||||||
|
QColor(0xff, 0x00, 0x00),
|
||||||
|
QColor(0x00, 0xff, 0x00),
|
||||||
|
QColor(0xff, 0xff, 0x00),
|
||||||
|
QColor(0x00, 0x00, 0xff),
|
||||||
|
QColor(0xff, 0x00, 0xff),
|
||||||
|
QColor(0x00, 0xff, 0xff),
|
||||||
|
QColor(0xff, 0xff, 0xff),
|
||||||
|
QColor(0x00, 0x00, 0x00),
|
||||||
|
QColor(0x00, 0x00, 0x5f),
|
||||||
|
QColor(0x00, 0x00, 0x87),
|
||||||
|
QColor(0x00, 0x00, 0xaf),
|
||||||
|
QColor(0x00, 0x00, 0xd7),
|
||||||
|
QColor(0x00, 0x00, 0xff),
|
||||||
|
QColor(0x00, 0x5f, 0x00),
|
||||||
|
QColor(0x00, 0x5f, 0x5f),
|
||||||
|
QColor(0x00, 0x5f, 0x87),
|
||||||
|
QColor(0x00, 0x5f, 0xaf),
|
||||||
|
QColor(0x00, 0x5f, 0xd7),
|
||||||
|
QColor(0x00, 0x5f, 0xff),
|
||||||
|
QColor(0x00, 0x87, 0x00),
|
||||||
|
QColor(0x00, 0x87, 0x5f),
|
||||||
|
QColor(0x00, 0x87, 0x87),
|
||||||
|
QColor(0x00, 0x87, 0xaf),
|
||||||
|
QColor(0x00, 0x87, 0xd7),
|
||||||
|
QColor(0x00, 0x87, 0xff),
|
||||||
|
QColor(0x00, 0xaf, 0x00),
|
||||||
|
QColor(0x00, 0xaf, 0x5f),
|
||||||
|
QColor(0x00, 0xaf, 0x87),
|
||||||
|
QColor(0x00, 0xaf, 0xaf),
|
||||||
|
QColor(0x00, 0xaf, 0xd7),
|
||||||
|
QColor(0x00, 0xaf, 0xff),
|
||||||
|
QColor(0x00, 0xd7, 0x00),
|
||||||
|
QColor(0x00, 0xd7, 0x5f),
|
||||||
|
QColor(0x00, 0xd7, 0x87),
|
||||||
|
QColor(0x00, 0xd7, 0xaf),
|
||||||
|
QColor(0x00, 0xd7, 0xd7),
|
||||||
|
QColor(0x00, 0xd7, 0xff),
|
||||||
|
QColor(0x00, 0xff, 0x00),
|
||||||
|
QColor(0x00, 0xff, 0x5f),
|
||||||
|
QColor(0x00, 0xff, 0x87),
|
||||||
|
QColor(0x00, 0xff, 0xaf),
|
||||||
|
QColor(0x00, 0xff, 0xd7),
|
||||||
|
QColor(0x00, 0xff, 0xff),
|
||||||
|
QColor(0x5f, 0x00, 0x00),
|
||||||
|
QColor(0x5f, 0x00, 0x5f),
|
||||||
|
QColor(0x5f, 0x00, 0x87),
|
||||||
|
QColor(0x5f, 0x00, 0xaf),
|
||||||
|
QColor(0x5f, 0x00, 0xd7),
|
||||||
|
QColor(0x5f, 0x00, 0xff),
|
||||||
|
QColor(0x5f, 0x5f, 0x00),
|
||||||
|
QColor(0x5f, 0x5f, 0x5f),
|
||||||
|
QColor(0x5f, 0x5f, 0x87),
|
||||||
|
QColor(0x5f, 0x5f, 0xaf),
|
||||||
|
QColor(0x5f, 0x5f, 0xd7),
|
||||||
|
QColor(0x5f, 0x5f, 0xff),
|
||||||
|
QColor(0x5f, 0x87, 0x00),
|
||||||
|
QColor(0x5f, 0x87, 0x5f),
|
||||||
|
QColor(0x5f, 0x87, 0x87),
|
||||||
|
QColor(0x5f, 0x87, 0xaf),
|
||||||
|
QColor(0x5f, 0x87, 0xd7),
|
||||||
|
QColor(0x5f, 0x87, 0xff),
|
||||||
|
QColor(0x5f, 0xaf, 0x00),
|
||||||
|
QColor(0x5f, 0xaf, 0x5f),
|
||||||
|
QColor(0x5f, 0xaf, 0x87),
|
||||||
|
QColor(0x5f, 0xaf, 0xaf),
|
||||||
|
QColor(0x5f, 0xaf, 0xd7),
|
||||||
|
QColor(0x5f, 0xaf, 0xff),
|
||||||
|
QColor(0x5f, 0xd7, 0x00),
|
||||||
|
QColor(0x5f, 0xd7, 0x5f),
|
||||||
|
QColor(0x5f, 0xd7, 0x87),
|
||||||
|
QColor(0x5f, 0xd7, 0xaf),
|
||||||
|
QColor(0x5f, 0xd7, 0xd7),
|
||||||
|
QColor(0x5f, 0xd7, 0xff),
|
||||||
|
QColor(0x5f, 0xff, 0x00),
|
||||||
|
QColor(0x5f, 0xff, 0x5f),
|
||||||
|
QColor(0x5f, 0xff, 0x87),
|
||||||
|
QColor(0x5f, 0xff, 0xaf),
|
||||||
|
QColor(0x5f, 0xff, 0xd7),
|
||||||
|
QColor(0x5f, 0xff, 0xff),
|
||||||
|
QColor(0x87, 0x00, 0x00),
|
||||||
|
QColor(0x87, 0x00, 0x5f),
|
||||||
|
QColor(0x87, 0x00, 0x87),
|
||||||
|
QColor(0x87, 0x00, 0xaf),
|
||||||
|
QColor(0x87, 0x00, 0xd7),
|
||||||
|
QColor(0x87, 0x00, 0xff),
|
||||||
|
QColor(0x87, 0x5f, 0x00),
|
||||||
|
QColor(0x87, 0x5f, 0x5f),
|
||||||
|
QColor(0x87, 0x5f, 0x87),
|
||||||
|
QColor(0x87, 0x5f, 0xaf),
|
||||||
|
QColor(0x87, 0x5f, 0xd7),
|
||||||
|
QColor(0x87, 0x5f, 0xff),
|
||||||
|
QColor(0x87, 0x87, 0x00),
|
||||||
|
QColor(0x87, 0x87, 0x5f),
|
||||||
|
QColor(0x87, 0x87, 0x87),
|
||||||
|
QColor(0x87, 0x87, 0xaf),
|
||||||
|
QColor(0x87, 0x87, 0xd7),
|
||||||
|
QColor(0x87, 0x87, 0xff),
|
||||||
|
QColor(0x87, 0xaf, 0x00),
|
||||||
|
QColor(0x87, 0xaf, 0x5f),
|
||||||
|
QColor(0x87, 0xaf, 0x87),
|
||||||
|
QColor(0x87, 0xaf, 0xaf),
|
||||||
|
QColor(0x87, 0xaf, 0xd7),
|
||||||
|
QColor(0x87, 0xaf, 0xff),
|
||||||
|
QColor(0x87, 0xd7, 0x00),
|
||||||
|
QColor(0x87, 0xd7, 0x5f),
|
||||||
|
QColor(0x87, 0xd7, 0x87),
|
||||||
|
QColor(0x87, 0xd7, 0xaf),
|
||||||
|
QColor(0x87, 0xd7, 0xd7),
|
||||||
|
QColor(0x87, 0xd7, 0xff),
|
||||||
|
QColor(0x87, 0xff, 0x00),
|
||||||
|
QColor(0x87, 0xff, 0x5f),
|
||||||
|
QColor(0x87, 0xff, 0x87),
|
||||||
|
QColor(0x87, 0xff, 0xaf),
|
||||||
|
QColor(0x87, 0xff, 0xd7),
|
||||||
|
QColor(0x87, 0xff, 0xff),
|
||||||
|
QColor(0xaf, 0x00, 0x00),
|
||||||
|
QColor(0xaf, 0x00, 0x5f),
|
||||||
|
QColor(0xaf, 0x00, 0x87),
|
||||||
|
QColor(0xaf, 0x00, 0xaf),
|
||||||
|
QColor(0xaf, 0x00, 0xd7),
|
||||||
|
QColor(0xaf, 0x00, 0xff),
|
||||||
|
QColor(0xaf, 0x5f, 0x00),
|
||||||
|
QColor(0xaf, 0x5f, 0x5f),
|
||||||
|
QColor(0xaf, 0x5f, 0x87),
|
||||||
|
QColor(0xaf, 0x5f, 0xaf),
|
||||||
|
QColor(0xaf, 0x5f, 0xd7),
|
||||||
|
QColor(0xaf, 0x5f, 0xff),
|
||||||
|
QColor(0xaf, 0x87, 0x00),
|
||||||
|
QColor(0xaf, 0x87, 0x5f),
|
||||||
|
QColor(0xaf, 0x87, 0x87),
|
||||||
|
QColor(0xaf, 0x87, 0xaf),
|
||||||
|
QColor(0xaf, 0x87, 0xd7),
|
||||||
|
QColor(0xaf, 0x87, 0xff),
|
||||||
|
QColor(0xaf, 0xaf, 0x00),
|
||||||
|
QColor(0xaf, 0xaf, 0x5f),
|
||||||
|
QColor(0xaf, 0xaf, 0x87),
|
||||||
|
QColor(0xaf, 0xaf, 0xaf),
|
||||||
|
QColor(0xaf, 0xaf, 0xd7),
|
||||||
|
QColor(0xaf, 0xaf, 0xff),
|
||||||
|
QColor(0xaf, 0xd7, 0x00),
|
||||||
|
QColor(0xaf, 0xd7, 0x5f),
|
||||||
|
QColor(0xaf, 0xd7, 0x87),
|
||||||
|
QColor(0xaf, 0xd7, 0xaf),
|
||||||
|
QColor(0xaf, 0xd7, 0xd7),
|
||||||
|
QColor(0xaf, 0xd7, 0xff),
|
||||||
|
QColor(0xaf, 0xff, 0x00),
|
||||||
|
QColor(0xaf, 0xff, 0x5f),
|
||||||
|
QColor(0xaf, 0xff, 0x87),
|
||||||
|
QColor(0xaf, 0xff, 0xaf),
|
||||||
|
QColor(0xaf, 0xff, 0xd7),
|
||||||
|
QColor(0xaf, 0xff, 0xff),
|
||||||
|
QColor(0xd7, 0x00, 0x00),
|
||||||
|
QColor(0xd7, 0x00, 0x5f),
|
||||||
|
QColor(0xd7, 0x00, 0x87),
|
||||||
|
QColor(0xd7, 0x00, 0xaf),
|
||||||
|
QColor(0xd7, 0x00, 0xd7),
|
||||||
|
QColor(0xd7, 0x00, 0xff),
|
||||||
|
QColor(0xd7, 0x5f, 0x00),
|
||||||
|
QColor(0xd7, 0x5f, 0x5f),
|
||||||
|
QColor(0xd7, 0x5f, 0x87),
|
||||||
|
QColor(0xd7, 0x5f, 0xaf),
|
||||||
|
QColor(0xd7, 0x5f, 0xd7),
|
||||||
|
QColor(0xd7, 0x5f, 0xff),
|
||||||
|
QColor(0xd7, 0x87, 0x00),
|
||||||
|
QColor(0xd7, 0x87, 0x5f),
|
||||||
|
QColor(0xd7, 0x87, 0x87),
|
||||||
|
QColor(0xd7, 0x87, 0xaf),
|
||||||
|
QColor(0xd7, 0x87, 0xd7),
|
||||||
|
QColor(0xd7, 0x87, 0xff),
|
||||||
|
QColor(0xd7, 0xaf, 0x00),
|
||||||
|
QColor(0xd7, 0xaf, 0x5f),
|
||||||
|
QColor(0xd7, 0xaf, 0x87),
|
||||||
|
QColor(0xd7, 0xaf, 0xaf),
|
||||||
|
QColor(0xd7, 0xaf, 0xd7),
|
||||||
|
QColor(0xd7, 0xaf, 0xff),
|
||||||
|
QColor(0xd7, 0xd7, 0x00),
|
||||||
|
QColor(0xd7, 0xd7, 0x5f),
|
||||||
|
QColor(0xd7, 0xd7, 0x87),
|
||||||
|
QColor(0xd7, 0xd7, 0xaf),
|
||||||
|
QColor(0xd7, 0xd7, 0xd7),
|
||||||
|
QColor(0xd7, 0xd7, 0xff),
|
||||||
|
QColor(0xd7, 0xff, 0x00),
|
||||||
|
QColor(0xd7, 0xff, 0x5f),
|
||||||
|
QColor(0xd7, 0xff, 0x87),
|
||||||
|
QColor(0xd7, 0xff, 0xaf),
|
||||||
|
QColor(0xd7, 0xff, 0xd7),
|
||||||
|
QColor(0xd7, 0xff, 0xff),
|
||||||
|
QColor(0xff, 0x00, 0x00),
|
||||||
|
QColor(0xff, 0x00, 0x5f),
|
||||||
|
QColor(0xff, 0x00, 0x87),
|
||||||
|
QColor(0xff, 0x00, 0xaf),
|
||||||
|
QColor(0xff, 0x00, 0xd7),
|
||||||
|
QColor(0xff, 0x00, 0xff),
|
||||||
|
QColor(0xff, 0x5f, 0x00),
|
||||||
|
QColor(0xff, 0x5f, 0x5f),
|
||||||
|
QColor(0xff, 0x5f, 0x87),
|
||||||
|
QColor(0xff, 0x5f, 0xaf),
|
||||||
|
QColor(0xff, 0x5f, 0xd7),
|
||||||
|
QColor(0xff, 0x5f, 0xff),
|
||||||
|
QColor(0xff, 0x87, 0x00),
|
||||||
|
QColor(0xff, 0x87, 0x5f),
|
||||||
|
QColor(0xff, 0x87, 0x87),
|
||||||
|
QColor(0xff, 0x87, 0xaf),
|
||||||
|
QColor(0xff, 0x87, 0xd7),
|
||||||
|
QColor(0xff, 0x87, 0xff),
|
||||||
|
QColor(0xff, 0xaf, 0x00),
|
||||||
|
QColor(0xff, 0xaf, 0x5f),
|
||||||
|
QColor(0xff, 0xaf, 0x87),
|
||||||
|
QColor(0xff, 0xaf, 0xaf),
|
||||||
|
QColor(0xff, 0xaf, 0xd7),
|
||||||
|
QColor(0xff, 0xaf, 0xff),
|
||||||
|
QColor(0xff, 0xd7, 0x00),
|
||||||
|
QColor(0xff, 0xd7, 0x5f),
|
||||||
|
QColor(0xff, 0xd7, 0x87),
|
||||||
|
QColor(0xff, 0xd7, 0xaf),
|
||||||
|
QColor(0xff, 0xd7, 0xd7),
|
||||||
|
QColor(0xff, 0xd7, 0xff),
|
||||||
|
QColor(0xff, 0xff, 0x00),
|
||||||
|
QColor(0xff, 0xff, 0x5f),
|
||||||
|
QColor(0xff, 0xff, 0x87),
|
||||||
|
QColor(0xff, 0xff, 0xaf),
|
||||||
|
QColor(0xff, 0xff, 0xd7),
|
||||||
|
QColor(0xff, 0xff, 0xff),
|
||||||
|
QColor(0x08, 0x08, 0x08),
|
||||||
|
QColor(0x12, 0x12, 0x12),
|
||||||
|
QColor(0x1c, 0x1c, 0x1c),
|
||||||
|
QColor(0x26, 0x26, 0x26),
|
||||||
|
QColor(0x30, 0x30, 0x30),
|
||||||
|
QColor(0x3a, 0x3a, 0x3a),
|
||||||
|
QColor(0x44, 0x44, 0x44),
|
||||||
|
QColor(0x4e, 0x4e, 0x4e),
|
||||||
|
QColor(0x58, 0x58, 0x58),
|
||||||
|
QColor(0x62, 0x62, 0x62),
|
||||||
|
QColor(0x6c, 0x6c, 0x6c),
|
||||||
|
QColor(0x76, 0x76, 0x76),
|
||||||
|
QColor(0x80, 0x80, 0x80),
|
||||||
|
QColor(0x8a, 0x8a, 0x8a),
|
||||||
|
QColor(0x94, 0x94, 0x94),
|
||||||
|
QColor(0x9e, 0x9e, 0x9e),
|
||||||
|
QColor(0xa8, 0xa8, 0xa8),
|
||||||
|
QColor(0xb2, 0xb2, 0xb2),
|
||||||
|
QColor(0xbc, 0xbc, 0xbc),
|
||||||
|
QColor(0xc6, 0xc6, 0xc6),
|
||||||
|
QColor(0xd0, 0xd0, 0xd0),
|
||||||
|
QColor(0xda, 0xda, 0xda),
|
||||||
|
QColor(0xe4, 0xe4, 0xe4),
|
||||||
|
QColor(0xee, 0xee, 0xee),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def qcolor(c):
|
||||||
|
return QColor(*[int(i) for i in c])
|
||||||
|
|
||||||
|
def hsi2rgb(c):
|
||||||
|
h, s, i = c
|
||||||
|
pi = math.pi * 2
|
||||||
|
|
||||||
|
if h <= pi / 3:
|
||||||
|
b = (1 - s) / 3
|
||||||
|
r = (1 + s * math.cos(h) / math.cos(pi / 6 - h)) / 3
|
||||||
|
g = 1 - (r + b)
|
||||||
|
elif h <= pi / 3 * 2:
|
||||||
|
h -= pi / 3
|
||||||
|
r = (1 - s) / 3
|
||||||
|
g = (1 + s * math.cos(h) / math.cos(pi / 6 - h)) / 3
|
||||||
|
b = 1 - (r + g)
|
||||||
|
else:
|
||||||
|
h -= pi / 3 * 2
|
||||||
|
g = (1 - s) / 3
|
||||||
|
b = (1 + s * math.cos(h) / math.cos(pi / 6 - h)) / 3
|
||||||
|
r = 1 - (g + b)
|
||||||
|
|
||||||
|
r = 3 * i * r
|
||||||
|
g = 3 * i * g
|
||||||
|
b = 3 * i * b
|
||||||
|
|
||||||
|
return [r, g, b]
|
||||||
|
|
||||||
|
def rgb2hsi(c):
|
||||||
|
r, g, b = c
|
||||||
|
rg = r - g
|
||||||
|
rb = r - b
|
||||||
|
gb = g - b
|
||||||
|
h = math.acos((rg + rb) / (2 * math.sqrt(rg ** 2 + rb * gb)))
|
||||||
|
if b > g:
|
||||||
|
h = 2 * math.pi - h
|
||||||
|
|
||||||
|
i = (r + g + b) / 3
|
||||||
|
s = 1 - min(r, g, b) / i
|
||||||
|
return [h, s, i]
|
||||||
|
|
||||||
|
def hsv2rgb(c):
|
||||||
|
h, s, v = c
|
||||||
|
y = v * s
|
||||||
|
x = y * (1 - abs(h / 60 % 2 - 1))
|
||||||
|
m = v - y
|
||||||
|
|
||||||
|
h %= 360
|
||||||
|
if h < 0:
|
||||||
|
h += 360
|
||||||
|
if h <= 60:
|
||||||
|
r, g, b = y, x, 0
|
||||||
|
elif h <= 120:
|
||||||
|
r, g, b = x, y, 0
|
||||||
|
elif h <= 180:
|
||||||
|
r, g, b = 0, y, x
|
||||||
|
elif h <= 240:
|
||||||
|
r, g, b = 0, x, y
|
||||||
|
elif h <= 300:
|
||||||
|
r, g, b = x, 0, y
|
||||||
|
else:
|
||||||
|
r, g, b = y, 0, x
|
||||||
|
|
||||||
|
|
||||||
|
r = (r + m) * 255
|
||||||
|
g = (g + m) * 255
|
||||||
|
b = (b + m) * 255
|
||||||
|
|
||||||
|
return [r, g, b]
|
||||||
|
|
||||||
|
black = "\033[0;30m"
|
||||||
|
dark_gray = "\033[1;30m"
|
||||||
|
red = "\033[0;31m"
|
||||||
|
light_red = "\033[1;31m"
|
||||||
|
green = "\033[0;32m"
|
||||||
|
light_green = "\033[1;32m"
|
||||||
|
orange = "\033[0;33m"
|
||||||
|
yellow = "\033[1;33m"
|
||||||
|
blue = "\033[0;34m"
|
||||||
|
light_blue = "\033[1;34m"
|
||||||
|
purple = "\033[0;35m"
|
||||||
|
light_purple = "\033[1;35m"
|
||||||
|
cyan = "\033[0;36m"
|
||||||
|
light_cyan = "\033[1;36m"
|
||||||
|
light_gray = "\033[0;37m"
|
||||||
|
white = "\033[1;37m"
|
||||||
|
nc = "\033[0m"
|
||||||
|
|
||||||
34
app/config.py
Normal file
34
app/config.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# if os.path.exists("_internal"):
|
||||||
|
# base_path = "./_internal"
|
||||||
|
# else:
|
||||||
|
# base_path = "./"
|
||||||
|
base_path = "./"
|
||||||
|
|
||||||
|
config_path = os.path.join(base_path, "config")
|
||||||
|
default_file = os.path.join(config_path, "default.yml")
|
||||||
|
config_file = os.path.join(config_path, "config.yml")
|
||||||
|
|
||||||
|
if not os.path.exists(config_path):
|
||||||
|
os.mkdir(config_path)
|
||||||
|
if os.path.exists(default_file):
|
||||||
|
with open(default_file, "r", encoding = "utf-8") as fobj:
|
||||||
|
config = yaml.safe_load(fobj)
|
||||||
|
if config is None:
|
||||||
|
config = {}
|
||||||
|
else:
|
||||||
|
config = {}
|
||||||
|
if os.path.exists(config_file):
|
||||||
|
with open(config_file, "r", encoding = "utf-8") as fobj:
|
||||||
|
config_new = yaml.safe_load(fobj)
|
||||||
|
if not (config_new is None):
|
||||||
|
for key, value in config_new.items():
|
||||||
|
config[key] = value
|
||||||
|
|
||||||
|
image_path = os.path.join(base_path, "image")
|
||||||
|
script_path = os.path.join(base_path, "script")
|
||||||
|
data_path = os.path.join(base_path, "data")
|
||||||
|
doc_path = os.path.join(base_path, "doc")
|
||||||
10
app/config/config.yml
Normal file
10
app/config/config.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
main_window:
|
||||||
|
geometry: !!binary |
|
||||||
|
AdnQywADAAAAAABCAAAAIAAAB38AAAQ3AAACDAAAAQEAAAchAAADmwAAAAACAAAAB4AAAABCAAAA
|
||||||
|
RQAAB38AAAQ3
|
||||||
|
state: !!binary |
|
||||||
|
AAAA/wAAAAD9AAAAAwAAAAAAAACYAAADWvwCAAAAAfsAAAAWAHcAaQBkAGcAZQB0ACAAdAByAGUA
|
||||||
|
ZQEAAAAYAAADWgAAAF4A////AAAAAQAAARAAAANa/AIAAAAB+wAAAB4AdwBpAGQAZwBlAHQAIABp
|
||||||
|
AG4AdABlAHIAYQBjAHQBAAAAGAAAA1oAAACxAP///wAAAAMAAAc+AAAAZfwBAAAAAfsAAAAWAHcA
|
||||||
|
aQBkAGcAZQB0ACAAdABlAHIAbQEAAAAAAAAHPgAAAJAA////AAAFigAAA1oAAAAEAAAABAAAAAgA
|
||||||
|
AAAI/AAAAAA=
|
||||||
217
app/config/default.yml
Normal file
217
app/config/default.yml
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
tree:
|
||||||
|
- id: coord_calc
|
||||||
|
label: 坐标计算
|
||||||
|
- id: measure
|
||||||
|
label: 轨道数据
|
||||||
|
|
||||||
|
display:
|
||||||
|
coord_calc:
|
||||||
|
- id: surface_statistics
|
||||||
|
label: 平面资料
|
||||||
|
type: table
|
||||||
|
row: 7
|
||||||
|
col: 8
|
||||||
|
column_header:
|
||||||
|
- 序号
|
||||||
|
- 交点号
|
||||||
|
- X(N)坐标
|
||||||
|
- Y(E)坐标
|
||||||
|
- 半径
|
||||||
|
- Ls1
|
||||||
|
- Ls2
|
||||||
|
- 桩号
|
||||||
|
format_script:
|
||||||
|
format_surface_statistic.py
|
||||||
|
datafile: surface_statistics.csv
|
||||||
|
- id: alignment_table
|
||||||
|
label: 直曲表
|
||||||
|
type: table
|
||||||
|
row: 5
|
||||||
|
col: 22
|
||||||
|
format_script:
|
||||||
|
format_alignment_table.py
|
||||||
|
datafile: alignment_table.csv
|
||||||
|
span:
|
||||||
|
- - 0
|
||||||
|
- 0
|
||||||
|
- 1
|
||||||
|
- 21
|
||||||
|
- - 1
|
||||||
|
- 0
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
- - 1
|
||||||
|
- 3
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
- - 1
|
||||||
|
- 1
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- - 1
|
||||||
|
- 4
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- - 1
|
||||||
|
- 6
|
||||||
|
- 1
|
||||||
|
- 7
|
||||||
|
- - 1
|
||||||
|
- 13
|
||||||
|
- 1
|
||||||
|
- 5
|
||||||
|
- - 1
|
||||||
|
- 18
|
||||||
|
- 1
|
||||||
|
- 3
|
||||||
|
- id: linear_feature
|
||||||
|
label: 线元要素表
|
||||||
|
type: table
|
||||||
|
row: 1
|
||||||
|
col: 11
|
||||||
|
column_header:
|
||||||
|
- 桩号
|
||||||
|
- 线元长度
|
||||||
|
- 起始半径
|
||||||
|
- 结束半径
|
||||||
|
- 起始曲率
|
||||||
|
- 结束曲率
|
||||||
|
- 方位角
|
||||||
|
- X
|
||||||
|
- Y
|
||||||
|
- 线元类型
|
||||||
|
- 缓曲参数
|
||||||
|
format_script:
|
||||||
|
format_linear_feature.py
|
||||||
|
- id: longitudinal_profile
|
||||||
|
label: 纵断面
|
||||||
|
type: table
|
||||||
|
row: 20
|
||||||
|
col: 8
|
||||||
|
column_header:
|
||||||
|
- 变坡点桩号
|
||||||
|
- 变坡点高程
|
||||||
|
- 半径
|
||||||
|
- 坡长
|
||||||
|
- i(%)
|
||||||
|
- L(m)
|
||||||
|
- T(m)
|
||||||
|
- E(m)
|
||||||
|
format_script:
|
||||||
|
format_longitudinal_profile.py
|
||||||
|
- id: coord_calc
|
||||||
|
label: 坐标计算
|
||||||
|
type: table
|
||||||
|
row: 6
|
||||||
|
col: 14
|
||||||
|
format_script:
|
||||||
|
format_coord_calc.py
|
||||||
|
datafile: coord_calc.csv
|
||||||
|
span:
|
||||||
|
- - 0
|
||||||
|
- 0
|
||||||
|
- 2
|
||||||
|
- 2
|
||||||
|
- - 0
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- - 0
|
||||||
|
- 4
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
- - 0
|
||||||
|
- 5
|
||||||
|
- 2
|
||||||
|
- 1
|
||||||
|
- - 0
|
||||||
|
- 6
|
||||||
|
- 1
|
||||||
|
- 4
|
||||||
|
- - 0
|
||||||
|
- 10
|
||||||
|
- 1
|
||||||
|
- 4
|
||||||
|
measure:
|
||||||
|
- id: measure_data
|
||||||
|
label: 轨道测量数据
|
||||||
|
type: table
|
||||||
|
col: 9
|
||||||
|
row: 1
|
||||||
|
column_header:
|
||||||
|
- 左轨编号
|
||||||
|
- 左轨高程(m)
|
||||||
|
- 右轨编号
|
||||||
|
- 右轨高程(m)
|
||||||
|
- 左右高差(mm)
|
||||||
|
- 左侧线形(mm)
|
||||||
|
- 右侧线形(mm)
|
||||||
|
- 左侧高低值(mm)
|
||||||
|
- 右侧高低值(mm)
|
||||||
|
format_script:
|
||||||
|
format_measure_data.py
|
||||||
|
- id: road_graph
|
||||||
|
label: 轨道竖向线形图
|
||||||
|
type: graph
|
||||||
|
axis_x_title: 线路测试断面
|
||||||
|
axis_y_title: 轨顶高程(m)
|
||||||
|
- id: road_relative_graph
|
||||||
|
label: 轨道轨道面相对竖向线形图
|
||||||
|
type: graph
|
||||||
|
axis_x_title: 线路测试断面
|
||||||
|
axis_y_title: 轨顶高程(mm)
|
||||||
|
- id: road_horizon_graph
|
||||||
|
label: 轨道水平情况
|
||||||
|
type: graph
|
||||||
|
axis_x_title: 线路测试断面
|
||||||
|
axis_y_title: 轨道水平(mm)
|
||||||
|
- id: road_concave_graph
|
||||||
|
label: 轨道高低情况
|
||||||
|
type: graph
|
||||||
|
axis_x_title: 线路测试断面
|
||||||
|
axis_y_title: 轨道高低(mm)
|
||||||
|
|
||||||
|
interact:
|
||||||
|
measure_data:
|
||||||
|
- type: input
|
||||||
|
label: 高差参数
|
||||||
|
id: height_diff_param
|
||||||
|
- type: button
|
||||||
|
label: 导入测量数据
|
||||||
|
script: load_measure_data.py
|
||||||
|
- type: button
|
||||||
|
label: 计算线形和高低值
|
||||||
|
script: calc_measure_data.py
|
||||||
|
surface_statistics:
|
||||||
|
- type: button
|
||||||
|
label: 生成直曲表
|
||||||
|
script: create_alignment_table.py
|
||||||
|
alignment_table:
|
||||||
|
- type: input
|
||||||
|
label: 直线段桩距
|
||||||
|
id: line_gap
|
||||||
|
text: "20"
|
||||||
|
- type: input
|
||||||
|
label: 曲线段桩距
|
||||||
|
id: curve_gap
|
||||||
|
text: "10"
|
||||||
|
- type: button
|
||||||
|
label: 桩号生成
|
||||||
|
script: create_stake_point.py
|
||||||
|
longitudinal_profile:
|
||||||
|
- type: button
|
||||||
|
label: 参数计算
|
||||||
|
script: calc_slope_param.py
|
||||||
|
- type: button
|
||||||
|
label: 清除计算结果
|
||||||
|
script: clear_slope_param.py
|
||||||
|
coord_calc:
|
||||||
|
- type: button
|
||||||
|
label: 中桩坐标计算
|
||||||
|
script: calc_center_stake.py
|
||||||
|
- type: button
|
||||||
|
label: 高程计算
|
||||||
|
script: calc_elevation.py
|
||||||
|
- type: button
|
||||||
|
label: 边桩坐标计算
|
||||||
|
script: calc_side_stake.py
|
||||||
3
app/data/alignment_table.csv
Normal file
3
app/data/alignment_table.csv
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
直 线 、曲 线 及 转 角 一 览 表,,,,,,,,,,,,,,,,,,,,
|
||||||
|
交点号,交点坐标,,交点桩号,偏角值,,曲线要素值,,,,,,,曲线主点桩号,,,,,直线长度及方向,,,左右偏
|
||||||
|
,N(X),E(Y),,左偏,右偏,R,Ls1,Ls2,T1,T2,L,E,ZH,HY,OZ,YH,HZ,直线长度,交点间距,计算方位角
|
||||||
|
6
app/data/coord_calc.csv
Normal file
6
app/data/coord_calc.csv
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
桩号,,中线坐标,,切线方位角,高程,左边桩坐标,,,,右边桩坐标,,,
|
||||||
|
,,N(X),E(Y),,,距离,右夹角,N(X),E(Y),距离,右夹角,N(X),E(Y)
|
||||||
|
,K1+794.807
|
||||||
|
,K1+799.647
|
||||||
|
,K1+805.054
|
||||||
|
,K1+809.894
|
||||||
|
7
app/data/surface_statistics.csv
Normal file
7
app/data/surface_statistics.csv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
1,QD,3194554.194,501250.714,,,,0
|
||||||
|
2,JD1,3194824.682,501594.205,700,200,200
|
||||||
|
3,JD2,3196190.070,501605.670,1500,200,200
|
||||||
|
4,JD3,3197202.632,502209.577,900,110,110
|
||||||
|
5,JD4,3197635.505,502305.092,700,130,130
|
||||||
|
6,JD5,3198039.668,502655.089,700,130,130
|
||||||
|
7,JD6,3200279.842,502851.404,,,
|
||||||
|
27
app/data/temp.csv
Normal file
27
app/data/temp.csv
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
,10.11743,,10.15147
|
||||||
|
,10.1187,,10.15095
|
||||||
|
,10.12116,,10.15089
|
||||||
|
,10.10898,,10.14153
|
||||||
|
,10.10388,,10.12936
|
||||||
|
,10.08782,,10.11529
|
||||||
|
,10.06438,,10.09277
|
||||||
|
,10.04788,,10.07494
|
||||||
|
,10.03274,,10.06181
|
||||||
|
,10.01209,,10.04402
|
||||||
|
,9.99383,,10.01958
|
||||||
|
,9.97242,,10.00426
|
||||||
|
,9.94911,,9.96898
|
||||||
|
,9.92069,,9.94508
|
||||||
|
,9.89699,,9.92501
|
||||||
|
,9.87115,,9.9056
|
||||||
|
,9.85531,,9.89211
|
||||||
|
,9.85643,,9.89182
|
||||||
|
,9.85109,,9.88541
|
||||||
|
,9.82577,,9.85734
|
||||||
|
,9.8073,,9.83796
|
||||||
|
,9.80619,,9.83136
|
||||||
|
,9.80005,,9.83231
|
||||||
|
,9.79032,,9.8218
|
||||||
|
,9.79293,,9.82281
|
||||||
|
,9.77558,,9.8075
|
||||||
|
,9.75878,,9.79372
|
||||||
|
63162
app/doc/a.css
Normal file
63162
app/doc/a.css
Normal file
File diff suppressed because one or more lines are too long
37397
app/doc/b.css
Normal file
37397
app/doc/b.css
Normal file
File diff suppressed because it is too large
Load Diff
539
app/doc/开发指南.html
Normal file
539
app/doc/开发指南.html
Normal file
File diff suppressed because one or more lines are too long
98
app/formatter.py
Normal file
98
app/formatter.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import re
|
||||||
|
import math
|
||||||
|
|
||||||
|
#将弧度转换为度分秒
|
||||||
|
def rad2dms(r):
|
||||||
|
d = r * 180 / math.pi
|
||||||
|
m = (d - int(d)) * 60
|
||||||
|
d = int(d)
|
||||||
|
s = (m - int(m)) * 60
|
||||||
|
m = int(m)
|
||||||
|
return d, m, s
|
||||||
|
|
||||||
|
#将度分秒转换为弧度
|
||||||
|
def dms2rad(d, m, s):
|
||||||
|
return (d + m / 60 + s / 360) / 180 * math.pi
|
||||||
|
|
||||||
|
class string:
|
||||||
|
dtype = str
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def str2val(self, s):
|
||||||
|
return s
|
||||||
|
def val2str(self, v):
|
||||||
|
return v
|
||||||
|
|
||||||
|
class integer:
|
||||||
|
dtype = int
|
||||||
|
def __init__(self, fmt = "%d"):
|
||||||
|
self.fmt = fmt
|
||||||
|
def str2val(self, s):
|
||||||
|
s = s.strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
v = int(s)
|
||||||
|
except:
|
||||||
|
v = int(float(s))
|
||||||
|
return v
|
||||||
|
def val2str(self, v):
|
||||||
|
return self.fmt % v
|
||||||
|
|
||||||
|
class decimal:
|
||||||
|
dtype = float
|
||||||
|
def __init__(self, fmt = "%.3f", epsilon = 1e-4):
|
||||||
|
self.fmt = fmt
|
||||||
|
self.epsilon = epsilon
|
||||||
|
def str2val(self, s):
|
||||||
|
s = s.strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
return float(s)
|
||||||
|
def val2str(self, v):
|
||||||
|
if abs(v) < self.epsilon:
|
||||||
|
v = 0.0
|
||||||
|
return self.fmt % v
|
||||||
|
|
||||||
|
class kdecimal:
|
||||||
|
dtype = float
|
||||||
|
def __init__(self, fmt = "%07.3f", epsilon = 1e-4):
|
||||||
|
self.fmt = fmt
|
||||||
|
self.epsilon = epsilon
|
||||||
|
def str2val(self, s):
|
||||||
|
s = s.strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
match = re.match(r"[kK](\d+)\+(\d+(?:\.\d+)?)", s)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError(f"pattern not matched: {s}")
|
||||||
|
a = match.group(1)
|
||||||
|
b = match.group(2)
|
||||||
|
return float(a + b)
|
||||||
|
def val2str(self, v):
|
||||||
|
a = v // 1000
|
||||||
|
b = v % 1000
|
||||||
|
if abs(b - 1000) < self.epsilon:
|
||||||
|
a += 1
|
||||||
|
b = 0.0
|
||||||
|
elif abs(b) < self.epsilon:
|
||||||
|
b = 0.0
|
||||||
|
return f"K%d+{self.fmt}" % (a, b)
|
||||||
|
|
||||||
|
class angle:
|
||||||
|
dtype = float
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def str2val(self, s):
|
||||||
|
s = s.strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
match = re.match(r"(\d+)°(\d+)′(\d+(?:\.\d+)?)″", s)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError(f"pattern not matched: {s}")
|
||||||
|
d = int(match.group(1))
|
||||||
|
m = int(match.group(2))
|
||||||
|
s = float(match.group(3))
|
||||||
|
return dms2rad(d, m, s)
|
||||||
|
def val2str(self, v):
|
||||||
|
return "%2d°%02d′%05.2f″" % rad2dms(v)
|
||||||
39
app/main.py
Normal file
39
app/main.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
from IPython.terminal.embed import InteractiveShellEmbed as interactive_shell_embed
|
||||||
|
import IPython.core.page
|
||||||
|
|
||||||
|
import config
|
||||||
|
from main_window import main_window
|
||||||
|
from main_app import main_app
|
||||||
|
|
||||||
|
def page_printer(data, start = 0, screen_lines = 0, pager_cmd = None):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
data = data["text/plain"]
|
||||||
|
print(data)
|
||||||
|
|
||||||
|
IPython.core.page.page = page_printer
|
||||||
|
ipshell = interactive_shell_embed(
|
||||||
|
user_ns = globals(), colors = "Linux",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog = "道路数据处理",
|
||||||
|
description = "",
|
||||||
|
# epilog = "Text at the bottom of help",
|
||||||
|
)
|
||||||
|
parser.add_argument("-f", "--file", type = str, help = "启动前加载的Python脚本文件", default = "")
|
||||||
|
arg = parser.parse_args()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = main_app(sys.argv)
|
||||||
|
window = main_window(ipshell)
|
||||||
|
window.show()
|
||||||
|
window.redirect()
|
||||||
|
if arg.file:
|
||||||
|
window.load_script(arg.file)
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
26
app/main_app.py
Normal file
26
app/main_app.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QApplication)
|
||||||
|
from PyQt6.QtGui import (QPalette, QColor, QColorConstants)
|
||||||
|
|
||||||
|
class main_app(QApplication):
|
||||||
|
def __init__(self, *arg):
|
||||||
|
super(main_app, self).__init__(*arg)
|
||||||
|
|
||||||
|
self.setStyle("Fusion")
|
||||||
|
palette = QPalette()
|
||||||
|
palette.setColor(QPalette.ColorRole.Window, QColor(53, 53, 53))
|
||||||
|
palette.setColor(QPalette.ColorRole.WindowText, QColorConstants.White)
|
||||||
|
palette.setColor(QPalette.ColorRole.Base, QColor(25, 25, 25))
|
||||||
|
palette.setColor(QPalette.ColorRole.AlternateBase, QColor(53, 53, 53))
|
||||||
|
palette.setColor(QPalette.ColorRole.ToolTipBase, QColorConstants.White)
|
||||||
|
palette.setColor(QPalette.ColorRole.ToolTipText, QColorConstants.Black)
|
||||||
|
palette.setColor(QPalette.ColorRole.Text, QColorConstants.White)
|
||||||
|
palette.setColor(QPalette.ColorRole.Button, QColor(53, 53, 53))
|
||||||
|
palette.setColor(QPalette.ColorRole.ButtonText, QColorConstants.White)
|
||||||
|
palette.setColor(QPalette.ColorRole.BrightText, QColorConstants.Red)
|
||||||
|
palette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218))
|
||||||
|
palette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218))
|
||||||
|
palette.setColor(QPalette.ColorRole.HighlightedText, QColorConstants.Black)
|
||||||
|
palette.setColor(QPalette.ColorRole.PlaceholderText, QColor(150, 150, 150))
|
||||||
|
self.setPalette(palette)
|
||||||
304
app/main_window.py
Normal file
304
app/main_window.py
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import yaml
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QMainWindow, QFileDialog,
|
||||||
|
QMessageBox,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QIcon, QAction, QFont,
|
||||||
|
)
|
||||||
|
|
||||||
|
import color
|
||||||
|
import config
|
||||||
|
from widget_display import widget_display
|
||||||
|
from widget_tree import widget_tree
|
||||||
|
from widget_interact import widget_interact
|
||||||
|
from widget_term import widget_term
|
||||||
|
import formatter
|
||||||
|
import sheet
|
||||||
|
|
||||||
|
font = QFont("consolas", 10)
|
||||||
|
font.setFamilies(["consolas", "黑体"])
|
||||||
|
|
||||||
|
class message_about(QMessageBox):
|
||||||
|
def __init__(self, window, *arg):
|
||||||
|
super(message_about, self).__init__(*arg)
|
||||||
|
self.window = window
|
||||||
|
self.setIcon(QMessageBox.Icon.Information)
|
||||||
|
self.setText("关于")
|
||||||
|
self.setInformativeText("by 李隆坤\n nugnikoll@qq.com")
|
||||||
|
self.setWindowTitle("关于")
|
||||||
|
# self.setDetailedText("The details are as follows:")
|
||||||
|
self.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||||
|
|
||||||
|
class main_window(QMainWindow):
|
||||||
|
def __init__(self, ipshell, *arg):
|
||||||
|
super(main_window, self).__init__(*arg)
|
||||||
|
self.ipshell = ipshell
|
||||||
|
self.stdout_save = sys.stdout
|
||||||
|
self.stderr_save = sys.stderr
|
||||||
|
|
||||||
|
sheet.window = self
|
||||||
|
self.ipshell.user_ns["formatter"] = formatter
|
||||||
|
self.ipshell.user_ns["sheet"] = sheet.sheet
|
||||||
|
self.ipshell.user_ns["cell"] = sheet.cell
|
||||||
|
self.ipshell.user_ns["graph"] = sheet.graph
|
||||||
|
self.ipshell.user_ns["plot"] = sheet.plot
|
||||||
|
self.ipshell.user_ns["param"] = sheet.param
|
||||||
|
|
||||||
|
self.setFont(font)
|
||||||
|
self.setWindowTitle("数据处理")
|
||||||
|
self.statusBar().showMessage("就绪")
|
||||||
|
|
||||||
|
self.create_menubar()
|
||||||
|
|
||||||
|
self.widget_display = widget_display(self)
|
||||||
|
self.widget_display.setObjectName("widget display")
|
||||||
|
self.setCentralWidget(self.widget_display)
|
||||||
|
self.widget_tree = widget_tree(self)
|
||||||
|
self.widget_tree.setObjectName("widget tree")
|
||||||
|
self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.widget_tree)
|
||||||
|
self.widget_interact = widget_interact(self)
|
||||||
|
self.widget_interact.setObjectName("widget interact")
|
||||||
|
self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.widget_interact)
|
||||||
|
self.widget_term = widget_term(self)
|
||||||
|
self.widget_term.setObjectName("widget term")
|
||||||
|
self.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, self.widget_term)
|
||||||
|
|
||||||
|
self.widget_display.initial_table()
|
||||||
|
|
||||||
|
if "main_window" in config.config:
|
||||||
|
if "geometry" in config.config["main_window"]:
|
||||||
|
self.restoreGeometry(config.config["main_window"]["geometry"])
|
||||||
|
if "state" in config.config["main_window"]:
|
||||||
|
self.restoreState(config.config["main_window"]["state"])
|
||||||
|
|
||||||
|
#redirect iostream
|
||||||
|
def redirect(self):
|
||||||
|
self.stdout_save = sys.stdout
|
||||||
|
self.stderr_save = sys.stderr
|
||||||
|
|
||||||
|
sys.stdout = self.widget_term.text_term
|
||||||
|
sys.stderr = self.widget_term.text_term
|
||||||
|
|
||||||
|
def restore_redirect(self):
|
||||||
|
sys.stdout = self.stdout_save
|
||||||
|
sys.stderr = self.stderr_save
|
||||||
|
|
||||||
|
def create_menubar(self):
|
||||||
|
menu_file = self.menuBar().addMenu("文件(&F)")
|
||||||
|
# act_new_game = QAction(
|
||||||
|
# "新文件(&N)", self, shortcut = "Ctrl+N",
|
||||||
|
# statusTip = "创建一个新文件", triggered = self.new_file
|
||||||
|
# )
|
||||||
|
# menu_file.addAction(act_new_game)
|
||||||
|
act_import_data = QAction(
|
||||||
|
"导入数据(&I)", self, shortcut = "Ctrl+I",
|
||||||
|
statusTip = "从文件导入数据到表格", triggered = self.on_import_data
|
||||||
|
)
|
||||||
|
menu_file.addAction(act_import_data)
|
||||||
|
act_export_data = QAction(
|
||||||
|
"导出数据(&E)", self, shortcut = "Ctrl+T",
|
||||||
|
statusTip = "将表格数据导出到文件", triggered = self.on_export_data
|
||||||
|
)
|
||||||
|
menu_file.addAction(act_export_data)
|
||||||
|
act_reset_data = QAction(
|
||||||
|
"重置数据(&R)", self, #shortcut = "Ctrl+R",
|
||||||
|
statusTip = "将表格数据重置为初始数据", triggered = self.on_reset_data
|
||||||
|
)
|
||||||
|
menu_file.addAction(act_reset_data)
|
||||||
|
act_load_script = QAction(
|
||||||
|
"运行脚本(&L)", self, shortcut = "Ctrl+L",
|
||||||
|
statusTip = "加载并运行一个Python脚本", triggered = self.on_load_script
|
||||||
|
)
|
||||||
|
menu_file.addAction(act_load_script)
|
||||||
|
act_quit = QAction(
|
||||||
|
"退出(&Q)", self, shortcut = "Alt+F4",
|
||||||
|
statusTip = "退出程序", triggered = self.close
|
||||||
|
)
|
||||||
|
menu_file.addAction(act_quit)
|
||||||
|
|
||||||
|
# menu_edit = self.menuBar().addMenu("编辑(&E)")
|
||||||
|
# act_undo = QAction(
|
||||||
|
# "撤销(&U)", self, shortcut = "Ctrl+Z",
|
||||||
|
# statusTip = "撤销最新修改", triggered = self.undo
|
||||||
|
# )
|
||||||
|
# menu_edit.addAction(act_undo)
|
||||||
|
# act_redo = QAction(
|
||||||
|
# "重做(&R)", self, shortcut = "Ctrl+Y",
|
||||||
|
# statusTip = "恢复最新修改", triggered = self.redo
|
||||||
|
# )
|
||||||
|
# menu_edit.addAction(act_redo)
|
||||||
|
|
||||||
|
menu_window = self.menuBar().addMenu("窗口(&W)")
|
||||||
|
act_widget_tree = QAction(
|
||||||
|
"项目窗口(&P)", self,
|
||||||
|
statusTip = "显示/隐藏 项目窗口", triggered = self.toggle_widget_tree
|
||||||
|
)
|
||||||
|
menu_window.addAction(act_widget_tree)
|
||||||
|
act_widget_interact = QAction(
|
||||||
|
"交互窗口(&I)", self,
|
||||||
|
statusTip = "显示/隐藏 交互窗口", triggered = self.toggle_widget_interact
|
||||||
|
)
|
||||||
|
menu_window.addAction(act_widget_interact)
|
||||||
|
act_widget_term = QAction(
|
||||||
|
"终端窗口(&T)", self,
|
||||||
|
statusTip = "显示/隐藏 终端窗口", triggered = self.toggle_widget_term
|
||||||
|
)
|
||||||
|
menu_window.addAction(act_widget_term)
|
||||||
|
|
||||||
|
menu_help = self.menuBar().addMenu("帮助(&H)")
|
||||||
|
act_doc_develop = QAction(
|
||||||
|
"开发指南(&D)", self, shortcut = "F1",
|
||||||
|
statusTip = "在浏览器中阅读开发指南", triggered = self.show_doc_develop
|
||||||
|
)
|
||||||
|
menu_help.addAction(act_doc_develop)
|
||||||
|
act_about = QAction(
|
||||||
|
"关于(&A)", self,
|
||||||
|
statusTip = "关于数据处理", triggered = self.show_about
|
||||||
|
)
|
||||||
|
menu_help.addAction(act_about)
|
||||||
|
|
||||||
|
def on_import_data(self, filename = None):
|
||||||
|
if self.widget_display.get_current_widget() is None:
|
||||||
|
return
|
||||||
|
if not filename:
|
||||||
|
if hasattr(self.on_import_data, "filename"):
|
||||||
|
path_hint = os.path.dirname(self.on_import_data.filename)
|
||||||
|
else:
|
||||||
|
path_hint = ""
|
||||||
|
filename, _ = QFileDialog.getOpenFileName(self, "从文件导入数据", path_hint, "CSV文件(*.csv)")
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(filename) < 4 or filename[-4:] != ".csv":
|
||||||
|
filename += ".csv"
|
||||||
|
|
||||||
|
filename = filename.replace("\\", "\\\\")
|
||||||
|
filename = filename.replace("\"", "\\\"")
|
||||||
|
|
||||||
|
widget_id = self.widget_display.get_current_id()
|
||||||
|
self.process(f"sheet(\"{widget_id}\").load_file(\"{filename}\")")
|
||||||
|
|
||||||
|
def on_export_data(self, filename = None):
|
||||||
|
if self.widget_display.get_current_widget() is None:
|
||||||
|
return
|
||||||
|
if not filename:
|
||||||
|
if hasattr(self.on_export_data, "filename"):
|
||||||
|
path_hint = os.path.dirname(self.on_export_data.filename)
|
||||||
|
else:
|
||||||
|
path_hint = ""
|
||||||
|
filename, _ = QFileDialog.getSaveFileName(self, "导出数据到文件", path_hint, "CSV文件(*.csv)")
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(filename) < 4 or filename[-4:] != ".csv":
|
||||||
|
filename += ".csv"
|
||||||
|
|
||||||
|
filename = filename.replace("\\", "\\\\")
|
||||||
|
filename = filename.replace("\"", "\\\"")
|
||||||
|
|
||||||
|
widget_id = self.widget_display.get_current_id()
|
||||||
|
self.process(f"sheet(\"{widget_id}\").dump_file(\"{filename}\")")
|
||||||
|
|
||||||
|
def on_reset_data(self):
|
||||||
|
widget_id = self.widget_display.get_current_id()
|
||||||
|
self.process(f"sheet(\"{widget_id}\").reset_content()")
|
||||||
|
|
||||||
|
def get_config(self):
|
||||||
|
data = {
|
||||||
|
"main_window": {
|
||||||
|
"geometry": self.saveGeometry().data(),
|
||||||
|
"state": self.saveState().data(),
|
||||||
|
#"widget_interact": self.widget_interact.get_config(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
self.restore_redirect()
|
||||||
|
|
||||||
|
data = self.get_config()
|
||||||
|
with open(config.config_file, "w", encoding = "utf-8") as fobj:
|
||||||
|
yaml.safe_dump(data, fobj, allow_unicode = True)
|
||||||
|
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
#process command
|
||||||
|
def process(self, s):
|
||||||
|
s = s.replace("\u2029", "\n")
|
||||||
|
|
||||||
|
print(f"{color.light_green}>>{color.nc}" + s)
|
||||||
|
|
||||||
|
if not s:
|
||||||
|
return
|
||||||
|
if s[0] == "%":
|
||||||
|
result = self.ipshell.magic(s)
|
||||||
|
if result:
|
||||||
|
print(result)
|
||||||
|
else:
|
||||||
|
query = re.search(r"[_0-9a-zA-Z.]+\?", s)
|
||||||
|
if query:
|
||||||
|
result = self.ipshell.magic("pinfo " + query.group()[:-1])
|
||||||
|
else:
|
||||||
|
query = re.search(r"[_0-9a-zA-Z.]+\?\?", s)
|
||||||
|
if query:
|
||||||
|
result = self.ipshell.magic("pinfo2 " + query.group()[:-1])
|
||||||
|
self.ipshell.ex(s)
|
||||||
|
|
||||||
|
#load and execute a script
|
||||||
|
def load_script(self, filename):
|
||||||
|
with open(filename, "r", encoding = "utf-8") as fobj:
|
||||||
|
content = fobj.read()
|
||||||
|
self.ipshell.ex(content)
|
||||||
|
|
||||||
|
#load and execute a script
|
||||||
|
def on_load_script(self, filename = None):
|
||||||
|
if not filename:
|
||||||
|
filename, _ = QFileDialog.getOpenFileName(self, "加载python脚本", "", "Python脚本(*.py)")
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
filename = filename.replace("\\", "\\\\")
|
||||||
|
filename = filename.replace("\"", "\\\"")
|
||||||
|
self.process(f"window.load_script(\"{filename}\")")
|
||||||
|
|
||||||
|
def undo(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def redo(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show_doc_develop(self, event):
|
||||||
|
doc_file = os.path.join(config.doc_path, "开发指南.html")
|
||||||
|
webbrowser.open_new_tab(doc_file)
|
||||||
|
|
||||||
|
def show_about(self, event):
|
||||||
|
if not hasattr(self, "msg_about"):
|
||||||
|
self.msg_about = message_about(self)
|
||||||
|
self.msg_about.exec()
|
||||||
|
|
||||||
|
def toggle_widget_interact(self, event):
|
||||||
|
if self.widget_interact.isVisible():
|
||||||
|
self.widget_interact.hide()
|
||||||
|
else:
|
||||||
|
self.widget_interact.show()
|
||||||
|
|
||||||
|
def toggle_widget_tree(self, event):
|
||||||
|
if self.widget_tree.isVisible():
|
||||||
|
self.widget_tree.hide()
|
||||||
|
else:
|
||||||
|
self.widget_tree.show()
|
||||||
|
|
||||||
|
def toggle_widget_term(self, event):
|
||||||
|
if self.widget_term.isVisible():
|
||||||
|
self.widget_term.hide()
|
||||||
|
else:
|
||||||
|
self.widget_term.show()
|
||||||
106
app/script/calc_center_stake.py
Normal file
106
app/script/calc_center_stake.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#计算中桩坐标
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
#直曲表
|
||||||
|
sheet_align = sheet("alignment_table")
|
||||||
|
|
||||||
|
#坐标计算表
|
||||||
|
sheet_coord = sheet("coord_calc")
|
||||||
|
|
||||||
|
row1 = 4
|
||||||
|
row2 = 2
|
||||||
|
|
||||||
|
with sheet_align:
|
||||||
|
ss1 = 0
|
||||||
|
while not sheet_coord[row2, 1].empty():
|
||||||
|
lxzh = sheet_coord[row2, 1].value #待计算点桩号
|
||||||
|
xjd = cell(row1, 1).value #交点x坐标
|
||||||
|
yjd = cell(row1, 2).value #交点y坐标
|
||||||
|
kjd = cell(row1, 3).value #jd点桩号
|
||||||
|
a0 = cell(row1, 20).value #后切线方位角
|
||||||
|
|
||||||
|
if cell(row1, 13).empty():
|
||||||
|
x = xjd - (kjd - lxzh) * math.cos(a0)
|
||||||
|
y = yjd - (kjd - lxzh) * math.sin(a0)
|
||||||
|
sheet_coord[row2, 2].value = x
|
||||||
|
sheet_coord[row2, 3].value = y
|
||||||
|
sheet_coord[row2, 4].value = a0
|
||||||
|
else:
|
||||||
|
a1 = cell(row1 + 1, 20).value #前切线方位角
|
||||||
|
r = cell(row1, 6).value #圆曲线半径
|
||||||
|
ls1 = cell(row1, 7).value #第一缓和曲线长
|
||||||
|
ls2 = cell(row1, 8).value #第二缓和曲线长
|
||||||
|
t1 = cell(row1, 9).value #第一切线长
|
||||||
|
t2 = cell(row1, 10).value #第二切线长
|
||||||
|
ll = cell(row1, 11).value #曲线长
|
||||||
|
ly = ll - ls1 - ls2 #圆曲线长
|
||||||
|
kzh = cell(row1, 13).value #zh点桩号
|
||||||
|
khy = cell(row1, 14).value #hy点桩号
|
||||||
|
kqz = cell(row1, 15).value #qz点桩号
|
||||||
|
kyh = cell(row1, 16).value #yh点桩号
|
||||||
|
khz = cell(row1, 17).value #hz点桩号
|
||||||
|
zy = cell(row1, 21).value #路线左右偏代码
|
||||||
|
|
||||||
|
if lxzh <= kzh: #直线段上的点
|
||||||
|
l = kzh - lxzh
|
||||||
|
x = xjd - (t1 + l) * math.cos(a0)
|
||||||
|
y = yjd - (t1 + l) * math.sin(a0)
|
||||||
|
sheet_coord[row2, 2].value = x
|
||||||
|
sheet_coord[row2, 3].value = y
|
||||||
|
sheet_coord[row2, 4].value = a0
|
||||||
|
elif lxzh > kzh and lxzh < khy: #第一回旋线上的点
|
||||||
|
if ss1 != 3:
|
||||||
|
x1 = xjd - t1 * math.cos(a0)
|
||||||
|
y1 = yjd - t1 * math.sin(a0)
|
||||||
|
ss1 = 3
|
||||||
|
l = lxzh - kzh
|
||||||
|
xp = l - l ** 5 / (40 * (r * ls1) ** 2) + l ** 9 / (3456 * (r * ls1) ** 4)
|
||||||
|
yp = l ** 3 / (6 * r * ls1) - l ** 7 / (336 * (r * ls1) ** 3) + l ** 11 / (42240 * (r * ls1) ** 5)
|
||||||
|
x = x1 + xp * math.cos(a0) - zy * yp * math.sin(a0)
|
||||||
|
y = y1 + xp * math.sin(a0) + zy * yp * math.cos(a0)
|
||||||
|
b = a0 + zy * l * l / (2 * r * ls1)
|
||||||
|
b %= math.pi * 2
|
||||||
|
sheet_coord[row2, 2].value = x
|
||||||
|
sheet_coord[row2, 3].value = y
|
||||||
|
sheet_coord[row2, 4].value = b
|
||||||
|
elif lxzh >= khy and lxzh <= kyh: #圆曲线上的点
|
||||||
|
if ss1 != 4:
|
||||||
|
x1 = xjd - t1 * math.cos(a0)
|
||||||
|
y1 = yjd - t1 * math.sin(a0)
|
||||||
|
q1 = ls1 / 2 - ls1 ** 3 / (240 * r ** 2) + ls1 ** 5 / (34560 * r ** 4)
|
||||||
|
p1 = ls1 ** 2 / (24 * r) - ls1 ** 4 / (2688 * r ** 3) + ls1 ** 6 / (506880 * r ** 5)
|
||||||
|
ss1 = 4
|
||||||
|
l = lxzh - kzh
|
||||||
|
tp = (2 * l - ls1) / 2 / r
|
||||||
|
xp = r * math.sin(tp) + q1
|
||||||
|
yp = r * (1 - math.cos(tp)) + p1
|
||||||
|
x = x1 + xp * math.cos(a0) - zy * yp * math.sin(a0)
|
||||||
|
y = y1 + xp * math.sin(a0) + zy * yp * math.cos(a0)
|
||||||
|
b = a0 + zy * tp
|
||||||
|
b %= math.pi * 2
|
||||||
|
sheet_coord[row2, 2].value = x
|
||||||
|
sheet_coord[row2, 3].value = y
|
||||||
|
sheet_coord[row2, 4].value = b
|
||||||
|
elif lxzh > kyh and lxzh < khz: #第二回旋线上的点
|
||||||
|
if ss1 != 5:
|
||||||
|
x1 = xjd + t2 * math.cos(a1)
|
||||||
|
y1 = yjd + t2 * math.sin(a1)
|
||||||
|
ss1 = 5
|
||||||
|
l = khz - lxzh
|
||||||
|
xp = l - l ** 5 / (40 * (r * ls2) ** 2) + l ** 9 / (3456 * (r * ls2) ** 4)
|
||||||
|
yp = l ** 3 / (6 * r * ls2) - l ** 7 / (336 * (r * ls2) ** 3) + l ** 11 / (42240 * (r * ls2) ** 5)
|
||||||
|
x = x1 - xp * math.cos(a1) - zy * yp * math.sin(a1)
|
||||||
|
y = y1 - xp * math.sin(a1) + zy * yp * math.cos(a1)
|
||||||
|
b = a1 - zy * l * l / (2 * ls2 * r)
|
||||||
|
b %= math.pi * 2
|
||||||
|
sheet_coord[row2, 2].value = x
|
||||||
|
sheet_coord[row2, 3].value = y
|
||||||
|
sheet_coord[row2, 4].value = b
|
||||||
|
else:
|
||||||
|
ss1 = 10
|
||||||
|
row1 += 1
|
||||||
|
row2 -= 1
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
sheet_coord.fit_content()
|
||||||
67
app/script/calc_elevation.py
Normal file
67
app/script/calc_elevation.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# 计算中桩设计高程
|
||||||
|
|
||||||
|
import color
|
||||||
|
|
||||||
|
#纵断面表
|
||||||
|
sheet_longitudinal = sheet("longitudinal_profile")
|
||||||
|
|
||||||
|
#坐标计算表
|
||||||
|
sheet_coord = sheet("coord_calc")
|
||||||
|
|
||||||
|
def sgn(x):
|
||||||
|
if x > 0:
|
||||||
|
return 1
|
||||||
|
if x < 0:
|
||||||
|
return -1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def calc_elevation():
|
||||||
|
with sheet_longitudinal:
|
||||||
|
row = 0
|
||||||
|
while not cell(row, 0).empty():
|
||||||
|
row += 1
|
||||||
|
if row < 2:
|
||||||
|
print(f"{color.red}错误{color.nc}:纵断面表缺少高程设计数据")
|
||||||
|
return
|
||||||
|
max_row = row
|
||||||
|
|
||||||
|
min_chainage = cell(0, 0).value
|
||||||
|
max_chainage = cell(max_row - 1, 0).value
|
||||||
|
|
||||||
|
row = 2
|
||||||
|
d = 0
|
||||||
|
while not sheet_coord[row, 1].empty():
|
||||||
|
chainage = sheet_coord[row, 1].value
|
||||||
|
if min_chainage <= chainage <= max_chainage:
|
||||||
|
row2 = 1
|
||||||
|
while not cell(row2, 0).empty():
|
||||||
|
if chainage < cell(row2, 0).value:
|
||||||
|
break
|
||||||
|
row2 = row2 + 1
|
||||||
|
start_chainage = cell(row2 - 1, 0).value #起始里程
|
||||||
|
end_chainage = cell(row2, 0).value #终止里程
|
||||||
|
if cell(row2 - 1, 2).empty():
|
||||||
|
start_radius = None
|
||||||
|
else:
|
||||||
|
start_radius = cell(row2 - 1, 2).value * sgn(cell(row2, 4).value - cell(row2 - 1, 4).value) #起始半径
|
||||||
|
end_radius = cell(row2, 2).value * sgn(cell(row2 + 1, 4).value - cell(row2, 4).value) #终止半径
|
||||||
|
start_t = cell(row2 - 1, 6).value or 0 #起始切线长
|
||||||
|
end_t = cell(row2, 6).value #终止切线长
|
||||||
|
start_h = cell(row2 - 1, 1).value #起始高程
|
||||||
|
ratio = cell(row2, 4).value / 100 #坡度
|
||||||
|
|
||||||
|
elevation = start_h + (chainage - start_chainage) * ratio
|
||||||
|
|
||||||
|
if chainage < start_chainage + start_t:
|
||||||
|
d = (start_chainage + start_t - chainage) ** 2 / 2 / start_radius
|
||||||
|
elif chainage > end_chainage - end_t:
|
||||||
|
if end_t < 0.000001:
|
||||||
|
d = 0
|
||||||
|
else:
|
||||||
|
d = (chainage - end_chainage + end_t) ** 2 / 2 / end_radius
|
||||||
|
elevation += d
|
||||||
|
sheet_coord[row, 5].value = elevation
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
calc_elevation()
|
||||||
71
app/script/calc_measure_data.py
Normal file
71
app/script/calc_measure_data.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#计算轨道数据
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import color
|
||||||
|
|
||||||
|
#轨道数据表
|
||||||
|
sheet_measure = sheet("measure_data")
|
||||||
|
|
||||||
|
def linear_regression(x, y):
|
||||||
|
n = x.size
|
||||||
|
sx = x.sum()
|
||||||
|
sx2 = (x ** 2).sum()
|
||||||
|
sy = y.sum()
|
||||||
|
sxy = (x * y).sum()
|
||||||
|
delta = n * sx2 - sx ** 2
|
||||||
|
a = (n * sxy - sx * sy) / delta
|
||||||
|
b = (sx2 * sy - sx * sxy) / delta
|
||||||
|
return a, b
|
||||||
|
|
||||||
|
def calc_measure_data():
|
||||||
|
diff_param = param("height_diff_param").strip()
|
||||||
|
if not diff_param:
|
||||||
|
print(f"{color.yellow}警告:{color.nc}缺少高差参数")
|
||||||
|
diff_param = 0
|
||||||
|
else:
|
||||||
|
diff_param = float(diff_param)
|
||||||
|
|
||||||
|
with sheet_measure:
|
||||||
|
row = 0
|
||||||
|
left = []
|
||||||
|
right = []
|
||||||
|
while not cell(row, 1).empty():
|
||||||
|
left.append(cell(row, 1).value)
|
||||||
|
right.append(cell(row, 3).value)
|
||||||
|
row += 1
|
||||||
|
left = np.array(left, dtype = np.float64)
|
||||||
|
right = np.array(right, dtype = np.float64)
|
||||||
|
n = left.size
|
||||||
|
index = np.array(range(left.size), dtype = np.float64)
|
||||||
|
h_diff = (left - right) * 1000 + diff_param
|
||||||
|
# left_a, left_b = linear_regression(index, left)
|
||||||
|
# right_a, right_b = linear_regression(index, right)
|
||||||
|
left_a = (left[-1] - left[0]) / (n - 1)
|
||||||
|
left_b = left[0]
|
||||||
|
right_a = (right[-1] - right[0]) / (n - 1)
|
||||||
|
right_b = right[0]
|
||||||
|
|
||||||
|
left_diff = (left - left_a * index - left_b) * 1000
|
||||||
|
right_diff = (right - right_a * index - right_b) * 1000
|
||||||
|
|
||||||
|
left_diff2 = left_diff[1: -1] - (left_diff[2:] + left_diff[:-2]) / 2
|
||||||
|
right_diff2 = right_diff[1: -1] - (right_diff[2:] + right_diff[:-2]) / 2
|
||||||
|
|
||||||
|
for row, data in enumerate(h_diff):
|
||||||
|
cell(row, 4).value = data
|
||||||
|
for row, data in enumerate(left_diff):
|
||||||
|
cell(row, 5).value = data
|
||||||
|
for row, data in enumerate(right_diff):
|
||||||
|
cell(row, 6).value = data
|
||||||
|
for row, data in enumerate(left_diff2):
|
||||||
|
cell(row + 1 , 7).value = data
|
||||||
|
for row, data in enumerate(right_diff2):
|
||||||
|
cell(row + 1 , 8).value = data
|
||||||
|
|
||||||
|
graph("road_graph").plot(index, left, "左轨", index, right, "右轨")
|
||||||
|
graph("road_relative_graph").plot(index, left_diff, "左轨", index, right_diff, "右轨")
|
||||||
|
graph("road_horizon_graph").plot(index, h_diff, "轨道水平")
|
||||||
|
graph("road_concave_graph").plot(index[1:-1], left_diff2, "左轨", index[1:-1], right_diff2, "右轨")
|
||||||
|
|
||||||
|
calc_measure_data()
|
||||||
|
sheet_measure.fit_content()
|
||||||
31
app/script/calc_side_stake.py
Normal file
31
app/script/calc_side_stake.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#计算边桩坐标
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
sheet_coord = sheet("coord_calc")
|
||||||
|
|
||||||
|
with sheet_coord:
|
||||||
|
row = 2
|
||||||
|
while not cell(row, 2).empty():
|
||||||
|
x = cell(row, 2).value
|
||||||
|
y = cell(row, 3).value
|
||||||
|
a = cell(row, 4).value
|
||||||
|
if not cell(row, 6).empty():
|
||||||
|
d = cell(row, 6).value
|
||||||
|
if cell(row, 7).empty():
|
||||||
|
ap = math.pi / 2
|
||||||
|
else:
|
||||||
|
ap = cell(row, 7).value
|
||||||
|
cell(row, 8).value = x - math.cos(a + ap) * d
|
||||||
|
cell(row, 9).value = y - math.sin(a + ap) * d
|
||||||
|
if not cell(row, 10).empty():
|
||||||
|
d = cell(row, 10).value
|
||||||
|
if cell(row, 11).empty():
|
||||||
|
ap = math.pi / 2
|
||||||
|
else:
|
||||||
|
ap = cell(row, 11).value
|
||||||
|
cell(row, 12).value = x + math.cos(a + ap) * d
|
||||||
|
cell(row, 13).value = y + math.sin(a + ap) * d
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
sheet_coord.fit_content()
|
||||||
20
app/script/calc_slope_param.py
Normal file
20
app/script/calc_slope_param.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#计算坡道参数
|
||||||
|
|
||||||
|
sheet_longitudinal = sheet("longitudinal_profile")
|
||||||
|
|
||||||
|
with sheet_longitudinal:
|
||||||
|
row = 1
|
||||||
|
while not cell(row, 1).empty():
|
||||||
|
cell(row, 3).value = cell(row, 0).value - cell(row - 1, 0).value #坡长
|
||||||
|
cell(row, 4).value = (cell(row, 1).value - cell(row - 1, 1).value) / cell(row, 3).value * 100 #坡度
|
||||||
|
row = row + 1
|
||||||
|
|
||||||
|
row = 1
|
||||||
|
while not cell(row + 1, 1).empty():
|
||||||
|
diff = cell(row + 1, 4).value - cell(row, 4).value #坡度差
|
||||||
|
cell(row, 5).value = cell(row, 2).value * abs(diff / 100) #曲线长
|
||||||
|
cell(row, 6).value = cell(row, 5).value / 2 #切线长
|
||||||
|
cell(row, 7).value = cell(row, 6).value * diff / 400 #外距
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
sheet_longitudinal.fit_content()
|
||||||
8
app/script/clear_slope_param.py
Normal file
8
app/script/clear_slope_param.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# 清空计算的坡道参数
|
||||||
|
|
||||||
|
sheet_longitudinal = sheet("longitudinal_profile")
|
||||||
|
|
||||||
|
with sheet_longitudinal:
|
||||||
|
for row in range(sheet_longitudinal.row):
|
||||||
|
for col in range(3, sheet_longitudinal.col):
|
||||||
|
cell(row, col).value = None
|
||||||
142
app/script/create_alignment_table.py
Normal file
142
app/script/create_alignment_table.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# 生成直曲表
|
||||||
|
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
#计算方位角
|
||||||
|
def azimuth(x1, y1, x2, y2):
|
||||||
|
dx = x2 - x1
|
||||||
|
dy = y2 - y1
|
||||||
|
if abs(dy) < 1e-6:
|
||||||
|
if dx > 0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return math.pi
|
||||||
|
theta = math.atan(dy / dx)
|
||||||
|
if dx < 0:
|
||||||
|
theta += math.pi
|
||||||
|
#print(dx, dy, theta / math.pi * 180)
|
||||||
|
return theta
|
||||||
|
|
||||||
|
#计算两点距离
|
||||||
|
def distance(x1, y1, x2, y2):
|
||||||
|
dx = x2 - x1
|
||||||
|
dy = y2 - y1
|
||||||
|
return math.sqrt(dx**2 + dy**2)
|
||||||
|
|
||||||
|
|
||||||
|
#平面资料表
|
||||||
|
sheet_surface = sheet("surface_statistics")
|
||||||
|
|
||||||
|
#直曲表
|
||||||
|
sheet_align = sheet("alignment_table")
|
||||||
|
|
||||||
|
#创建3*5矩阵 数据类型为64位浮点数 初始值为0
|
||||||
|
#用于存储中间数据
|
||||||
|
pm = np.zeros((3, 5), dtype = np.float64)
|
||||||
|
|
||||||
|
with sheet_surface:
|
||||||
|
jd = cell(0, 1).value
|
||||||
|
pm[0, 0] = cell(0, 2).value
|
||||||
|
pm[0, 1] = cell(0, 3).value
|
||||||
|
stt = cell(0, 7).value
|
||||||
|
|
||||||
|
with sheet_align:
|
||||||
|
sheet_align.clear_content()
|
||||||
|
sheet_align.initial()
|
||||||
|
|
||||||
|
cell(3, 0).value = jd
|
||||||
|
cell(3, 1).value = pm[0, 0]
|
||||||
|
cell(3, 2).value = pm[0, 1]
|
||||||
|
cell(3, 3).value = stt
|
||||||
|
|
||||||
|
for row in range(0, sheet_surface.row):
|
||||||
|
with sheet_surface:
|
||||||
|
if cell(row + 2, 2).empty():
|
||||||
|
break
|
||||||
|
|
||||||
|
pm[0,0] = cell(row, 2).value
|
||||||
|
pm[0,1] = cell(row, 3).value
|
||||||
|
jd = cell(row + 1, 1).value
|
||||||
|
|
||||||
|
for col in range(2, 7):
|
||||||
|
pm[1, col - 2] = cell(row + 1, col).value
|
||||||
|
pm[2, col - 2] = cell(row + 2, col).value
|
||||||
|
|
||||||
|
theta1 = azimuth(pm[0, 0], pm[0, 1], pm[1, 0], pm[1, 1])
|
||||||
|
theta2 = azimuth(pm[1, 0], pm[1, 1], pm[2, 0], pm[2, 1])
|
||||||
|
d = distance(pm[0, 0], pm[0, 1], pm[1, 0], pm[1, 1])
|
||||||
|
dtheta = theta2 - theta1
|
||||||
|
sign = 1
|
||||||
|
if dtheta < 0:
|
||||||
|
sign = -1
|
||||||
|
dtheta = abs(dtheta)
|
||||||
|
|
||||||
|
# 计算曲线要素
|
||||||
|
q1 = pm[1, 3] / 2 - pm[1, 3] ** 3 / (240 * pm[1, 2] ** 2) + pm[1, 3] ** 5 / (34560 * pm[1, 2] ** 4)
|
||||||
|
q2 = pm[1, 4] / 2 - pm[1, 4] ** 3 / (240 * pm[1, 2] ** 2) + pm[1, 4] ** 5 / (34560 * pm[1, 2] ** 4)
|
||||||
|
p1 = pm[1, 3] ** 2 / (24 * pm[1, 2]) - pm[1, 3] ** 4 / (2688 * pm[1, 2] ** 3) + pm[1, 3] ** 6 / (506880 * pm[1, 2] ** 5)
|
||||||
|
p2 = pm[1, 4] ** 2 / (24 * pm[1, 2]) - pm[1, 4] ** 4 / (2688 * pm[1, 2] ** 3) + pm[1, 4] ** 6 / (506880 * pm[1, 2] ** 5)
|
||||||
|
t1 = q1 + (pm[1, 2] + p2 - (pm[1, 2] + p1) * math.cos(dtheta)) / math.sin(dtheta)
|
||||||
|
t2 = q2 + (pm[1, 2] + p1 - (pm[1, 2] + p2) * math.cos(dtheta)) / math.sin(dtheta)
|
||||||
|
ly = pm[1, 2] * dtheta - (pm[1, 3] + pm[1, 4]) / 2
|
||||||
|
ll = ly + pm[1, 3] + pm[1, 4]
|
||||||
|
eh = (pm[1, 2] + p1 / 2 + p2 / 2) / math.cos(dtheta / 2) - pm[1, 2]
|
||||||
|
|
||||||
|
# 计算主点桩号
|
||||||
|
|
||||||
|
# 填写直曲表
|
||||||
|
cell(row + 4, 0).value = jd
|
||||||
|
cell(row + 4, 1).value = pm[1, 0]
|
||||||
|
cell(row + 4, 2).value = pm[1, 1]
|
||||||
|
if sign == 1:
|
||||||
|
cell(row + 4, 5).value = dtheta
|
||||||
|
else:
|
||||||
|
cell(row + 4, 4).value = dtheta
|
||||||
|
cell(row + 4, 6).value = pm[1, 2] #半径
|
||||||
|
cell(row + 4, 7).value = pm[1, 3] #第一缓和曲线
|
||||||
|
cell(row + 4, 8).value = pm[1, 4] #第二缓和曲线
|
||||||
|
cell(row + 4, 9).value = t1 #第一切线长
|
||||||
|
cell(row + 4, 10).value = t2 #第二切线长
|
||||||
|
cell(row + 4, 11).value = ll #曲线长
|
||||||
|
cell(row + 4, 12).value = eh #外距
|
||||||
|
cell(row + 4, 19).value = d #交点间距
|
||||||
|
|
||||||
|
t1_before = cell(row + 3, 9).value or 0
|
||||||
|
t2_before = cell(row + 3, 10).value or 0
|
||||||
|
ll_before = cell(row + 3, 11).value or 0
|
||||||
|
cell(row + 4, 18).value = cell(row + 4, 19).value - t2_before - cell(row + 4, 9).value #夹直线长度
|
||||||
|
cell(row + 4, 20).value = theta1 #计算方位角
|
||||||
|
cell(row + 4, 21).value = sign #路线左右偏代码
|
||||||
|
cell(row + 4, 13).value = cell(row + 3, 3).value - t1_before + ll_before + cell(row + 4, 18).value #ZH点桩号
|
||||||
|
cell(row + 4, 3).value = cell(row + 4, 13).value + cell(row + 4, 9).value #交点桩号
|
||||||
|
cell(row + 4, 17).value = cell(row + 4, 13).value + cell(row + 4, 11).value #HZ点桩号
|
||||||
|
cell(row + 4, 14).value = cell(row + 4, 13).value + cell(row + 4, 7).value #HY点桩号
|
||||||
|
cell(row + 4, 16).value = cell(row + 4, 17).value - cell(row + 4, 8).value #YH点桩号
|
||||||
|
cell(row + 4, 15).value = cell(row + 4, 14).value + ly / 2 #QZ点桩号
|
||||||
|
|
||||||
|
with sheet_surface:
|
||||||
|
pm[0, 0] = cell(row, 2).value
|
||||||
|
pm[0, 1] = cell(row, 3).value
|
||||||
|
jd = cell(row + 1, 1).value
|
||||||
|
|
||||||
|
for col in range(5):
|
||||||
|
pm[1, col] = cell(row + 1, col + 2).value
|
||||||
|
|
||||||
|
#计算方位角
|
||||||
|
theta1 = azimuth(pm[0, 0], pm[0, 1], pm[1, 0], pm[1, 1])
|
||||||
|
d = distance(pm[0, 0], pm[0, 1], pm[1, 0], pm[1, 1])
|
||||||
|
|
||||||
|
#填写直曲表
|
||||||
|
cell(row + 4, 0).value = jd
|
||||||
|
cell(row + 4, 1).value = pm[1, 0]
|
||||||
|
cell(row + 4, 2).value = pm[1, 1]
|
||||||
|
cell(row + 4, 19).value = d
|
||||||
|
cell(row + 4, 18).value = cell(row + 4, 19).value - cell(row + 3, 10).value - (cell(row + 4, 9).value or 0)
|
||||||
|
cell(row + 4, 3).value = cell(row + 3, 17).value + cell(row + 4, 18).value
|
||||||
|
cell(row + 4, 20).value = theta1
|
||||||
|
|
||||||
|
sheet_align.fit_content()
|
||||||
|
|
||||||
|
script_file = os.path.join(config.script_path,"create_linear_feature.py")
|
||||||
|
window.load_script(script_file)
|
||||||
167
app/script/create_linear_feature.py
Normal file
167
app/script/create_linear_feature.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# 生成线元要素表
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
#直曲表
|
||||||
|
sheet_align = sheet("alignment_table")
|
||||||
|
|
||||||
|
#线元数据表
|
||||||
|
sheet_linear = sheet("linear_feature")
|
||||||
|
|
||||||
|
#生成线元数据
|
||||||
|
sheet_linear.clear_content()
|
||||||
|
|
||||||
|
row1 = 3
|
||||||
|
row2 = 0
|
||||||
|
epsilon = 0.0005
|
||||||
|
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 3].value
|
||||||
|
sheet_linear[row2, 6].value = sheet_align[row1 + 1, 20].value
|
||||||
|
sheet_linear[row2, 7].value = sheet_align[row1, 1].value
|
||||||
|
sheet_linear[row2, 8].value = sheet_align[row1, 2].value
|
||||||
|
|
||||||
|
row1 += 1
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
while not sheet_align[row1, 6].empty():
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 13].value
|
||||||
|
if abs(sheet_linear[row2, 0].value - sheet_linear[row2 - 1, 0].value) > epsilon:
|
||||||
|
sheet_linear[row2, 2].value = 0
|
||||||
|
sheet_linear[row2, 3].value = 0
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 14].value
|
||||||
|
if abs(sheet_linear[row2, 0].value - sheet_linear[row2 - 1, 0].value) > epsilon:
|
||||||
|
sheet_linear[row2, 2].value = 0
|
||||||
|
sheet_linear[row2, 3].value = sheet_align[row1, 6].value * sheet_align[row1, 21].value
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 16].value
|
||||||
|
if abs(sheet_linear[row2, 0].value - sheet_linear[row2 - 1, 0].value) > epsilon:
|
||||||
|
sheet_linear[row2, 2].value = sheet_align[row1, 6].value * sheet_align[row1, 21].value
|
||||||
|
sheet_linear[row2, 3].value = sheet_linear[row2, 2].value
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 17].value
|
||||||
|
if abs(sheet_linear[row2, 0].value - sheet_linear[row2 - 1, 0].value) > epsilon:
|
||||||
|
sheet_linear[row2, 2].value = sheet_align[row1, 6].value * sheet_align[row1, 21].value
|
||||||
|
sheet_linear[row2, 3].value = 0
|
||||||
|
row2 += 1
|
||||||
|
|
||||||
|
row1 += 1
|
||||||
|
|
||||||
|
sheet_linear[row2, 0].value = sheet_align[row1, 3].value
|
||||||
|
sheet_linear[row2, 2].value = 0
|
||||||
|
sheet_linear[row2, 3].value = 0
|
||||||
|
|
||||||
|
#线元要素计算
|
||||||
|
|
||||||
|
def calc_linear_type(x, y):
|
||||||
|
#0-圆曲线,1-直线,2-缓和曲线
|
||||||
|
epsilon = 0.00001
|
||||||
|
if abs(x - y) >= epsilon:
|
||||||
|
return 2
|
||||||
|
if abs(x) < epsilon:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 定义桩点
|
||||||
|
class stake_point:
|
||||||
|
def __init__(
|
||||||
|
self, chainage = 0, x = 0, y = 0, z = 0,
|
||||||
|
alpha = 0, beta = 0, rho = 0
|
||||||
|
):
|
||||||
|
self.chainage = chainage #里程
|
||||||
|
self.x = x #x坐标
|
||||||
|
self.y = y #y坐标
|
||||||
|
self.z = z #高程
|
||||||
|
self.alpha = alpha #切线方位角
|
||||||
|
self.beta = beta #
|
||||||
|
self.rho = rho #曲率
|
||||||
|
|
||||||
|
# 缓和曲线
|
||||||
|
# 起点里程、曲率、坐标、切线方位角以及终点里程、曲率确定一条缓和曲线
|
||||||
|
class curve:
|
||||||
|
def __init__(self, start = None, end = None):
|
||||||
|
if start is None:
|
||||||
|
self.start = stake_point()
|
||||||
|
else:
|
||||||
|
self.start = copy.copy(start) #起始桩点
|
||||||
|
if end is None:
|
||||||
|
self.end = stake_point()
|
||||||
|
else:
|
||||||
|
self.end = copy.copy(end) #终止桩点
|
||||||
|
|
||||||
|
# 求缓和曲线曲率变化参数
|
||||||
|
def get_ratio(self):
|
||||||
|
return math.sqrt((self.end.chainage - self.start.chainage) / abs(self.end.rho - self.start.rho))
|
||||||
|
|
||||||
|
# 求里程为chainage的一点处的曲率、坐标及切线方位角,采用Gauss-Legendre公式计算
|
||||||
|
def get_point(self, chainage):
|
||||||
|
|
||||||
|
l = chainage - self.start.chainage
|
||||||
|
ll = self.end.chainage - self.start.chainage
|
||||||
|
if abs(ll) < 0.001:
|
||||||
|
return copy.copy(self.start)
|
||||||
|
|
||||||
|
sp = stake_point(chainage = chainage)
|
||||||
|
# 求点的切线方位角及曲率
|
||||||
|
sp.rho = self.start.rho + (self.end.rho - self.start.rho) * l / ll # 曲率线性变化
|
||||||
|
sp.alpha = self.start.alpha + (self.start.rho + sp.rho) * l / 2
|
||||||
|
sp.alpha %= 2 * math.pi
|
||||||
|
|
||||||
|
f = np.zeros((2, 4), dtype = np.float64)
|
||||||
|
f[0, 0] = 0.0694318442
|
||||||
|
f[0, 1] = 0.3300094782
|
||||||
|
f[0, 2] = 1 - f[0, 1]
|
||||||
|
f[0, 3] = 1 - f[0, 0]
|
||||||
|
f[1, 0] = 0.1739274226
|
||||||
|
f[1, 1] = 0.3260725774
|
||||||
|
f[1, 2] = f[1, 1]
|
||||||
|
f[1, 3] = f[1, 0]
|
||||||
|
|
||||||
|
s1 = 0
|
||||||
|
s2 = 0
|
||||||
|
a0 = self.start.alpha
|
||||||
|
c0 = self.start.rho
|
||||||
|
c1 = self.end.rho
|
||||||
|
h = (c1 - c0) * l * l / (2 * ll)
|
||||||
|
for i in range(4):
|
||||||
|
s1 = s1 + f[1, i] * math.cos(a0 + c0 * l * f[0, i] + h * f[0, i] * f[0, i])
|
||||||
|
s2 = s2 + f[1, i] * math.sin(a0 + c0 * l * f[0, i] + h * f[0, i] * f[0, i])
|
||||||
|
sp.x = self.start.x + l * s1
|
||||||
|
sp.y = self.start.y + l * s2
|
||||||
|
|
||||||
|
return sp
|
||||||
|
|
||||||
|
row = 1
|
||||||
|
with sheet_linear:
|
||||||
|
while not cell(row, 0).empty():
|
||||||
|
cell(row, 1).value = cell(row, 0).value - cell(row - 1, 0).value
|
||||||
|
if cell(row, 2).value != 0:
|
||||||
|
cell(row, 4).value = 1 / cell(row, 2).value
|
||||||
|
if cell(row, 3).value != 0:
|
||||||
|
cell(row, 5).value = 1 / cell(row, 3).value
|
||||||
|
cell(row, 9).value = calc_linear_type(cell(row, 2).value, cell(row, 3).value)
|
||||||
|
|
||||||
|
cv = curve()
|
||||||
|
cv.start.chainage = cell(row - 1, 0).value
|
||||||
|
cv.start.rho = (cell(row, 4).value or 0)
|
||||||
|
cv.start.alpha = cell(row - 1, 6).value
|
||||||
|
cv.start.x = cell(row - 1, 7).value
|
||||||
|
cv.start.y = cell(row - 1, 8).value
|
||||||
|
cv.end.chainage = cell(row, 0).value
|
||||||
|
cv.end.rho = (cell(row, 5).value or 0)
|
||||||
|
|
||||||
|
if cell(row, 9).value == 2:
|
||||||
|
cell(row, 10).value = cv.get_ratio()
|
||||||
|
|
||||||
|
sp = cv.get_point(cv.end.chainage)
|
||||||
|
cell(row, 6).value = sp.alpha
|
||||||
|
cell(row, 7).value = sp.x
|
||||||
|
cell(row, 8).value = sp.y
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
sheet_linear.fit_content()
|
||||||
59
app/script/create_stake_point.py
Normal file
59
app/script/create_stake_point.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# 生成桩号
|
||||||
|
|
||||||
|
import re
|
||||||
|
import color
|
||||||
|
|
||||||
|
line_gap = int(param("line_gap"))
|
||||||
|
curve_gap = int(param("curve_gap"))
|
||||||
|
|
||||||
|
with sheet("alignment_table"):
|
||||||
|
if cell(3, 2).empty():
|
||||||
|
print(f"{color.red}错误{color.nc}:缺少数据")
|
||||||
|
start = cell(3, 3).value
|
||||||
|
j = 4
|
||||||
|
while not cell(j, 3).empty():
|
||||||
|
j += 1
|
||||||
|
end = cell(j - 1, 3).value
|
||||||
|
|
||||||
|
align_table = [0] * 5
|
||||||
|
list_label = ["ZH", "HY", "QZ", "YH", "HZ", ""]
|
||||||
|
list_stake = []
|
||||||
|
pos = start - (start % line_gap)
|
||||||
|
with sheet("alignment_table"):
|
||||||
|
row = 5
|
||||||
|
while not cell(row, 13).empty():
|
||||||
|
for k in range(13, 18):
|
||||||
|
align_table[k - 13] = cell(row, k).value
|
||||||
|
|
||||||
|
while pos + line_gap < align_table[0]:
|
||||||
|
pos += line_gap
|
||||||
|
list_stake.append([row, pos, 5])
|
||||||
|
|
||||||
|
pos = align_table[0] - (align_table[0] % curve_gap)
|
||||||
|
while pos + curve_gap < align_table[4]:
|
||||||
|
pos += curve_gap
|
||||||
|
list_stake.append([row, pos, 5])
|
||||||
|
pos = align_table[4] - (align_table[4] % line_gap)
|
||||||
|
|
||||||
|
for k in range(5):
|
||||||
|
list_stake.append([row, align_table[k], k])
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
while pos + line_gap < end:
|
||||||
|
pos += line_gap
|
||||||
|
list_stake.append([row, pos, 5])
|
||||||
|
|
||||||
|
list_stake.append([row, end, 5])
|
||||||
|
|
||||||
|
with sheet("coord_calc") as sheet_coord:
|
||||||
|
for row in range(2, sheet_coord.row):
|
||||||
|
for col in range(6):
|
||||||
|
cell(row, col).clear()
|
||||||
|
list_stake.sort()
|
||||||
|
for row, stake in enumerate(list_stake):
|
||||||
|
row += 2
|
||||||
|
cell(row, 0).text = list_label[stake[2]]
|
||||||
|
cell(row, 1).value = stake[1]
|
||||||
|
|
||||||
|
sheet_coord.fit_content()
|
||||||
15
app/script/format_alignment_table.py
Normal file
15
app/script/format_alignment_table.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if row < 3: # 直曲表前3行为表头,不做格式转化
|
||||||
|
return None
|
||||||
|
if col in [6, 7, 8]:
|
||||||
|
return formatter.integer()
|
||||||
|
elif col in [1, 2, 9, 10, 11, 12, 18, 19]:
|
||||||
|
return formatter.decimal()
|
||||||
|
elif col in [3, 13, 14, 15, 16, 17]:
|
||||||
|
return formatter.kdecimal()
|
||||||
|
elif col in [4, 5, 20]:
|
||||||
|
return formatter.angle()
|
||||||
|
return None
|
||||||
15
app/script/format_coord_calc.py
Normal file
15
app/script/format_coord_calc.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if row < 2:
|
||||||
|
return None
|
||||||
|
if col in [2, 3, 5, 6, 8, 9, 10, 12, 13, 14, 15, 16]:
|
||||||
|
return formatter.decimal()
|
||||||
|
elif col in [1]:
|
||||||
|
return formatter.kdecimal()
|
||||||
|
elif col in [4, 7, 11]:
|
||||||
|
return formatter.angle()
|
||||||
|
elif col in []:
|
||||||
|
return formatter.integer()
|
||||||
|
return None
|
||||||
31
app/script/format_linear_feature.py
Normal file
31
app/script/format_linear_feature.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
class linear_type:
|
||||||
|
dtype = int
|
||||||
|
table = ["圆曲线", "直线", "缓和曲线"]
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def str2val(self, s):
|
||||||
|
return linear_type.table.index(s)
|
||||||
|
|
||||||
|
def val2str(self, v):
|
||||||
|
return linear_type.table[v]
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if col in [2, 3]:
|
||||||
|
return formatter.integer()
|
||||||
|
elif col in [1, 7, 8]:
|
||||||
|
return formatter.decimal()
|
||||||
|
elif col in [0]:
|
||||||
|
return formatter.kdecimal()
|
||||||
|
elif col in [6]:
|
||||||
|
return formatter.angle()
|
||||||
|
elif col in [4, 5]:
|
||||||
|
return formatter.decimal(fmt = "%.8f")
|
||||||
|
elif col in [10]:
|
||||||
|
return formatter.decimal(fmt = "%.2f")
|
||||||
|
elif col in [9]:
|
||||||
|
return linear_type()
|
||||||
|
return None
|
||||||
|
|
||||||
13
app/script/format_longitudinal_profile.py
Normal file
13
app/script/format_longitudinal_profile.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if col in [2]:
|
||||||
|
return formatter.integer()
|
||||||
|
elif col in [1, 3, 4, 5, 6, 7]:
|
||||||
|
return formatter.decimal()
|
||||||
|
elif col in [0]:
|
||||||
|
return formatter.kdecimal()
|
||||||
|
elif col in []:
|
||||||
|
return formatter.angle()
|
||||||
|
return None
|
||||||
11
app/script/format_measure_data.py
Normal file
11
app/script/format_measure_data.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if col in []:
|
||||||
|
return formatter.decimal() # 小数 默认精确到后三位
|
||||||
|
elif col in [4, 5, 6, 7, 8]:
|
||||||
|
return formatter.decimal("%.2f") # 小数 精确到后两位
|
||||||
|
elif col in [1, 3]:
|
||||||
|
return formatter.decimal("%.5f") # 小数 精确到后五位
|
||||||
|
return None
|
||||||
13
app/script/format_surface_statistic.py
Normal file
13
app/script/format_surface_statistic.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import formatter
|
||||||
|
|
||||||
|
#规定表格中的数据格式
|
||||||
|
def table_formatter(row, col):
|
||||||
|
if col in [0, 4, 5, 6, 7]:
|
||||||
|
return formatter.integer() # 整数
|
||||||
|
elif col in [2, 3]:
|
||||||
|
return formatter.decimal() # 小数
|
||||||
|
elif col in []:
|
||||||
|
return formatter.kdecimal() # k#+###.###格式的小数
|
||||||
|
elif col in []:
|
||||||
|
return formatter.angle() # 角度
|
||||||
|
return None
|
||||||
123
app/script/load_measure_data.py
Normal file
123
app/script/load_measure_data.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
#导入测量的轨道数据
|
||||||
|
|
||||||
|
import re
|
||||||
|
from PyQt6.QtWidgets import QFileDialog
|
||||||
|
import color
|
||||||
|
|
||||||
|
def get_filename(filename = None):
|
||||||
|
if not filename:
|
||||||
|
if hasattr(get_filename, "filename"):
|
||||||
|
path_hint = os.path.dirname(get_filename.filename)
|
||||||
|
else:
|
||||||
|
path_hint = ""
|
||||||
|
filename, _ = QFileDialog.getOpenFileName(window, "从DAT文件导入数据", path_hint, "DAT数据文件(*.dat *.DAT)")
|
||||||
|
if not filename:
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(filename) < 4 or filename[-4:].lower() != ".dat":
|
||||||
|
filename += ".dat"
|
||||||
|
|
||||||
|
filename = filename.replace("\\", "\\\\")
|
||||||
|
filename = filename.replace("\"", "\\\"")
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def load_measure_data(file_name):
|
||||||
|
with open(file_name, "r") as fobj:
|
||||||
|
raw = fobj.readlines()
|
||||||
|
|
||||||
|
flag = False
|
||||||
|
r1 = []
|
||||||
|
|
||||||
|
for line in raw:
|
||||||
|
line = line.strip()
|
||||||
|
match = re.match(r"For\s+M5\|Adr\s+", line)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError("Unrecognized data: {line}")
|
||||||
|
m = match.group(0)
|
||||||
|
pos = len(m)
|
||||||
|
line = line[pos:]
|
||||||
|
|
||||||
|
match = re.match(r"\d+\|([0-9A-Z]+)\s+", line)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError("Unrecognized data: {line}")
|
||||||
|
m = match.group(0)
|
||||||
|
pos = len(m)
|
||||||
|
line = line[pos:]
|
||||||
|
|
||||||
|
match = re.match(r"[0-9a-zA-Z\.\-]+", line)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError("Unrecognized data: {line}")
|
||||||
|
label = match.group(0)
|
||||||
|
pos = len(label)
|
||||||
|
line = line[pos:]
|
||||||
|
|
||||||
|
match = re.match(r"KZ\d*", label)
|
||||||
|
if not match is None:
|
||||||
|
label = match.group(0)
|
||||||
|
pos = len(label)
|
||||||
|
line = line[pos:]
|
||||||
|
#print("控制点", label, line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if label == "Intermediate":
|
||||||
|
flag = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if label == "End":
|
||||||
|
flag = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not flag:
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.match(r"[A-Z]*\d+[\.\-]\d+", label)
|
||||||
|
if match is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data = line.split("|")
|
||||||
|
data = data[3]
|
||||||
|
match = re.match(r"[0-9a-zA-Z]+\s+(\d+\.\d+)", data)
|
||||||
|
if match is None:
|
||||||
|
raise RuntimeError(f"找不到所需数据: {data}")
|
||||||
|
data = match.group(1)
|
||||||
|
|
||||||
|
#print("轨道点", label, data)
|
||||||
|
r1.append([label, data])
|
||||||
|
|
||||||
|
r2 = []
|
||||||
|
for label, data in r1:
|
||||||
|
match = re.match(r"([A-Z]*)(\d+)[\.\-](\d+)", label)
|
||||||
|
l = match.group(1)
|
||||||
|
d1 = int(match.group(2))
|
||||||
|
d2 = int(match.group(3))
|
||||||
|
d3 = float(data)
|
||||||
|
r2.append([l, d1, d2, d3])
|
||||||
|
|
||||||
|
r2.sort(key = lambda x: (x[1], x[2]))
|
||||||
|
# for line in r2:
|
||||||
|
# print(f"{line[0]}{line[1]}.{line[2]} {line[3]}")
|
||||||
|
return r2
|
||||||
|
|
||||||
|
filename = get_filename()
|
||||||
|
if filename:
|
||||||
|
#轨道数据表
|
||||||
|
sheet_measure = sheet("measure_data")
|
||||||
|
|
||||||
|
data = load_measure_data(filename)
|
||||||
|
sheet_measure.clear_content()
|
||||||
|
with sheet_measure:
|
||||||
|
for line in data:
|
||||||
|
label = "%s%d.%d" % tuple(line[:3])
|
||||||
|
row = line[1] - 1
|
||||||
|
flag = line[2] > 1
|
||||||
|
if line[2] > 2:
|
||||||
|
print(f"{color.yellow}警告{color.nc}:编号{label}第2个数字大于2,当作右轨处理")
|
||||||
|
data = line[3]
|
||||||
|
if flag:
|
||||||
|
cell(row, 2).value = label
|
||||||
|
cell(row, 3).value = data
|
||||||
|
else:
|
||||||
|
cell(row, 0).value = label
|
||||||
|
cell(row, 1).value = data
|
||||||
|
|
||||||
|
sheet_measure.fit_content()
|
||||||
164
app/sheet.py
Normal file
164
app/sheet.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
window = None
|
||||||
|
|
||||||
|
class project:
|
||||||
|
list_id = []
|
||||||
|
def __init__(self, id):
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
project.list_id.append(self.id)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
project.list_id.pop()
|
||||||
|
|
||||||
|
class sheet:
|
||||||
|
list_widget = []
|
||||||
|
def __init__(self, sheet_id):
|
||||||
|
widget = window.widget_display.map_table[sheet_id]
|
||||||
|
self.id = sheet_id
|
||||||
|
self.widget = widget
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
sheet.list_widget.append(self.widget)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
sheet.list_widget.pop()
|
||||||
|
|
||||||
|
def at(self, *args):
|
||||||
|
c = cell(*args, widget = self.widget)
|
||||||
|
return c
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if type(index) is tuple:
|
||||||
|
return self.at(*index)
|
||||||
|
else:
|
||||||
|
return self.at(index)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def row(self):
|
||||||
|
return self.widget.row
|
||||||
|
|
||||||
|
@row.setter
|
||||||
|
def row(self, row):
|
||||||
|
self.widget.row = row
|
||||||
|
|
||||||
|
@property
|
||||||
|
def col(self):
|
||||||
|
return self.widget.col
|
||||||
|
|
||||||
|
@col.setter
|
||||||
|
def col(self, col):
|
||||||
|
self.widget.col = col
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.widget.clear()
|
||||||
|
|
||||||
|
def clear_content(self):
|
||||||
|
self.widget.clearContents()
|
||||||
|
|
||||||
|
def initial(self):
|
||||||
|
self.widget.initial()
|
||||||
|
|
||||||
|
def reset_content(self):
|
||||||
|
self.widget.reset_content()
|
||||||
|
|
||||||
|
def fit_content(self):
|
||||||
|
self.widget.fit_content()
|
||||||
|
|
||||||
|
def load_data(self, *arg, **kwarg):
|
||||||
|
self.widget.load_data(*arg, **kwarg)
|
||||||
|
|
||||||
|
def dump_data(self, *arg, **kwarg):
|
||||||
|
return self.widget.dump_data(*arg, **kwarg)
|
||||||
|
|
||||||
|
def load_str(self, *arg, **kwarg):
|
||||||
|
self.widget.load_str(*arg, **kwarg)
|
||||||
|
|
||||||
|
def dump_str(self, *arg, **kwarg):
|
||||||
|
return self.widget.dump_str(*arg, **kwarg)
|
||||||
|
|
||||||
|
def load_file(self, *arg, **kwarg):
|
||||||
|
self.widget.load_file(*arg, **kwarg)
|
||||||
|
|
||||||
|
def dump_file(self, *arg, **kwarg):
|
||||||
|
self.widget.dump_file(*arg, **kwarg)
|
||||||
|
|
||||||
|
class cell:
|
||||||
|
def __init__(self, *arg, widget = None):
|
||||||
|
if len(arg) == 1:
|
||||||
|
pos = arg[0]
|
||||||
|
pos = pos.lower()
|
||||||
|
row = None
|
||||||
|
col = None
|
||||||
|
match = re.match(r"([a-z]+)(\d+)", pos)
|
||||||
|
if not match:
|
||||||
|
raise RuntimeError(f"unrecognized pos: \"{pos}\"")
|
||||||
|
row = match.group(2)
|
||||||
|
cl = match.group(1)
|
||||||
|
row = int(row) - 1
|
||||||
|
col = 0
|
||||||
|
for chr in cl:
|
||||||
|
col *= 26
|
||||||
|
col += ord(chr) - ord("a")
|
||||||
|
|
||||||
|
elif len(arg) == 2:
|
||||||
|
row = arg[0]
|
||||||
|
col = arg[1]
|
||||||
|
else:
|
||||||
|
raise RuntimeError("The number of params should not be more than 2")
|
||||||
|
|
||||||
|
self.item = None
|
||||||
|
if widget is None and sheet.list_widget:
|
||||||
|
widget = sheet.list_widget[-1]
|
||||||
|
if widget:
|
||||||
|
self.item = widget.at(row, col)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return self.item.text
|
||||||
|
|
||||||
|
@text.setter
|
||||||
|
def text(self, s):
|
||||||
|
self.item.text = s
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
return self.item.value
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, v):
|
||||||
|
self.item.value = v
|
||||||
|
|
||||||
|
def empty(self):
|
||||||
|
return self.item.empty()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.item.value = None
|
||||||
|
|
||||||
|
class graph:
|
||||||
|
list_widget = []
|
||||||
|
def __init__(self, graph_id):
|
||||||
|
widget = window.widget_display.map_graph[graph_id]
|
||||||
|
self.id = graph_id
|
||||||
|
self.widget = widget
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
graph.list_widget.append(self.widget)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
graph.list_widget.pop()
|
||||||
|
|
||||||
|
def plot(self, *arg, **kwarg):
|
||||||
|
self.widget.plot(*arg, **kwarg)
|
||||||
|
|
||||||
|
def plot(*arg, **kwarg):
|
||||||
|
widget = graph.list_widget[-1]
|
||||||
|
widget.plot(*arg, **kwarg)
|
||||||
|
|
||||||
|
def param(id):
|
||||||
|
map_input = window.widget_interact.widget_grid.map_input
|
||||||
|
result = map_input.get(id)
|
||||||
|
return result
|
||||||
110
app/text_term.py
Normal file
110
app/text_term.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QTextEdit,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QColor,
|
||||||
|
QTextCharFormat, QTextCursor
|
||||||
|
)
|
||||||
|
|
||||||
|
import color
|
||||||
|
|
||||||
|
class text_term(QTextEdit):
|
||||||
|
def __init__(self, window, limit = None, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.window = window
|
||||||
|
self.setTabStopDistance(40)
|
||||||
|
self.setReadOnly(True)
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
|
def erase_text(self, pos_begin, pos_end):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
cursor.setPosition(pos_begin)
|
||||||
|
cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, pos_end - pos_begin)
|
||||||
|
cursor.removeSelectedText()
|
||||||
|
|
||||||
|
def write(self, s):
|
||||||
|
cursor = self.textCursor()
|
||||||
|
cursor.setPosition(cursor.currentFrame().lastPosition())
|
||||||
|
ptn_color = r"\x1b\[([0-9]+(?:;[0-9]+)*)m"
|
||||||
|
g = re.finditer(ptn_color, s)
|
||||||
|
pos1 = 0
|
||||||
|
fmt = QTextCharFormat()
|
||||||
|
fore_color = QColor(200, 200, 200)
|
||||||
|
back_color = QColor(0, 0, 0)
|
||||||
|
|
||||||
|
for i in g:
|
||||||
|
cursor.insertText(s[pos1:i.span()[0]], fmt)
|
||||||
|
text_color = i.group()
|
||||||
|
match = re.fullmatch(ptn_color, text_color)
|
||||||
|
match = match.group(1)
|
||||||
|
match = [int(i) for i in match.split(";")]
|
||||||
|
# print(text_color, match, file = self.window.stdout_save)
|
||||||
|
match.reverse()
|
||||||
|
flag_normal = False
|
||||||
|
flag_bold = False
|
||||||
|
|
||||||
|
while match:
|
||||||
|
m = match.pop()
|
||||||
|
if m <= 1:
|
||||||
|
fore_color = QColor(200, 200, 200)
|
||||||
|
back_color = QColor(0, 0, 0)
|
||||||
|
if m == 1:
|
||||||
|
flag_bold = True
|
||||||
|
else:
|
||||||
|
flag_normal = True
|
||||||
|
elif (
|
||||||
|
30 <= m <= 37 or 40 <= m <= 47
|
||||||
|
or 90 <= m <= 97 or 100 <= m <= 107
|
||||||
|
):
|
||||||
|
flag = False
|
||||||
|
if m >= 40:
|
||||||
|
m -= 10
|
||||||
|
flag = True
|
||||||
|
if m >= 90:
|
||||||
|
m -= 90 - 38
|
||||||
|
m -= 30
|
||||||
|
if flag_normal:
|
||||||
|
fore_color = color.color_normal_table[m]
|
||||||
|
elif flag_bold:
|
||||||
|
fore_color = color.color_bold_table[m]
|
||||||
|
elif not flag:
|
||||||
|
fore_color = color.color4_table[m]
|
||||||
|
else:
|
||||||
|
back_color = color.color4_table[m]
|
||||||
|
elif m == 38 or m == 48:
|
||||||
|
if m == 38:
|
||||||
|
m = match.pop()
|
||||||
|
m = match.pop()
|
||||||
|
fore_color = color.color8_table[m]
|
||||||
|
elif m == 48:
|
||||||
|
m = match.pop()
|
||||||
|
m = match.pop()
|
||||||
|
back_color = color.color8_table[m]
|
||||||
|
elif m in (39, 49):
|
||||||
|
if m == 39:
|
||||||
|
fore_color = QColor(200, 200, 200)
|
||||||
|
else:
|
||||||
|
back_color = QColor(0, 0, 0)
|
||||||
|
else:
|
||||||
|
# cursor.insertText(text_color)
|
||||||
|
break
|
||||||
|
|
||||||
|
fmt.setForeground(fore_color)
|
||||||
|
fmt.setBackground(back_color)
|
||||||
|
self.setTextColor(fore_color)
|
||||||
|
pos1 = i.span()[1]
|
||||||
|
|
||||||
|
cursor.insertText(s[pos1:], fmt)
|
||||||
|
|
||||||
|
if not (self.limit is None):
|
||||||
|
surplus = self.document().characterCount() - self.limit
|
||||||
|
if surplus > 0:
|
||||||
|
self.erase_text(0, surplus)
|
||||||
|
|
||||||
|
self.ensureCursorVisible()
|
||||||
|
self.setTextCursor(cursor)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
pass
|
||||||
7
app/util.py
Normal file
7
app/util.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import importlib.util
|
||||||
|
|
||||||
|
def load_module(path):
|
||||||
|
spec = importlib.util.spec_from_file_location("dynamic_module", path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
763
app/widget_display.py
Normal file
763
app/widget_display.py
Normal file
@ -0,0 +1,763 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import copy
|
||||||
|
import math
|
||||||
|
from functools import partial
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from PyQt6.QtCore import (
|
||||||
|
Qt, QSize, QPoint, QPointF,
|
||||||
|
QMimeData,
|
||||||
|
)
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QTabWidget, QTableWidget, QTableWidgetItem,
|
||||||
|
QVBoxLayout, QMenu, QSizePolicy, QHeaderView,
|
||||||
|
QApplication, QStyledItemDelegate,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QIcon, QPainter, QColor, QPen, QBrush, QPalette,
|
||||||
|
QFont, QPainterPath, QImage, QAction,
|
||||||
|
QValidator, QIntValidator, QDoubleValidator,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt6.QtCharts import QChartView, QChart, QLineSeries, QScatterSeries, QValueAxis
|
||||||
|
|
||||||
|
from config import config, data_path, script_path
|
||||||
|
import util
|
||||||
|
|
||||||
|
font = QFont("consolas", 14)
|
||||||
|
font.setFamilies(["consolas", "黑体"])
|
||||||
|
|
||||||
|
def str2data(s, delimiter = None, line_end = "\n"):
|
||||||
|
if delimiter is None:
|
||||||
|
delimiter = [",", "\t", ";"]
|
||||||
|
data = s.split(line_end)
|
||||||
|
data = [line for line in data]
|
||||||
|
for i, line in enumerate(data):
|
||||||
|
ss = ""
|
||||||
|
l = []
|
||||||
|
for c in line:
|
||||||
|
if c in delimiter:
|
||||||
|
l.append(ss)
|
||||||
|
ss = ""
|
||||||
|
else:
|
||||||
|
ss += c
|
||||||
|
l.append(ss)
|
||||||
|
data[i] = l
|
||||||
|
return data
|
||||||
|
|
||||||
|
def data2str(data, delimiter = ",", line_end = "\n"):
|
||||||
|
return line_end.join([delimiter.join(line) for line in data])
|
||||||
|
|
||||||
|
class optional_int_validator(QIntValidator):
|
||||||
|
def validate(self, text, pos):
|
||||||
|
if not text:
|
||||||
|
return (QValidator.State.Acceptable, text, pos)
|
||||||
|
return super().validate(text, pos)
|
||||||
|
|
||||||
|
class optional_doublle_validator(QDoubleValidator):
|
||||||
|
def validate(self, text, pos):
|
||||||
|
if not text:
|
||||||
|
return (QValidator.State.Acceptable, text, pos)
|
||||||
|
return super().validate(text, pos)
|
||||||
|
|
||||||
|
def get_step(span):
|
||||||
|
base = math.log10(span)
|
||||||
|
base += 0.1
|
||||||
|
b = int(base)
|
||||||
|
c = int((b - base) * 3)
|
||||||
|
b = 10.0 ** b
|
||||||
|
if c == 0:
|
||||||
|
step = b / 10
|
||||||
|
elif c == 1:
|
||||||
|
step = b / 5
|
||||||
|
else:
|
||||||
|
step = b / 2
|
||||||
|
return step
|
||||||
|
|
||||||
|
def get_tick(a, b):
|
||||||
|
step = get_step(b - a)
|
||||||
|
list_tick = []
|
||||||
|
tick = a - a % step
|
||||||
|
if tick < a:
|
||||||
|
tick += step
|
||||||
|
while tick <= b:
|
||||||
|
list_tick.append(tick)
|
||||||
|
tick += step
|
||||||
|
|
||||||
|
return list_tick
|
||||||
|
|
||||||
|
class header_view(QHeaderView):
|
||||||
|
def __init__(self, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.setContextMenuPolicy(Qt.ContextMenuPolicy.DefaultContextMenu)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
# Get the clicked header position
|
||||||
|
pos = event.pos() # Position relative to the header
|
||||||
|
|
||||||
|
# Get the logical index of the clicked section
|
||||||
|
orient = self.orientation()
|
||||||
|
index = self.logicalIndexAt(pos)
|
||||||
|
parent = self.parent()
|
||||||
|
|
||||||
|
menu_table = QMenu()
|
||||||
|
act_insert_above = QAction("在上方插入行", menu_table, triggered = partial(parent.insert_row, index))
|
||||||
|
act_insert_below = QAction("在下方插入行", menu_table, triggered = partial(parent.insert_row, index + 1))
|
||||||
|
act_remove_row = QAction("删除行", menu_table, triggered = partial(parent.remove_row, index))
|
||||||
|
act_insert_left = QAction("在左侧插入列", menu_table, triggered = partial(parent.insert_col, index))
|
||||||
|
act_insert_right = QAction("在右侧插入列", menu_table, triggered = partial(parent.insert_col, index + 1))
|
||||||
|
act_remove_col = QAction("删除列", menu_table, triggered = partial(parent.remove_col, index))
|
||||||
|
if orient == Qt.Orientation.Horizontal:
|
||||||
|
menu_table.addActions([
|
||||||
|
act_insert_left,
|
||||||
|
act_insert_right,
|
||||||
|
act_remove_col,
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
menu_table.addActions([
|
||||||
|
act_insert_above,
|
||||||
|
act_insert_below,
|
||||||
|
act_remove_row,
|
||||||
|
])
|
||||||
|
menu_table.exec(event.globalPos())
|
||||||
|
|
||||||
|
class table_item(QTableWidgetItem):
|
||||||
|
def __init__(self, text = "", value = None, formatter = None, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.data = None
|
||||||
|
self.formatter = formatter
|
||||||
|
if text:
|
||||||
|
self.text = text
|
||||||
|
elif value:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self):
|
||||||
|
return super().text()
|
||||||
|
|
||||||
|
@text.setter
|
||||||
|
def text(self, s):
|
||||||
|
if self.formatter:
|
||||||
|
self.data = self.formatter.str2val(s)
|
||||||
|
if self.data is None:
|
||||||
|
s = ""
|
||||||
|
else:
|
||||||
|
s = self.formatter.val2str(self.data)
|
||||||
|
super().setText(s)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
if not (self.data is None):
|
||||||
|
return self.data
|
||||||
|
if self.formatter:
|
||||||
|
value = self.formatter.str2val(super().text())
|
||||||
|
return value
|
||||||
|
return super().text()
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, v):
|
||||||
|
if v is None:
|
||||||
|
super().setText("")
|
||||||
|
elif self.formatter:
|
||||||
|
super().setText(self.formatter.val2str(v))
|
||||||
|
else:
|
||||||
|
super().setText(str(v))
|
||||||
|
self.data = v
|
||||||
|
|
||||||
|
def empty(self):
|
||||||
|
return self.data is None
|
||||||
|
|
||||||
|
class item_delegate(QStyledItemDelegate):
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
# Create the editor widget (e.g., QLineEdit)
|
||||||
|
editor = super().createEditor(parent, option, index)
|
||||||
|
|
||||||
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
|
item = self.parent().at(row, col)
|
||||||
|
if item.formatter is None:
|
||||||
|
pass
|
||||||
|
elif item.data is None:
|
||||||
|
item.setText("")
|
||||||
|
else:
|
||||||
|
item.setText(str(item.data))
|
||||||
|
|
||||||
|
if item.formatter:
|
||||||
|
if item.formatter.dtype is int:
|
||||||
|
editor.setValidator(optional_int_validator())
|
||||||
|
if item.formatter.dtype is float:
|
||||||
|
editor.setValidator(optional_doublle_validator())
|
||||||
|
width = self.parent().horizontalHeader().sectionSize(col)
|
||||||
|
editor.setFixedWidth(width)
|
||||||
|
#editor.setMinimumWidth(editor.width())
|
||||||
|
return editor
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
# Process data before saving to the model
|
||||||
|
text = editor.text()
|
||||||
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
|
item = self.parent().at(row, col)
|
||||||
|
if text == "":
|
||||||
|
item.value = None
|
||||||
|
else:
|
||||||
|
if item.formatter:
|
||||||
|
item.value = item.formatter.dtype(text)
|
||||||
|
else:
|
||||||
|
item.text = text
|
||||||
|
|
||||||
|
# def displayText(self, value, locale):
|
||||||
|
# return super().displayText(value, locale)
|
||||||
|
|
||||||
|
# def updateEditorGeometry(self, editor, option, index):
|
||||||
|
# row = index.row()
|
||||||
|
# col = index.column()
|
||||||
|
# item = self.parent().at(row, col)
|
||||||
|
# editor.setWidth(item.width())
|
||||||
|
# editor.setGeometry(option.rect)
|
||||||
|
# # editor.setGeometry(option.rect.adjusted(0, 0, 0, 0))
|
||||||
|
|
||||||
|
class widget_table(QTableWidget):
|
||||||
|
def __init__(self, window, id, config_tab, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
self.app = QApplication.instance()
|
||||||
|
horizontal_header = header_view(Qt.Orientation.Horizontal, self)
|
||||||
|
horizontal_header.setMinimumSectionSize(100)
|
||||||
|
self.setHorizontalHeader(horizontal_header)
|
||||||
|
self.setVerticalHeader(header_view(Qt.Orientation.Vertical, self))
|
||||||
|
self.setItemDelegate(item_delegate(self))
|
||||||
|
self.id = id
|
||||||
|
self.config_tab = config_tab
|
||||||
|
self.setFont(font)
|
||||||
|
self.formatter = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def row(self):
|
||||||
|
return super().rowCount()
|
||||||
|
|
||||||
|
@row.setter
|
||||||
|
def row(self, row):
|
||||||
|
super().setRowCount(row)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def col(self):
|
||||||
|
return super().columnCount()
|
||||||
|
|
||||||
|
@col.setter
|
||||||
|
def col(self, col):
|
||||||
|
super().setColumnCount(col)
|
||||||
|
|
||||||
|
def at(self, row, col):
|
||||||
|
if row >= super().rowCount():
|
||||||
|
self.setRowCount(row + 1)
|
||||||
|
if col >= super().columnCount():
|
||||||
|
self.setColumnCount(col + 1)
|
||||||
|
item = super().item(row, col)
|
||||||
|
if item is None:
|
||||||
|
formatter = None
|
||||||
|
if self.formatter:
|
||||||
|
formatter = self.formatter(row, col)
|
||||||
|
item = table_item(formatter = formatter)
|
||||||
|
super().setItem(row, col, item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def clear_content(self):
|
||||||
|
super().clearContents()
|
||||||
|
|
||||||
|
def load_data(self, data, row_begin = 0, col_begin = 0, row_end = None, col_end = None, as_value = False):
|
||||||
|
row = row_begin
|
||||||
|
while (not row_end or row < row_end) and row < len(data):
|
||||||
|
line = data[row]
|
||||||
|
col = col_begin
|
||||||
|
while (not col_end or col < col_end) and col < len(line):
|
||||||
|
if as_value:
|
||||||
|
self.at(row, col).value = line[col]
|
||||||
|
else:
|
||||||
|
self.at(row, col).text = line[col]
|
||||||
|
col += 1
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
def dump_data(self, row_begin = 0, col_begin = 0, row_end = None, col_end = None, as_value = False):
|
||||||
|
if row_end is None:
|
||||||
|
row_end = self.row
|
||||||
|
if col_end is None:
|
||||||
|
col_end = self.col
|
||||||
|
if row_end <= row_begin or col_end <= col_begin:
|
||||||
|
return []
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for row in range(row_begin, row_end):
|
||||||
|
line = []
|
||||||
|
for col in range(col_begin, col_end):
|
||||||
|
if as_value:
|
||||||
|
line.append(self.at(row, col).value)
|
||||||
|
else:
|
||||||
|
line.append(self.at(row, col).text)
|
||||||
|
data.append(line)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def load_str(self, s, delimiter = None, line_end = "\n", *arg, **kwarg):
|
||||||
|
data = str2data(s, delimiter, line_end)
|
||||||
|
self.load_data(data, *arg, **kwarg)
|
||||||
|
|
||||||
|
def dump_str(self, delimiter = ",", line_end = "\n", *arg, **kwarg):
|
||||||
|
data = self.dump_data(*arg, **kwarg)
|
||||||
|
s = data2str(data, delimiter, line_end)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def load_file(self, filename, *arg, **kwarg):
|
||||||
|
with open(filename, "r", encoding="utf-8") as fobj:
|
||||||
|
content = fobj.read()
|
||||||
|
self.load_str(content, *arg, **kwarg)
|
||||||
|
|
||||||
|
def dump_file(self, filename, *arg, **kwarg):
|
||||||
|
s = self.dump_str(*arg, **kwarg)
|
||||||
|
with open(filename, "w", encoding="utf-8") as fobj:
|
||||||
|
fobj.write(s)
|
||||||
|
|
||||||
|
def initial(self):
|
||||||
|
config_tab = self.config_tab
|
||||||
|
if "row" in config_tab:
|
||||||
|
self.row = config_tab["row"]
|
||||||
|
if "col" in config_tab:
|
||||||
|
self.col = config_tab["col"]
|
||||||
|
if "column_header" in config_tab:
|
||||||
|
self.setHorizontalHeaderLabels(config_tab["column_header"])
|
||||||
|
if "format_script" in config_tab:
|
||||||
|
filename = os.path.join(script_path, config_tab["format_script"])
|
||||||
|
module = util.load_module(filename)
|
||||||
|
self.formatter = module.table_formatter
|
||||||
|
if "data" in config_tab:
|
||||||
|
data = config_tab["data"]
|
||||||
|
self.load_data(data)
|
||||||
|
if "datafile" in config_tab:
|
||||||
|
datafile = os.path.join(data_path, config_tab["datafile"])
|
||||||
|
self.load_file(datafile)
|
||||||
|
if "span" in config_tab:
|
||||||
|
for span in config_tab["span"]:
|
||||||
|
self.setSpan(*span)
|
||||||
|
self.fit_content()
|
||||||
|
|
||||||
|
def reset_content(self):
|
||||||
|
self.clear_content()
|
||||||
|
self.initial()
|
||||||
|
|
||||||
|
def fit_content(self):
|
||||||
|
header = self.horizontalHeader()
|
||||||
|
header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
|
||||||
|
|
||||||
|
def calc_index(self):
|
||||||
|
list_index = self.selectedIndexes()
|
||||||
|
if not list_index:
|
||||||
|
return
|
||||||
|
min_r = min(list_index, key = lambda x:x.row()).row()
|
||||||
|
max_r = max(list_index, key = lambda x:x.row()).row() + 1
|
||||||
|
min_c = min(list_index, key = lambda x:x.column()).column()
|
||||||
|
max_c = max(list_index, key = lambda x:x.column()).column() + 1
|
||||||
|
flag_rect = (max_r - min_r) * (max_c - min_c) != len(list_index)
|
||||||
|
flag_row = flag_rect and min_c == 0 and max_c == self.columnCount()
|
||||||
|
flag_col = flag_rect and min_r == 0 and max_r == self.rowCount()
|
||||||
|
return list_index, min_r, max_r, min_c, max_c, flag_rect, flag_row, flag_col
|
||||||
|
|
||||||
|
def load_clipboard(self):
|
||||||
|
clipboard = self.app.clipboard()
|
||||||
|
mimedata = clipboard.mimeData()
|
||||||
|
|
||||||
|
# if mimedata.hasHtml():
|
||||||
|
# # Handle HTML with explicit UTF-16 decoding
|
||||||
|
# raw_html = bytes(mimedata.data("text/html"))
|
||||||
|
# try:
|
||||||
|
# html = raw_html.decode("utf-16")
|
||||||
|
# except UnicodeDecodeError:
|
||||||
|
# html = raw_html.decode("utf-8", errors="replace")
|
||||||
|
# self.parse_html_table(html)
|
||||||
|
|
||||||
|
text = None
|
||||||
|
if mimedata.hasText():
|
||||||
|
# Handle plain text with encoding detection
|
||||||
|
raw_text = bytes(mimedata.data("text/plain"))
|
||||||
|
|
||||||
|
# Check for UTF-16 BOM
|
||||||
|
if raw_text.startswith(b"\xff\xfe") or raw_text.startswith(b"\xfe\xff"):
|
||||||
|
text = raw_text.decode("utf-16")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
text = raw_text.decode("utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
text = raw_text.decode(locale.getpreferredencoding(), errors="replace")
|
||||||
|
if text is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = str2data(text, delimiter=["\t"])
|
||||||
|
return data
|
||||||
|
|
||||||
|
def clear_select(self):
|
||||||
|
list_index = self.selectedIndexes()
|
||||||
|
for index in list_index:
|
||||||
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
|
self.at(row, col).value = None
|
||||||
|
|
||||||
|
def copy_select(self, as_value = False):
|
||||||
|
data = self.calc_index()
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
list_index, min_r, max_r, min_c, max_c, flag_rect, flag_row, flag_col = data
|
||||||
|
|
||||||
|
data = [[""] * (max_c - min_c) for row in range(max_r - min_r)]
|
||||||
|
for index in list_index:
|
||||||
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
|
item = self.at(row, col)
|
||||||
|
if as_value:
|
||||||
|
if item.value is None:
|
||||||
|
data[row - min_r][col - min_c] = ""
|
||||||
|
else:
|
||||||
|
data[row - min_r][col - min_c] = str(self.at(row, col).value)
|
||||||
|
else:
|
||||||
|
data[row - min_r][col - min_c] = self.at(row, col).text
|
||||||
|
|
||||||
|
text = data2str(data, delimiter = "\t")
|
||||||
|
clipboard = self.app.clipboard()
|
||||||
|
mimedata = QMimeData()
|
||||||
|
mimedata.setText(text)
|
||||||
|
clipboard.setMimeData(mimedata)
|
||||||
|
|
||||||
|
def paste_select(self, as_value = False):
|
||||||
|
data = self.calc_index()
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
list_index, min_r, max_r, min_c, max_c, flag_rect, flag_row, flag_col = data
|
||||||
|
|
||||||
|
data = self.load_clipboard()
|
||||||
|
|
||||||
|
for index in list_index:
|
||||||
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
|
r = row - min_r
|
||||||
|
if r >= len(data):
|
||||||
|
continue
|
||||||
|
c = col - min_c
|
||||||
|
line = data[r]
|
||||||
|
if c >= len(line):
|
||||||
|
continue
|
||||||
|
item = self.at(row, col)
|
||||||
|
if as_value and item.formatter:
|
||||||
|
if line[c] == "":
|
||||||
|
item.value = None
|
||||||
|
else:
|
||||||
|
dtype = item.formatter.dtype
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
item.value = dtype(line[c])
|
||||||
|
except:
|
||||||
|
if dtype is int:
|
||||||
|
item.value = int(float(line[c]))
|
||||||
|
except:
|
||||||
|
raise RuntimeError(f"无法将\"{line[c]}\"转化为{dype}类型")
|
||||||
|
else:
|
||||||
|
item.text = line[c]
|
||||||
|
|
||||||
|
def insert_row(self, row):
|
||||||
|
self.insertRow(row)
|
||||||
|
|
||||||
|
def remove_row(self, row):
|
||||||
|
self.removeRow(row)
|
||||||
|
|
||||||
|
def insert_col(self, col):
|
||||||
|
self.insertColumn(col)
|
||||||
|
|
||||||
|
def remove_col(self, col):
|
||||||
|
self.removeColumn(col)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
data = self.calc_index()
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
list_index, min_r, max_r, min_c, max_c, flag_rect, flag_row, flag_col = data
|
||||||
|
|
||||||
|
menu_table = QMenu()
|
||||||
|
act_clear_select = QAction("清空所选区域", menu_table, triggered = self.clear_select)
|
||||||
|
act_copy_text = QAction("复制所选区域文本", menu_table, triggered = self.copy_select)
|
||||||
|
act_copy_value = QAction("复制所选区域数据", menu_table, triggered = partial(self.copy_select, True))
|
||||||
|
act_paste_text = QAction("粘贴文本到所选区域", menu_table, triggered = self.paste_select)
|
||||||
|
act_paste_value = QAction("粘贴数据到所选区域", menu_table, triggered = partial(self.paste_select, True))
|
||||||
|
menu_table.addActions([
|
||||||
|
act_clear_select,
|
||||||
|
act_copy_text,
|
||||||
|
act_copy_value,
|
||||||
|
act_paste_text,
|
||||||
|
act_paste_value,
|
||||||
|
])
|
||||||
|
menu_table.exec(event.globalPos())
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if (
|
||||||
|
event.key() == Qt.Key.Key_Delete
|
||||||
|
):
|
||||||
|
self.clear_select()
|
||||||
|
event.accept()
|
||||||
|
elif (
|
||||||
|
event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||||
|
and event.key() == Qt.Key.Key_C
|
||||||
|
):
|
||||||
|
self.copy_select()
|
||||||
|
event.accept()
|
||||||
|
elif (
|
||||||
|
(event.modifiers() == Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
|
||||||
|
and event.key() == Qt.Key.Key_C
|
||||||
|
):
|
||||||
|
self.copy_select(as_value = True)
|
||||||
|
event.accept()
|
||||||
|
elif (
|
||||||
|
event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||||
|
and event.key() == Qt.Key.Key_V
|
||||||
|
):
|
||||||
|
self.paste_select()
|
||||||
|
event.accept()
|
||||||
|
elif (
|
||||||
|
(event.modifiers() == Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
|
||||||
|
and event.key() == Qt.Key.Key_V
|
||||||
|
):
|
||||||
|
self.paste_select(as_value = True)
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
|
class chart_plot(QChart):
|
||||||
|
def __init__(self, window, config_tab, *arg):
|
||||||
|
super().__init__(*arg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setBackgroundBrush(QBrush(QColor(0, 0, 0)))
|
||||||
|
self.setAnimationOptions(QChart.AnimationOption.SeriesAnimations)
|
||||||
|
self.legend().setLabelColor(QColor(220, 220, 220))
|
||||||
|
|
||||||
|
self.axis_x = QValueAxis()
|
||||||
|
self.axis_y = QValueAxis()
|
||||||
|
|
||||||
|
if "axis_x_title" in config_tab:
|
||||||
|
title = config_tab["axis_x_title"]
|
||||||
|
self.axis_x.setTitleText(title)
|
||||||
|
if "axis_y_title" in config_tab:
|
||||||
|
title = config_tab["axis_y_title"]
|
||||||
|
self.axis_y.setTitleText(title)
|
||||||
|
|
||||||
|
axis_list = [self.axis_x, self.axis_y]
|
||||||
|
for axis in axis_list:
|
||||||
|
axis.setTitleBrush(QBrush(QColor(220, 220, 220)))
|
||||||
|
axis.setLabelsColor(QColor(220, 220, 220))
|
||||||
|
axis.setLinePenColor(QColor(200, 200, 200))
|
||||||
|
axis.setGridLineColor(QColor(100, 100, 100))
|
||||||
|
|
||||||
|
self.addAxis(self.axis_x, Qt.AlignmentFlag.AlignBottom)
|
||||||
|
self.addAxis(self.axis_y, Qt.AlignmentFlag.AlignLeft)
|
||||||
|
|
||||||
|
self.list_name = []
|
||||||
|
|
||||||
|
def plot(self, *arg, **kwarg):
|
||||||
|
self.removeAllSeries()
|
||||||
|
self.list_name.clear()
|
||||||
|
|
||||||
|
min_x, max_x = None, None
|
||||||
|
min_y, max_y = None, None
|
||||||
|
|
||||||
|
for i in range((len(arg) + 2) // 3):
|
||||||
|
x = arg[i * 3]
|
||||||
|
y = arg[i * 3 + 1]
|
||||||
|
line = QLineSeries()
|
||||||
|
if i * 3 + 2 < len(arg):
|
||||||
|
name = arg[i * 3 + 2]
|
||||||
|
else:
|
||||||
|
name = f"数据{i}"
|
||||||
|
line.setName(name)
|
||||||
|
self.list_name.append(name)
|
||||||
|
self.addSeries(line)
|
||||||
|
|
||||||
|
scatter = QScatterSeries()
|
||||||
|
scatter.setColor(line.color())
|
||||||
|
scatter.setMarkerSize(10)
|
||||||
|
scatter.setPointLabelsColor(line.color())
|
||||||
|
#scatter.setPointLabelsVisible()
|
||||||
|
self.addSeries(scatter)
|
||||||
|
|
||||||
|
line.attachAxis(self.axis_x)
|
||||||
|
line.attachAxis(self.axis_y)
|
||||||
|
scatter.attachAxis(self.axis_x)
|
||||||
|
scatter.attachAxis(self.axis_y)
|
||||||
|
|
||||||
|
marker = self.legend().markers(scatter)
|
||||||
|
for i in marker:
|
||||||
|
i.setVisible(False)
|
||||||
|
|
||||||
|
for i in range(len(x)):
|
||||||
|
xx = x[i]
|
||||||
|
yy = y[i]
|
||||||
|
point = QPointF(xx, yy)
|
||||||
|
line.append(point)
|
||||||
|
scatter.append(point)
|
||||||
|
|
||||||
|
# Calculate and set axis ranges
|
||||||
|
x1, x2 = min(x), max(x)
|
||||||
|
y1, y2 = min(y), max(y)
|
||||||
|
if min_x is None:
|
||||||
|
min_x, max_x = x1, x2
|
||||||
|
min_y, max_y = y1, y2
|
||||||
|
else:
|
||||||
|
min_x, max_x = min(min_x, x1), max(max_x, x2)
|
||||||
|
min_y, max_y = min(min_y, y1), max(max_y, y2)
|
||||||
|
|
||||||
|
# Add 10% padding to axis ranges
|
||||||
|
|
||||||
|
min_x, max_x = min_x - 0.1 * (max_x - min_x), max_x + 0.1 * (max_x - min_x)
|
||||||
|
min_y, max_y = min_y - 0.1 * (max_y - min_y), max_y + 0.1 * (max_y - min_y)
|
||||||
|
|
||||||
|
self.axis_x.setRange(min_x, max_x)
|
||||||
|
self.axis_y.setRange(min_y, max_y)
|
||||||
|
|
||||||
|
step = get_step(max_x - min_x)
|
||||||
|
self.axis_x.setTickType(QValueAxis.TickType.TicksDynamic);
|
||||||
|
self.axis_x.setTickAnchor(min_x - min_x % step);
|
||||||
|
self.axis_x.setTickInterval(step);
|
||||||
|
step = get_step(max_y - min_y)
|
||||||
|
self.axis_y.setTickType(QValueAxis.TickType.TicksDynamic);
|
||||||
|
self.axis_y.setTickAnchor(min_y - min_y % step);
|
||||||
|
self.axis_y.setTickInterval(step);
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
if not self.list_name:
|
||||||
|
return
|
||||||
|
menu = QMenu()
|
||||||
|
for index, name in enumerate(self.list_name):
|
||||||
|
act_toggle_series = QAction(f"显示/隐藏{name}", menu, triggered = partial(self.toggle_series, index))
|
||||||
|
menu.addAction(act_toggle_series)
|
||||||
|
menu.exec(event.screenPos())
|
||||||
|
|
||||||
|
def toggle_series(self, index):
|
||||||
|
line = self.series()[index * 2]
|
||||||
|
scatter = self.series()[index * 2 + 1]
|
||||||
|
flag = line.isVisible()
|
||||||
|
flag = not flag
|
||||||
|
line.setVisible(flag)
|
||||||
|
scatter.setVisible(flag)
|
||||||
|
marker = self.legend().markers(scatter)
|
||||||
|
for i in marker:
|
||||||
|
i.setVisible(False)
|
||||||
|
|
||||||
|
class chart_view(QChartView):
|
||||||
|
def __init__(self, window, id, config_tab, *arg):
|
||||||
|
super().__init__(*arg)
|
||||||
|
self.window = window
|
||||||
|
self.id = id
|
||||||
|
self.config_tab = config_tab
|
||||||
|
|
||||||
|
self.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||||
|
self.chart_plot = chart_plot(window, config_tab)
|
||||||
|
self.setChart(self.chart_plot)
|
||||||
|
|
||||||
|
def plot(self, *arg, **kwarg):
|
||||||
|
self.chart_plot.plot(*arg, **kwarg)
|
||||||
|
|
||||||
|
class tab_widget(QTabWidget):
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
self.currentChanged.connect(self.on_current_changed)
|
||||||
|
self.setFont(font)
|
||||||
|
|
||||||
|
def on_current_changed(self, index):
|
||||||
|
list_id = self.window.widget_display.list_id
|
||||||
|
if index < 0 or not list_id:
|
||||||
|
return
|
||||||
|
table_id = list_id[index]
|
||||||
|
self.window.widget_interact.load_interact(table_id)
|
||||||
|
|
||||||
|
class widget_display(QWidget):
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setFont(font)
|
||||||
|
|
||||||
|
layout_base = QVBoxLayout()
|
||||||
|
self.setLayout(layout_base)
|
||||||
|
self.tab_widget = tab_widget(window)
|
||||||
|
layout_base.addWidget(self.tab_widget)
|
||||||
|
|
||||||
|
self.map_table = {}
|
||||||
|
self.map_graph = {}
|
||||||
|
self.list_id = []
|
||||||
|
|
||||||
|
if not ("display" in config):
|
||||||
|
return
|
||||||
|
config_display = config["display"]
|
||||||
|
for project, config_project in config_display.items():
|
||||||
|
for config_tab in config_project:
|
||||||
|
if not "type" in config_tab:
|
||||||
|
continue
|
||||||
|
if config_tab["type"] == "table":
|
||||||
|
id = config_tab["id"]
|
||||||
|
label = config_tab["label"]
|
||||||
|
widget = widget_table(self.window, id, config_tab)
|
||||||
|
self.map_table[id] = widget
|
||||||
|
elif config_tab["type"] == "graph":
|
||||||
|
id = config_tab["id"]
|
||||||
|
label = config_tab["label"]
|
||||||
|
widget = chart_view(self.window, id, config_tab)
|
||||||
|
self.map_graph[id] = widget
|
||||||
|
|
||||||
|
def initial_table(self):
|
||||||
|
for widget in self.map_table.values():
|
||||||
|
widget.initial()
|
||||||
|
|
||||||
|
def load_project(self, project):
|
||||||
|
self.list_id.clear()
|
||||||
|
self.tab_widget.clear()
|
||||||
|
if not ("display" in config):
|
||||||
|
return
|
||||||
|
|
||||||
|
config_display = config["display"]
|
||||||
|
if not (project in config_display):
|
||||||
|
return
|
||||||
|
config_project = config_display[project]
|
||||||
|
for config_tab in config_project:
|
||||||
|
if not "type" in config_tab:
|
||||||
|
continue
|
||||||
|
id = config_tab["id"]
|
||||||
|
label = config_tab["label"]
|
||||||
|
widget = None
|
||||||
|
if config_tab["type"] == "table":
|
||||||
|
widget = self.map_table[id]
|
||||||
|
elif config_tab["type"] == "graph":
|
||||||
|
widget = self.map_graph[id]
|
||||||
|
self.list_id.append(id)
|
||||||
|
self.tab_widget.addTab(widget, label)
|
||||||
|
self.tab_widget.setTabToolTip(self.tab_widget.count() - 1, f"id:{id}")
|
||||||
|
|
||||||
|
def get_current_widget(self):
|
||||||
|
widget = self.tab_widget.currentWidget()
|
||||||
|
return widget
|
||||||
|
|
||||||
|
def get_current_id(self):
|
||||||
|
widget = self.get_current_widget()
|
||||||
|
if widget is None:
|
||||||
|
return
|
||||||
|
return self.get_current_widget().id
|
||||||
|
|
||||||
|
def load_table(self, data):
|
||||||
|
widget = self.get_current_widget()
|
||||||
|
if widget is None:
|
||||||
|
return
|
||||||
|
widget.load_table(data)
|
||||||
|
|
||||||
|
def dump_table(self):
|
||||||
|
widget = self.get_current_widget()
|
||||||
|
if widget is None:
|
||||||
|
return
|
||||||
|
return widget.dump_table()
|
||||||
|
|
||||||
|
|
||||||
130
app/widget_interact.py
Normal file
130
app/widget_interact.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# -*- encoding:utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
from PyQt6.QtCore import Qt, QSize
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QWidget, QDockWidget, QTabWidget, QVBoxLayout, QHBoxLayout,
|
||||||
|
QGridLayout, QPushButton, QLabel, QLineEdit, QGroupBox,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QFont, QIcon, QColor, QIntValidator, QDoubleValidator,
|
||||||
|
)
|
||||||
|
|
||||||
|
import color
|
||||||
|
from config import config, script_path
|
||||||
|
|
||||||
|
font = QFont("consolas", 14)
|
||||||
|
font.setFamilies(["consolas", "黑体"])
|
||||||
|
|
||||||
|
class edit_input(QLineEdit):
|
||||||
|
def __init__(self, window, id, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
self.id = id
|
||||||
|
self.textChanged.connect(self.on_text_changed)
|
||||||
|
|
||||||
|
def on_text_changed(self):
|
||||||
|
if self.id:
|
||||||
|
self.window.widget_interact.widget_grid.map_input[self.id] = self.text()
|
||||||
|
|
||||||
|
class button_input(QPushButton):
|
||||||
|
def __init__(self, window, script, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
self.script = script
|
||||||
|
self.clicked.connect(self.on_click)
|
||||||
|
|
||||||
|
def on_click(self):
|
||||||
|
if not self.script:
|
||||||
|
return
|
||||||
|
filename = os.path.join(script_path, self.script)
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
raise RuntimeError(f"the path is not a valid file: \"{filename}\"")
|
||||||
|
|
||||||
|
filename = filename.replace("\\", "\\\\")
|
||||||
|
filename = filename.replace("\"", "\\\"")
|
||||||
|
self.window.process(f"window.load_script(\"{filename}\")")
|
||||||
|
|
||||||
|
class widget_grid(QWidget):
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
layout_base = QVBoxLayout()
|
||||||
|
self.setLayout(layout_base)
|
||||||
|
|
||||||
|
group_grid = QGroupBox()
|
||||||
|
layout_base.addWidget(group_grid)
|
||||||
|
self.layout_grid = QGridLayout()
|
||||||
|
group_grid.setLayout(self.layout_grid)
|
||||||
|
|
||||||
|
self.list_widget = []
|
||||||
|
self.map_input = {}
|
||||||
|
|
||||||
|
def load_interact(self, table_id):
|
||||||
|
for widget in self.list_widget:
|
||||||
|
self.layout_grid.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
self.list_widget.clear()
|
||||||
|
|
||||||
|
if not ("interact" in config):
|
||||||
|
return
|
||||||
|
config_interact = config["interact"]
|
||||||
|
if not (table_id in config_interact):
|
||||||
|
return
|
||||||
|
config_interact = config_interact[table_id]
|
||||||
|
|
||||||
|
row = 0
|
||||||
|
col = 0
|
||||||
|
for cfg in config_interact:
|
||||||
|
tp = cfg["type"]
|
||||||
|
label = cfg["label"]
|
||||||
|
if tp == "input":
|
||||||
|
if col > 0:
|
||||||
|
row += 1
|
||||||
|
col = 0
|
||||||
|
id = cfg.get("id")
|
||||||
|
if id in self.map_input:
|
||||||
|
text = self.map_input[id]
|
||||||
|
else:
|
||||||
|
text = cfg.get("text", "")
|
||||||
|
self.map_input[id] = text
|
||||||
|
label = QLabel(label)
|
||||||
|
label.setFont(font)
|
||||||
|
label.setToolTip(f"id:{id}")
|
||||||
|
|
||||||
|
edit = edit_input(self.window, id, text)
|
||||||
|
self.list_widget.append(label)
|
||||||
|
self.list_widget.append(edit)
|
||||||
|
self.layout_grid.addWidget(label, row, 0)
|
||||||
|
self.layout_grid.addWidget(edit, row, 1)
|
||||||
|
row += 1
|
||||||
|
elif tp == "button":
|
||||||
|
if "script" in cfg:
|
||||||
|
script = cfg["script"]
|
||||||
|
else:
|
||||||
|
script = None
|
||||||
|
button = button_input(self.window, script, label)
|
||||||
|
self.list_widget.append(button)
|
||||||
|
self.layout_grid.addWidget(button, row, col)
|
||||||
|
if col == 0:
|
||||||
|
col += 1
|
||||||
|
else:
|
||||||
|
row += 1
|
||||||
|
col = 0
|
||||||
|
|
||||||
|
class widget_interact(QDockWidget):
|
||||||
|
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea)
|
||||||
|
self.setFont(font)
|
||||||
|
|
||||||
|
self.widget_grid = widget_grid(window)
|
||||||
|
self.setWidget(self.widget_grid)
|
||||||
|
|
||||||
|
def load_interact(self, table_id):
|
||||||
|
self.widget_grid.load_interact(table_id)
|
||||||
|
|
||||||
|
|
||||||
173
app/widget_term.py
Normal file
173
app/widget_term.py
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import re
|
||||||
|
from PyQt6.QtCore import Qt, QPoint, QStringListModel
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QDockWidget, QTextEdit, QPlainTextEdit,
|
||||||
|
QSplitter, QMenu, QCompleter,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QFont, QTextCursor, QKeySequence,
|
||||||
|
QAction, QPalette, QColorConstants,
|
||||||
|
)
|
||||||
|
from IPython.core.completer import (
|
||||||
|
provisionalcompleter, cursor_to_position,
|
||||||
|
_deduplicate_completions
|
||||||
|
)
|
||||||
|
|
||||||
|
from text_term import text_term
|
||||||
|
|
||||||
|
class completer_py(QCompleter):
|
||||||
|
def __init__(self, widget, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
model = QStringListModel([], self)
|
||||||
|
self.setWidget(widget)
|
||||||
|
self.setModel(model)
|
||||||
|
self.setModelSorting(QCompleter.ModelSorting.CaseInsensitivelySortedModel)
|
||||||
|
self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||||
|
self.setWrapAround(False)
|
||||||
|
self.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
|
||||||
|
self.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||||
|
self.activated.connect(widget.insertCompletion)
|
||||||
|
|
||||||
|
class text_input(QPlainTextEdit):
|
||||||
|
def __init__(self, window, *arg):
|
||||||
|
super().__init__(*arg)
|
||||||
|
self.window = window
|
||||||
|
self.setTabStopDistance(40)
|
||||||
|
self.setPlaceholderText(
|
||||||
|
"按 %s 发送命令\n"
|
||||||
|
"按 %s 自动补全\n"
|
||||||
|
"按 %s 或 %s 浏览命令历史\n"
|
||||||
|
% (
|
||||||
|
QKeySequence("Ctrl+Enter").toString(QKeySequence.SequenceFormat.NativeText),
|
||||||
|
QKeySequence("Ctrl+E").toString(QKeySequence.SequenceFormat.NativeText),
|
||||||
|
QKeySequence("Ctrl+Up").toString(QKeySequence.SequenceFormat.NativeText),
|
||||||
|
QKeySequence("Ctrl+Down").toString(QKeySequence.SequenceFormat.NativeText),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.completer_py = completer_py(self)
|
||||||
|
self.history = [""]
|
||||||
|
self.history_idx = 0
|
||||||
|
|
||||||
|
def insertCompletion(self, completion):
|
||||||
|
tc = self.textCursor()
|
||||||
|
extra = len(completion) - len(self.completer_py.completionPrefix())
|
||||||
|
tc.movePosition(QTextCursor.MoveOperation.Left)
|
||||||
|
tc.movePosition(QTextCursor.MoveOperation.EndOfWord)
|
||||||
|
tc.insertText(completion[-extra:])
|
||||||
|
self.setTextCursor(tc)
|
||||||
|
|
||||||
|
def textUnderCursor(self):
|
||||||
|
tc = self.textCursor()
|
||||||
|
tc.select(QTextCursor.SelectionType.WordUnderCursor)
|
||||||
|
|
||||||
|
return tc.selectedText()
|
||||||
|
|
||||||
|
def focusInEvent(self, event):
|
||||||
|
if self.completer_py is not None:
|
||||||
|
self.completer_py.setWidget(self)
|
||||||
|
|
||||||
|
super().focusInEvent(event)
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if(
|
||||||
|
event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return)
|
||||||
|
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
|
||||||
|
):
|
||||||
|
s = self.toPlainText()
|
||||||
|
if not s:
|
||||||
|
return
|
||||||
|
self.window.process(s)
|
||||||
|
self.history[-1] = s
|
||||||
|
if len(self.history) >= 2 and self.history[-1] == self.history[-2]:
|
||||||
|
self.history.pop()
|
||||||
|
self.history_idx = len(self.history)
|
||||||
|
self.history.append("")
|
||||||
|
self.clear()
|
||||||
|
return
|
||||||
|
|
||||||
|
if(
|
||||||
|
event.key() == Qt.Key.Key_Up
|
||||||
|
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
|
||||||
|
and self.history_idx > 0
|
||||||
|
):
|
||||||
|
if self.history_idx == len(self.history) - 1:
|
||||||
|
self.history[-1] = self.toPlainText()
|
||||||
|
self.history_idx -= 1
|
||||||
|
self.setPlainText(self.history[self.history_idx])
|
||||||
|
return
|
||||||
|
|
||||||
|
if(
|
||||||
|
event.key() == Qt.Key.Key_Down
|
||||||
|
and bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier)
|
||||||
|
and self.history_idx < len(self.history) - 1
|
||||||
|
):
|
||||||
|
self.history_idx += 1
|
||||||
|
self.setPlainText(self.history[self.history_idx])
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.completer_py is not None and self.completer_py.popup().isVisible():
|
||||||
|
# The following keys are forwarded by the completer to the widget.
|
||||||
|
if event.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return, Qt.Key.Key_Escape, Qt.Key.Key_Tab, Qt.Key.Key_Backtab):
|
||||||
|
event.ignore()
|
||||||
|
# Let the completer do default behavior.
|
||||||
|
return
|
||||||
|
|
||||||
|
isShortcut = (bool(event.modifiers() & Qt.KeyboardModifier.ControlModifier) and event.key() == Qt.Key.Key_E)
|
||||||
|
if self.completer_py is None or not isShortcut:
|
||||||
|
# Do not process the shortcut when we have a completer.
|
||||||
|
super().keyPressEvent(event)
|
||||||
|
|
||||||
|
ctrlOrShift = event.modifiers() & (Qt.KeyboardModifier.ControlModifier | Qt.KeyboardModifier.ShiftModifier)
|
||||||
|
if self.completer_py is None or (ctrlOrShift and len(event.text()) == 0):
|
||||||
|
return
|
||||||
|
|
||||||
|
eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="
|
||||||
|
hasModifier = (event.modifiers() != Qt.KeyboardModifier.NoModifier) and not ctrlOrShift
|
||||||
|
completionPrefix = self.textUnderCursor()
|
||||||
|
|
||||||
|
if not isShortcut and (hasModifier or len(event.text()) == 0 or len(completionPrefix) < 3 or event.text()[-1] in eow):
|
||||||
|
self.completer_py.popup().hide()
|
||||||
|
return
|
||||||
|
|
||||||
|
pos = self.textCursor().position()
|
||||||
|
value = self.document().toRawText()
|
||||||
|
hint = re.search(r"[_0-9a-zA-Z.%]*$", value[:pos]).group()
|
||||||
|
pos_hint = pos - len(hint)
|
||||||
|
hint += re.search(r"^[_0-9a-zA-Z.%]*", value[pos:]).group()
|
||||||
|
with provisionalcompleter():
|
||||||
|
complete_list = _deduplicate_completions(hint, self.window.ipshell.Completer.completions(hint, pos - pos_hint))
|
||||||
|
complete_list = list(complete_list)
|
||||||
|
complete_list = [i.text for i in complete_list]
|
||||||
|
# complete_list = self.window.ipshell.complete(hint, cursor_pos = pos - pos_hint)
|
||||||
|
# print(complete_list)
|
||||||
|
self.completer_py.model().setStringList(complete_list)
|
||||||
|
|
||||||
|
if completionPrefix != self.completer_py.completionPrefix():
|
||||||
|
self.completer_py.setCompletionPrefix(completionPrefix)
|
||||||
|
self.completer_py.popup().setCurrentIndex(
|
||||||
|
self.completer_py.completionModel().index(0, 0))
|
||||||
|
|
||||||
|
cr = self.cursorRect()
|
||||||
|
cr.setWidth(self.completer_py.popup().sizeHintForColumn(0) + self.completer_py.popup().verticalScrollBar().sizeHint().width())
|
||||||
|
self.completer_py.complete(cr)
|
||||||
|
|
||||||
|
class widget_term(QDockWidget):
|
||||||
|
def __init__(self, window, *arg):
|
||||||
|
super().__init__(*arg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setAllowedAreas(
|
||||||
|
Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea
|
||||||
|
| Qt.DockWidgetArea.TopDockWidgetArea | Qt.DockWidgetArea.BottomDockWidgetArea
|
||||||
|
)
|
||||||
|
font = QFont("consolas", 12)
|
||||||
|
font.setFamilies(["consolas", "黑体"])
|
||||||
|
self.setFont(font)
|
||||||
|
split = QSplitter(Qt.Orientation.Horizontal)
|
||||||
|
self.setWidget(split)
|
||||||
|
|
||||||
|
self.text_term = text_term(window, limit = 2 ** 16)
|
||||||
|
split.addWidget(self.text_term)
|
||||||
|
|
||||||
|
self.text_input = text_input(window)
|
||||||
|
split.addWidget(self.text_input)
|
||||||
68
app/widget_tree.py
Normal file
68
app/widget_tree.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from PyQt6.QtCore import Qt
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QDockWidget, QTabWidget, QTreeWidget, QTreeWidgetItem,
|
||||||
|
)
|
||||||
|
from PyQt6.QtGui import (
|
||||||
|
QFont, QIcon,
|
||||||
|
)
|
||||||
|
|
||||||
|
from config import config
|
||||||
|
|
||||||
|
font = QFont("consolas", 14)
|
||||||
|
font.setFamilies(["consolas", "黑体"])
|
||||||
|
|
||||||
|
class tree_item(QTreeWidgetItem):
|
||||||
|
def __init__(self, data, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
class tree_widget(QTreeWidget):
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setColumnCount(1)
|
||||||
|
self.setHeaderLabels(["项目"])
|
||||||
|
|
||||||
|
if "tree" in config:
|
||||||
|
config_tree = config["tree"]
|
||||||
|
|
||||||
|
def search(node, config):
|
||||||
|
for child in config:
|
||||||
|
id = child["id"]
|
||||||
|
label = child["label"]
|
||||||
|
data = {
|
||||||
|
"id": id,
|
||||||
|
}
|
||||||
|
item = tree_item(data, [label])
|
||||||
|
node.addChild(item)
|
||||||
|
if "child" in child:
|
||||||
|
search(item, child["child"])
|
||||||
|
|
||||||
|
search(self, config_tree)
|
||||||
|
|
||||||
|
self.itemClicked.connect(self.on_item_clicked)
|
||||||
|
|
||||||
|
def addChild(self, item):
|
||||||
|
return super().addTopLevelItem(item)
|
||||||
|
|
||||||
|
def on_item_clicked(self, item, column):
|
||||||
|
self.window.widget_display.load_project(item.data["id"])
|
||||||
|
|
||||||
|
class widget_tree(QDockWidget):
|
||||||
|
|
||||||
|
def __init__(self, window, *arg, **kwarg):
|
||||||
|
super().__init__(*arg, **kwarg)
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
self.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea | Qt.DockWidgetArea.RightDockWidgetArea)
|
||||||
|
self.setFont(font)
|
||||||
|
|
||||||
|
self.tree_widget = tree_widget(window)
|
||||||
|
self.setWidget(self.tree_widget)
|
||||||
|
|
||||||
19
pyinstaller/build.ps1
Normal file
19
pyinstaller/build.ps1
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
. "E:/program/env_win_gui/Scripts/Activate.ps1"
|
||||||
|
pyinstaller -y program.spec
|
||||||
|
|
||||||
|
$src = "../app/"
|
||||||
|
$dist = "./dist/dataprocess/"
|
||||||
|
$files = @("config/", "data/", "script/", "doc/")
|
||||||
|
|
||||||
|
foreach ($file in $files) {
|
||||||
|
$src_path = Join-Path -Path $src -ChildPath $file
|
||||||
|
$dist_path = Join-Path -Path $dist -ChildPath $file
|
||||||
|
|
||||||
|
# Check if the file exists before copying
|
||||||
|
if (Test-Path $src_path) {
|
||||||
|
Copy-Item -Recurse -Path $src_path -Destination $dist_path -Verbose
|
||||||
|
Write-Host "Copied: $src_path → $dist_path"
|
||||||
|
} else {
|
||||||
|
Write-Warning "Path not found: $src_path"
|
||||||
|
}
|
||||||
|
}
|
||||||
172
pyinstaller/program.spec
Normal file
172
pyinstaller/program.spec
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# -*- mode: python coding: utf-8 -*-
|
||||||
|
|
||||||
|
# freeze the python application into a stand-alone executable
|
||||||
|
# https://www.pyinstaller.org/
|
||||||
|
|
||||||
|
# usage:
|
||||||
|
# pyinstaller -y visualstock.spec
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
["../app/main.py"],
|
||||||
|
pathex = ["./"],
|
||||||
|
binaries = [
|
||||||
|
#("../python/*.dll", "./"),
|
||||||
|
],
|
||||||
|
datas = [
|
||||||
|
#("../app/config/", "config"),
|
||||||
|
#("../app/data/", "data"),
|
||||||
|
#("../app/script/", "script"),
|
||||||
|
],
|
||||||
|
hiddenimports = ["numpy.core.multiarray"],
|
||||||
|
hookspath = [],
|
||||||
|
runtime_hooks = [],
|
||||||
|
excludes = [
|
||||||
|
"astroid",
|
||||||
|
"astropy",
|
||||||
|
"babel",
|
||||||
|
"bcolz",
|
||||||
|
"bcrypt",
|
||||||
|
"black",
|
||||||
|
"blib2to3",
|
||||||
|
"blosc",
|
||||||
|
"bokeh",
|
||||||
|
"boto3",
|
||||||
|
"botocore",
|
||||||
|
"bottleneck",
|
||||||
|
"branca",
|
||||||
|
#"certifi",
|
||||||
|
"contourpy",
|
||||||
|
"coverage",
|
||||||
|
"cryptography",
|
||||||
|
"cv2",
|
||||||
|
"cvxopt",
|
||||||
|
"cytoolz",
|
||||||
|
"Cython",
|
||||||
|
"dask",
|
||||||
|
"distributed",
|
||||||
|
"django",
|
||||||
|
"docutils",
|
||||||
|
"duckdb",
|
||||||
|
"erfa",
|
||||||
|
"greenlet",
|
||||||
|
"h5py",
|
||||||
|
"jedi",
|
||||||
|
"jsonschema",
|
||||||
|
"markupsafe",
|
||||||
|
"matplotlib",
|
||||||
|
"nacl",
|
||||||
|
"netCDF4",
|
||||||
|
"netcdftime",
|
||||||
|
"notebook",
|
||||||
|
"numexpr",
|
||||||
|
"parso",
|
||||||
|
"PIL",
|
||||||
|
"psutil",
|
||||||
|
"pyarrow",
|
||||||
|
"pydantic",
|
||||||
|
"pymongo",
|
||||||
|
"PyQt5",
|
||||||
|
"Pythonwin",
|
||||||
|
#"pytz",
|
||||||
|
#"regex",
|
||||||
|
"scikit-image",
|
||||||
|
"scikit-learn",
|
||||||
|
"scipy",
|
||||||
|
"sphinx",
|
||||||
|
"sqlalchemy",
|
||||||
|
"tables",
|
||||||
|
#"tcl",
|
||||||
|
"tcl8",
|
||||||
|
#"tk",
|
||||||
|
"torch",
|
||||||
|
"torchaudio",
|
||||||
|
"torchvision",
|
||||||
|
"tornado",
|
||||||
|
"msgpack",
|
||||||
|
"nbconvert",
|
||||||
|
"nbformat",
|
||||||
|
"simplejson",
|
||||||
|
#"win32",
|
||||||
|
"win32com",
|
||||||
|
"wx",
|
||||||
|
"zmq",
|
||||||
|
"zope",
|
||||||
|
],
|
||||||
|
win_no_prefer_redirects = False,
|
||||||
|
win_private_assemblies = False,
|
||||||
|
cipher = block_cipher,
|
||||||
|
noarchive = False
|
||||||
|
)
|
||||||
|
|
||||||
|
custom_exclude = [
|
||||||
|
"mkl_avx.dll",
|
||||||
|
"mkl_avx2.dll",
|
||||||
|
"mkl_avx512.dll",
|
||||||
|
#"mkl_core.dll",
|
||||||
|
#"mkl_def.dll",
|
||||||
|
#"mkl_intel_thread.dll",
|
||||||
|
"mkl_mc.dll",
|
||||||
|
"mkl_mc3.dll",
|
||||||
|
#"mkl_rt.dll",
|
||||||
|
"mkl_sequential.dll",
|
||||||
|
"mkl_tbb_thread.dll",
|
||||||
|
"mkl_vml_avx.dll",
|
||||||
|
"mkl_vml_avx2.dll",
|
||||||
|
"mkl_vml_avx512.dll",
|
||||||
|
"mkl_vml_cmpt.dll",
|
||||||
|
"mkl_vml_def.dll",
|
||||||
|
"mkl_vml_mc.dll",
|
||||||
|
"mkl_vml_mc2.dll",
|
||||||
|
"mkl_vml_mc3.dll",
|
||||||
|
"svml_dispmd.dll",
|
||||||
|
]
|
||||||
|
binaries = []
|
||||||
|
for item in a.binaries:
|
||||||
|
flag = True
|
||||||
|
for ptn in custom_exclude:
|
||||||
|
l = len(ptn)
|
||||||
|
i = item[0]
|
||||||
|
if len(i) < l:
|
||||||
|
continue
|
||||||
|
if i[-l:] == ptn:
|
||||||
|
flag = False
|
||||||
|
if flag:
|
||||||
|
binaries.append(item)
|
||||||
|
#print(len(binaries), binaries)
|
||||||
|
a.binaries = binaries
|
||||||
|
|
||||||
|
pyz = PYZ(
|
||||||
|
a.pure, a.zipped_data,
|
||||||
|
cipher = block_cipher
|
||||||
|
)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries = True,
|
||||||
|
name = "DataProcess",
|
||||||
|
debug = False,
|
||||||
|
bootloader_ignore_signals = False,
|
||||||
|
strip = False,
|
||||||
|
upx = False,
|
||||||
|
console = False,
|
||||||
|
#icon = "../image/visualstock.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
coll = COLLECT(
|
||||||
|
exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip = False,
|
||||||
|
upx = True,
|
||||||
|
upx_exclude = [],
|
||||||
|
name = "dataprocess"
|
||||||
|
)
|
||||||
10
pyinstaller/test.bat
Normal file
10
pyinstaller/test.bat
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
pushd dist\dataprocess
|
||||||
|
|
||||||
|
set PATH_SAVE=%PATH%
|
||||||
|
set PATH=C:\WINDOWS\System32;
|
||||||
|
|
||||||
|
dataprocess.exe
|
||||||
|
|
||||||
|
set PATH=%PATH_SAVE%
|
||||||
|
|
||||||
|
popd
|
||||||
Loading…
Reference in New Issue
Block a user