String.format("%1$02d",var)其中%1$0nd是啥意思

本文详细解释了在编程中如何使用格式化字符串进行输出,特别是针对占位符%1$0nd的含义进行了深入解读。文章指出%1$0nd中的1指代的是第一个参数,$0表示当数值长度不足时用0来填充,n代表具体的填充长度,而d则意味着将变量转换为十进制字符串输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

%后的1指第一个参数,当前只有var一个可变参数,所以就是指var。

$后的0表示,位数不够用0补齐,如果没有这个0(如%1$nd)就以空格补齐,0后面的n表示总长度,总长度可以可以是大于9例如(%1$010d),d表示将var按十进制转字符串,长度不够的话用0或空格补齐。

import openpyxl as pyxl class Surface: ''' Surface结构体 变量含:曲率半径(radius), 厚度(thickness), 物方折射率及像方折射率(nd0, nF0, nC0, nd1, nF1, nC1), 半通光孔径(semi_dia) (物在无穷远时代表视场角) ''' radius = 0 thickness = 0 nd0 = 0 nF0 = 0 nC0 = 0 nd1 = 0 nF1 = 0 nC1 = 0 semi_dia = 0 def __init__(self, _radius, _thickness, _nd1, _nF1, _nC1, _semi_dia): self.radius = _radius self.thickness = _thickness self.nd1 = _nd1 self.nF1 = _nF1 self.nC1 = _nC1 self.semi_dia = _semi_dia def read_excel(input_file_name = 'input.xlsx'): """ 作用: 读取输入表中的物面、镜面、入瞳、像面数据 输入: 同一目录下的文件名,或指定文件路径。缺省默认为'input.xlsx' 输出: object; lens[ ]数组,元素是每面数据; entrance_pupil; image """ wb = pyxl.load_workbook(input_file_name) ws = wb['Sheet1'] temp = ws.iter_rows() print(temp) parameter = [] for row in ws.rows: for cell in row: parameter.append(cell.value) i = 0 objdata = [] lensdata = [] imgdata = [] entrance_pupil = [] """ 作用: 首先略过输入表的列标题,读取物面信息, 再逐面读取光学面信息, 最后读取像面信息; """ for i in range(len(parameter)): if(i >= 14 and i < 21): objdata.append(parameter[i]) elif(i >= 21 and i < len(parameter)-7): lensdata.append(parameter[i]) elif(i >= len(parameter)-7): imgdata.append(parameter[i]) lensnumber = int(len(lensdata) / 7) object = Surface(objdata[1], objdata[2], objdata[3], objdata[4], objdata[5], objdata[6]) lens = [] for i in range(lensnumber-1): lens.append(Surface(lensdata[i*7+1], lensdata[i*7+2], lensdata[i*7+3], lensdata[i*7+4], lensdata[i*7+5], lensdata[i*7+6])) lens[i].nd0 = parameter[(i+2)*7+3] lens[i].nF0 = parameter[(i+2)*7+4] lens[i].nC0 = parameter[(i+2)*7+5] #读取光阑信息 entrance_pupil = Surface(lensdata[(lensnumber-1)*7+1], lensdata[(lensnumber-1)*7+2], lensdata[(lensnumber-1)*7+3], lensdata[(lensnumber-1)*7+4],\ lensdata[(lensnumber-1)*7+5], lensdata[(lensnumber-1)*7+6]) image = Surface(imgdata[1], imgdata[2], imgdata[3], imgdata[4], imgdata[5], imgdata[6]) return object, lens, entrance_pupil, image def write_excel(data1 = [], data2 = [], data3 = [], data4 = [], data5 = [], data6 = [], filename = 'output.xlsx'): """ 作用: 写EXCEL 输入: 共38个数据,分为 data1: f', l', lH', lp', y0', y0, 0.707', xt', xs', delta_x', F光近轴像位置, C光近轴像位置; 共12个数据 data2: 全孔径的 d光像点, F光像点, C光像点, 球差, 位置色差; 共5个数据 data3: 0.707孔径的 d光像点, F光像点, C光像点, 球差, 位置色差; 共5个数据 data4: 0孔径位置色差; 共1个数据 data5: 全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差; 共8个数据 data6: 0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差; 共8个数据 filename: 输出文件名 """ wb = pyxl.Workbook() ws = wb.active label1 = ['f\'', 'l\'', 'lH\'', 'lp\'', 'y0\'', 'y0, 0.707\'', 'xt\'', 'xs\'', 'delta_x\'', 'F光近轴像位置', 'C光近轴像位置'] for i in range(len(label1)): ws.cell(row = 1, column = i+1, value = label1[i]) ws.cell(row = 2, column = i+1, value = data1[i]) ws.merge_cells('A4:E4') ws['A4'] = '轴上点全孔径d、F、C光' label_DFC = ['d光像点', 'F光像点', 'C光像点', '球差', '位置色差'] for i in range(len(label_DFC)): ws.cell(row = 5, column = i+1, value = label_DFC[i]) ws.cell(row = 6, column = i+1, value = data2[i]) ws.merge_cells('A8:E8') ws['A8'] = '轴上点0.7孔径d、F、C光' for i in range(len(label_DFC)): ws.cell(row = 9, column = i+1, value = label_DFC[i]) ws.cell(row = 10, column = i+1, value = data3[i]) ws['A12'] = '轴上点0孔径位置色差' ws['A13'] = data4[0] ws.merge_cells('A15:H15') ws['A15'] = '全视场' label3 = ['全孔径子午彗差', '0.7孔径子午彗差', 'd光实际像高', 'F光实际像高', 'C光实际像高', '绝对畸变', '相对畸变', '倍率色差'] for i in range(len(label3)): ws.cell(row = 16, column = i+1, value = label3[i]) ws.cell(row = 17, column = i+1, value = data5[i]) ws.merge_cells('A19:H19') ws['A19'] = '0.7视场' for i in range(len(label3)): ws.cell(row = 20, column = i+1, value = label3[i]) ws.cell(row = 21, column = i+1, value = data6[i]) for c in range(1, 12): # 注意,列序数从1开始,但必须转变为A\B等字母 w = pyxl.utils.get_column_letter(c) # 把列序数转变为字母 ws.column_dimensions[w].width = 15 wb.save(filename) ''' ---迭代递推模块--- 包含: lens_recursion(...), 给定入射光线L、U,对各镜面进行光线追迹并返回各面L、U数据 astigmatism_recursion(...), 给定各面L、U、I、PA参数,求子午场曲,弧矢场曲,像散 ''' EPS = 1e-5 #PA校验精度 def lens_recursion(L, U, H, lens, raytype, isParallel): """ 函数作用: 给定轴上光线入射初值, 对每个镜面进行递推 输入: 初始物方截距(L)、物方倾斜角(U)、镜面数据(lens)、光线类型(raytype = 'd', 'F', 'C') 是否平行于主光轴(isParallel, 1平行, 0倾斜) 输出: 光线经过每面镜面后的L, L', U', I, I', PA数组 """ lensnumber = len(lens) recursion = 0 sinI = 0; sinII = 0 I = 0; II = 0 UU = 0 LL = 0 L_data = [L] LL_data = [] UU_data = [U] I_data = [] II_data = [] PA_data = [] if(raytype == 'd'): #d光,折射率取nd for recursion in range(lensnumber): if(recursion == 0 and isParallel): sinI = H / lens[recursion].radius else: sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U) sinII = lens[recursion].nd0 / lens[recursion].nd1 * sinI if(np.abs(sinI) < 1 and np.abs(sinII) < 1): I = np.arcsin(sinI); II = np.arcsin(sinII) I_data.append(I); II_data.append(II) UU = U + I - II LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU) LL_data.append(LL); UU_data.append(UU) #PA校对法 PA1 = L * np.sin(U) / np.cos((I-U)/2) PA2 = LL * np.sin(UU) / np.cos((II-UU)/2) if(np.abs(PA1-PA2) > EPS and isParallel == 0): print("Recursion not right.") print(f'PA1 = {PA1}, PA2 = {PA2}') PA_data.append(PA2) #过渡公式 U = UU L = LL - lens[recursion].thickness #舍去最后一个过渡的物方截距 if(recursion != lensnumber - 1): L_data.append(L) if(raytype == 'F'): #F光,折射率取nF for recursion in range(lensnumber): if(recursion == 0 and isParallel): sinI = H / lens[recursion].radius else: sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U) sinII = lens[recursion].nF0 / lens[recursion].nF1 * sinI if(np.abs(sinI) < 1 and np.abs(sinII) < 1): I = np.arcsin(sinI); II = np.arcsin(sinII) I_data.append(I); II_data.append(II) UU = U + I - II LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU) LL_data.append(LL); UU_data.append(UU) #PA校对法 PA1 = L * np.sin(U) / np.cos((I-U)/2) PA2 = LL * np.sin(UU) / np.cos((II-UU)/2) if(np.abs(PA1-PA2) > EPS and isParallel == 0): print("Recursion not right.") print(f'PA1 = {PA1}, PA2 = {PA2}') PA_data.append(PA2) #过渡公式 U = UU L = LL - lens[recursion].thickness #舍去最后一个过渡的物方截距 if(recursion != lensnumber - 1): L_data.append(L) if(raytype == 'C'): #C光,折射率取nC for recursion in range(lensnumber): if(recursion == 0 and isParallel): sinI = H / lens[recursion].radius else: sinI = (L - lens[recursion].radius) / lens[recursion].radius * np.sin(U) sinII = lens[recursion].nC0 / lens[recursion].nC1 * sinI if(np.abs(sinI) < 1 and np.abs(sinII) < 1): I = np.arcsin(sinI); II = np.arcsin(sinII) I_data.append(I); II_data.append(II) UU = U + I - II LL = lens[recursion].radius + lens[recursion].radius * sinII / np.sin(UU) LL_data.append(LL); UU_data.append(UU) #PA校对法 PA1 = L * np.sin(U) / np.cos((I-U)/2) PA2 = LL * np.sin(UU) / np.cos((II-UU)/2) if(np.abs(PA1-PA2) > EPS and isParallel == 0): print("Recursion not right.") print(f'PA1 = {PA1}, PA2 = {PA2}') PA_data.append(PA2) #过渡公式 U = UU L = LL - lens[recursion].thickness #舍去最后一个过渡的物方截距 if(recursion != lensnumber - 1): L_data.append(L) #print(f'LEN_DATA\n{len(L_data)}, {len(LL_data)}, {len(UU_data)}, {len(I_data)}, {len(II_data)}, {len(PA_data)}') return L_data, LL_data, UU_data, I_data, II_data, PA_data def astigmatism_recursion(L_data, LL_data, UU_data, I_data, II_data, PA_data, lens, raytype, isParallel): """ 函数作用: 求像散, 场曲 输入: 由lens_recursion算得的各参数 输出: 子午场曲(xt), 弧矢场曲(xs), 像散(delta_x) """ lensnumber = len(lens) for recursion in range(lensnumber): if(raytype == 'd'): x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius) #求t、s初值 if(recursion == 0 and isParallel == 0): t = s = (L_data[0] - x) / np.cos(UU_data[0]) if(recursion == 1 and isParallel == 1): t = s = (L_data[1] - x) / np.cos(UU_data[1]) temp_tt = (lens[recursion].nd1 * np.cos(II_data[recursion]) - lens[recursion].nd0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nd0 * np.cos(I_data[recursion]) ** 2 / t tt = 1 / temp_tt * lens[recursion].nd1 * np.cos(II_data[recursion]) ** 2 #t' temp_ss = (lens[recursion].nd1 * np.cos(II_data[recursion]) - lens[recursion].nd0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nd0 / s ss = 1 / temp_ss * lens[recursion].nd1 #s' if(recursion < lensnumber-1): #过渡公式 xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1) D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1]) t = tt - D s = ss - D if(recursion == lensnumber-1): #最后一面 lt = tt * np.cos(UU_data[recursion + 1]) + x ls = ss * np.cos(UU_data[recursion + 1]) + x xt = lt - LL_data[-1] xs = ls - LL_data[-1] delta_x = xt - xs if(raytype == 'C'): x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius) #求t、s初值 if(recursion == 0 and isParallel == 0): t = s = (L_data[0] - x) / np.cos(UU_data[0]) if(recursion == 1 and isParallel == 1): t = s = (L_data[1] - x) / np.cos(UU_data[1]) temp_tt = (lens[recursion].nC1 * np.cos(II_data[recursion]) - lens[recursion].nC0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nC0 * np.cos(I_data[recursion]) ** 2 / t tt = 1 / temp_tt * lens[recursion].nC1 * np.cos(II_data[recursion]) ** 2 #t' temp_ss = (lens[recursion].nC1 * np.cos(II_data[recursion]) - lens[recursion].nC0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nC0 / s ss = 1 / temp_ss * lens[recursion].nC1 #s' if(recursion < lensnumber-1): #过渡公式 xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1) D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1]) t = tt - D s = ss - D if(recursion == lensnumber-1): #最后一面 lt = tt * np.cos(UU_data[recursion + 1]) + x ls = ss * np.cos(UU_data[recursion + 1]) + x xt = lt - LL_data[recursion] xs = ls - LL_data[recursion] delta_x = xt - xs if(raytype == 'F'): x = PA_data[recursion] ** 2 / (2 * lens[recursion].radius) #求t、s初值 if(recursion == 0 and isParallel == 0): t = s = (L_data[0] - x) / np.cos(UU_data[0]) if(recursion == 1 and isParallel == 1): t = s = (L_data[1] - x) / np.cos(UU_data[1]) temp_tt = (lens[recursion].nF1 * np.cos(II_data[recursion]) - lens[recursion].nF0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nF0 * np.cos(I_data[recursion]) ** 2 / t tt = 1 / temp_tt * lens[recursion].nF1 * np.cos(II_data[recursion]) ** 2 #t' temp_ss = (lens[recursion].nF1 * np.cos(II_data[recursion]) - lens[recursion].nF0 * np.cos(I_data[recursion]))\ / lens[recursion].radius + lens[recursion].nF0 / s ss = 1 / temp_ss * lens[recursion].nF1 #s' if(recursion < lensnumber-1): #过渡公式 xx = PA_data[recursion + 1] ** 2 / (2 * lens[recursion+1].radius) #x(i+1) D = (lens[recursion].thickness - x + xx) / np.cos(UU_data[recursion+1]) t = tt - D s = ss - D if(recursion == lensnumber-1): #最后一面 lt = tt * np.cos(UU_data[recursion + 1]) + x ls = ss * np.cos(UU_data[recursion + 1]) + x xt = lt - LL_data[recursion] xs = ls - LL_data[recursion] delta_x = xt - xs return xt, xs, delta_x ''' ---L、U计算模块--- 包含: Calc(...), 给定孔径系数、视场系数、物面、镜面、入瞳信息,光线类型,利用迭代递推模块进行中间计算,返回各面L、U、I、PA信息。 ''' def Calc(K_eta, K_W, object, lens, pupil, image, raytype): """ 作用:计算某一孔径、视场光线在逐个面的(L,U)坐标数据 输入:K_eta为孔径取点系数,K_W为视场取点系数 """ #初始化(L,U)坐标列表 L0 = [] L1 = [] U = [] I0 = [] I1 = [] PA = [] A = pupil.semi_dia #入瞳半径 eta = K_eta * A #像高 #物在无穷远且光线与光轴平行时的情形 if(object.thickness == None and K_W == 0): h0 = K_eta * pupil.semi_dia L0, L1, U, I0, I1, PA = lens_recursion(0, 0, h0, lens, raytype, 1) #其余情形 -> 转化为轴上点在有限远处 else: if((object.thickness == None and K_W != 0)): U = -K_W * object.semi_dia * np.pi/180 L = pupil.thickness + eta/np.tan(U) elif(object.semi_dia == 0): L = -object.thickness U = np.arcsin(K_eta * A/np.sqrt(A**2 + Lp**2)) elif(object.semi_dia != 0): Lp = pupil.thickness #第一面到入瞳的距离 y = K_W * object.semi_dia #物方线视场 U = np.arctan((y-eta)/(Lp+object.thickness)) L = Lp + eta/((y-eta)/(Lp+object.thickness)) L0, L1, U, I0, I1, PA = lens_recursion(L, U, 0, lens, raytype, 0) return L0, L1, U, I0, I1, PA ''' ---数据处理模块--- 包含: data_processing(...), 总输出函数,给定物面、镜面、入瞳、像面信息,向主函数返回所有待求量 Calc_ff(...), 计算计算像方焦距、焦点位置、主点位置 Calc_ll(...), 计算像距、像高、像散、场曲 Calc_lp(...), 计算出瞳距 Calc_sp(...), 计算全孔径和0.7孔径时的实际像距、球差、位置色差 Calc_coma(...), 计算全视场、0.7视场、正负全孔径、正负0.7孔径的彗差、两个视场dFC光实际像高,绝对畸变、相对畸变、倍率色差 ''' def data_processing(object, lens, pupil, image): ''' 共输出38个数据 data1:f', l', lH', lp', y0', y0, 0.707', xt', xs', delta_x', F光近轴像位置, C光近轴像位置;共12个数据 data2:轴上点全孔径的 d光像点, F光像点, C光像点, 球差, 位置色差;共5个数据 data3:轴上点0.707孔径的 d光像点, F光像点, C光像点, 球差, 位置色差;共5个数据 data4:轴上点0孔径位置色差;共1个数据 data5:全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差;共8个数据 data6:0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差;共8个数据 ''' oo = 1e-20 #定义近轴小量 ff, l_H = Calc_ff(object, lens, pupil, image, oo) ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x = Calc_ll(object, lens, pupil, image, oo, l_H, ff) l_p = Calc_lp(lens, pupil, image, oo) data1 = [ff, ll, l_H, l_p, y0, y0_707, xt, xs, delta_x, ll_F, ll_C] data2, data3, data4 = Calc_sp(object, lens, pupil, image, ll, oo) data5, data6 = Calc_coma(object, lens, pupil, image, oo, y0, y0_707) return data1, data2, data3, data4, data5, data6 #1、计算像方焦距、焦点位置、主点位置 def Calc_ff(object, lens, pupil, image, oo): #定义一个近轴无穷远物面 object_ = Surface(0, None, object.nd1, object.nF1, object.nC1, None) #计算平行入射的近轴光线 L0 = Calc(oo, 0, object_, lens, pupil, image, 'd')[0] L1 = Calc(oo, 0, object_, lens, pupil, image, 'd')[1] #计算L0,L1的乘积 L0_product = L1_product = L0F_product = L1F_product = L0C_product = L1C_product = 1 for i in range(len(L0)-1): L0_product = L0_product * L0[i+1] for i in range(len(L1)): L1_product = L1_product * L1[i] #利用正切计算法计算焦距 ff = L1_product/L0_product #计算像方焦点和主点位置 l_F = L1[-1] l_H = ff - l_F return ff, l_H #2、计算像距和像高,像散及场曲 def Calc_ll(object, lens, pupil, image, oo, l_H, ff): #计算物面入射的近轴光线 if(object.thickness == None): ll = ff-l_H else: L0 = Calc(oo, 0, object, lens, pupil, image, 'd')[0] L1 = Calc(oo, 0, object, lens, pupil, image, 'd')[1] ll = L1[-1] #最后一个光学面的像距 #计算像高 if(object.thickness == None): y0 = ff * np.tan(object.semi_dia * np.pi/180) y0_707 = ff * np.tan(0.70 * object.semi_dia * np.pi/180) else: A = object.semi_dia y0 = A for i in range(len(L1)): n0 = lens[i].nd0 n1 = lens[i].nd1 l0 = L0[i] l1 = L1[i] beta = (n0*l1)/(n1*l0) #理想的横向放大倍率 y0 = y0 * beta y0_707 = 0.70 * y0 if(object.thickness == None and object.semi_dia == None): isparallel = 1 else: isparallel = 0 #计算C光和F光的近轴像距 L1F = Calc(oo, 0, object, lens, pupil, image, 'F')[1] ll_F = L1F[-1] #F光 L1C = Calc(oo, 0, object, lens, pupil, image, 'C')[1] ll_C = L1C[-1] #C光 #计算场曲及像散 L0, L1, U, I0, I1, PA = Calc(0, 1, object, lens, pupil, image, 'd') if(object.thickness == None): L0[0] = 10**15 else: L0[0] = -object.thickness L1 = Calc(oo, 0, object, lens, pupil, image, 'd')[1] xt, xs, delta_x = astigmatism_recursion(L0, L1, U, I0, I1, PA, lens, 'd', isparallel) return ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x #3、计算出瞳距 def Calc_lp(lens, pupil, image, oo): #定义入瞳面 object_ = Surface(pupil.radius, pupil.thickness, pupil.nd1, pupil.nC1, pupil.nF1, pupil.semi_dia) L1 = Calc(oo, 0, object_, lens, pupil, image, 'd')[1] l_p = L1[-1] return l_p """ 4、计算全孔径和0.7孔径时的实际像距、球差、位置色差 变量:SPAB代表球差spherical aberration,PCA代表位置色差Position Chromatic Aberration """ def Calc_sp(object, lens, pupil, image, ll, oo): L1d1 = Calc(1, 0, object, lens, pupil, image, 'd')[1] ll_d1 = L1d1[-1] SPAB1 = ll_d1 - ll L1d707 = Calc(0.70, 0, object, lens, pupil, image, 'd')[1] ll_d707 = L1d707[-1] SPAB707 = ll_d707 - ll L1F1 = Calc(1, 0, object, lens, pupil, image, 'F')[1] ll_F1 = L1F1[-1] L1C1 = Calc(1, 0, object, lens, pupil, image, 'C')[1] ll_C1 = L1C1[-1] L1F707 = Calc(0.70, 0, object, lens, pupil, image, 'F')[1] ll_F707 = L1F707[-1] L1C707 = Calc(0.70, 0, object, lens, pupil, image, 'C')[1] ll_C707 = L1C707[-1] L1F0 = Calc(oo, 0, object, lens, pupil, image, 'F')[1] ll_F0 = L1F0[-1] L1C0 = Calc(oo, 0, object, lens, pupil, image, 'C')[1] ll_C0 = L1C0[-1] PCA1 = ll_F1 - ll_C1 PCA707 = ll_F707 - ll_C707 PCA0 = ll_F0 - ll_C0 data2 = [ll_d1, ll_F1, ll_C1, SPAB1, PCA1] data3 = [ll_d707, ll_F707, ll_C707, SPAB707, PCA707] data4 = [PCA0] return data2, data3, data4 #5、计算全视场、0.7视场、正负全孔径、正负0.7孔径的彗差、两个视场dFC光实际像高,绝对畸变、相对畸变、倍率色差 def Calc_coma(object, lens, pupil, image, oo, y0, y0_707): #轴上物点,求理想像面 LL_perfect_d = Calc(oo, 0, object, lens, pupil, image, 'd')[1] '''获取实际光线数据''' #全视场,全孔径数据 LL_upper_d = Calc(1, -1, object, lens, pupil, image, 'd')[1] UU_upper_d = Calc(1, -1, object, lens, pupil, image, 'd')[2] LL_central_d = Calc(0, -1, object, lens, pupil, image, 'd')[1] UU_central_d = Calc(0, -1, object, lens, pupil, image, 'd')[2] LL_down_d = Calc(-1, -1, object, lens, pupil, image, 'd')[1] UU_down_d = Calc(-1, -1, object, lens, pupil, image, 'd')[2] #全视场,0.7孔径数据 LL_upper_d_1_07 = Calc(0.7, -1, object, lens, pupil, image, 'd')[1] UU_upper_d_1_07 = Calc(0.7, -1, object, lens, pupil, image, 'd')[2] LL_central_d_1_07 = Calc(0, -1, object, lens, pupil, image, 'd')[1] UU_central_d_1_07 = Calc(0, -1, object, lens, pupil, image, 'd')[2] LL_down_d_1_07 = Calc(-0.7, -1, object, lens, pupil, image, 'd')[1] UU_down_d_1_07 = Calc(-0.7, -1, object, lens, pupil, image, 'd')[2] #0.7视场,全孔径数据 LL_upper_d_07_1 = Calc(1, -0.7, object, lens, pupil, image, 'd')[1] UU_upper_d_07_1 = Calc(1, -0.7, object, lens, pupil, image, 'd')[2] LL_central_d_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'd')[1] UU_central_d_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'd')[2] LL_down_d_07_1 = Calc(-1, -0.7, object, lens, pupil, image, 'd')[1] UU_down_d_07_1 = Calc(-1, -0.7, object, lens, pupil, image, 'd')[2] #0.7视场,0.7孔径数据 LL_upper_d_07_07 = Calc(0.7, -0.7, object, lens, pupil, image, 'd')[1] UU_upper_d_07_07 = Calc(0.7, -0.7, object, lens, pupil, image, 'd')[2] LL_central_d_07_07 = Calc(0, -0.7, object, lens, pupil, image, 'd')[1] UU_central_d_07_07 = Calc(0, -0.7, object, lens, pupil, image, 'd')[2] LL_down_d_07_07 = Calc(-0.7, -0.7, object, lens, pupil, image, 'd')[1] UU_down_d_07_07 = Calc(-0.7, -0.7, object, lens, pupil, image, 'd')[2] '''计算彗差''' #全视场、全孔径彗差 y_upper = (LL_perfect_d[-1] - LL_upper_d[-1]) * np.tan(UU_upper_d[-1]) y_down = (LL_perfect_d[-1] - LL_down_d[-1]) * np.tan(UU_down_d[-1]) y_central = (LL_perfect_d[-1] - LL_central_d[-1]) * np.tan(UU_central_d[-1]) COMA1 = (y_upper + y_down) / 2 - y_central #全视场,0.7孔径彗差 y_upper_1_07 = (LL_perfect_d[-1] - LL_upper_d_1_07[-1]) * np.tan(UU_upper_d_1_07[-1]) y_down_1_07 = (LL_perfect_d[-1] - LL_down_d_1_07[-1]) * np.tan(UU_down_d_1_07[-1]) y_central_1_07 = (LL_perfect_d[-1] - LL_central_d_1_07[-1]) * np.tan(UU_central_d_1_07[-1]) COMA2 = (y_upper_1_07 + y_down_1_07) / 2 - y_central_1_07 #0.7视场,全孔径 y_upper_07_1 = (LL_perfect_d[-1] - LL_upper_d_07_1[-1]) * np.tan(UU_upper_d_07_1[-1]) y_down_07_1 = (LL_perfect_d[-1] - LL_down_d_07_1[-1]) * np.tan(UU_down_d_07_1[-1]) y_central_07_1 = (LL_perfect_d[-1] - LL_central_d_07_1[-1]) * np.tan(UU_central_d_07_1[-1]) COMA3 = (y_upper_07_1 + y_down_07_1) / 2 - y_central_07_1 #0.7视场,0.7孔径 y_upper_07_07 = (LL_perfect_d[-1] - LL_upper_d_07_07[-1]) * np.tan(UU_upper_d_07_07[-1]) y_down_07_07 = (LL_perfect_d[-1] - LL_down_d_07_07[-1]) * np.tan(UU_down_d_07_07[-1]) y_central_07_07 = (LL_perfect_d[-1] - LL_central_d_07_07[-1]) * np.tan(UU_central_d_07_07[-1]) COMA4 = (y_upper_07_07 + y_down_07_07) / 2 - y_central_07_07 '''计算实际像高''' #全视场dFC光实际像高 LL_central_C = Calc(0, -1, object, lens, pupil, image, 'C')[1] UU_central_C = Calc(0, -1, object, lens, pupil, image, 'C')[2] LL_central_F = Calc(0, -1, object, lens, pupil, image, 'F')[1] UU_central_F = Calc(0, -1, object, lens, pupil, image, 'F')[2] y_d_1 = (LL_perfect_d[-1] - LL_central_d[-1]) * np.tan(UU_central_d[-1]) y_C_1 = (LL_perfect_d[-1] - LL_central_C[-1]) * np.tan(UU_central_C[-1]) y_F_1 = (LL_perfect_d[-1] - LL_central_F[-1]) * np.tan(UU_central_F[-1]) #0.7视场dFC光实际像高 LL_central_C_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'C')[1] UU_central_C_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'C')[2] LL_central_F_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'F')[1] UU_central_F_07_1 = Calc(0, -0.7, object, lens, pupil, image, 'F')[2] y_d_07 = (LL_perfect_d[-1] - LL_central_d_07_1[-1]) * np.tan(UU_central_d_07_1[-1]) y_C_07 = (LL_perfect_d[-1] - LL_central_C_07_1[-1]) * np.tan(UU_central_C_07_1[-1]) y_F_07 = (LL_perfect_d[-1] - LL_central_F_07_1[-1]) * np.tan(UU_central_F_07_1[-1]) '''计算倍率色差''' MCA1 = (y_F_1 - y_C_1) #全视场倍率色差 MCA07 = (y_F_07 - y_C_07) #0.7视场倍率色差 '''计算绝对畸变,相对畸变''' DIS1 = y_d_1 - y0 rel_DIS1 = DIS1 / y0 DIS07 = y_d_07 - y0_707 rel_DIS07 = DIS07 / y0_707 #全视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差 data5 = [COMA1, COMA2, y_d_1, y_F_1, y_C_1, DIS1, rel_DIS1, MCA1] #0.707视场的 全孔径子午彗差, 0.7孔径子午彗差, d光实际像高, F光实际像高, C光实际像高, 绝对畸变, 相对畸变, 倍率色差 data6 = [COMA3, COMA4, y_d_07, y_F_07, y_C_07, DIS07, rel_DIS07, MCA07] return data5, data6 input_label = ['输入-无限远平行光.xlsx', '输入-轴上有限远物.xlsx'] output_label = ['输出-无限远平行光.xlsx', '输出-轴上有限远物.xlsx'] for i in range(2): object, lens, pupil, image = read_excel(input_label[i]) data1, data2, data3, data4, data5, data6 = data_processing(object, lens, pupil, image) write_excel(data1, data2, data3, data4, data5, data6, output_label[i]) print("\nOUTPUT SUCCESSFULLY!\n")以上是一个光学计算软件的程序,请为其编写一个GUI界面包括各种镜头参数的输入,光学表面及其参数的输入,结果的输出与显示,类似下面这种(形式差不多)class MainApplication: def __init__(self, root): self.root = root self.root.title("光学系统计算程序") self.root.geometry("1000x850") # 创建光学系统对象 self.system = OpticalSystem() # 创建GUI组件 self.create_widgets() # 加载默认设置(如果有) self.load_default_settings() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 文件操作框架 file_frame = ttk.LabelFrame(main_frame, text="文件操作") file_frame.pack(fill=tk.X, padx=5, pady=5) # 文件路径输入 ttk.Label(file_frame, text="文件路径:").grid(row=0, column=0, padx=5, pady=5) self.file_path_entry = ttk.Entry(file_frame, width=50) self.file_path_entry.grid(row=0, column=1, padx=5, pady=5) # 文件操作按钮 browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file) browse_btn.grid(row=0, column=2, padx=5, pady=5) load_btn = ttk.Button(file_frame, text="加载系统参数", command=self.load_system) load_btn.grid(row=0, column=3, padx=5, pady=5) save_btn = ttk.Button(file_frame, text="保存系统参数", command=self.save_system) save_btn.grid(row=0, column=4, padx=5, pady=5) # 左侧面板 - 输入参数 left_frame = ttk.LabelFrame(main_frame, text="系统参数输入") left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧面板 - 结果展示 right_frame = ttk.LabelFrame(main_frame, text="计算结果") right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建左侧面板的子组件 self.create_input_panel(left_frame) # 创建右侧面板的子组件 self.create_result_panel(right_frame) # 计算按钮 calc_frame = ttk.Frame(main_frame) calc_frame.pack(fill=tk.X, padx=5, pady=10) calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.calculate, width=15) calc_btn.pack(side=tk.LEFT, padx=10) save_result_btn = ttk.Button(calc_frame, text="保存计算结果", command=self.save_results, width=15) save_result_btn.pack(side=tk.LEFT, padx=10) clear_btn = ttk.Button(calc_frame, text="清除所有", command=self.clear_all, width=15) clear_btn.pack(side=tk.LEFT, padx=10) def browse_file(self): """浏览文件按钮处理函数""" file_path = filedialog.askopenfilename( title="选择文件", filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")] ) if file_path: self.file_path_entry.delete(0, tk.END) self.file_path_entry.insert(0, file_path) def create_input_panel(self, parent): # 入瞳参数 pupil_frame = ttk.LabelFrame(parent, text="入瞳参数") pupil_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳直径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.entrance_diameter_entry = ttk.Entry(pupil_frame, width=15) self.entrance_diameter_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳位置 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.entrance_position_entry = ttk.Entry(pupil_frame, width=15) self.entrance_position_entry.grid(row=0, column=3, padx=5, pady=5) # 色光类型选择 ttk.Label(pupil_frame, text="色光类型:").grid(row=1, column=0, padx=5, pady=5, sticky="w") self.light_type_var = tk.StringVar(value="d") self.light_type_combo = ttk.Combobox(pupil_frame, textvariable=self.light_type_var, width=12, state="readonly") self.light_type_combo["values"] = ("d", "f", "c") self.light_type_combo.grid(row=1, column=1, padx=5, pady=5, sticky="w") # 物方参数 object_frame = ttk.LabelFrame(parent, text="物方参数") object_frame.pack(fill=tk.X, padx=5, pady=5) # 物距选择 self.object_var = tk.BooleanVar(value=True) # True: 无穷远, False: 有限远 ttk.Radiobutton(object_frame, text="物在无穷远", variable=self.object_var, value=True, command=self.toggle_object_input).grid(row=0, column=0, padx=5, pady=5) ttk.Radiobutton(object_frame, text="物在有限远", variable=self.object_var, value=False, command=self.toggle_object_input).grid(row=0, column=1, padx=5, pady=5) # 无穷远参数 self.infinite_frame = ttk.Frame(object_frame) self.infinite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) ttk.Label(self.infinite_frame, text="半视场角 (度):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.field_angle_entry = ttk.Entry(self.infinite_frame, width=15) self.field_angle_entry.grid(row=0, column=1, padx=5, pady=5) # 有限远参数 (初始隐藏) self.finite_frame = ttk.Frame(object_frame) self.finite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) self.finite_frame.grid_remove() # 初始隐藏 ttk.Label(self.finite_frame, text="物距 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.object_distance_entry = ttk.Entry(self.finite_frame, width=15) self.object_distance_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(self.finite_frame, text="物高 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.object_height_entry = ttk.Entry(self.finite_frame, width=15) self.object_height_entry.grid(row=0, column=3, padx=5, pady=5) ttk.Label(self.finite_frame, text="孔径角 (度):").grid(row=0, column=4, padx=5, pady=5, sticky="w") self.aperture_angle_entry = ttk.Entry(self.finite_frame, width=15) self.aperture_angle_entry.grid(row=0, column=5, padx=5, pady=5) # 光学表面输入 surface_frame = ttk.LabelFrame(parent, text="光学表面参数") surface_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 表面输入控件 input_frame = ttk.Frame(surface_frame) input_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5) self.radius_entry = ttk.Entry(input_frame, width=10) self.radius_entry.grid(row=0, column=1, padx=5, pady=5) self.radius_entry.insert(0, "50") ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5) self.thickness_entry = ttk.Entry(input_frame, width=10) self.thickness_entry.grid(row=0, column=3, padx=5, pady=5) self.thickness_entry.insert(0, "5") ttk.Label(input_frame, text="折射率 (nd):").grid(row=0, column=4, padx=5, pady=5) self.nd_entry = ttk.Entry(input_frame, width=10) self.nd_entry.grid(row=0, column=5, padx=5, pady=5) self.nd_entry.insert(0, "1.5") ttk.Label(input_frame, text="阿贝数 (vd):").grid(row=0, column=6, padx=5, pady=5) self.vd_entry = ttk.Entry(input_frame, width=10) self.vd_entry.grid(row=0, column=7, padx=5, pady=5) self.vd_entry.insert(0, "60") button_frame = ttk.Frame(input_frame) button_frame.grid(row=0, column=8, padx=10) add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surface) add_btn.pack(side=tk.LEFT, padx=5) remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surface) remove_btn.pack(side=tk.LEFT, padx=5) # 表面列表 list_frame = ttk.Frame(surface_frame) list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) columns = ("#", "曲率半径 (mm)", "厚度 (mm)", "折射率 (nd)", "阿贝数 (vd)") self.surface_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8) for col in columns: self.surface_tree.heading(col, text=col) self.surface_tree.column(col, width=100, anchor=tk.CENTER) vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.surface_tree.yview) self.surface_tree.configure(yscrollcommand=vsb.set) self.surface_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) vsb.pack(side=tk.RIGHT, fill=tk.Y) def create_result_panel(self, parent): # 结果文本框 self.result_text = tk.Text(parent, wrap=tk.WORD) result_scroll_y = ttk.Scrollbar(parent, orient="vertical", command=self.result_text.yview) result_scroll_x = ttk.Scrollbar(parent, orient="horizontal", command=self.result_text.xview) self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set) result_scroll_y.pack(side=tk.RIGHT, fill=tk.Y) result_scroll_x.pack(side=tk.BOTTOM, fill=tk.X) self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 设置初始文本 self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) def toggle_object_input(self): """切换物方参数输入界面""" if self.object_var.get(): # 无穷远 self.infinite_frame.grid() self.finite_frame.grid_remove() else: # 有限远 self.infinite_frame.grid_remove() self.finite_frame.grid() def add_surface(self): """添加光学表面""" try: # 获取输入值 r = self.radius_entry.get().strip() d = self.thickness_entry.get().strip() nd = self.nd_entry.get().strip() vd = self.vd_entry.get().strip() # 处理输入值 r = float(r) if r and r.lower() != "inf" else float('inf') d = float(d) if d else 0.0 nd = float(nd) if nd else 1.0 vd = float(vd) if vd else 0.0 # 添加到系统 surface = Surface(r, d, nd, vd) self.system.surfaces.append(surface) # 添加到树形视图 r_str = "平面" if r == float('inf') else f"{r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{d:.2f}", f"{nd:.4f}", f"{vd:.1f}" )) # 清空输入框 self.radius_entry.delete(0, tk.END) self.thickness_entry.delete(0, tk.END) self.nd_entry.delete(0, tk.END) self.vd_entry.delete(0, tk.END) self.radius_entry.focus_set() except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") def remove_surface(self): """删除选中的光学表面""" selected = self.surface_tree.selection() if selected: # 从树形视图中删除 for item in selected: index = int(self.surface_tree.item(item, "values")[0]) - 1 self.surface_tree.delete(item) # 从系统中删除 if 0 <= index < len(self.system.surfaces): self.system.surfaces.pop(index) # 更新剩余表面的序号 for i, item in enumerate(self.surface_tree.get_children()): values = list(self.surface_tree.item(item, "values")) values[0] = i + 1 self.surface_tree.item(item, values=values) def load_system(self): """加载系统参数文件""" file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入文件路径") return try: with open(file_path, 'r') as f: data = json.load(f) # 加载系统参数 self.system.entrance_pupil_diameter = data.get("entrance_pupil_diameter") self.system.entrance_pupil_position = data.get("entrance_pupil_position") self.system.object_infinite = data.get("object_infinite", True) self.system.field_angle = data.get("field_angle") self.system.object_distance = data.get("object_distance") self.system.object_height = data.get("object_height") self.system.aperture_angle = data.get("aperture_angle") # 更新UI中的参数值 if self.system.entrance_pupil_diameter is not None: self.entrance_diameter_entry.delete(0, tk.END) self.entrance_diameter_entry.insert(0, str(self.system.entrance_pupil_diameter)) if self.system.entrance_pupil_position is not None: self.entrance_position_entry.delete(0, tk.END) self.entrance_position_entry.insert(0, str(self.system.entrance_pupil_position)) self.object_var.set(self.system.object_infinite) self.toggle_object_input() if self.system.field_angle is not None: self.field_angle_entry.delete(0, tk.END) self.field_angle_entry.insert(0, str(self.system.field_angle)) if self.system.object_distance is not None: self.object_distance_entry.delete(0, tk.END) self.object_distance_entry.insert(0, str(self.system.object_distance)) if self.system.object_height is not None: self.object_height_entry.delete(0, tk.END) self.object_height_entry.insert(0, str(self.system.object_height)) if self.system.aperture_angle is not None: self.aperture_angle_entry.delete(0, tk.END) self.aperture_angle_entry.insert(0, str(self.system.aperture_angle)) # 加载表面数据 self.system.surfaces = [] self.surface_tree.delete(*self.surface_tree.get_children()) surfaces_data = data.get("surfaces", []) for surf_data in surfaces_data: surface = Surface.from_dict(surf_data) self.system.surfaces.append(surface) r_str = "平面" if surface.r == float('inf') else f"{surface.r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{surface.d:.2f}", f"{surface.nd:.4f}", f"{surface.vd:.1f}" )) messagebox.showinfo("成功", f"系统参数已从 {os.path.basename(file_path)} 加载") # 显示加载的系统信息 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, f"已加载系统参数文件: {file_path}\n") self.result_text.insert(tk.END, f"包含 {len(self.system.surfaces)} 个光学表面\n") self.result_text.configure(state=tk.DISABLED) except FileNotFoundError: messagebox.showerror("错误", f"文件不存在: {file_path}") except Exception as e: messagebox.showerror("加载错误", f"加载文件失败: {str(e)}") def save_system(self): """保存系统参数文件""" # 更新系统参数 if not self.update_system_from_ui(): return # 如果没有表面数据,提示用户 if not self.system.surfaces: messagebox.showwarning("警告", "没有光学表面数据,无法保存") return file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入保存路径") return try: # 准备保存数据 data = { "entrance_pupil_diameter": self.system.entrance_pupil_diameter, "entrance_pupil_position": self.system.entrance_pupil_position, "object_infinite": self.system.object_infinite, "field_angle": self.system.field_angle, "object_distance": self.system.object_distance, "object_height": self.system.object_height, "aperture_angle": self.system.aperture_angle, "surfaces": [surf.to_dict() for surf in self.system.surfaces] } with open(file_path, 'w') as f: json.dump(data, f, indent=4) messagebox.showinfo("成功", f"系统参数已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"系统参数已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存文件失败: {str(e)}") def save_results(self): """保存计算结果到文件""" if not self.system.results or all(v is None for v in self.system.results.values()): messagebox.showwarning("警告", "没有计算结果可保存") return file_path = filedialog.asksaveasfilename( title="保存计算结果", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if not file_path: return try: with open(file_path, 'w') as f: # 写入系统参数摘要 f.write("===== 光学系统参数 =====\n") f.write(f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") f.write(f"入瞳位置: {self.system.entrance_pupil_position} mm\n") f.write(f"色光类型: {self.system.light_type}\n") f.write("物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") if self.system.object_infinite: f.write(f"半视场角: {self.system.field_angle} 度\n") else: f.write(f"物距: {self.system.object_distance} mm\n") f.write(f"物高: {self.system.object_height} mm\n") f.write(f"孔径角: {self.system.aperture_angle} 度\n") f.write("\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" f.write(f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 写入计算结果 f.write("\n\n===== 计算结果 =====\n") for key, value in self.system.results.items(): if value is not None: # 格式化键名 label = self.format_result_label(key) f.write(f"{label}: {value}\n") messagebox.showinfo("成功", f"计算结果已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"计算结果已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存计算结果失败: {str(e)}") def format_result_label(self, key): """格式化结果标签为中文描述""" labels = { "focal_length": "焦距 f' (mm)", "ideal_image_distance": "理想像距 l' (mm)", "actual_image_position": "实际像位置 (mm)", "image_principal_plane": "像方主面位置 lH' (mm)", "exit_pupil_distance": "出瞳距 lp' (mm)", "ideal_image_height": "理想像高 y0' (mm)", "spherical_aberration": "球差 (mm)", "longitudinal_chromatic": "位置色差 (mm)", "tangential_field_curvature": "子午场曲 xt' (mm)", "sagittal_field_curvature": "弧矢场曲 xs' (mm)", "astigmatism": "像散 Δxts' (mm)", "actual_image_height": "实际像高 (mm)", "relative_distortion": "相对畸变 (%)", "absolute_distortion": "绝对畸变 (mm)", "lateral_chromatic": "倍率色差 (mm)", "tangential_coma": "子午慧差 (mm)" } return labels.get(key, key) def update_system_from_ui(self): """从UI更新系统参数""" try: # 入瞳参数 if self.entrance_diameter_entry.get(): self.system.entrance_pupil_diameter = float(self.entrance_diameter_entry.get()) if self.entrance_position_entry.get(): self.system.entrance_pupil_position = float(self.entrance_position_entry.get()) # 色光类型 self.system.light_type = self.light_type_var.get() # 物方参数 self.system.object_infinite = self.object_var.get() if self.system.object_infinite: if self.field_angle_entry.get(): self.system.field_angle = float(self.field_angle_entry.get()) else: if self.object_distance_entry.get(): self.system.object_distance = float(self.object_distance_entry.get()) if self.object_height_entry.get(): self.system.object_height = float(self.object_height_entry.get()) if self.aperture_angle_entry.get(): self.system.aperture_angle = float(self.aperture_angle_entry.get()) except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") return False return True def calculate(self): """执行光学计算""" # 更新系统参数 if not self.update_system_from_ui(): return # 检查必要参数 if not self.system.surfaces: messagebox.showwarning("警告", "请至少添加一个光学表面") return if self.system.entrance_pupil_diameter is None: messagebox.showwarning("警告", "请输入入瞳直径") return if self.system.object_infinite and self.system.field_angle is None: messagebox.showwarning("警告", "请输入半视场角") return if not self.system.object_infinite and ( self.system.object_distance is None or self.system.object_height is None or self.system.aperture_angle is None ): messagebox.showwarning("警告", "请输入完整的有限远参数") return # 执行计算 - 这里调用您的计算函数 self.perform_calculations() # 显示结果 self.display_results() # 显示计算完成消息 messagebox.showinfo("计算完成", "光学计算已完成,结果已显示在结果区域") def perform_calculations(self): """执行光学计算 - 这里调用您的实际计算函数""" # 重置结果 for key in self.system.results: self.system.results[key] = None # 示例:设置一些假结果用于演示 # 实际应用中,您需要调用您自己的计算函数 self.system.results["focal_length"] = Calculate.calculate_focal_length(self.system)[0] self.system.results["ideal_image_distance"] = 0#Calculate.calculate_ideal_image_distance(self.system) self.system.results["actual_image_position"] =0# Calculate.calculate_actual_image_position(self.system) self.system.results["image_principal_plane"] = Calculate.calculate_focal_length(self.system)[1] self.system.results["exit_pupil_distance"] = 0#Calculate.calculate_exit_pupil_distance(self.system) self.system.results["ideal_image_height"] =0# Calculate.calculate_ideal_image_height(self.system) self.system.results["spherical_aberration"] = 0#Calculate.calculate_spherical_aberration(self.system) self.system.results["longitudinal_chromatic"] = 0#Calculate.calculate_longitudinal_chromatic(self.system) self.system.results["tangential_field_curvature"] = 0.15 self.system.results["sagittal_field_curvature"] = 0.12 self.system.results["astigmatism"] = 0#Calculate.calculate_astigmatism(self.system) self.system.results["actual_image_height"] =0#Calculate.calculate_actual_image_height(self.system) self.system.results["relative_distortion"] = -0.5 self.system.results["absolute_distortion"] =0# Calculate.calculate_distortion(self.system) self.system.results["lateral_chromatic"] =0# Calculate.calculate_lateral_chromatic(self.system) self.system.results["tangential_coma"] =0# Calculate.calculate_tangential_coma(self.system) def display_results(self): """在结果文本框中显示计算结果""" self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) # 添加系统参数摘要 self.result_text.insert(tk.END, "===== 光学系统参数 =====\n", "title") self.result_text.insert(tk.END, f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") self.result_text.insert(tk.END, f"入瞳位置: {self.system.entrance_pupil_position} mm\n") self.result_text.insert(tk.END, "物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") self.result_text.insert(tk.END, f"色光类型: {self.system.light_type}\n") if self.system.object_infinite: self.result_text.insert(tk.END, f"半视场角: {self.system.field_angle} 度\n") else: self.result_text.insert(tk.END, f"物距: {self.system.object_distance} mm\n") self.result_text.insert(tk.END, f"物高: {self.system.object_height} mm\n") self.result_text.insert(tk.END, f"孔径角: {self.system.aperture_angle} 度\n") self.result_text.insert(tk.END, "\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" self.result_text.insert(tk.END, f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 添加计算结果 self.result_text.insert(tk.END, "\n\n===== 计算结果 =====\n", "title") # 计算关键结果 key_results = [ "focal_length", "ideal_image_distance", "actual_image_position", "image_principal_plane", "exit_pupil_distance", "ideal_image_height" ] for key in key_results: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") # 添加像差结果 self.result_text.insert(tk.END, "\n像差分析:\n", "subtitle") aberrations = [ "spherical_aberration", "longitudinal_chromatic", "tangential_field_curvature", "sagittal_field_curvature", "astigmatism", "actual_image_height", "relative_distortion", "absolute_distortion", "lateral_chromatic", "tangential_coma" ] for key in aberrations: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") self.result_text.configure(state=tk.DISABLED) def clear_all(self): """清除所有输入和结果""" # 清除系统参数 self.system = OpticalSystem() # 清除UI输入 self.file_path_entry.delete(0, tk.END) self.entrance_diameter_entry.delete(0, tk.END) self.entrance_position_entry.delete(0, tk.END) self.field_angle_entry.delete(0, tk.END) self.object_distance_entry.delete(0, tk.END) self.object_height_entry.delete(0, tk.END) self.aperture_angle_entry.delete(0, tk.END) self.radius_entry.delete(0, tk.END) self.radius_entry.insert(0, "50") self.thickness_entry.delete(0, tk.END) self.thickness_entry.insert(0, "5") self.nd_entry.delete(0, tk.END) self.nd_entry.insert(0, "1.5") self.vd_entry.delete(0, tk.END) self.vd_entry.insert(0, "60") # 清除表面列表 self.surface_tree.delete(*self.surface_tree.get_children()) self.system.surfaces = [] # 清除结果 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) # 重置物距类型 self.object_var.set(True) self.toggle_object_input() messagebox.showinfo("清除完成", "所有输入和结果已清除") def load_default_settings(self): """加载默认设置(可选)""" # 这里可以添加加载默认设置的逻辑 pass
06-27
import numpy as np class Surface: ''' Surface结构体 - 表示光学表面 参数: radius: 曲率半径 thickness: 厚度 nd1, nF1, nC1: 像方折射率 (d光, F光, C光) semi_dia: 半通光孔径 ''' def __init__(self, radius, thickness, nd1, nF1, nC1, semi_dia): self.radius = radius self.thickness = thickness self.nd1 = nd1 self.nF1 = nF1 self.nC1 = nC1 self.semi_dia = semi_dia # 物方折射率初始化为0,将在后续计算中设置 self.nd0 = 0 self.nF0 = 0 self.nC0 = 0 class Calculate: """ 光学计算核心类,封装所有光学计算功能 参数: object_surface: 物面信息 (Surface对象) lens_surfaces: 镜面列表 (Surface对象列表) entrance_pupil: 入瞳信息 (Surface对象) image_surface: 像面信息 (Surface对象) """ EPS = 1e-5 # PA校验精度 def __init__(self, object_surface, lens_surfaces, entrance_pupil, image_surface): self.object = object_surface self.lens = lens_surfaces self.pupil = entrance_pupil self.image = image_surface # 设置物方折射率 self._set_refractive_indices() def _set_refractive_indices(self): """设置每个镜面的物方折射率""" # 第一面的物方折射率等于物面的像方折射率 if len(self.lens) > 0: self.lens[0].nd0 = self.object.nd1 self.lens[0].nF0 = self.object.nF1 self.lens[0].nC0 = self.object.nC1 # 后续面的物方折射率等于前一面的像方折射率 for i in range(1, len(self.lens)): self.lens[i].nd0 = self.lens[i-1].nd1 self.lens[i].nF0 = self.lens[i-1].nF1 self.lens[i].nC0 = self.lens[i-1].nC1 def calc(self, K_eta, K_W, raytype, isParallel=False): """ 计算指定孔径和视场的光线在光学系统中的路径 参数: K_eta: 孔径取点系数 (0~1) K_W: 视场取点系数 (0~1) raytype: 光线类型 ('d', 'F', 'C') isParallel: 是否平行于光轴 (默认为False) 返回: L0, L1, U, I0, I1, PA: 各面的光线参数 """ # 初始化结果列表 L0, L1, U, I0, I1, PA = [], [], [], [], [], [] # 获取入瞳半径 A = self.pupil.semi_dia eta = K_eta * A # 像高 # 处理不同物距情况 if self.object.thickness is None and K_W == 0 and isParallel: # 物在无穷远且光线平行于光轴 h0 = K_eta * self.pupil.semi_dia L0, L1, U, I0, I1, PA = self._lens_recursion(0, 0, h0, raytype, True) else: # 其他情况 if self.object.thickness is None and K_W != 0: # 物在无穷远但非轴上点 U_val = -K_W * self.object.semi_dia * np.pi/180 L_val = self.pupil.thickness + eta / np.tan(U_val) elif self.object.semi_dia == 0: # 轴上点在有限远处 L_val = -self.object.thickness Lp = self.pupil.thickness U_val = np.arcsin(K_eta * A / np.sqrt(A**2 + Lp**2)) else: # 有限物距 Lp = self.pupil.thickness y = K_W * self.object.semi_dia U_val = np.arctan((y - eta) / (Lp + self.object.thickness)) L_val = Lp + eta / ((y - eta) / (Lp + self.object.thickness)) L0, L1, U, I0, I1, PA = self._lens_recursion(L_val, U_val, 0, raytype, False) return L0, L1, U, I0, I1, PA def _lens_recursion(self, L, U, H, raytype, isParallel): """ 光线追迹递归计算 (内部方法) """ L_data = [L] L1_data = [] U_data = [U] I_data = [] I1_data = [] PA_data = [] for i, surface in enumerate(self.lens): # 计算入射角 if i == 0 and isParallel: sinI = H / surface.radius else: sinI = (L - surface.radius) / surface.radius * np.sin(U) # 根据光线类型选择折射率 if raytype == 'd': n0, n1 = surface.nd0, surface.nd1 elif raytype == 'F': n0, n1 = surface.nF0, surface.nF1 else: # 'C' n0, n1 = surface.nC0, surface.nC1 # 计算折射角 sinII = n0 / n1 * sinI # 处理可能的数值误差 if abs(sinI) < 1 and abs(sinII) < 1: I_val = np.arcsin(sinI) II_val = np.arcsin(sinII) I_data.append(I_val) I1_data.append(II_val) else: I_val = None II_val = None # 计算折射后的角度 U_new = U + I_val - II_val if I_val is not None and II_val is not None else U L_new = surface.radius + surface.radius * sinII / np.sin(U_new) if II_val is not None else surface.radius L1_data.append(L_new) U_data.append(U_new) # PA校对法验证 if I_val is not None and II_val is not None and not isParallel: PA1 = L * np.sin(U) / np.cos((I_val - U)/2) PA2 = L_new * np.sin(U_new) / np.cos((II_val - U_new)/2) if abs(PA1 - PA2) > self.EPS: print(f"PA校验不通过: PA1={PA1:.6f}, PA2={PA2:.6f}") # 过渡到下一面 U = U_new L = L_new - surface.thickness # 记录物方截距 if i < len(self.lens) - 1: L_data.append(L) return L_data, L1_data, U_data, I_data, I1_data, PA_data def _astigmatism_recursion(self, L_data, L1_data, U_data, I_data, I1_data, PA_data, raytype, isParallel): """ 计算像散和场曲 (内部方法) """ lensnumber = len(self.lens) xt, xs, delta_x = 0, 0, 0 for i, surface in enumerate(self.lens): # 根据光线类型选择折射率 if raytype == 'd': n0, n1 = surface.nd0, surface.nd1 elif raytype == 'F': n0, n1 = surface.nF0, surface.nF1 else: # 'C' n0, n1 = surface.nC0, surface.nC1 # 计算表面弯曲 x = PA_data[i]**2 / (2 * surface.radius) if i < len(PA_data) else 0 # 初始化t和s值 if i == 0 and not isParallel: t = s = (L_data[0] - x) / np.cos(U_data[0]) elif i == 0 and isParallel: t = s = (L_data[1] - x) / np.cos(U_data[1]) # 计算子午和弧矢场曲 cosI = np.cos(I_data[i]) if i < len(I_data) else 1 cosII = np.cos(I1_data[i]) if i < len(I1_data) else 1 temp_tt = (n1 * cosII - n0 * cosI) / surface.radius + n0 * cosI**2 / t tt = (n1 * cosII**2) / temp_tt # t' temp_ss = (n1 * cosII - n0 * cosI) / surface.radius + n0 / s ss = n1 / temp_ss # s' # 过渡到下一面 if i < lensnumber - 1: next_x = PA_data[i+1]**2 / (2 * self.lens[i+1].radius) if i+1 < len(PA_data) else 0 D = (surface.thickness - x + next_x) / np.cos(U_data[i+1]) t = tt - D s = ss - D else: # 最后一面 lt = tt * np.cos(U_data[i+1]) + x ls = ss * np.cos(U_data[i+1]) + x xt = lt - L1_data[i] xs = ls - L1_data[i] delta_x = xt - xs return xt, xs, delta_x def process(self): """ 主计算函数,返回所有光学计算结果 返回: data1, data2, data3, data4, data5, data6: 六组计算结果 """ oo = 1e-20 # 近轴小量 # 计算焦距和主点位置 ff, l_H = self._calc_ff(oo) # 计算像距、像高、像散和场曲 ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x = self._calc_ll(oo, l_H, ff) # 计算出瞳距 l_p = self._calc_lp(oo) # 计算球差和位置色差 data2, data3, data4 = self._calc_sp(oo, ll) # 计算彗差、畸变和倍率色差 data5, data6 = self._calc_coma(oo, y0, y0_707) # 组装第一组数据 data1 = [ ff, ll, l_H, l_p, y0, y0_707, xt, xs, delta_x, ll_F, ll_C ] return data1, data2, data3, data4, data5, data6 def _calc_ff(self, oo): """计算像方焦距和主点位置""" # 创建一个近轴无穷远物面 object_ = Surface( self.object.radius, None, self.object.nd1, self.object.nF1, self.object.nC1, self.object.semi_dia ) # 计算平行入射的近轴光线 _, L1, _, _, _, _ = self.calc(oo, 0, 'd', True) # 计算焦距 ff = L1[-1] if L1 else 0 # 计算像方焦点和主点位置 l_F = L1[-1] if L1 else 0 l_H = ff - l_F return ff, l_H def _calc_ll(self, oo, l_H, ff): """计算像距、像高、像散和场曲""" # 计算物面入射的近轴光线 L0, L1, _, _, _, _ = self.calc(oo, 0, 'd') # 计算像距 if self.object.thickness is None: ll = ff - l_H else: ll = L1[-1] if L1 else 0 # 计算像高 if self.object.thickness is None: y0 = ff * np.tan(self.object.semi_dia * np.pi / 180) y0_707 = ff * np.tan(0.707 * self.object.semi_dia * np.pi / 180) else: y0 = self.object.semi_dia for i, surface in enumerate(self.lens): n0 = surface.nd0 n1 = surface.nd1 l0 = L0[i] if i < len(L0) else 0 l1 = L1[i] if i < len(L1) else 0 beta = (n0 * l1) / (n1 * l0) # 横向放大倍率 y0 *= beta y0_707 = 0.707 * y0 # 计算C光和F光的近轴像距 _, L1F, _, _, _, _ = self.calc(oo, 0, 'F') ll_F = L1F[-1] if L1F else 0 _, L1C, _, _, _, _ = self.calc(oo, 0, 'C') ll_C = L1C[-1] if L1C else 0 # 计算场曲及像散 L0_full, L1_full, U_full, I0_full, I1_full, PA_full = self.calc(0, 1, 'd') if self.object.thickness is None: L0_full[0] = 1e15 # 模拟无穷远 else: L0_full[0] = -self.object.thickness isParallel = self.object.thickness is None and self.object.semi_dia is None xt, xs, delta_x = self._astigmatism_recursion( L0_full, L1_full, U_full, I0_full, I1_full, PA_full, 'd', isParallel ) return ll, ll_F, ll_C, y0, y0_707, xt, xs, delta_x def _calc_lp(self, oo): """计算出瞳距""" # 定义入瞳面 object_ = Surface( self.pupil.radius, self.pupil.thickness, self.pupil.nd1, self.pupil.nC1, self.pupil.nF1, self.pupil.semi_dia ) _, L1, _, _, _, _ = self.calc(oo, 0, 'd') l_p = L1[-1] if L1 else 0 return l_p def _calc_sp(self, oo, ll): """计算球差和位置色差""" # 全孔径计算 _, L1d1, _, _, _, _ = self.calc(1, 0, 'd') ll_d1 = L1d1[-1] if L1d1 else 0 SPAB1 = ll_d1 - ll _, L1F1, _, _, _, _ = self.calc(1, 0, 'F') ll_F1 = L1F1[-1] if L1F1 else 0 _, L1C1, _, _, _, _ = self.calc(1, 0, 'C') ll_C1 = L1C1[-1] if L1C1 else 0 PCA1 = ll_F1 - ll_C1 # 0.707孔径计算 _, L1d707, _, _, _, _ = self.calc(0.707, 0, 'd') ll_d707 = L1d707[-1] if L1d707 else 0 SPAB707 = ll_d707 - ll _, L1F707, _, _, _, _ = self.calc(0.707, 0, 'F') ll_F707 = L1F707[-1] if L1F707 else 0 _, L1C707, _, _, _, _ = self.calc(0.707, 0, 'C') ll_C707 = L1C707[-1] if L1C707 else 0 PCA707 = ll_F707 - ll_C707 # 0孔径位置色差 _, L1F0, _, _, _, _ = self.calc(oo, 0, 'F') ll_F0 = L1F0[-1] if L1F0 else 0 _, L1C0, _, _, _, _ = self.calc(oo, 0, 'C') ll_C0 = L1C0[-1] if L1C0 else 0 PCA0 = ll_F0 - ll_C0 # 组装数据 data2 = [ll_d1, ll_F1, ll_C1, SPAB1, PCA1] data3 = [ll_d707, ll_F707, ll_C707, SPAB707, PCA707] data4 = [PCA0] return data2, data3, data4 def _calc_coma(self, oo, y0, y0_707): """计算彗差、畸变和倍率色差""" # 获取理想像面位置 _, L_perfect_d, _, _, _, _ = self.calc(oo, 0, 'd') ll_perfect = L_perfect_d[-1] if L_perfect_d else 0 # 计算彗差 coma_results = {} for field in [-1, -0.707]: for aperture in [1, 0.707, -1, -0.707]: key = f"{abs(field)}_{abs(aperture)}" _, L1, U, _, _, _ = self.calc(aperture, field, 'd') y = (ll_perfect - L1[-1]) * np.tan(U[-1]) if L1 and U else 0 coma_results[key] = y # 全视场彗差 COMA1 = (coma_results['1_1'] + coma_results['1_1']) / 2 - coma_results['1_0'] COMA2 = (coma_results['1_0.707'] + coma_results['1_0.707']) / 2 - coma_results['1_0'] # 0.707视场彗差 COMA3 = (coma_results['0.707_1'] + coma_results['0.707_1']) / 2 - coma_results['0.707_0'] COMA4 = (coma_results['0.707_0.707'] + coma_results['0.707_0.707']) / 2 - coma_results['0.707_0'] # 计算实际像高 _, L1_central_d, U_central_d, _, _, _ = self.calc(0, -1, 'd') y_d_1 = (ll_perfect - L1_central_d[-1]) * np.tan(U_central_d[-1]) if L1_central_d and U_central_d else 0 _, L1_central_F, U_central_F, _, _, _ = self.calc(0, -1, 'F') y_F_1 = (ll_perfect - L1_central_F[-1]) * np.tan(U_central_F[-1]) if L1_central_F and U_central_F else 0 _, L1_central_C, U_central_C, _, _, _ = self.calc(0, -1, 'C') y_C_1 = (ll_perfect - L1_central_C[-1]) * np.tan(U_central_C[-1]) if L1_central_C and U_central_C else 0 # 0.707视场实际像高 _, L1_central_d_07, U_central_d_07, _, _, _ = self.calc(0, -0.707, 'd') y_d_07 = (ll_perfect - L1_central_d_07[-1]) * np.tan(U_central_d_07[-1]) if L1_central_d_07 and U_central_d_07 else 0 _, L1_central_F_07, U_central_F_07, _, _, _ = self.calc(0, -0.707, 'F') y_F_07 = (ll_perfect - L1_central_F_07[-1]) * np.tan(U_central_F_07[-1]) if L1_central_F_07 and U_central_F_07 else 0 _, L1_central_C_07, U_central_C_07, _, _, _ = self.calc(0, -0.707, 'C') y_C_07 = (ll_perfect - L1_central_C_07[-1]) * np.tan(U_central_C_07[-1]) if L1_central_C_07 and U_central_C_07 else 0 # 倍率色差 MCA1 = y_F_1 - y_C_1 MCA07 = y_F_07 - y_C_07 # 畸变计算 DIS1 = y_d_1 - y0 rel_DIS1 = DIS1 / y0 if y0 != 0 else 0 DIS07 = y_d_07 - y0_707 rel_DIS07 = DIS07 / y0_707 if y0_707 != 0 else 0 # 组装数据 data5 = [ COMA1, COMA2, y_d_1, y_F_1, y_C_1, DIS1, rel_DIS1, MCA1 ] data6 = [ COMA3, COMA4, y_d_07, y_F_07, y_C_07, DIS07, rel_DIS07, MCA07 ] return data5, data6 ###上方为计算相关 ###下方为GUI相关 class MainApplication: def __init__(self, root): self.root = root self.root.title("光学系统计算程序") self.root.geometry("1000x850") # 创建光学系统对象 self.system = OpticalSystem() # 创建GUI组件 self.create_widgets() # 加载默认设置(如果有) self.load_default_settings() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 文件操作框架 file_frame = ttk.LabelFrame(main_frame, text="文件操作") file_frame.pack(fill=tk.X, padx=5, pady=5) # 文件路径输入 ttk.Label(file_frame, text="文件路径:").grid(row=0, column=0, padx=5, pady=5) self.file_path_entry = ttk.Entry(file_frame, width=50) self.file_path_entry.grid(row=0, column=1, padx=5, pady=5) # 文件操作按钮 browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file) browse_btn.grid(row=0, column=2, padx=5, pady=5) load_btn = ttk.Button(file_frame, text="加载系统参数", command=self.load_system) load_btn.grid(row=0, column=3, padx=5, pady=5) save_btn = ttk.Button(file_frame, text="保存系统参数", command=self.save_system) save_btn.grid(row=0, column=4, padx=5, pady=5) # 左侧面板 - 输入参数 left_frame = ttk.LabelFrame(main_frame, text="系统参数输入") left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧面板 - 结果展示 right_frame = ttk.LabelFrame(main_frame, text="计算结果") right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建左侧面板的子组件 self.create_input_panel(left_frame) # 创建右侧面板的子组件 self.create_result_panel(right_frame) # 计算按钮 calc_frame = ttk.Frame(main_frame) calc_frame.pack(fill=tk.X, padx=5, pady=10) calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.calculate, width=15) calc_btn.pack(side=tk.LEFT, padx=10) save_result_btn = ttk.Button(calc_frame, text="保存计算结果", command=self.save_results, width=15) save_result_btn.pack(side=tk.LEFT, padx=10) clear_btn = ttk.Button(calc_frame, text="清除所有", command=self.clear_all, width=15) clear_btn.pack(side=tk.LEFT, padx=10) def browse_file(self): """浏览文件按钮处理函数""" file_path = filedialog.askopenfilename( title="选择文件", filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")] ) if file_path: self.file_path_entry.delete(0, tk.END) self.file_path_entry.insert(0, file_path) def create_input_panel(self, parent): # 入瞳参数 pupil_frame = ttk.LabelFrame(parent, text="入瞳参数") pupil_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳直径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.entrance_diameter_entry = ttk.Entry(pupil_frame, width=15) self.entrance_diameter_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳位置 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.entrance_position_entry = ttk.Entry(pupil_frame, width=15) self.entrance_position_entry.grid(row=0, column=3, padx=5, pady=5) # 色光类型选择 ttk.Label(pupil_frame, text="色光类型:").grid(row=1, column=0, padx=5, pady=5, sticky="w") self.light_type_var = tk.StringVar(value="d") self.light_type_combo = ttk.Combobox(pupil_frame, textvariable=self.light_type_var, width=12, state="readonly") self.light_type_combo["values"] = ("d", "f", "c") self.light_type_combo.grid(row=1, column=1, padx=5, pady=5, sticky="w") # 物方参数 object_frame = ttk.LabelFrame(parent, text="物方参数") object_frame.pack(fill=tk.X, padx=5, pady=5) # 物距选择 self.object_var = tk.BooleanVar(value=True) # True: 无穷远, False: 有限远 ttk.Radiobutton(object_frame, text="物在无穷远", variable=self.object_var, value=True, command=self.toggle_object_input).grid(row=0, column=0, padx=5, pady=5) ttk.Radiobutton(object_frame, text="物在有限远", variable=self.object_var, value=False, command=self.toggle_object_input).grid(row=0, column=1, padx=5, pady=5) # 无穷远参数 self.infinite_frame = ttk.Frame(object_frame) self.infinite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) ttk.Label(self.infinite_frame, text="半视场角 (度):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.field_angle_entry = ttk.Entry(self.infinite_frame, width=15) self.field_angle_entry.grid(row=0, column=1, padx=5, pady=5) # 有限远参数 (初始隐藏) self.finite_frame = ttk.Frame(object_frame) self.finite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) self.finite_frame.grid_remove() # 初始隐藏 ttk.Label(self.finite_frame, text="物距 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.object_distance_entry = ttk.Entry(self.finite_frame, width=15) self.object_distance_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(self.finite_frame, text="物高 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.object_height_entry = ttk.Entry(self.finite_frame, width=15) self.object_height_entry.grid(row=0, column=3, padx=5, pady=5) ttk.Label(self.finite_frame, text="孔径角 (度):").grid(row=0, column=4, padx=5, pady=5, sticky="w") self.aperture_angle_entry = ttk.Entry(self.finite_frame, width=15) self.aperture_angle_entry.grid(row=0, column=5, padx=5, pady=5) # 光学表面输入 surface_frame = ttk.LabelFrame(parent, text="光学表面参数") surface_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 表面输入控件 input_frame = ttk.Frame(surface_frame) input_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5) self.radius_entry = ttk.Entry(input_frame, width=10) self.radius_entry.grid(row=0, column=1, padx=5, pady=5) self.radius_entry.insert(0, "50") ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5) self.thickness_entry = ttk.Entry(input_frame, width=10) self.thickness_entry.grid(row=0, column=3, padx=5, pady=5) self.thickness_entry.insert(0, "5") ttk.Label(input_frame, text="折射率 (nd):").grid(row=0, column=4, padx=5, pady=5) self.nd_entry = ttk.Entry(input_frame, width=10) self.nd_entry.grid(row=0, column=5, padx=5, pady=5) self.nd_entry.insert(0, "1.5") ttk.Label(input_frame, text="阿贝数 (vd):").grid(row=0, column=6, padx=5, pady=5) self.vd_entry = ttk.Entry(input_frame, width=10) self.vd_entry.grid(row=0, column=7, padx=5, pady=5) self.vd_entry.insert(0, "60") button_frame = ttk.Frame(input_frame) button_frame.grid(row=0, column=8, padx=10) add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surface) add_btn.pack(side=tk.LEFT, padx=5) remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surface) remove_btn.pack(side=tk.LEFT, padx=5) # 表面列表 list_frame = ttk.Frame(surface_frame) list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) columns = ("#", "曲率半径 (mm)", "厚度 (mm)", "折射率 (nd)", "阿贝数 (vd)") self.surface_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8) for col in columns: self.surface_tree.heading(col, text=col) self.surface_tree.column(col, width=100, anchor=tk.CENTER) vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.surface_tree.yview) self.surface_tree.configure(yscrollcommand=vsb.set) self.surface_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) vsb.pack(side=tk.RIGHT, fill=tk.Y) def create_result_panel(self, parent): # 结果文本框 self.result_text = tk.Text(parent, wrap=tk.WORD) result_scroll_y = ttk.Scrollbar(parent, orient="vertical", command=self.result_text.yview) result_scroll_x = ttk.Scrollbar(parent, orient="horizontal", command=self.result_text.xview) self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set) result_scroll_y.pack(side=tk.RIGHT, fill=tk.Y) result_scroll_x.pack(side=tk.BOTTOM, fill=tk.X) self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 设置初始文本 self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) def toggle_object_input(self): """切换物方参数输入界面""" if self.object_var.get(): # 无穷远 self.infinite_frame.grid() self.finite_frame.grid_remove() else: # 有限远 self.infinite_frame.grid_remove() self.finite_frame.grid() def add_surface(self): """添加光学表面""" try: # 获取输入值 r = self.radius_entry.get().strip() d = self.thickness_entry.get().strip() nd = self.nd_entry.get().strip() vd = self.vd_entry.get().strip() # 处理输入值 r = float(r) if r and r.lower() != "inf" else float('inf') d = float(d) if d else 0.0 nd = float(nd) if nd else 1.0 vd = float(vd) if vd else 0.0 # 添加到系统 surface = Surface(r, d, nd, vd) self.system.surfaces.append(surface) # 添加到树形视图 r_str = "平面" if r == float('inf') else f"{r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{d:.2f}", f"{nd:.4f}", f"{vd:.1f}" )) # 清空输入框 self.radius_entry.delete(0, tk.END) self.thickness_entry.delete(0, tk.END) self.nd_entry.delete(0, tk.END) self.vd_entry.delete(0, tk.END) self.radius_entry.focus_set() except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") def remove_surface(self): """删除选中的光学表面""" selected = self.surface_tree.selection() if selected: # 从树形视图中删除 for item in selected: index = int(self.surface_tree.item(item, "values")[0]) - 1 self.surface_tree.delete(item) # 从系统中删除 if 0 <= index < len(self.system.surfaces): self.system.surfaces.pop(index) # 更新剩余表面的序号 for i, item in enumerate(self.surface_tree.get_children()): values = list(self.surface_tree.item(item, "values")) values[0] = i + 1 self.surface_tree.item(item, values=values) def load_system(self): """加载系统参数文件""" file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入文件路径") return try: with open(file_path, 'r') as f: data = json.load(f) # 加载系统参数 self.system.entrance_pupil_diameter = data.get("entrance_pupil_diameter") self.system.entrance_pupil_position = data.get("entrance_pupil_position") self.system.object_infinite = data.get("object_infinite", True) self.system.field_angle = data.get("field_angle") self.system.object_distance = data.get("object_distance") self.system.object_height = data.get("object_height") self.system.aperture_angle = data.get("aperture_angle") # 更新UI中的参数值 if self.system.entrance_pupil_diameter is not None: self.entrance_diameter_entry.delete(0, tk.END) self.entrance_diameter_entry.insert(0, str(self.system.entrance_pupil_diameter)) if self.system.entrance_pupil_position is not None: self.entrance_position_entry.delete(0, tk.END) self.entrance_position_entry.insert(0, str(self.system.entrance_pupil_position)) self.object_var.set(self.system.object_infinite) self.toggle_object_input() if self.system.field_angle is not None: self.field_angle_entry.delete(0, tk.END) self.field_angle_entry.insert(0, str(self.system.field_angle)) if self.system.object_distance is not None: self.object_distance_entry.delete(0, tk.END) self.object_distance_entry.insert(0, str(self.system.object_distance)) if self.system.object_height is not None: self.object_height_entry.delete(0, tk.END) self.object_height_entry.insert(0, str(self.system.object_height)) if self.system.aperture_angle is not None: self.aperture_angle_entry.delete(0, tk.END) self.aperture_angle_entry.insert(0, str(self.system.aperture_angle)) # 加载表面数据 self.system.surfaces = [] self.surface_tree.delete(*self.surface_tree.get_children()) surfaces_data = data.get("surfaces", []) for surf_data in surfaces_data: surface = Surface.from_dict(surf_data) self.system.surfaces.append(surface) r_str = "平面" if surface.r == float('inf') else f"{surface.r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{surface.d:.2f}", f"{surface.nd:.4f}", f"{surface.vd:.1f}" )) messagebox.showinfo("成功", f"系统参数已从 {os.path.basename(file_path)} 加载") # 显示加载的系统信息 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, f"已加载系统参数文件: {file_path}\n") self.result_text.insert(tk.END, f"包含 {len(self.system.surfaces)} 个光学表面\n") self.result_text.configure(state=tk.DISABLED) except FileNotFoundError: messagebox.showerror("错误", f"文件不存在: {file_path}") except Exception as e: messagebox.showerror("加载错误", f"加载文件失败: {str(e)}") def save_system(self): """保存系统参数文件""" # 更新系统参数 if not self.update_system_from_ui(): return # 如果没有表面数据,提示用户 if not self.system.surfaces: messagebox.showwarning("警告", "没有光学表面数据,无法保存") return file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入保存路径") return try: # 准备保存数据 data = { "entrance_pupil_diameter": self.system.entrance_pupil_diameter, "entrance_pupil_position": self.system.entrance_pupil_position, "object_infinite": self.system.object_infinite, "field_angle": self.system.field_angle, "object_distance": self.system.object_distance, "object_height": self.system.object_height, "aperture_angle": self.system.aperture_angle, "surfaces": [surf.to_dict() for surf in self.system.surfaces] } with open(file_path, 'w') as f: json.dump(data, f, indent=4) messagebox.showinfo("成功", f"系统参数已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"系统参数已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存文件失败: {str(e)}") def save_results(self): """保存计算结果到文件""" if not self.system.results or all(v is None for v in self.system.results.values()): messagebox.showwarning("警告", "没有计算结果可保存") return file_path = filedialog.asksaveasfilename( title="保存计算结果", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if not file_path: return try: with open(file_path, 'w') as f: # 写入系统参数摘要 f.write("===== 光学系统参数 =====\n") f.write(f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") f.write(f"入瞳位置: {self.system.entrance_pupil_position} mm\n") f.write(f"色光类型: {self.system.light_type}\n") f.write("物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") if self.system.object_infinite: f.write(f"半视场角: {self.system.field_angle} 度\n") else: f.write(f"物距: {self.system.object_distance} mm\n") f.write(f"物高: {self.system.object_height} mm\n") f.write(f"孔径角: {self.system.aperture_angle} 度\n") f.write("\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" f.write(f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 写入计算结果 f.write("\n\n===== 计算结果 =====\n") for key, value in self.system.results.items(): if value is not None: # 格式化键名 label = self.format_result_label(key) f.write(f"{label}: {value}\n") messagebox.showinfo("成功", f"计算结果已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"计算结果已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存计算结果失败: {str(e)}") def format_result_label(self, key): """格式化结果标签为中文描述""" labels = { "focal_length": "焦距 f' (mm)", "ideal_image_distance": "理想像距 l' (mm)", "actual_image_position": "实际像位置 (mm)", "image_principal_plane": "像方主面位置 lH' (mm)", "exit_pupil_distance": "出瞳距 lp' (mm)", "ideal_image_height": "理想像高 y0' (mm)", "spherical_aberration": "球差 (mm)", "longitudinal_chromatic": "位置色差 (mm)", "tangential_field_curvature": "子午场曲 xt' (mm)", "sagittal_field_curvature": "弧矢场曲 xs' (mm)", "astigmatism": "像散 Δxts' (mm)", "actual_image_height": "实际像高 (mm)", "relative_distortion": "相对畸变 (%)", "absolute_distortion": "绝对畸变 (mm)", "lateral_chromatic": "倍率色差 (mm)", "tangential_coma": "子午慧差 (mm)" } return labels.get(key, key) def update_system_from_ui(self): """从UI更新系统参数""" try: # 入瞳参数 if self.entrance_diameter_entry.get(): self.system.entrance_pupil_diameter = float(self.entrance_diameter_entry.get()) if self.entrance_position_entry.get(): self.system.entrance_pupil_position = float(self.entrance_position_entry.get()) # 色光类型 self.system.light_type = self.light_type_var.get() # 物方参数 self.system.object_infinite = self.object_var.get() if self.system.object_infinite: if self.field_angle_entry.get(): self.system.field_angle = float(self.field_angle_entry.get()) else: if self.object_distance_entry.get(): self.system.object_distance = float(self.object_distance_entry.get()) if self.object_height_entry.get(): self.system.object_height = float(self.object_height_entry.get()) if self.aperture_angle_entry.get(): self.system.aperture_angle = float(self.aperture_angle_entry.get()) except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") return False return True def calculate(self): """执行光学计算""" # 更新系统参数 if not self.update_system_from_ui(): return # 检查必要参数 if not self.system.surfaces: messagebox.showwarning("警告", "请至少添加一个光学表面") return if self.system.entrance_pupil_diameter is None: messagebox.showwarning("警告", "请输入入瞳直径") return if self.system.object_infinite and self.system.field_angle is None: messagebox.showwarning("警告", "请输入半视场角") return if not self.system.object_infinite and ( self.system.object_distance is None or self.system.object_height is None or self.system.aperture_angle is None ): messagebox.showwarning("警告", "请输入完整的有限远参数") return # 执行计算 - 这里调用您的计算函数 self.perform_calculations() # 显示结果 self.display_results() # 显示计算完成消息 messagebox.showinfo("计算完成", "光学计算已完成,结果已显示在结果区域") def perform_calculations(self): """执行光学计算 - 这里调用您的实际计算函数""" # 重置结果 for key in self.system.results: self.system.results[key] = None # 示例:设置一些假结果用于演示 # 实际应用中,您需要调用您自己的计算函数 self.system.results["focal_length"] = Calculate.calculate_focal_length(self.system)[0] self.system.results["ideal_image_distance"] = 0#Calculate.calculate_ideal_image_distance(self.system) self.system.results["actual_image_position"] =0# Calculate.calculate_actual_image_position(self.system) self.system.results["image_principal_plane"] = Calculate.calculate_focal_length(self.system)[1] self.system.results["exit_pupil_distance"] = 0#Calculate.calculate_exit_pupil_distance(self.system) self.system.results["ideal_image_height"] =0# Calculate.calculate_ideal_image_height(self.system) self.system.results["spherical_aberration"] = 0#Calculate.calculate_spherical_aberration(self.system) self.system.results["longitudinal_chromatic"] = 0#Calculate.calculate_longitudinal_chromatic(self.system) self.system.results["tangential_field_curvature"] = 0.15 self.system.results["sagittal_field_curvature"] = 0.12 self.system.results["astigmatism"] = 0#Calculate.calculate_astigmatism(self.system) self.system.results["actual_image_height"] =0#Calculate.calculate_actual_image_height(self.system) self.system.results["relative_distortion"] = -0.5 self.system.results["absolute_distortion"] =0# Calculate.calculate_distortion(self.system) self.system.results["lateral_chromatic"] =0# Calculate.calculate_lateral_chromatic(self.system) self.system.results["tangential_coma"] =0# Calculate.calculate_tangential_coma(self.system) def display_results(self): """在结果文本框中显示计算结果""" self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) # 添加系统参数摘要 self.result_text.insert(tk.END, "===== 光学系统参数 =====\n", "title") self.result_text.insert(tk.END, f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") self.result_text.insert(tk.END, f"入瞳位置: {self.system.entrance_pupil_position} mm\n") self.result_text.insert(tk.END, "物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") self.result_text.insert(tk.END, f"色光类型: {self.system.light_type}\n") if self.system.object_infinite: self.result_text.insert(tk.END, f"半视场角: {self.system.field_angle} 度\n") else: self.result_text.insert(tk.END, f"物距: {self.system.object_distance} mm\n") self.result_text.insert(tk.END, f"物高: {self.system.object_height} mm\n") self.result_text.insert(tk.END, f"孔径角: {self.system.aperture_angle} 度\n") self.result_text.insert(tk.END, "\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" self.result_text.insert(tk.END, f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 添加计算结果 self.result_text.insert(tk.END, "\n\n===== 计算结果 =====\n", "title") # 计算关键结果 key_results = [ "focal_length", "ideal_image_distance", "actual_image_position", "image_principal_plane", "exit_pupil_distance", "ideal_image_height" ] for key in key_results: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") # 添加像差结果 self.result_text.insert(tk.END, "\n像差分析:\n", "subtitle") aberrations = [ "spherical_aberration", "longitudinal_chromatic", "tangential_field_curvature", "sagittal_field_curvature", "astigmatism", "actual_image_height", "relative_distortion", "absolute_distortion", "lateral_chromatic", "tangential_coma" ] for key in aberrations: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") self.result_text.configure(state=tk.DISABLED) def clear_all(self): """清除所有输入和结果""" # 清除系统参数 self.system = OpticalSystem() # 清除UI输入 self.file_path_entry.delete(0, tk.END) self.entrance_diameter_entry.delete(0, tk.END) self.entrance_position_entry.delete(0, tk.END) self.field_angle_entry.delete(0, tk.END) self.object_distance_entry.delete(0, tk.END) self.object_height_entry.delete(0, tk.END) self.aperture_angle_entry.delete(0, tk.END) self.radius_entry.delete(0, tk.END) self.radius_entry.insert(0, "50") self.thickness_entry.delete(0, tk.END) self.thickness_entry.insert(0, "5") self.nd_entry.delete(0, tk.END) self.nd_entry.insert(0, "1.5") self.vd_entry.delete(0, tk.END) self.vd_entry.insert(0, "60") # 清除表面列表 self.surface_tree.delete(*self.surface_tree.get_children()) self.system.surfaces = [] # 清除结果 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) # 重置物距类型 self.object_var.set(True) self.toggle_object_input() messagebox.showinfo("清除完成", "所有输入和结果已清除") def load_default_settings(self): """加载默认设置(可选)""" # 这里可以添加加载默认设置的逻辑 pass if __name__ == "__main__": root = tk.Tk() app = MainApplication(root) # 设置文本样式 app.result_text.tag_config("title", font=("Arial", 10, "bold")) app.result_text.tag_config("subtitle", font=("Arial", 9, "bold")) root.mainloop() 整理成一个光学计算程序,因为这是两个人写的代码,所以都可以重编,但要保证能够顺利计算并显示结果,参数输入等都可以添加或删除
06-27
from re import A import tkinter as tk from tkinter import ttk, messagebox, filedialog import json import os import math import numpy as np class OpticalSystem: def __init__(self): self.surfaces = [] # 存储光学表面对象 self.entrance_pupil_diameter = None # 入瞳直径 (mm) self.entrance_pupil_position = None # 入瞳位置 (相对第一个面顶点) (mm) self.object_infinite = True # 物在无穷远 self.field_angle = None # 半视场角 (度) self.object_distance = None # 物距 (有限远) (mm) self.object_height = None # 物高 (有限远) (mm) self.aperture_angle = None # 孔径角 (有限远) (度) self.light_type = "d" # 色光类型,默认d光 self.aperture_coefficient = 1.0 # 孔径系数 self.field_coefficient = 1.0 # 视场系数 # 计算结果存储 self.results = { "focal_length": None, # 焦距 f' "ideal_image_distance": None, # 理想像距 l' "actual_image_position": None, # 实际像位置 "image_principal_plane": None, # 像方主面位置 lH' "exit_pupil_distance": None, # 出瞳距 lp' "ideal_image_height": None, # 理想像高 y0' "spherical_aberration": None, # 球差 "longitudinal_chromatic": None, # 位置色差 "tangential_field_curvature": None, # 子午场曲 xt' "sagittal_field_curvature": None, # 弧矢场曲 xs' "astigmatism": None, # 像散 Δxts' "actual_image_height": None, # 实际像高 "relative_distortion": None, # 相对畸变 "absolute_distortion": None, # 绝对畸变 "lateral_chromatic": None, # 倍率色差 "tangential_coma": None # 子午慧差 } class Surface: def __init__(self, r=float('inf'), d=0.0, nd=1.0, vd=0.0): self.r = r # 曲率半径 (mm) self.d = d # 厚度/间隔 (mm) self.nd = nd # d光折射率 self.vd = vd # 阿贝数 def to_dict(self): """将表面数据转换为字典""" return { "r": self.r, "d": self.d, "nd": self.nd, "vd": self.vd } @classmethod def from_dict(cls, data): """从字典创建表面对象""" return cls( r=data.get('r', float('inf')), d=data.get('d', 0.0), nd=data.get('nd', 1.0), vd=data.get('vd', 0.0) ) class calculate: def trace_paraxial_ray(surfaces, h0, u0, n0=1.0): """ 近轴光线追迹函数 :param surfaces: 光学表面列表 :param h0: 初始光线高度 :param u0: 初始光线角度(弧度) :param n0: 初始介质折射率 :return: 最后的光线高度和角度 """ h = h0 u = u0 n = n0 # 当前介质折射率 for i, surf in enumerate(surfaces): # 计算曲率(平面时曲率为0) c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r # 折射公式:n'u' = nu + (n - n') * c * h n_prime = surf.nd # 折射后折射率 u_prime = (n * u + (n - n_prime) * c * h) / n_prime # 如果当前不是最后一个面,计算传播到下个面的高度 if i < len(surfaces) - 1: d = surf.d # 到下一个面的距离 h_next = h + d * u_prime else: h_next = h # 最后一个面后不需要传播 # 更新参数 h = h_next u = u_prime n = n_prime # 更新为当前面后的折射率 return h, u def calculate_focal_length(surfaces): """ 计算系统焦距 :param surfaces: 光学表面列表 :return: 焦距值(mm) """ # 追迹平行于光轴的光线(无穷远物) h0 = 1.0 # 任意高度,取1便于计算 u0 = 0.0 # 平行于光轴 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 焦距 f' = -h0 / u_final if abs(u_final) < 1e-10: # 防止除零错误 return float('inf') return -h0 / u_final def calculate_ideal_image_distance(surfaces, object_infinite, object_distance=None): """ 计算理想像距 :param surfaces: 光学表面列表 :param object_infinite: 物是否在无穷远 :param object_distance: 物距(有限远时) :return: 理想像距(mm) """ if object_infinite: # 无穷远物:理想像距等于焦距对应的像距 return calculate.calculate_focal_length(surfaces)+calculate.calculate_principal_plane_position(surfaces) else: # 有限远物:使用高斯公式计算理想像距 if object_distance is None: raise ValueError("有限远物需要提供物距") # 计算系统焦距 f_prime = calculate.calculate_focal_length(surfaces) # 高斯公式:1/f' = 1/l' - 1/l # 其中 l 为物距(负值),l' 为像距 l = -object_distance # 物距为负值(物在左侧) # 计算像距 l' if abs(f_prime) < 1e-10: return float('inf') l_prime = 1 / (1/f_prime + 1/l) l_prime = l_prime+calculate.calculate_principal_plane_position(surfaces) return l_prime def calculate_principal_plane_position(surfaces): """ 计算像方主面位置 :param surfaces: 光学表面列表 :return: 主面位置(相对于最后一个面顶点,正值表示在右侧) """ # 计算焦距 f_prime = calculate.calculate_focal_length(surfaces) # 追迹平行于光轴的光线 h0 = 1.0 # 初始高度 u0 = 0.0 # 平行于光轴 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 主面位置 lH' = -h_final / u_final - f_prime if abs(u_final) < 1e-10: return float('inf') return -h_final / u_final - f_prime def calculate_exit_pupil_distance(surfaces, entrance_pupil_position): """ 计算系统的出瞳距 :param surfaces: 光学表面列表 :param entrance_pupil_position: 入瞳位置(相对于第一个面顶点) :return: 出瞳距(相对于最后一个面顶点) """ # 定义初始光线参数 u0 = 0.01 # 小角度(弧度) # 计算第一个面的光线高度 h0 = -entrance_pupil_position * u0 # 进行近轴光线追迹 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 计算出瞳距:l' = -h_final / u_final if abs(u_final) < 1e-10: # 防止除零错误 return float('inf') return -h_final / u_final def calculate_ideal_image_height(system): """ 计算理想像高 :param system: OpticalSystem对象 :return: 理想像高(mm) """ # 获取系统焦距 focal_length = calculate.calculate_focal_length(system.surfaces) if system.object_infinite: # 无穷远物 # 理想像高 y0' = -f' * tan(ω) field_angle_rad = math.radians(system.field_angle) return -focal_length * math.tan(field_angle_rad) else: # 有限远物 # 获取理想像距 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 获取主面位置 principal_plane = calculate.calculate_principal_plane_position(system.surfaces) # 计算实际像位置(从最后一个面顶点) actual_image_position = ideal_image_distance + principal_plane # 计算放大率 β = l'/l # l = -object_distance(物在左侧为负) magnification = actual_image_position / (-system.object_distance) # 理想像高 y0' = β * y return magnification * system.object_height def trace_real_ray(surfaces, h0, u0, l0,light_type): """ 实际光线追迹函数 :param surfaces: 光学表面列表 :param h0: 初始光线高度 :param u0: 初始光线角度(弧度) :param l0: 初始光线距离(mm) :return: 最后的光线距离和角度 """ h = h0 u = u0 L = l0 # 当前光线距离 npre = 1 LL = [] PA =[] LI =[] LR =[] LW = [L] UU = [u] #初始化内部变量 si = 0 sii = 0 uu = 0 for i, surf in enumerate(surfaces): # 选择色光折射率 if light_type == 'd': n_surf = surf.nd elif light_type == 'f': n_surf = surf.nd + (surf.nd - 1) / (2 * surf.vd) elif light_type == 'c': n_surf = surf.nd - (surf.nd - 1) / (3 * surf.vd) else: n_surf = surf.nd # 默认d光 # 计算曲率(平面时曲率为0) c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r if(i == 0 and u==0.0): si = h/surf.r else: si = (L-surf.r)*np.sin(u)/surf.r sii = npre*si/n_surf uu = u +np.arcsin(si)-np.arcsin(sii) L = surf.r+surf.r*sii/np.sin(uu) PA .append( L * np.sin(uu) / np.cos((np.arcsin(sii)-uu)/2)) LL.append(L) UU.append(uu) LI.append(np.arcsin(si)) LR.append(np.arcsin(sii)) #传递到下一个表面 if i < len(surfaces) - 1: u = uu L = L -surf.d npre = n_surf # 折射后折射率 LW.append(L) return LW, LL,UU,LI,LR,PA def calculate_actual_image(system,light_type): """ 计算轴上点的实际像位置(相对于最后一个面顶点) 参数: system: OpticalSystem对象,包含: - surfaces: 表面列表 - entrance_pupil_diameter: 入瞳直径 (mm) - entrance_pupil_position: 入瞳位置 (相对第一个面顶点) (mm) - aperture_coefficient: 孔径系数 (0~1) - object_infinite: 物是否在无穷远 - object_distance: 物距 (有限远时) (mm) - light_type: 色光类型 (这里只影响折射率选择) 返回: 实际像位置 (mm) (正值表示在最后一个面右侧) """ # 1. 计算实际入瞳半径 actual_entrance_radius = (system.entrance_pupil_diameter * system.aperture_coefficient) / 2 # 2. 根据物方情况设置初始光线参数 if system.object_infinite: # 无穷远物:光线平行于光轴 u0 = 0.0 # 初始角度 (弧度) h0 = actual_entrance_radius # 初始高度 (取入瞳边缘) l0 = 0 # 物方截距 (无穷远) L_final = calculate.trace_real_ray(system.surfaces, h0, u0, l0,light_type)[1] else: # 有限远物 # 计算物点到入瞳平面的距离 d_object_to_entrance = system.object_distance - system.entrance_pupil_position # 计算初始角度 (物点发出的光线通过入瞳边缘) u0 = math.atan(actual_entrance_radius/ d_object_to_entrance) l0 = system.object_distance # 负号表示在左侧 h0 = 0 L_final= calculate.trace_real_ray(system.surfaces, h0, u0, l0,light_type)[1] return L_final[-1] def calculate_spherical_aberration(system): # 计算实际像位置 actual_image_position = calculate.calculate_actual_image(system,system.light_type) # 计算理想像距 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 计算球差 return actual_image_position - ideal_image_distance def calculate_longitudinal_chromatic(system): #计算位置色差 #F光 imageF = calculate.calculate_actual_image(system,'f') #C光 imageC = calculate.calculate_actual_image(system,'c') return imageF -imageC def calculate_actual_image_height(system,light_type): """ 计算实际像高(主光线在像平面上的高度) 参数: system: OpticalSystem对象 返回: 实际像高 (mm) """ # 1. 计算像面位置(相对于最后一个面顶点) l_prime = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 2. 确定主光线初始参数 if system.object_infinite: # 无限远物 omega_rad = math.radians(system.field_angle) u0 = omega_rad # 主光线角度 h0 = 0 L0 = system.entrance_pupil_position else: # 有限远物 d_obj = system.object_distance - system.entrance_pupil_position u0 = math.atan(system.object_height / d_obj) d0 = -system.entrance_pupil_position h0 = d0 * math.tan(u0) L0 = system.entrance_pupil_position # 3. 追迹主光线 L_last = calculate.trace_real_ray(system.surfaces, h0, u0, L0,light_type)[1] u_last = calculate.trace_real_ray(system.surfaces, h0, u0, L0,light_type)[2] # 4. 计算实际像高 actual_image_height = (L_last[-1] - l_prime) * math.tan(u_last[-1]) return actual_image_height def astigmatism(system,light_type): # . 确定主光线初始参数 if system.object_infinite: # 无限远物 omega_rad = math.radians(system.field_angle) u0 = omega_rad # 主光线角度 h0 = 0 L0 = system.entrance_pupil_position else: # 有限远物 d_obj = system.object_distance - system.entrance_pupil_position u0 = math.atan(system.object_height / d_obj) d0 = -system.entrance_pupil_position h0 = d0 * math.tan(u0) L0 = system.entrance_pupil_position # . 追迹主光线 LW,LL,UU,LI,LR,PA = calculate.trace_real_ray(system.surfaces, h0, u0, L0,light_type) #递推 npre = 1 for i, surf in enumerate(system.surfaces): x = PA[i] ** 2 / (2 * surf.r) #求t、s初值 if(i == 0 and system.object_infinite): t = s = (1e15-x ) / np.cos(UU[0]) else: t = s = (LW[0] - x) / np.cos(UU[0]) ##if(i == 1 and UU[0] == 0): ##t = s = (LW[1] - x) / np.cos(UU[1]) temp_tt = (surf.nd * np.cos(LR[i]) - npre * np.cos(LI[i]))/ surf.r + npre * np.cos(LI[i]) ** 2 / t tt = 1 / temp_tt * surf.nd * np.cos(LR[i]) ** 2 #t' temp_ss = (surf.nd * np.cos(LR[i]) - npre* np.cos(LI[i]))/ surf.r + npre / s ss = 1 / temp_ss * surf.nd #s' if(i < len(system.surfaces)-1): #过渡公式 xx = PA[i + 1] ** 2 / (2 * system.surfaces[i+1].r) #x(i+1) D = (surf.d - x + xx) / np.cos(UU[i+1]) t = tt - D s = ss - D npre = surf.nd #折射率更新 if(i == len(system.surfaces)-1): #最后一面 lt = tt * np.cos(UU[i+1]) + x ls = ss * np.cos(UU[i+1]) + x xt = lt - LL[-1] xs = ls - LL[-1] delta_x = xt-xs return xt,xs,delta_x def calculate_astigmatism(system, light_type): # 1. 获取主光线参数 if system.object_infinite: omega_rad = math.radians(system.field_angle) u0 = omega_rad h0 = 0 L0 = system.entrance_pupil_position else: d_obj = system.object_distance - system.entrance_pupil_position u0 = math.atan(system.object_height / d_obj) h0 = -system.entrance_pupil_position * math.tan(u0) L0 = system.entrance_pupil_position # 2. 追迹主光线获取入射/折射角 _, _, UU, LI, LR, PA = calculate.trace_real_ray(system.surfaces, h0, u0, L0, light_type) # 3. 初始化子午和弧矢截距 n = 1.0 # 初始介质折射率(空气) t = s = 0.0 # 根据物方条件设置初始截距 if system.object_infinite: t = s = float('inf') else: t = s = -system.object_distance # 物在左侧为负 # 4. 逐面计算 for i, surf in enumerate(system.surfaces): # 获取当前面折射率 if light_type == 'd': n_prime = surf.nd elif light_type == 'f': n_prime = surf.nd + (surf.nd - 1) / (2 * surf.vd) elif light_type == 'c': n_prime = surf.nd - (surf.nd - 1) / (3 * surf.vd) else: n_prime = surf.nd # 计算曲率 c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r # 获取主光线角度 U = UU[i] I = LI[i] I_prime = LR[i] # Coddington方程计算新截距 # 子午光线 if math.isinf(t): num_t = n_prime * math.cos(I_prime)**2 den_t = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c t_prime = num_t / den_t if den_t != 0 else float('inf') else: term_t = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c t_prime = n_prime * math.cos(I_prime)**2 / (term_t + n * math.cos(I)**2 / t) # 弧矢光线 if math.isinf(s): num_s = n_prime den_s = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c s_prime = num_s / den_s if den_s != 0 else float('inf') else: term_s = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c s_prime = n_prime / (term_s + n / s) # 5. 面间过渡(最后一面前) if i < len(system.surfaces) - 1: # 计算沿主光线方向的实际距离 d_eff = surf.d / math.cos(U) # 更新截距 t = t_prime - d_eff s = s_prime - d_eff n = n_prime # 更新折射率 # 6. 计算理想像距作为参考 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 7. 计算场曲和像散 # 子午场曲 = 子午焦点位置 - 理想像面位置 xt_prime = t_prime - ideal_image_distance # 弧矢场曲 = 弧矢焦点位置 - 理想像面位置 xs_prime = s_prime - ideal_image_distance # 像散 = 子午场曲 - 弧矢场曲 delta_xts = xt_prime - xs_prime return xt_prime, xs_prime, delta_xts class MainApplication: def __init__(self, root): self.root = root self.root.title("光学系统计算程序") self.root.geometry("1000x850") # 创建光学系统对象 self.system = OpticalSystem() # 创建GUI组件 self.create_widgets() # 加载默认设置(如果有) self.load_default_settings() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 文件操作框架 file_frame = ttk.LabelFrame(main_frame, text="文件操作") file_frame.pack(fill=tk.X, padx=5, pady=5) # 文件路径输入 ttk.Label(file_frame, text="文件路径:").grid(row=0, column=0, padx=5, pady=5) self.file_path_entry = ttk.Entry(file_frame, width=50) self.file_path_entry.grid(row=0, column=1, padx=5, pady=5) # 文件操作按钮 browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file) browse_btn.grid(row=0, column=2, padx=5, pady=5) load_btn = ttk.Button(file_frame, text="加载系统参数", command=self.load_system) load_btn.grid(row=0, column=3, padx=5, pady=5) save_btn = ttk.Button(file_frame, text="保存系统参数", command=self.save_system) save_btn.grid(row=0, column=4, padx=5, pady=5) # 左侧面板 - 输入参数 left_frame = ttk.LabelFrame(main_frame, text="系统参数输入") left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧面板 - 结果展示 right_frame = ttk.LabelFrame(main_frame, text="计算结果") right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建左侧面板的子组件 self.create_input_panel(left_frame) # 创建右侧面板的子组件 self.create_result_panel(right_frame) # 计算按钮 calc_frame = ttk.Frame(main_frame) calc_frame.pack(fill=tk.X, padx=5, pady=10) calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.calculate, width=15) calc_btn.pack(side=tk.LEFT, padx=10) save_result_btn = ttk.Button(calc_frame, text="保存计算结果", command=self.save_results, width=15) save_result_btn.pack(side=tk.LEFT, padx=10) clear_btn = ttk.Button(calc_frame, text="清除所有", command=self.clear_all, width=15) clear_btn.pack(side=tk.LEFT, padx=10) def browse_file(self): """浏览文件按钮处理函数""" file_path = filedialog.askopenfilename( title="选择文件", filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")] ) if file_path: self.file_path_entry.delete(0, tk.END) self.file_path_entry.insert(0, file_path) def create_input_panel(self, parent): # 入瞳参数 pupil_frame = ttk.LabelFrame(parent, text="入瞳参数") pupil_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳直径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.entrance_diameter_entry = ttk.Entry(pupil_frame, width=15) self.entrance_diameter_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳位置 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.entrance_position_entry = ttk.Entry(pupil_frame, width=15) self.entrance_position_entry.grid(row=0, column=3, padx=5, pady=5) # 色光类型选择 ttk.Label(pupil_frame, text="色光类型:").grid(row=1, column=0, padx=5, pady=5, sticky="w") self.light_type_var = tk.StringVar(value="d") self.light_type_combo = ttk.Combobox(pupil_frame, textvariable=self.light_type_var, width=12, state="readonly") self.light_type_combo["values"] = ("d", "f", "c") self.light_type_combo.grid(row=1, column=1, padx=5, pady=5, sticky="w") # 添加孔径系数和视场系数输入 ttk.Label(pupil_frame, text="孔径系数:").grid(row=1, column=2, padx=5, pady=5, sticky="w") self.aperture_coeff_entry = ttk.Entry(pupil_frame, width=8) self.aperture_coeff_entry.insert(0, "1.0") self.aperture_coeff_entry.grid(row=1, column=3, padx=5, pady=5) ttk.Label(pupil_frame, text="视场系数:").grid(row=1, column=4, padx=5, pady=5, sticky="w") self.field_coeff_entry = ttk.Entry(pupil_frame, width=8) self.field_coeff_entry.insert(0, "1.0") self.field_coeff_entry.grid(row=1, column=5, padx=5, pady=5) # 物方参数 object_frame = ttk.LabelFrame(parent, text="物方参数") object_frame.pack(fill=tk.X, padx=5, pady=5) # 物距选择 self.object_var = tk.BooleanVar(value=True) # True: 无穷远, False: 有限远 ttk.Radiobutton(object_frame, text="物在无穷远", variable=self.object_var, value=True, command=self.toggle_object_input).grid(row=0, column=0, padx=5, pady=5) ttk.Radiobutton(object_frame, text="物在有限远", variable=self.object_var, value=False, command=self.toggle_object_input).grid(row=0, column=1, padx=5, pady=5) # 无穷远参数 self.infinite_frame = ttk.Frame(object_frame) self.infinite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) ttk.Label(self.infinite_frame, text="半视场角 (度):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.field_angle_entry = ttk.Entry(self.infinite_frame, width=15) self.field_angle_entry.grid(row=0, column=1, padx=5, pady=5) # 有限远参数 (初始隐藏) self.finite_frame = ttk.Frame(object_frame) self.finite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) self.finite_frame.grid_remove() # 初始隐藏 ttk.Label(self.finite_frame, text="物距 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.object_distance_entry = ttk.Entry(self.finite_frame, width=15) self.object_distance_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(self.finite_frame, text="物高 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.object_height_entry = ttk.Entry(self.finite_frame, width=15) self.object_height_entry.grid(row=0, column=3, padx=5, pady=5) ttk.Label(self.finite_frame, text="孔径角 (度):").grid(row=0, column=4, padx=5, pady=5, sticky="w") self.aperture_angle_entry = ttk.Entry(self.finite_frame, width=15) self.aperture_angle_entry.grid(row=0, column=5, padx=5, pady=5) # 光学表面输入 surface_frame = ttk.LabelFrame(parent, text="光学表面参数") surface_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 表面输入控件 input_frame = ttk.Frame(surface_frame) input_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5) self.radius_entry = ttk.Entry(input_frame, width=10) self.radius_entry.grid(row=0, column=1, padx=5, pady=5) self.radius_entry.insert(0, "50") ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5) self.thickness_entry = ttk.Entry(input_frame, width=10) self.thickness_entry.grid(row=0, column=3, padx=5, pady=5) self.thickness_entry.insert(0, "5") ttk.Label(input_frame, text="折射率 (nd):").grid(row=0, column=4, padx=5, pady=5) self.nd_entry = ttk.Entry(input_frame, width=10) self.nd_entry.grid(row=0, column=5, padx=5, pady=5) self.nd_entry.insert(0, "1.5") ttk.Label(input_frame, text="阿贝数 (vd):").grid(row=0, column=6, padx=5, pady=5) self.vd_entry = ttk.Entry(input_frame, width=10) self.vd_entry.grid(row=0, column=7, padx=5, pady=5) self.vd_entry.insert(0, "60") button_frame = ttk.Frame(input_frame) button_frame.grid(row=0, column=8, padx=10) add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surface) add_btn.pack(side=tk.LEFT, padx=5) remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surface) remove_btn.pack(side=tk.LEFT, padx=5) # 表面列表 list_frame = ttk.Frame(surface_frame) list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) columns = ("#", "曲率半径 (mm)", "厚度 (mm)", "折射率 (nd)", "阿贝数 (vd)") self.surface_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8) for col in columns: self.surface_tree.heading(col, text=col) self.surface_tree.column(col, width=100, anchor=tk.CENTER) vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.surface_tree.yview) self.surface_tree.configure(yscrollcommand=vsb.set) self.surface_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) vsb.pack(side=tk.RIGHT, fill=tk.Y) def create_result_panel(self, parent): # 结果文本框 self.result_text = tk.Text(parent, wrap=tk.WORD) result_scroll_y = ttk.Scrollbar(parent, orient="vertical", command=self.result_text.yview) result_scroll_x = ttk.Scrollbar(parent, orient="horizontal", command=self.result_text.xview) self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set) result_scroll_y.pack(side=tk.RIGHT, fill=tk.Y) result_scroll_x.pack(side=tk.BOTTOM, fill=tk.X) self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 设置初始文本 self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) def toggle_object_input(self): """切换物方参数输入界面""" if self.object_var.get(): # 无穷远 self.infinite_frame.grid() self.finite_frame.grid_remove() else: # 有限远 self.infinite_frame.grid_remove() self.finite_frame.grid() def add_surface(self): """添加光学表面""" try: # 获取输入值 r = self.radius_entry.get().strip() d = self.thickness_entry.get().strip() nd = self.nd_entry.get().strip() vd = self.vd_entry.get().strip() # 处理输入值 r = float(r) if r and r.lower() != "inf" else float('inf') d = float(d) if d else 0.0 nd = float(nd) if nd else 1.0 vd = float(vd) if vd else 0.0 # 添加到系统 surface = Surface(r, d, nd, vd) self.system.surfaces.append(surface) # 添加到树形视图 r_str = "平面" if r == float('inf') else f"{r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{d:.2f}", f"{nd:.8f}", f"{vd:.8f}" )) # 清空输入框 self.radius_entry.delete(0, tk.END) self.thickness_entry.delete(0, tk.END) self.nd_entry.delete(0, tk.END) self.vd_entry.delete(0, tk.END) self.radius_entry.focus_set() except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") def remove_surface(self): """删除选中的光学表面""" selected = self.surface_tree.selection() if selected: # 从树形视图中删除 for item in selected: index = int(self.surface_tree.item(item, "values")[0]) - 1 self.surface_tree.delete(item) # 从系统中删除 if 0 <= index < len(self.system.surfaces): self.system.surfaces.pop(index) # 更新剩余表面的序号 for i, item in enumerate(self.surface_tree.get_children()): values = list(self.surface_tree.item(item, "values")) values[0] = i + 1 self.surface_tree.item(item, values=values) def load_system(self): """加载系统参数文件""" file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入文件路径") return try: with open(file_path, 'r') as f: data = json.load(f) # 加载系统参数 self.system.entrance_pupil_diameter = data.get("entrance_pupil_diameter") self.system.entrance_pupil_position = data.get("entrance_pupil_position") self.system.object_infinite = data.get("object_infinite", True) self.system.field_angle = data.get("field_angle") self.system.object_distance = data.get("object_distance") self.system.object_height = data.get("object_height") self.system.aperture_angle = data.get("aperture_angle") self.system.aperture_coefficient = data.get("aperture_coefficient", 1.0) self.system.field_coefficient = data.get("field_coefficient", 1.0) # 更新UI中的参数值 if self.system.entrance_pupil_diameter is not None: self.entrance_diameter_entry.delete(0, tk.END) self.entrance_diameter_entry.insert(0, str(self.system.entrance_pupil_diameter)) if self.system.entrance_pupil_position is not None: self.entrance_position_entry.delete(0, tk.END) self.entrance_position_entry.insert(0, str(self.system.entrance_pupil_position)) self.object_var.set(self.system.object_infinite) self.toggle_object_input() if self.system.field_angle is not None: self.field_angle_entry.delete(0, tk.END) self.field_angle_entry.insert(0, str(self.system.field_angle)) if self.system.object_distance is not None: self.object_distance_entry.delete(0, tk.END) self.object_distance_entry.insert(0, str(self.system.object_distance)) if self.system.object_height is not None: self.object_height_entry.delete(0, tk.END) self.object_height_entry.insert(0, str(self.system.object_height)) if self.system.aperture_angle is not None: self.aperture_angle_entry.delete(0, tk.END) self.aperture_angle_entry.insert(0, str(self.system.aperture_angle)) if self.aperture_coeff_entry.get(): self.system.aperture_coefficient = float(self.aperture_coeff_entry.get()) if self.field_coeff_entry.get(): self.system.field_coefficient = float(self.field_coeff_entry.get()) # 加载表面数据 self.system.surfaces = [] self.surface_tree.delete(*self.surface_tree.get_children()) surfaces_data = data.get("surfaces", []) for surf_data in surfaces_data: surface = Surface.from_dict(surf_data) self.system.surfaces.append(surface) r_str = "平面" if surface.r == float('inf') else f"{surface.r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{surface.d:.2f}", f"{surface.nd:.4f}", f"{surface.vd:.1f}" )) messagebox.showinfo("成功", f"系统参数已从 {os.path.basename(file_path)} 加载") # 显示加载的系统信息 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, f"已加载系统参数文件: {file_path}\n") self.result_text.insert(tk.END, f"包含 {len(self.system.surfaces)} 个光学表面\n") self.result_text.configure(state=tk.DISABLED) except FileNotFoundError: messagebox.showerror("错误", f"文件不存在: {file_path}") except Exception as e: messagebox.showerror("加载错误", f"加载文件失败: {str(e)}") def save_system(self): """保存系统参数文件""" # 更新系统参数 if not self.update_system_from_ui(): return # 如果没有表面数据,提示用户 if not self.system.surfaces: messagebox.showwarning("警告", "没有光学表面数据,无法保存") return file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入保存路径") return try: # 准备保存数据 data = { "entrance_pupil_diameter": self.system.entrance_pupil_diameter, "entrance_pupil_position": self.system.entrance_pupil_position, "object_infinite": self.system.object_infinite, "field_angle": self.system.field_angle, "object_distance": self.system.object_distance, "object_height": self.system.object_height, "aperture_angle": self.system.aperture_angle, "surfaces": [surf.to_dict() for surf in self.system.surfaces], "aperture_coefficient": self.system.aperture_coefficient, "field_coefficient": self.system.field_coefficient, } with open(file_path, 'w') as f: json.dump(data, f, indent=4) messagebox.showinfo("成功", f"系统参数已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"系统参数已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存文件失败: {str(e)}") def save_results(self): """保存计算结果到文件""" if not self.system.results or all(v is None for v in self.system.results.values()): messagebox.showwarning("警告", "没有计算结果可保存") return file_path = filedialog.asksaveasfilename( title="保存计算结果", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if not file_path: return try: with open(file_path, 'w') as f: # 写入系统参数摘要 f.write("===== 光学系统参数 =====\n") f.write(f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") f.write(f"入瞳位置: {self.system.entrance_pupil_position} mm\n") f.write(f"色光类型: {self.system.light_type}\n") f.write("物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") if self.system.object_infinite: f.write(f"半视场角: {self.system.field_angle} 度\n") else: f.write(f"物距: {self.system.object_distance} mm\n") f.write(f"物高: {self.system.object_height} mm\n") f.write(f"孔径角: {self.system.aperture_angle} 度\n") f.write("\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" f.write(f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 写入计算结果 f.write("\n\n===== 计算结果 =====\n") for key, value in self.system.results.items(): if value is not None: # 格式化键名 label = self.format_result_label(key) f.write(f"{label}: {value}\n") messagebox.showinfo("成功", f"计算结果已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"计算结果已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存计算结果失败: {str(e)}") def format_result_label(self, key): """格式化结果标签为中文描述""" labels = { "focal_length": "焦距 f' (mm)", "ideal_image_distance": "理想像距 l' (mm)", "actual_image_position": "实际像位置 (mm)", "image_principal_plane": "像方主面位置 lH' (mm)", "exit_pupil_distance": "出瞳距 lp' (mm)", "ideal_image_height": "理想像高 y0' (mm)", "spherical_aberration": "球差 (mm)", "longitudinal_chromatic": "位置色差 (mm)", "tangential_field_curvature": "子午场曲 xt' (mm)", "sagittal_field_curvature": "弧矢场曲 xs' (mm)", "astigmatism": "像散 Δxts' (mm)", "actual_image_height": "实际像高 (mm)", "relative_distortion": "相对畸变 (%)", "absolute_distortion": "绝对畸变 (mm)", "lateral_chromatic": "倍率色差 (mm)", "tangential_coma": "子午慧差 (mm)" } return labels.get(key, key) def update_system_from_ui(self): """从UI更新系统参数""" try: # 入瞳参数 if self.entrance_diameter_entry.get(): self.system.entrance_pupil_diameter = float(self.entrance_diameter_entry.get()) if self.entrance_position_entry.get(): self.system.entrance_pupil_position = float(self.entrance_position_entry.get()) # 色光类型 self.system.light_type = self.light_type_var.get() # 物方参数 self.system.object_infinite = self.object_var.get() if self.system.object_infinite: if self.field_angle_entry.get(): self.system.field_angle = float(self.field_angle_entry.get()) else: if self.object_distance_entry.get(): self.system.object_distance = float(self.object_distance_entry.get()) if self.object_height_entry.get(): self.system.object_height = float(self.object_height_entry.get()) if self.aperture_angle_entry.get(): self.system.aperture_angle = float(self.aperture_angle_entry.get()) except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") return False return True def calculate(self): """执行光学计算""" # 更新系统参数 if not self.update_system_from_ui(): return # 检查必要参数 if not self.system.surfaces: messagebox.showwarning("警告", "请至少添加一个光学表面") return if self.system.entrance_pupil_diameter is None: messagebox.showwarning("警告", "请输入入瞳直径") return if self.system.object_infinite and self.system.field_angle is None: messagebox.showwarning("警告", "请输入半视场角") return if not self.system.object_infinite and ( self.system.object_distance is None or self.system.object_height is None or self.system.aperture_angle is None ): messagebox.showwarning("警告", "请输入完整的有限远参数") return # 执行计算 - 这里调用您的计算函数 self.perform_calculations() # 显示结果 self.display_results() # 显示计算完成消息 messagebox.showinfo("计算完成", "光学计算已完成,结果已显示在结果区域") def perform_calculations(self): """执行光学计算 - 这里调用计算函数""" # 重置结果 for key in self.system.results: self.system.results[key] = None # 示例:设置一些假结果用于演示 # 实际应用中,您需要调用您自己的计算函数 self.system.results["focal_length"] = calculate.calculate_focal_length(self.system.surfaces) self.system.results["ideal_image_distance"] = calculate.calculate_ideal_image_distance(self.system.surfaces,self.system.object_infinite, self.system.object_distance) self.system.results["actual_image_position"] = calculate.calculate_actual_image(self.system, self.system.light_type) self.system.results["image_principal_plane"] = calculate.calculate_principal_plane_position(self.system.surfaces) self.system.results["exit_pupil_distance"] = calculate.calculate_exit_pupil_distance(self.system.surfaces,self.system.entrance_pupil_position) self.system.results["ideal_image_height"] = calculate.calculate_ideal_image_height(self.system) self.system.results["spherical_aberration"] = calculate.calculate_spherical_aberration(self.system) self.system.results["longitudinal_chromatic"] = calculate.calculate_longitudinal_chromatic(self.system) self.system.results["tangential_field_curvature"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[0] self.system.results["sagittal_field_curvature"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[1] self.system.results["astigmatism"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[2] self.system.results["actual_image_height"] = calculate.calculate_actual_image_height(self.system,self.system.light_type) self.system.results["relative_distortion"] = -0.5 self.system.results["absolute_distortion"] = -0.05 self.system.results["lateral_chromatic"] = 0.01 self.system.results["tangential_coma"] = 0.04 def display_results(self): """在结果文本框中显示计算结果""" self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) # 添加系统参数摘要 self.result_text.insert(tk.END, "===== 光学系统参数 =====\n", "title") self.result_text.insert(tk.END, f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") self.result_text.insert(tk.END, f"入瞳位置: {self.system.entrance_pupil_position} mm\n") self.result_text.insert(tk.END, "物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") self.result_text.insert(tk.END, f"色光类型: {self.system.light_type}\n") self.result_text.insert(tk.END, f"孔径系数: {self.system.aperture_coefficient}\n") self.result_text.insert(tk.END, f"视场系数: {self.system.field_coefficient}\n") if self.system.object_infinite: self.result_text.insert(tk.END, f"半视场角: {self.system.field_angle} 度\n") else: self.result_text.insert(tk.END, f"物距: {self.system.object_distance} mm\n") self.result_text.insert(tk.END, f"物高: {self.system.object_height} mm\n") self.result_text.insert(tk.END, f"孔径角: {self.system.aperture_angle} 度\n") self.result_text.insert(tk.END, "\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" self.result_text.insert(tk.END, f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.4f}, vd={surf.vd:.1f}\n") # 添加计算结果 self.result_text.insert(tk.END, "\n\n===== 计算结果 =====\n", "title") # 计算关键结果 key_results = [ "focal_length", "ideal_image_distance", "actual_image_position", "image_principal_plane", "exit_pupil_distance", "ideal_image_height" ] for key in key_results: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") # 添加像差结果 self.result_text.insert(tk.END, "\n像差分析:\n", "subtitle") aberrations = [ "spherical_aberration", "longitudinal_chromatic", "tangential_field_curvature", "sagittal_field_curvature", "astigmatism", "actual_image_height", "relative_distortion", "absolute_distortion", "lateral_chromatic", "tangential_coma" ] for key in aberrations: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") self.result_text.configure(state=tk.DISABLED) def clear_all(self): """清除所有输入和结果""" # 清除系统参数 self.system = OpticalSystem() # 清除UI输入 self.file_path_entry.delete(0, tk.END) self.entrance_diameter_entry.delete(0, tk.END) self.entrance_position_entry.delete(0, tk.END) self.field_angle_entry.delete(0, tk.END) self.object_distance_entry.delete(0, tk.END) self.object_height_entry.delete(0, tk.END) self.aperture_angle_entry.delete(0, tk.END) self.aperture_coeff_entry.delete(0, tk.END) self.aperture_coeff_entry.insert(0, "1") self.field_coeff_entry.delete(0, tk.END) self.field_coeff_entry.insert(0, "0") self.radius_entry.delete(0, tk.END) self.radius_entry.insert(0, "50") self.thickness_entry.delete(0, tk.END) self.thickness_entry.insert(0, "5") self.nd_entry.delete(0, tk.END) self.nd_entry.insert(0, "1.5") self.vd_entry.delete(0, tk.END) self.vd_entry.insert(0, "60") # 清除表面列表 self.surface_tree.delete(*self.surface_tree.get_children()) self.system.surfaces = [] # 清除结果 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) # 重置物距类型 self.object_var.set(True) self.toggle_object_input() messagebox.showinfo("清除完成", "所有输入和结果已清除") def load_default_settings(self): """加载默认设置(可选)""" # 这里可以添加加载默认设置的逻辑 pass if __name__ == "__main__": root = tk.Tk() app = MainApplication(root) # 设置文本样式 app.result_text.tag_config("title", font=("Arial", 10, "bold")) app.result_text.tag_config("subtitle", font=("Arial", 9, "bold")) root.mainloop() 现在可以输入8位小数的nd和vd但计算和保存时仍然是一位小时,精度不够,应该怎么修改
最新发布
06-28
from re import A import tkinter as tk from tkinter import ttk, messagebox, filedialog import json import os import math import numpy as np class OpticalSystem: def __init__(self): self.surfaces = [] # 存储光学表面对象 self.entrance_pupil_diameter = None # 入瞳直径 (mm) self.entrance_pupil_position = None # 入瞳位置 (相对第一个面顶点) (mm) self.object_infinite = True # 物在无穷远 self.field_angle = None # 半视场角 (度) self.object_distance = None # 物距 (有限远) (mm) self.object_height = None # 物高 (有限远) (mm) self.aperture_angle = None # 孔径角 (有限远) (度) self.light_type = "d" # 色光类型,默认d光 self.aperture_coefficient = 1.0 # 孔径系数 self.field_coefficient = 1.0 # 视场系数 # 计算结果存储 self.results = { "focal_length": None, # 焦距 f' "ideal_image_distance": None, # 理想像距 l' "actual_image_position": None, # 实际像位置 "image_principal_plane": None, # 像方主面位置 lH' "exit_pupil_distance": None, # 出瞳距 lp' "ideal_image_height": None, # 理想像高 y0' "spherical_aberration": None, # 球差 "longitudinal_chromatic": None, # 位置色差 "tangential_field_curvature": None, # 子午场曲 xt' "sagittal_field_curvature": None, # 弧矢场曲 xs' "astigmatism": None, # 像散 Δxts' "actual_image_height": None, # 实际像高 "relative_distortion": None, # 相对畸变 "absolute_distortion": None, # 绝对畸变 "lateral_chromatic": None, # 倍率色差 "tangential_coma": None # 子午慧差 } class Surface: def __init__(self, r=float('inf'), d=0.0, nd=1.0, nf = 1.0, nc = 1.0): self.r = r # 曲率半径 (mm) self.d = d # 厚度/间隔 (mm) self.nd = nd # d光折射率 self.nf = nf # F self.nc = nc def to_dict(self): """将表面数据转换为字典""" return { "r": self.r, "d": self.d, "nd": self.nd, "nf": self.nf, "nc": self.nc } @classmethod def from_dict(cls, data): """从字典创建表面对象""" return cls( r=data.get('r', float('inf')), d=data.get('d', 0.0), nd=data.get('nd', 1.0), nf=data.get('nf', 1.0), nc=data.get('nc', 1.0) ) class calculate: def trace_paraxial_ray(surfaces, h0, u0, n0=1.0): """ 近轴光线追迹函数 :param surfaces: 光学表面列表 :param h0: 初始光线高度 :param u0: 初始光线角度(弧度) :param n0: 初始介质折射率 :return: 最后的光线高度和角度 """ h = h0 u = u0 n = n0 # 当前介质折射率 for i, surf in enumerate(surfaces): # 计算曲率(平面时曲率为0) c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r # 折射公式:n'u' = nu + (n - n') * c * h n_prime = surf.nd # 折射后折射率 u_prime = (n * u + (n - n_prime) * c * h) / n_prime # 如果当前不是最后一个面,计算传播到下个面的高度 if i < len(surfaces) - 1: d = surf.d # 到下一个面的距离 h_next = h + d * u_prime else: h_next = h # 最后一个面后不需要传播 # 更新参数 h = h_next u = u_prime n = n_prime # 更新为当前面后的折射率 return h, u def calculate_focal_length(surfaces): """ 计算系统焦距 :param surfaces: 光学表面列表 :return: 焦距值(mm) """ # 追迹平行于光轴的光线(无穷远物) h0 = 1.0 # 任意高度,取1便于计算 u0 = 0.0 # 平行于光轴 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 焦距 f' = -h0 / u_final if abs(u_final) < 1e-10: # 防止除零错误 return float('inf') return -h0 / u_final def calculate_ideal_image_distance(surfaces, object_infinite, object_distance=None): """ 计算理想像距 :param surfaces: 光学表面列表 :param object_infinite: 物是否在无穷远 :param object_distance: 物距(有限远时) :return: 理想像距(mm) """ if object_infinite: # 无穷远物:理想像距等于焦距对应的像距 return calculate.calculate_focal_length(surfaces)+calculate.calculate_principal_plane_position(surfaces) else: # 有限远物:使用高斯公式计算理想像距 if object_distance is None: raise ValueError("有限远物需要提供物距") # 计算系统焦距 f_prime = calculate.calculate_focal_length(surfaces) # 高斯公式:1/f' = 1/l' - 1/l # 其中 l 为物距(负值),l' 为像距 l = -object_distance # 物距为负值(物在左侧) # 计算像距 l' if abs(f_prime) < 1e-10: return float('inf') l_prime = 1 / (1/f_prime + 1/l) l_prime = l_prime+calculate.calculate_principal_plane_position(surfaces) return l_prime def calculate_principal_plane_position(surfaces): """ 计算像方主面位置 :param surfaces: 光学表面列表 :return: 主面位置(相对于最后一个面顶点,正值表示在右侧) """ # 计算焦距 f_prime = calculate.calculate_focal_length(surfaces) # 追迹平行于光轴的光线 h0 = 1.0 # 初始高度 u0 = 0.0 # 平行于光轴 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 主面位置 lH' = -h_final / u_final - f_prime if abs(u_final) < 1e-10: return float('inf') return -h_final / u_final - f_prime def calculate_exit_pupil_distance(surfaces, entrance_pupil_position): """ 计算系统的出瞳距 :param surfaces: 光学表面列表 :param entrance_pupil_position: 入瞳位置(相对于第一个面顶点) :return: 出瞳距(相对于最后一个面顶点) """ # 定义初始光线参数 u0 = 0.01 # 小角度(弧度) # 计算第一个面的光线高度 h0 = -entrance_pupil_position * u0 # 进行近轴光线追迹 h_final, u_final = calculate.trace_paraxial_ray(surfaces, h0, u0) # 计算出瞳距:l' = -h_final / u_final if abs(u_final) < 1e-10: # 防止除零错误 return float('inf') return -h_final / u_final def calculate_ideal_image_height(system): """ 计算理想像高 :param system: OpticalSystem对象 :return: 理想像高(mm) """ # 获取系统焦距 focal_length = calculate.calculate_focal_length(system.surfaces) if system.object_infinite: # 无穷远物 # 理想像高 y0' = -f' * tan(ω) field_angle_rad = math.radians(system.field_angle) return -focal_length * math.tan(field_angle_rad) else: # 有限远物 # 获取理想像距 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 获取主面位置 principal_plane = calculate.calculate_principal_plane_position(system.surfaces) # 计算实际像位置(从最后一个面顶点) actual_image_position = ideal_image_distance + principal_plane # 计算放大率 β = l'/l # l = -object_distance(物在左侧为负) magnification = actual_image_position / (-system.object_distance) # 理想像高 y0' = β * y return magnification * system.object_height def trace_real_ray(surfaces, h0, u0, l0,light_type): """ 实际光线追迹函数 :param surfaces: 光学表面列表 :param h0: 初始光线高度 :param u0: 初始光线角度(弧度) :param l0: 初始光线距离(mm) :return: 最后的光线距离和角度 """ h = h0 u = u0 L = l0 # 当前光线距离 npre = 1 LL = [] PA =[] LI =[] LR =[] LW = [L] UU = [u] #初始化内部变量 si = 0 sii = 0 uu = 0 for i, surf in enumerate(surfaces): # 选择色光折射率 if light_type == 'd': n_surf = surf.nd elif light_type == 'f': n_surf = surf.nf elif light_type == 'c': n_surf = surf.nc else: n_surf = surf.nd # 默认d光 # 计算曲率(平面时曲率为0) c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r if(i == 0 and u==0.0): si = h/surf.r else: si = (L-surf.r)*np.sin(u)/surf.r sii = npre*si/n_surf uu = u +np.arcsin(si)-np.arcsin(sii) L = surf.r+surf.r*sii/np.sin(uu) PA .append( L * np.sin(uu) / np.cos((np.arcsin(sii)-uu)/2)) LL.append(L) UU.append(uu) LI.append(np.arcsin(si)) LR.append(np.arcsin(sii)) #传递到下一个表面 if i < len(surfaces) - 1: u = uu L = L -surf.d npre = n_surf # 折射后折射率 LW.append(L) return LW, LL,UU,LI,LR,PA def calculate_actual_image(system,light_type): """ 计算轴上点的实际像位置(相对于最后一个面顶点) 参数: system: OpticalSystem对象,包含: - surfaces: 表面列表 - entrance_pupil_diameter: 入瞳直径 (mm) - entrance_pupil_position: 入瞳位置 (相对第一个面顶点) (mm) - aperture_coefficient: 孔径系数 (0~1) - object_infinite: 物是否在无穷远 - object_distance: 物距 (有限远时) (mm) - light_type: 色光类型 (这里只影响折射率选择) 返回: 实际像位置 (mm) (正值表示在最后一个面右侧) """ # 1. 计算实际入瞳半径 actual_entrance_radius = (system.entrance_pupil_diameter * system.aperture_coefficient) / 2 # 2. 根据物方情况设置初始光线参数 if system.object_infinite: # 无穷远物:光线平行于光轴 u0 = 0.0 # 初始角度 (弧度) h0 = actual_entrance_radius # 初始高度 (取入瞳边缘) l0 = 0 # 物方截距 (无穷远) L_final = calculate.trace_real_ray(system.surfaces, h0, u0, l0,light_type)[1] else: # 有限远物 # 计算物点到入瞳平面的距离 d_object_to_entrance = system.object_distance - system.entrance_pupil_position # 计算初始角度 (物点发出的光线通过入瞳边缘) u0 = math.atan(actual_entrance_radius/ d_object_to_entrance) l0 = system.object_distance # 负号表示在左侧 h0 = 0 L_final= calculate.trace_real_ray(system.surfaces, h0, u0, l0,light_type)[1] return L_final[-1] def calculate_spherical_aberration(system): # 计算实际像位置 actual_image_position = calculate.calculate_actual_image(system,system.light_type) # 计算理想像距 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 计算球差 return actual_image_position - ideal_image_distance def calculate_longitudinal_chromatic(system): #计算位置色差 #F光 imageF = calculate.calculate_actual_image(system,'f') #C光 imageC = calculate.calculate_actual_image(system,'c') return imageF -imageC def calculate_actual_image_height(system,light_type): """ 计算实际像高(主光线在像平面上的高度) 参数: system: OpticalSystem对象 返回: 实际像高 (mm) """ # 1. 计算像面位置(相对于最后一个面顶点) l_prime = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 2. 确定主光线初始参数 if system.object_infinite: # 无限远物 omega_rad = math.radians(system.field_angle) u0 = omega_rad # 主光线角度 h0 = 0 L0 = system.entrance_pupil_position else: # 有限远物 d_obj = system.object_distance - system.entrance_pupil_position u0 = math.atan(system.object_height / d_obj) d0 = -system.entrance_pupil_position h0 = d0 * math.tan(u0) L0 = system.entrance_pupil_position # 3. 追迹主光线 L_last = calculate.trace_real_ray(system.surfaces, h0, u0, L0,light_type)[1] u_last = calculate.trace_real_ray(system.surfaces, h0, u0, L0,light_type)[2] # 4. 计算实际像高 actual_image_height = (L_last[-1] - l_prime) * math.tan(u_last[-1]) return actual_image_height def calculate_astigmatism(system, light_type): # 1. 获取主光线参数 if system.object_infinite: omega_rad = math.radians(system.field_angle) u0 = omega_rad h0 = 0 L0 = system.entrance_pupil_position else: d_obj = system.object_distance - system.entrance_pupil_position u0 = math.atan(system.object_height / d_obj) h0 = -system.entrance_pupil_position * math.tan(u0) L0 = system.entrance_pupil_position # 2. 追迹主光线获取入射/折射角 _, _, UU, LI, LR, PA = calculate.trace_real_ray(system.surfaces, h0, u0, L0, light_type) # 3. 初始化子午和弧矢截距 n = 1.0 # 初始介质折射率(空气) t = s = 0.0 # 根据物方条件设置初始截距 if system.object_infinite: t = s = float('inf') else: t = s = -system.object_distance # 物在左侧为负 # 4. 逐面计算 for i, surf in enumerate(system.surfaces): # 获取当前面折射率 if light_type == 'd': n_prime = surf.nd elif light_type == 'f': n_prime = surf.nf elif light_type == 'c': n_prime = surf.nc else: n_prime = surf.nd # 计算曲率 c = 0.0 if math.isinf(surf.r) else 1.0 / surf.r # 获取主光线角度 U = UU[i] I = LI[i] I_prime = LR[i] # Coddington方程计算新截距 # 子午光线 if math.isinf(t): num_t = n_prime * math.cos(I_prime)**2 den_t = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c t_prime = num_t / den_t if den_t != 0 else float('inf') else: term_t = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c t_prime = n_prime * math.cos(I_prime)**2 / (term_t + n * math.cos(I)**2 / t) # 弧矢光线 if math.isinf(s): num_s = n_prime den_s = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c s_prime = num_s / den_s if den_s != 0 else float('inf') else: term_s = (n_prime * math.cos(I_prime) - n * math.cos(I)) * c s_prime = n_prime / (term_s + n / s) # 5. 面间过渡(最后一面前) if i < len(system.surfaces) - 1: # 计算沿主光线方向的实际距离 d_eff = surf.d / math.cos(U) # 更新截距 t = t_prime - d_eff s = s_prime - d_eff n = n_prime # 更新折射率 # 6. 计算理想像距作为参考 ideal_image_distance = calculate.calculate_ideal_image_distance( system.surfaces, system.object_infinite, system.object_distance ) # 7. 计算场曲和像散 # 子午场曲 = 子午焦点位置 - 理想像面位置 xt_prime = t_prime - ideal_image_distance # 弧矢场曲 = 弧矢焦点位置 - 理想像面位置 xs_prime = s_prime - ideal_image_distance # 像散 = 子午场曲 - 弧矢场曲 delta_xts = xt_prime - xs_prime return xt_prime, xs_prime, delta_xts class MainApplication: def __init__(self, root): self.root = root self.root.title("光学系统计算程序") self.root.geometry("1000x850") # 创建光学系统对象 self.system = OpticalSystem() # 创建GUI组件 self.create_widgets() # 加载默认设置(如果有) self.load_default_settings() def create_widgets(self): # 主框架 main_frame = ttk.Frame(self.root) main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # 文件操作框架 file_frame = ttk.LabelFrame(main_frame, text="文件操作") file_frame.pack(fill=tk.X, padx=5, pady=5) # 文件路径输入 ttk.Label(file_frame, text="文件路径:").grid(row=0, column=0, padx=5, pady=5) self.file_path_entry = ttk.Entry(file_frame, width=50) self.file_path_entry.grid(row=0, column=1, padx=5, pady=5) # 文件操作按钮 browse_btn = ttk.Button(file_frame, text="浏览...", command=self.browse_file) browse_btn.grid(row=0, column=2, padx=5, pady=5) load_btn = ttk.Button(file_frame, text="加载系统参数", command=self.load_system) load_btn.grid(row=0, column=3, padx=5, pady=5) save_btn = ttk.Button(file_frame, text="保存系统参数", command=self.save_system) save_btn.grid(row=0, column=4, padx=5, pady=5) # 左侧面板 - 输入参数 left_frame = ttk.LabelFrame(main_frame, text="系统参数输入") left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 右侧面板 - 结果展示 right_frame = ttk.LabelFrame(main_frame, text="计算结果") right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建左侧面板的子组件 self.create_input_panel(left_frame) # 创建右侧面板的子组件 self.create_result_panel(right_frame) # 计算按钮 calc_frame = ttk.Frame(main_frame) calc_frame.pack(fill=tk.X, padx=5, pady=10) calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.calculate, width=15) calc_btn.pack(side=tk.LEFT, padx=10) save_result_btn = ttk.Button(calc_frame, text="保存计算结果", command=self.save_results, width=15) save_result_btn.pack(side=tk.LEFT, padx=10) clear_btn = ttk.Button(calc_frame, text="清除所有", command=self.clear_all, width=15) clear_btn.pack(side=tk.LEFT, padx=10) def browse_file(self): """浏览文件按钮处理函数""" file_path = filedialog.askopenfilename( title="选择文件", filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")] ) if file_path: self.file_path_entry.delete(0, tk.END) self.file_path_entry.insert(0, file_path) def create_input_panel(self, parent): # 入瞳参数 pupil_frame = ttk.LabelFrame(parent, text="入瞳参数") pupil_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳直径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.entrance_diameter_entry = ttk.Entry(pupil_frame, width=15) self.entrance_diameter_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(pupil_frame, text="入瞳位置 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.entrance_position_entry = ttk.Entry(pupil_frame, width=15) self.entrance_position_entry.grid(row=0, column=3, padx=5, pady=5) # 色光类型选择 ttk.Label(pupil_frame, text="色光类型:").grid(row=1, column=0, padx=5, pady=5, sticky="w") self.light_type_var = tk.StringVar(value="d") self.light_type_combo = ttk.Combobox(pupil_frame, textvariable=self.light_type_var, width=12, state="readonly") self.light_type_combo["values"] = ("d", "f", "c") self.light_type_combo.grid(row=1, column=1, padx=5, pady=5, sticky="w") # 添加孔径系数和视场系数输入 ttk.Label(pupil_frame, text="孔径系数:").grid(row=1, column=2, padx=5, pady=5, sticky="w") self.aperture_coeff_entry = ttk.Entry(pupil_frame, width=8) self.aperture_coeff_entry.insert(0, "1.0") self.aperture_coeff_entry.grid(row=1, column=3, padx=5, pady=5) ttk.Label(pupil_frame, text="视场系数:").grid(row=1, column=4, padx=5, pady=5, sticky="w") self.field_coeff_entry = ttk.Entry(pupil_frame, width=8) self.field_coeff_entry.insert(0, "1.0") self.field_coeff_entry.grid(row=1, column=5, padx=5, pady=5) # 物方参数 object_frame = ttk.LabelFrame(parent, text="物方参数") object_frame.pack(fill=tk.X, padx=5, pady=5) # 物距选择 self.object_var = tk.BooleanVar(value=True) # True: 无穷远, False: 有限远 ttk.Radiobutton(object_frame, text="物在无穷远", variable=self.object_var, value=True, command=self.toggle_object_input).grid(row=0, column=0, padx=5, pady=5) ttk.Radiobutton(object_frame, text="物在有限远", variable=self.object_var, value=False, command=self.toggle_object_input).grid(row=0, column=1, padx=5, pady=5) # 无穷远参数 self.infinite_frame = ttk.Frame(object_frame) self.infinite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) ttk.Label(self.infinite_frame, text="半视场角 (度):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.field_angle_entry = ttk.Entry(self.infinite_frame, width=15) self.field_angle_entry.grid(row=0, column=1, padx=5, pady=5) # 有限远参数 (初始隐藏) self.finite_frame = ttk.Frame(object_frame) self.finite_frame.grid(row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5) self.finite_frame.grid_remove() # 初始隐藏 ttk.Label(self.finite_frame, text="物距 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.object_distance_entry = ttk.Entry(self.finite_frame, width=15) self.object_distance_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(self.finite_frame, text="物高 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="w") self.object_height_entry = ttk.Entry(self.finite_frame, width=15) self.object_height_entry.grid(row=0, column=3, padx=5, pady=5) ttk.Label(self.finite_frame, text="孔径角 (度):").grid(row=0, column=4, padx=5, pady=5, sticky="w") self.aperture_angle_entry = ttk.Entry(self.finite_frame, width=15) self.aperture_angle_entry.grid(row=0, column=5, padx=5, pady=5) # 光学表面输入 surface_frame = ttk.LabelFrame(parent, text="光学表面参数") surface_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 表面输入控件 input_frame = ttk.Frame(surface_frame) input_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5) self.radius_entry = ttk.Entry(input_frame, width=10) self.radius_entry.grid(row=0, column=1, padx=5, pady=5) self.radius_entry.insert(0, "50") ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5) self.thickness_entry = ttk.Entry(input_frame, width=10) self.thickness_entry.grid(row=0, column=3, padx=5, pady=5) self.thickness_entry.insert(0, "5") ttk.Label(input_frame, text="d光折射率 (nd):").grid(row=0, column=4, padx=5, pady=5) self.nd_entry = ttk.Entry(input_frame, width=10) self.nd_entry.grid(row=0, column=5, padx=5, pady=5) self.nd_entry.insert(0, "1.5") ttk.Label(input_frame, text="F光折射率 (nf):").grid(row=0, column=6, padx=5, pady=5) self.nf_entry = ttk.Entry(input_frame, width=10) self.nf_entry.grid(row=0, column=7, padx=5, pady=5) self.nf_entry.insert(0, "1.505") ttk.Label(input_frame, text="C光折射率 (nc):").grid(row=0, column=8, padx=5, pady=5) self.nc_entry = ttk.Entry(input_frame, width=10) self.nc_entry.grid(row=0, column=9, padx=5, pady=5) self.nc_entry.insert(0, "1.495") button_frame = ttk.Frame(input_frame) button_frame.grid(row=0, column=8, padx=10) add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surface) add_btn.pack(side=tk.LEFT, padx=5) remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surface) remove_btn.pack(side=tk.LEFT, padx=5) # 表面列表 list_frame = ttk.Frame(surface_frame) list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) columns = ("#", "曲率半径 (mm)", "厚度 (mm)", "nd", "nf", "nc") self.surface_tree = ttk.Treeview(list_frame, columns=columns, show="headings", height=8) for col in columns: self.surface_tree.heading(col, text=col) self.surface_tree.column(col, width=80, anchor=tk.CENTER) vsb = ttk.Scrollbar(list_frame, orient="vertical", command=self.surface_tree.yview) self.surface_tree.configure(yscrollcommand=vsb.set) self.surface_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) vsb.pack(side=tk.RIGHT, fill=tk.Y) def create_result_panel(self, parent): # 结果文本框 self.result_text = tk.Text(parent, wrap=tk.WORD) result_scroll_y = ttk.Scrollbar(parent, orient="vertical", command=self.result_text.yview) result_scroll_x = ttk.Scrollbar(parent, orient="horizontal", command=self.result_text.xview) self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set) result_scroll_y.pack(side=tk.RIGHT, fill=tk.Y) result_scroll_x.pack(side=tk.BOTTOM, fill=tk.X) self.result_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # 设置初始文本 self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) def toggle_object_input(self): """切换物方参数输入界面""" if self.object_var.get(): # 无穷远 self.infinite_frame.grid() self.finite_frame.grid_remove() else: # 有限远 self.infinite_frame.grid_remove() self.finite_frame.grid() def add_surface(self): """添加光学表面""" try: # 获取输入值 r = self.radius_entry.get().strip() d = self.thickness_entry.get().strip() nd = self.nd_entry.get().strip() nf = self.nf_entry.get().strip() nc = self.nc_entry.get().strip() # 处理输入值 r = float(r) if r and r.lower() != "inf" else float('inf') d = float(d) if d else 0.0 nd = float(nd) if nd else 1.0 nf = float(nf) if nf else 1.0 nc = float(nc) if nc else 1.0 # 添加到系统 surface = Surface(r, d, nd, nf, nc) self.system.surfaces.append(surface) # 添加到树形视图 r_str = "平面" if r == float('inf') else f"{r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{d:.2f}", f"{nd:.8f}", f"{nf:.8f}", f"{nc:.8f}" )) # 清空输入框 self.radius_entry.delete(0, tk.END) self.thickness_entry.delete(0, tk.END) self.nd_entry.delete(0, tk.END) self.nf_entry.delete(0, tk.END) self.nc_entry.delete(0, tk.END) self.radius_entry.focus_set() except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") def remove_surface(self): """删除选中的光学表面""" selected = self.surface_tree.selection() if selected: # 从树形视图中删除 for item in selected: index = int(self.surface_tree.item(item, "values")[0]) - 1 self.surface_tree.delete(item) # 从系统中删除 if 0 <= index < len(self.system.surfaces): self.system.surfaces.pop(index) # 更新剩余表面的序号 for i, item in enumerate(self.surface_tree.get_children()): values = list(self.surface_tree.item(item, "values")) values[0] = i + 1 self.surface_tree.item(item, values=values) def load_system(self): """加载系统参数文件""" file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入文件路径") return try: with open(file_path, 'r') as f: data = json.load(f) # 加载系统参数 self.system.entrance_pupil_diameter = data.get("entrance_pupil_diameter") self.system.entrance_pupil_position = data.get("entrance_pupil_position") self.system.object_infinite = data.get("object_infinite", True) self.system.field_angle = data.get("field_angle") self.system.object_distance = data.get("object_distance") self.system.object_height = data.get("object_height") self.system.aperture_angle = data.get("aperture_angle") self.system.aperture_coefficient = data.get("aperture_coefficient", 1.0) self.system.field_coefficient = data.get("field_coefficient", 1.0) # 更新UI中的参数值 if self.system.entrance_pupil_diameter is not None: self.entrance_diameter_entry.delete(0, tk.END) self.entrance_diameter_entry.insert(0, str(self.system.entrance_pupil_diameter)) if self.system.entrance_pupil_position is not None: self.entrance_position_entry.delete(0, tk.END) self.entrance_position_entry.insert(0, str(self.system.entrance_pupil_position)) self.object_var.set(self.system.object_infinite) self.toggle_object_input() if self.system.field_angle is not None: self.field_angle_entry.delete(0, tk.END) self.field_angle_entry.insert(0, str(self.system.field_angle)) if self.system.object_distance is not None: self.object_distance_entry.delete(0, tk.END) self.object_distance_entry.insert(0, str(self.system.object_distance)) if self.system.object_height is not None: self.object_height_entry.delete(0, tk.END) self.object_height_entry.insert(0, str(self.system.object_height)) if self.system.aperture_angle is not None: self.aperture_angle_entry.delete(0, tk.END) self.aperture_angle_entry.insert(0, str(self.system.aperture_angle)) if self.aperture_coeff_entry.get(): self.system.aperture_coefficient = float(self.aperture_coeff_entry.get()) if self.field_coeff_entry.get(): self.system.field_coefficient = float(self.field_coeff_entry.get()) # 加载表面数据 self.system.surfaces = [] self.surface_tree.delete(*self.surface_tree.get_children()) surfaces_data = data.get("surfaces", []) for surf_data in surfaces_data: surface = Surface.from_dict(surf_data) self.system.surfaces.append(surface) r_str = "平面" if surface.r == float('inf') else f"{surface.r:.2f}" self.surface_tree.insert("", "end", values=( len(self.system.surfaces), r_str, f"{surface.d:.2f}", f"{surface.nd:.8f}", f"{surface.nf:.8f}", f"{surface.nc:.8f}" )) messagebox.showinfo("成功", f"系统参数已从 {os.path.basename(file_path)} 加载") # 显示加载的系统信息 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, f"已加载系统参数文件: {file_path}\n") self.result_text.insert(tk.END, f"包含 {len(self.system.surfaces)} 个光学表面\n") self.result_text.configure(state=tk.DISABLED) except FileNotFoundError: messagebox.showerror("错误", f"文件不存在: {file_path}") except Exception as e: messagebox.showerror("加载错误", f"加载文件失败: {str(e)}") def save_system(self): """保存系统参数文件""" # 更新系统参数 if not self.update_system_from_ui(): return # 如果没有表面数据,提示用户 if not self.system.surfaces: messagebox.showwarning("警告", "没有光学表面数据,无法保存") return file_path = self.file_path_entry.get().strip() if not file_path: messagebox.showwarning("警告", "请输入保存路径") return try: # 准备保存数据 data = { "entrance_pupil_diameter": self.system.entrance_pupil_diameter, "entrance_pupil_position": self.system.entrance_pupil_position, "object_infinite": self.system.object_infinite, "field_angle": self.system.field_angle, "object_distance": self.system.object_distance, "object_height": self.system.object_height, "aperture_angle": self.system.aperture_angle, "surfaces": [surf.to_dict() for surf in self.system.surfaces], "aperture_coefficient": self.system.aperture_coefficient, "field_coefficient": self.system.field_coefficient, } with open(file_path, 'w') as f: json.dump(data, f, indent=4) messagebox.showinfo("成功", f"系统参数已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"系统参数已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存文件失败: {str(e)}") def save_results(self): """保存计算结果到文件""" if not self.system.results or all(v is None for v in self.system.results.values()): messagebox.showwarning("警告", "没有计算结果可保存") return file_path = filedialog.asksaveasfilename( title="保存计算结果", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] ) if not file_path: return try: with open(file_path, 'w') as f: # 写入系统参数摘要 f.write("===== 光学系统参数 =====\n") f.write(f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") f.write(f"入瞳位置: {self.system.entrance_pupil_position} mm\n") f.write(f"色光类型: {self.system.light_type}\n") f.write("物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") if self.system.object_infinite: f.write(f"半视场角: {self.system.field_angle} 度\n") else: f.write(f"物距: {self.system.object_distance} mm\n") f.write(f"物高: {self.system.object_height} mm\n") f.write(f"孔径角: {self.system.aperture_angle} 度\n") f.write("\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" f.write(f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.8f}, vd={surf.vd:.8f}\n") # 写入计算结果 f.write("\n\n===== 计算结果 =====\n") for key, value in self.system.results.items(): if value is not None: # 格式化键名 label = self.format_result_label(key) f.write(f"{label}: {value}\n") messagebox.showinfo("成功", f"计算结果已保存到 {os.path.basename(file_path)}") # 显示保存信息 self.result_text.configure(state=tk.NORMAL) self.result_text.insert(tk.END, f"计算结果已保存到: {file_path}\n") self.result_text.configure(state=tk.DISABLED) except Exception as e: messagebox.showerror("保存错误", f"保存计算结果失败: {str(e)}") def format_result_label(self, key): """格式化结果标签为中文描述""" labels = { "focal_length": "焦距 f' (mm)", "ideal_image_distance": "理想像距 l' (mm)", "actual_image_position": "实际像位置 (mm)", "image_principal_plane": "像方主面位置 lH' (mm)", "exit_pupil_distance": "出瞳距 lp' (mm)", "ideal_image_height": "理想像高 y0' (mm)", "spherical_aberration": "球差 (mm)", "longitudinal_chromatic": "位置色差 (mm)", "tangential_field_curvature": "子午场曲 xt' (mm)", "sagittal_field_curvature": "弧矢场曲 xs' (mm)", "astigmatism": "像散 Δxts' (mm)", "actual_image_height": "实际像高 (mm)", "relative_distortion": "相对畸变 (%)", "absolute_distortion": "绝对畸变 (mm)", "lateral_chromatic": "倍率色差 (mm)", "tangential_coma": "子午慧差 (mm)" } return labels.get(key, key) def update_system_from_ui(self): """从UI更新系统参数""" try: # 入瞳参数 if self.entrance_diameter_entry.get(): self.system.entrance_pupil_diameter = float(self.entrance_diameter_entry.get()) if self.entrance_position_entry.get(): self.system.entrance_pupil_position = float(self.entrance_position_entry.get()) # 色光类型 self.system.light_type = self.light_type_var.get() # 物方参数 self.system.object_infinite = self.object_var.get() if self.system.object_infinite: if self.field_angle_entry.get(): self.system.field_angle = float(self.field_angle_entry.get()) else: if self.object_distance_entry.get(): self.system.object_distance = float(self.object_distance_entry.get()) if self.object_height_entry.get(): self.system.object_height = float(self.object_height_entry.get()) if self.aperture_angle_entry.get(): self.system.aperture_angle = float(self.aperture_angle_entry.get()) except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") return False return True def calculate(self): """执行光学计算""" # 更新系统参数 if not self.update_system_from_ui(): return # 检查必要参数 if not self.system.surfaces: messagebox.showwarning("警告", "请至少添加一个光学表面") return if self.system.entrance_pupil_diameter is None: messagebox.showwarning("警告", "请输入入瞳直径") return if self.system.object_infinite and self.system.field_angle is None: messagebox.showwarning("警告", "请输入半视场角") return if not self.system.object_infinite and ( self.system.object_distance is None or self.system.object_height is None or self.system.aperture_angle is None ): messagebox.showwarning("警告", "请输入完整的有限远参数") return # 执行计算 - 这里调用您的计算函数 self.perform_calculations() # 显示结果 self.display_results() # 显示计算完成消息 messagebox.showinfo("计算完成", "光学计算已完成,结果已显示在结果区域") def perform_calculations(self): """执行光学计算 - 这里调用计算函数""" # 重置结果 for key in self.system.results: self.system.results[key] = None # 示例:设置一些假结果用于演示 # 实际应用中,您需要调用您自己的计算函数 self.system.results["focal_length"] = calculate.calculate_focal_length(self.system.surfaces) self.system.results["ideal_image_distance"] = calculate.calculate_ideal_image_distance(self.system.surfaces,self.system.object_infinite, self.system.object_distance) self.system.results["actual_image_position"] = calculate.calculate_actual_image(self.system, self.system.light_type) self.system.results["image_principal_plane"] = calculate.calculate_principal_plane_position(self.system.surfaces) self.system.results["exit_pupil_distance"] = calculate.calculate_exit_pupil_distance(self.system.surfaces,self.system.entrance_pupil_position) self.system.results["ideal_image_height"] = calculate.calculate_ideal_image_height(self.system) self.system.results["spherical_aberration"] = calculate.calculate_spherical_aberration(self.system) self.system.results["longitudinal_chromatic"] = calculate.calculate_longitudinal_chromatic(self.system) self.system.results["tangential_field_curvature"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[0] self.system.results["sagittal_field_curvature"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[1] self.system.results["astigmatism"] = calculate.calculate_astigmatism(self.system,self.system.light_type)[2] self.system.results["actual_image_height"] = calculate.calculate_actual_image_height(self.system,self.system.light_type) self.system.results["relative_distortion"] = -0.5 self.system.results["absolute_distortion"] = -0.05 self.system.results["lateral_chromatic"] = 0.01 self.system.results["tangential_coma"] = 0.04 def display_results(self): """在结果文本框中显示计算结果""" self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) # 添加系统参数摘要 self.result_text.insert(tk.END, "===== 光学系统参数 =====\n", "title") self.result_text.insert(tk.END, f"入瞳直径: {self.system.entrance_pupil_diameter} mm\n") self.result_text.insert(tk.END, f"入瞳位置: {self.system.entrance_pupil_position} mm\n") self.result_text.insert(tk.END, "物距类型: " + ("无穷远" if self.system.object_infinite else "有限远") + "\n") self.result_text.insert(tk.END, f"色光类型: {self.system.light_type}\n") self.result_text.insert(tk.END, f"孔径系数: {self.system.aperture_coefficient}\n") self.result_text.insert(tk.END, f"视场系数: {self.system.field_coefficient}\n") if self.system.object_infinite: self.result_text.insert(tk.END, f"半视场角: {self.system.field_angle} 度\n") else: self.result_text.insert(tk.END, f"物距: {self.system.object_distance} mm\n") self.result_text.insert(tk.END, f"物高: {self.system.object_height} mm\n") self.result_text.insert(tk.END, f"孔径角: {self.system.aperture_angle} 度\n") self.result_text.insert(tk.END, "\n光学表面参数:\n") for i, surf in enumerate(self.system.surfaces): r_str = "平面" if surf.r == float('inf') else f"{surf.r:.2f} mm" self.result_text.insert(tk.END, f"表面 {i+1}: r={r_str}, d={surf.d:.2f} mm, nd={surf.nd:.8f}, nf={surf.nf:.8f}, nc={surf.nc:.8f}\n") # 添加计算结果 self.result_text.insert(tk.END, "\n\n===== 计算结果 =====\n", "title") # 计算关键结果 key_results = [ "focal_length", "ideal_image_distance", "actual_image_position", "image_principal_plane", "exit_pupil_distance", "ideal_image_height" ] for key in key_results: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") # 添加像差结果 self.result_text.insert(tk.END, "\n像差分析:\n", "subtitle") aberrations = [ "spherical_aberration", "longitudinal_chromatic", "tangential_field_curvature", "sagittal_field_curvature", "astigmatism", "actual_image_height", "relative_distortion", "absolute_distortion", "lateral_chromatic", "tangential_coma" ] for key in aberrations: if self.system.results[key] is not None: label = self.format_result_label(key) self.result_text.insert(tk.END, f"{label}: {self.system.results[key]}\n") self.result_text.configure(state=tk.DISABLED) def clear_all(self): """清除所有输入和结果""" # 清除系统参数 self.system = OpticalSystem() # 清除UI输入 self.file_path_entry.delete(0, tk.END) self.entrance_diameter_entry.delete(0, tk.END) self.entrance_position_entry.delete(0, tk.END) self.field_angle_entry.delete(0, tk.END) self.object_distance_entry.delete(0, tk.END) self.object_height_entry.delete(0, tk.END) self.aperture_angle_entry.delete(0, tk.END) self.aperture_coeff_entry.delete(0, tk.END) self.aperture_coeff_entry.insert(0, "1") self.field_coeff_entry.delete(0, tk.END) self.field_coeff_entry.insert(0, "0") self.radius_entry.delete(0, tk.END) self.radius_entry.insert(0, "50") self.thickness_entry.delete(0, tk.END) self.thickness_entry.insert(0, "5") self.nd_entry.delete(0, tk.END) self.nd_entry.insert(0, "1.5") self.nf_entry.delete(0, tk.END) self.nf_entry.insert(0, "1.52") self.nc_entry.delete(0, tk.END) self.nc_entry.insert(0, "1.49") # 清除表面列表 self.surface_tree.delete(*self.surface_tree.get_children()) self.system.surfaces = [] # 清除结果 self.result_text.configure(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "计算结果将显示在此处...\n\n") self.result_text.configure(state=tk.DISABLED) # 重置物距类型 self.object_var.set(True) self.toggle_object_input() messagebox.showinfo("清除完成", "所有输入和结果已清除") def load_default_settings(self): """加载默认设置(可选)""" # 这里可以添加加载默认设置的逻辑 pass if __name__ == "__main__": root = tk.Tk() app = MainApplication(root) # 设置文本样式 app.result_text.tag_config("title", font=("Arial", 10, "bold")) app.result_text.tag_config("subtitle", font=("Arial", 9, "bold")) root.mainloop() 优化GUI程序布局(缩减左边输入界面的宽度),添加一个保存结果的按钮,按下后新建一个文件保存显示出的参数和对应结果
06-28
import tkinter as tk from tkinter import ttk, messagebox, filedialog import globals class Surface: def __init__(self, r=float('inf'), d=0.0, n=1.0, vd=0.0): self.r = r # 曲率半径 (mm) self.d = d # 厚度/间隔 (mm) self.n = n # 折射率 self.vd = vd # 阿贝数 self.x = 0.0 # 表面位置,将在系统中设置 def __str__(self): r_str = f"{self.r:.2f}" if abs(self.r) < 1e10 else "平面" return f"表面: r={r_str}, d={self.d:.2f}, n={self.n:.4f}, vd={self.vd:.1f}" # 字典文件处理函数 def parse_file_to_dict(file_path): """从文件读取键值对到字典""" result = {} try: with open(file_path, 'r') as file: for line in file: cleaned_line = line.strip() if not cleaned_line or ':' not in cleaned_line: continue parts = cleaned_line.split(':', 1) key = parts[0].strip() value = parts[1].strip() if key: result[key] = value return result except Exception as e: messagebox.showerror("错误", f"读取文件失败: {str(e)}") return {} def write_dict_to_file(file_path, data_dict): """将字典写入文件""" try: with open(file_path, 'w') as file: for key, value in data_dict.items(): file.write(f"{key}:{value}\n") return True except Exception as e: messagebox.showerror("错误", f"写入文件失败: {str(e)}") return False # 计算函数 - 留作接口 def calculate(surfaces, var1, var2, var3, var4, var5, var6): """计算函数,留作接口""" # 这里可以添加实际的计算逻辑 result = "计算结果:\n" result += f"变量1: {var1}, 变量2: {var2}, 变量3: {var3}\n" result += f"变量4: {var4}, 变量5: {var5}, 变量6: {var6}\n\n" result += "光学表面参数:\n" for i, surf in enumerate(surfaces): result += f"表面 {i+1}: {surf}\n" return result class MainApplication: def __init__(self, root): self.root = root self.root.title("光学系统计算工具") self.root.geometry("850x750") # 存储光学表面的列表 self.surfs = [] # 变量定义 self.var1 = None self.var2 = None self.var3 = None self.var4 = None self.var5 = None self.var6 = None # 自定义数据字典 self.data_dict = {} # 存储选项变量的字典 self.option_vars = {} self.entry_vars = {} self.create_widgets() self.create_option_widgets() self.create_optical_surface_ui() def create_widgets(self): # 文件操作框架 file_frame = ttk.LabelFrame(self.root, text="文件操作") file_frame.pack(padx=10, pady=5, fill="x") # 加载路径 ttk.Label(file_frame, text="镜头数据路径:").grid(row=0, column=0, padx=5, pady=5, sticky="e") self.load_path_entry = ttk.Entry(file_frame, width=50) self.load_path_entry.grid(row=0, column=1, padx=5, pady=5) load_btn = ttk.Button(file_frame, text="加载", command=self.load_data) load_btn.grid(row=0, column=2, padx=5, pady=5) # 保存路径 ttk.Label(file_frame, text="保存镜头数据:").grid(row=1, column=0, padx=5, pady=5, sticky="e") self.save_path_entry = ttk.Entry(file_frame, width=50) self.save_path_entry.grid(row=1, column=1, padx=5, pady=5) save_btn = ttk.Button(file_frame, text="保存", command=self.save_data) save_btn.grid(row=1, column=2, padx=5, pady=5) def create_option_widgets(self): """创建选项控件和输入框""" option_frame = ttk.LabelFrame(self.root, text="参数选项") option_frame.pack(padx=10, pady=5, fill="x") # 选项配置 options = [ ("物距", ["有限", "无限"]), ("孔径", ["入瞳直径", "半孔径角", "物高"]), ("视场", ["半视场角", "物高"]) ] for i, (label, choices) in enumerate(options): # 选项框架 frame = ttk.Frame(option_frame) frame.pack(fill="x", padx=5, pady=5) # 选项标签 ttk.Label(frame, text=f"{label}:").pack(side=tk.LEFT, padx=5) # 单选按钮组 var = tk.StringVar(value=choices[0]) self.option_vars[label] = var for choice in choices: rb = ttk.Radiobutton(frame, text=choice, variable=var, value=choice) rb.pack(side=tk.LEFT, padx=5) # 输入框 ttk.Label(frame, text="值:").pack(side=tk.LEFT, padx=(15, 5)) entry_var = tk.StringVar() self.entry_vars[label] = entry_var entry = ttk.Entry(frame, textvariable=entry_var, width=20) entry.pack(side=tk.LEFT) # 导入按钮 import_btn = ttk.Button(option_frame, text="导入参数", command=self.import_parameters) import_btn.pack(pady=5) def create_optical_surface_ui(self): """创建光学表面输入界面""" input_frame = ttk.LabelFrame(self.root, text="光学表面参数") input_frame.pack(padx=10, pady=5, fill="x") ttk.Label(input_frame, text="曲率半径 (mm):").grid(row=0, column=0, padx=5, pady=5, sticky="e") ttk.Label(input_frame, text="厚度 (mm):").grid(row=0, column=2, padx=5, pady=5, sticky="e") ttk.Label(input_frame, text="折射率 (nd):").grid(row=0, column=4, padx=5, pady=5, sticky="e") ttk.Label(input_frame, text="阿贝数 (vd):").grid(row=0, column=6, padx=5, pady=5, sticky="e") self.radius_entry = ttk.Entry(input_frame, width=10) self.radius_entry.grid(row=0, column=1, padx=5, pady=5) self.radius_entry.insert(0, "50") self.thickness_entry = ttk.Entry(input_frame, width=10) self.thickness_entry.grid(row=0, column=3, padx=5, pady=5) self.thickness_entry.insert(0, "5") self.nd_entry = ttk.Entry(input_frame, width=10) self.nd_entry.grid(row=0, column=5, padx=5, pady=5) self.nd_entry.insert(0, "1.5") self.vd_entry = ttk.Entry(input_frame, width=10) self.vd_entry.grid(row=0, column=7, padx=5, pady=5) self.vd_entry.insert(0, "60") button_frame = ttk.Frame(input_frame) button_frame.grid(row=1, column=0, columnspan=8, pady=5) add_btn = ttk.Button(button_frame, text="添加表面", command=self.add_surf) add_btn.pack(side=tk.LEFT, padx=5) remove_btn = ttk.Button(button_frame, text="删除表面", command=self.remove_surf) remove_btn.pack(side=tk.LEFT, padx=5) tree_frame = ttk.LabelFrame(self.root, text="光学系统表面") tree_frame.pack(padx=10, pady=5, fill="both", expand=True) columns = ("radius", "thickness", "nd", "vd") self.tree = ttk.Treeview(tree_frame, columns=columns, show="headings") self.tree.heading("radius", text="曲率半径 (mm)") self.tree.heading("thickness", text="厚度 (mm)") self.tree.heading("nd", text="折射率 (nd)") self.tree.heading("vd", text="阿贝数 (vd)") vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview) self.tree.configure(yscrollcommand=vsb.set) self.tree.pack(side=tk.LEFT, fill="both", expand=True) vsb.pack(side=tk.RIGHT, fill="y") # 计算按钮 calc_frame = ttk.Frame(self.root) calc_frame.pack(pady=10) calc_btn = ttk.Button(calc_frame, text="开始计算", command=self.run_calculation) calc_btn.pack(side=tk.LEFT, padx=5) # 结果显示框 result_frame = ttk.LabelFrame(self.root, text="计算结果") result_frame.pack(padx=10, pady=5, fill="both", expand=True) self.result_text = tk.Text(result_frame, wrap=tk.WORD) result_scroll_y = ttk.Scrollbar(result_frame, orient="vertical", command=self.result_text.yview) result_scroll_x = ttk.Scrollbar(result_frame, orient="horizontal", command=self.result_text.xview) self.result_text.configure(yscrollcommand=result_scroll_y.set, xscrollcommand=result_scroll_x.set) result_scroll_y.pack(side=tk.RIGHT, fill="y") result_scroll_x.pack(side=tk.BOTTOM, fill="x") self.result_text.pack(side=tk.LEFT, fill="both", expand=True) def add_surf(self): try: r = self.radius_entry.get().strip() d = self.thickness_entry.get().strip() n = self.nd_entry.get().strip() v = self.vd_entry.get().strip() r = float(r) if r else float('inf') d = float(d) if d else 0.0 n = float(n) if n else 1.0 v = float(v) if v else 0.0 self.tree.insert("", "end", values=(f"{r if r != float('inf') else '平面'}", d, n, v)) self.surfs.append(Surface(r, d, n, v)) self.radius_entry.delete(0, tk.END) self.thickness_entry.delete(0, tk.END) self.nd_entry.delete(0, tk.END) self.vd_entry.delete(0, tk.END) self.radius_entry.focus_set() except ValueError: messagebox.showerror("输入错误", "请输入有效的数字") def remove_surf(self): selected = self.tree.selection() if selected: for item in selected: self.tree.delete(item) if self.surfs: self.surfs.pop() def run_calculation(self): """执行计算""" if not self.surfs: messagebox.showerror("错误", "请至少添加一个光学表面") return # 检查变量是否已导入 if any(v is None for v in [self.var1, self.var2, self.var3, self.var4, self.var5, self.var6]): messagebox.showwarning("警告", "请先导入所有参数") return try: # 调用计算函数 result = calculate( self.surfs, self.var1, self.var2, self.var3, self.var4, self.var5, self.var6 ) # 显示结果 self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, result) except Exception as e: messagebox.showerror("计算错误", f"计算过程中发生错误: {str(e)}") def import_parameters(self): """导入参数按钮处理函数""" try: # 根据选项分配值 for label, var in self.option_vars.items(): entry_value = self.entry_vars[label].get().strip() if not entry_value: messagebox.showwarning("警告", f"{label}的输入值不能为空") return # 尝试转换为浮点数,如果失败则保留字符串 try: entry_value = float(entry_value) except ValueError: pass # 根据选项分配值 if label == "物距": if var.get() == "有限": self.var1 = entry_value else: self.var1 = 1e15 # 无限远 elif label == "有限": if var.get() == "入瞳直径": self.var2 = entry_value elif var.get() == "半孔径角": self.var3 = entry_value else: self.var4 = entry_value elif label == "视场": if var.get() == "半视场角": self.var5 = entry_value else: self.var6 = entry_value # 显示导入结果 self.log_message("参数导入成功:") self.log_message(f"变量1: {self.var1}, 变量2: {self.var2}") self.log_message(f"变量3: {self.var3}, 变量4: {self.var4},变量5: {self.var5}") self.log_message(f"变量5: {self.var5}, 变量6: {self.var6}") except Exception as e: messagebox.showerror("导入错误", f"参数导入失败: {str(e)}") def log_message(self, message): """在结果文本框中添加消息""" self.result_text.insert(tk.END, message + "\n") self.result_text.see(tk.END) # 滚动到末尾 def load_data(self): """加载数据按钮处理函数""" file_path = self.load_path_entry.get() if not file_path: messagebox.showwarning("警告", "请输入文件路径") return data_dict = parse_file_to_dict(file_path) if data_dict: messagebox.showinfo("成功", f"成功加载 {len(data_dict)} 条数据") self.data_dict = data_dict self.log_message(f"已加载文件: {file_path}") def save_data(self): """保存数据按钮处理函数""" file_path = self.save_path_entry.get() if not file_path: messagebox.showwarning("警告", "请输入保存路径") return # 创建要保存的数据字典 save_dict = {} # 添加光学表面数据 for i, surf in enumerate(self.surfs): save_dict[f"surface_{i+1}_r"] = surf.r save_dict[f"surface_{i+1}_d"] = surf.d save_dict[f"surface_{i+1}_n"] = surf.n save_dict[f"surface_{i+1}_vd"] = surf.vd # 添加变量数据 save_dict["var1"] = self.var1 save_dict["var2"] = self.var2 save_dict["var3"] = self.var3 save_dict["var4"] = self.var4 save_dict["var5"] = self.var5 save_dict["var6"] = self.var6 globals.inputData['objectPosition']= self.var1 globals.inputData['entranceDiameter'] = self.var2 globals.inputData['entrancePupilAngle'] = self.var3 globals.inputData['objectHeight'] = self.var4 globals.inputData['objectAngle'] = self.var5 globals.inputData['entrancePupilAngle'] = self.var6 # 合并其他数据 save_dict.update(self.data_dict) if write_dict_to_file(file_path, save_dict): messagebox.showinfo("成功", "数据保存成功") self.log_message(f"数据已保存到: {file_path}") if __name__ == "__main__": root = tk.Tk() app = MainApplication(root) root.mainloop() 这部分代码已经不够用了,现在我要做一个光学系统的计算程序,请编写一个框架可以文件读取系统参数和保存结果,可以直接输入参数并显示结果;输入的参数包括:各折射面的曲面半径r、厚度d、d光折射率nd、阿贝数vd;入曈直径及入曈位置(相对第一个面顶点);选择输入物在无穷远(此时需输入半视场角)或物在有限远(此时需输入物高,物距,孔径角)。显示可以保存为文件的结果,包括焦距f’、理想像距l’(以透镜最后一面为参考、实际像位置(以透镜最后一面为参考、像方主面位置lH’、出瞳距lp’、理想像高y0’、球差、位置色差、子午场曲xt’、弧矢场曲xs’、像散Δxts’、实际像高、相对畸变、绝对畸变、倍率色差、子午慧差(不考虑符号,绝对值正确即可)具体计算时每个都有对应的函数(留出来,我自己编写)
06-26
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值