三钱筮法项目技术说明
1. 技术栈
-
GUI框架: CustomTkinter
- 现代化的Tkinter扩展
- 提供美观的界面组件
- 支持主题定制
-
数据存储: JSON
- 卦象数据: gua_info.json
- 记忆数据: memory.json
- 易经解释: detail.json
-
图像处理: PIL (Python Imaging Library)
- 处理图标和图片资源
2. 主要功能模块
2.1 占卜核心 (divination.py)
- 模拟铜钱投掷
- 计算卦象
- 生成爻符
- 解析卦象含义
2.2 界面实现 (threecoins_gui.py)
- 主窗口布局
- 弹出窗口管理
- 事件处理
- 动态显示结果
3. 摇卦说明
3.1 基本原理
- 使用三枚铜钱模拟传统的筮法
- 每次投掷产生一个爻
- 六次投掷形成完整卦象
3.2 计算规则
- 三枚铜钱正面(阳)记为1,反面(阴)记为0
- 计算规则:
- 三背(0) = 老阳
- 一正二背(1) = 少阴
- 二正一背(2) = 少阳
- 三正(3) = 老阴
3.3 卦象形成
- 下卦由下三爻组成
- 上卦由上三爻组成
- 组合形成六十四卦之一
4. 特色功能
- 卦序歌记忆
- 八卦口诀查看
- 卦名读音对照
- 详细卦象解释
- 滚动显示界面
- 实时动态更新
5. 使用说明
- 点击"起卦"按钮开始占卜
- 系统自动模拟六次投掷
- 显示完整卦象和解释
- 可查看相关参考资料
6. 代码实现
加入相关模块:
requirements.txt
pillow
customtkinter
requests
beautifulsoup4
zhdate
sv-ttk
tinui
tkcalendar
tkinter
import random
import json
import tkinter as tk
from tkinter import Labeldef yao(n):"""计算每爻的阴阳和符号"""if not 0 <= n <= 3:raise ValueError("输入数字必须在0-3之间")if n == 0: # 三背为重,老阳return 1, "O"elif n == 1: # 一字为拆,少阴return 0, "--"elif n == 2: # 两字为单,少阳return 1, "一"else: # 三字为交,老阴return 0, "X"def throw_coins():"""模拟投掷三枚铜钱"""coins = [random.choice([0, 1]) for _ in range(3)]return sum(coins)def gua(n1, n2, n3):"""由三爻得一卦"""return 1 + (not n3) + (not n2)*2 + (not n1)*4def duan_gua(inner, outer):"""由内外卦得到卦名和对应关系"""bagua_names = ['乾', '兑', '离', '震', '巽', '坎', '艮', '坤']gua_list = [['乾为天','天泽履','天火同人','天雷无妄','天风姤','天水讼','天山遁','天地否'],['泽天夬','兑为泽','泽火革','泽雷随','泽风大过','泽水困','泽山咸','泽地萃'],['火天大有','火泽睽','离为火','火雷噬嗑','火风鼎','火水未济','火山旅','火地晋'],['雷天大壮','雷泽归妹','雷火丰','震为雷','雷风恒','雷水解','雷山小过','雷地豫'],['风天小畜','泽中孚','风火家人','风雷益','巽为风','风水涣','风山渐','风地观'],['水天需','水泽节','水火既济','水雷屯','水风井','坎为水','水山蹇','水地比'],['山天大畜','山泽损','山火贲','山雷颐','山风蛊','山水蒙','艮为山','山地剥'],['地天泰','地泽临','地火明夷','地雷复','地风升','地水师','地山谦','坤为地']]if not (1 <= inner <= 8 and 1 <= outer <= 8):raise ValueError("卦序号必须在1-8之间")return (gua_list[inner-1][outer-1], bagua_names[inner-1], bagua_names[outer-1])def calculate_gua(numbers):"""计算卦象"""if len(numbers) != 6:raise ValueError("必须输入6个数字")# 计算六爻的阴阳和符号yao_results = [yao(n) for n in numbers]yao_values = [y[0] for y in yao_results]yao_symbols = [y[1] for y in yao_results]# 计算内外卦inner_gua = gua(yao_values[0], yao_values[1], yao_values[2])outer_gua = gua(yao_values[3], yao_values[4], yao_values[5])gua_result = duan_gua(inner_gua, outer_gua)return (*gua_result, yao_symbols)def auto_divination():"""自动进行六次铜钱占卜"""numbers = [throw_coins() for _ in range(6)]result, inner_name, outer_name, yao_symbols = calculate_gua(numbers)return result, numbers, inner_name, outer_name, yao_symbolsclass DivinationGUI:def __init__(self):# 加载卦象详细解释数据with open('source/gua_info.json', 'r', encoding='utf-8') as f:self.detail_data = json.load(f)def show_gua_detail(self, gua_name):"""显示某卦的详细解释"""if gua_name in self.detail_data:detail_info = self.detail_data[gua_name]# 在界面上显示详细信息text = f"卦名: {gua_name}\n\n"text += f"卦辞: {detail_info['卦辞']}\n\n"text += "爻辞:\n"for yao, desc in detail_info['爻辞'].items():text += f"{yao}: {desc}\n"# 更新界面显示self.detail_text.delete('1.0', tk.END) self.detail_text.insert('1.0', text)if __name__ == "__main__":try:result, numbers, inner_name, outer_name, yao_symbols = auto_divination()print(f"六次投掷结果: {numbers}")print(f"得到的卦象: {result}")print(f"内卦为: {inner_name}")print(f"外卦为: {outer_name}")print(f"完整卦名: {outer_name}{inner_name}卦")coin_meanings = {0: "三背(老阳)",1: "一字(少阴)",2: "两字(少阳)", 3: "三字(老阴)"}print("\n详细解释:")for i, (n, symbol) in enumerate(zip(numbers, yao_symbols), 1):print(f"第{i}爻: {n}个铜钱朝上 - {coin_meanings[n]} {symbol}")print("\n卦象图示(从下往上):")for symbol in yao_symbols:print(f" {symbol}")except ValueError as e:print(f"错误: {e}")
让我详细解释这段代码的功能:
1. 核心功能模块
1.1 爻的计算 (yao函数)
def yao(n):"""计算每爻的阴阳和符号"""if n == 0: # 三背为重,老阳return 1, "O"elif n == 1: # 一字为拆,少阴return 0, "--"# ...
- 将铜钱结果转换为爻的阴阳属性和符号
- 返回值:(阴阳值, 符号)
1.2 铜钱投掷模拟 (throw_coins函数)
def throw_coins():"""模拟投掷三枚铜钱"""coins = [random.choice([0, 1]) for _ in range(3)]return sum(coins)
- 随机模拟三枚铜钱的正反面
- 返回正面朝上的数量
1.3 卦的计算 (gua函数)
def gua(n1, n2, n3):"""由三爻得一卦"""return 1 + (not n3) + (not n2)*2 + (not n1)*4
- 将三个爻的阴阳值转换为八卦序号
1.4 卦象判断 (duan_gua函数)
def duan_gua(inner, outer):"""由内外卦得到卦名和对应关系"""bagua_names = ['乾', '兑', '离', '震', '巽', '坎', '艮', '坤']gua_list = [...]
- 根据内外卦的序号确定完整卦象
- 返回:(卦名, 内卦名, 外卦名)
2. 自动占卜流程
2.1 计算卦象 (calculate_gua函数)
def calculate_gua(numbers):"""计算卦象"""# 计算六爻的阴阳和符号yao_results = [yao(n) for n in numbers]# ...
- 处理六个数字得到完整卦象
- 返回卦象信息和爻符号
2.2 自动占卜 (auto_divination函数)
def auto_divination():"""自动进行六次铜钱占卜"""numbers = [throw_coins() for _ in range(6)]# ...
- 自动执行完整的占卜过程
- 返回所有相关结果
3. GUI界面类
class DivinationGUI:def __init__(self):# 加载卦象数据with open('source/gua_info.json', 'r', encoding='utf-8') as f:self.detail_data = json.load(f)
- 提供图形界面
- 加载和显示卦象详细信息
4. 占卜结果展示
主程序展示:
- 六次投掷的数字结果
- 得到的卦象名称
- 内外卦信息
- 每爻的详细解释
- 卦象的图形表示
5. 使用示例
result, numbers, inner_name, outer_name, yao_symbols = auto_divination()
print(f"六次投掷结果: {numbers}")
print(f"得到的卦象: {result}")
这段代码实现了一个完整的易经占卜系统,包括:
- 铜钱投掷的模拟
- 爻的计算和转换
- 卦象的判断和解释
- 图形界面的显示
- 详细结果的输出
6. 界面实现
import customtkinter as ctk
from divination import auto_divination
from PIL import Image
import json# 八卦列表
gua8_hanzi = ['乾','兑','离','震','巽','坎','艮','坤']
gua8_symbols = ['☰', '☱', '☲', '☳', '☴', '☵', '☶', '☷']# 加载记忆数据
try:with open('source/memory.json', 'r', encoding='utf-8') as f:memory_data = json.load(f)print("成功加载记忆数据:", memory_data.keys())
except Exception as e:print(f"加载记忆数据失败: {e}")memory_data = {}class MemoryWindow:def __init__(self, title, content, parent):self.window = ctk.CTkToplevel(parent)self.window.title(title)self.window.geometry("500x600")# 先隐藏窗口,等位置设置好后再显示self.window.withdraw()# 等待父窗口更新完成parent.update_idletasks()# 获取父窗口的位置和大小parent_width = parent.winfo_width()parent_height = parent.winfo_height()parent_x = parent.winfo_x()parent_y = parent.winfo_y()# 计算父窗口的中心点parent_center_x = parent_x + parent_width // 2parent_center_y = parent_y + parent_height // 2# 设置新窗口的位置window_width = 500window_height = 600x = parent_center_x - window_width // 2y = parent_center_y - window_height // 2# 应用位置self.window.geometry(f"{window_width}x{window_height}+{x}+{y}")# 显示窗口self.window.deiconify()# 设置窗口始终显示在最前面self.window.transient(parent)self.window.grab_set()# 创建滚动框架self.scroll_frame = ctk.CTkScrollableFrame(self.window,width=460,height=550)self.scroll_frame.pack(padx=20, pady=20, fill="both", expand=True)# 显示内容if isinstance(content, list):# 卦序歌格式for line in content:ctk.CTkLabel(self.scroll_frame,text=line,font=("Microsoft YaHei", 14),wraplength=420).pack(pady=5)elif isinstance(content, dict):if title == "六十四卦读音":# 读音格式 - 每行显示两个items = list(content.items())for i in range(0, len(items), 2): # 每次取两个项目frame = ctk.CTkFrame(self.scroll_frame)frame.pack(pady=2, fill="x", padx=20)# 左边的卦名和读音left_gua, left_pinyin = items[i]ctk.CTkLabel(frame,text=f"{left_gua}:{left_pinyin}",font=("Microsoft YaHei", 14),width=200,anchor="w").pack(side="left", padx=10)# 右边的卦名和读音(如果存在)if i + 1 < len(items):right_gua, right_pinyin = items[i + 1]ctk.CTkLabel(frame,text=f"{right_gua}:{right_pinyin}",font=("Microsoft YaHei", 14),width=200,anchor="w").pack(side="left", padx=10)else:# 八卦口诀格式for gua, verses in content.items():frame = ctk.CTkFrame(self.scroll_frame)frame.pack(pady=10, fill="x")ctk.CTkLabel(frame,text=f"【{gua}】",font=("Microsoft YaHei", 14, "bold")).pack(pady=5)for verse in verses:ctk.CTkLabel(frame,text=verse,font=("Microsoft YaHei", 14)).pack(pady=2)try:with open('source/gua_info.json', 'r', encoding='utf-8') as f:gua_info = json.load(f)print(f"成功加载卦象数据,共有 {len(gua_info['gua'])} 个卦象")
except Exception as e:print(f"加载卦象数据失败: {e}")gua_info = {'gua': []}# 打印所有卦象以调试
# print(gua_info)class DivinationGUI:def __init__(self, master=None):ctk.set_appearance_mode("green")ctk.set_default_color_theme("green")self.root = ctk.CTk()self.root.title("三钱筮法")self.root.geometry("600x850")# 修改标题部分为框架self.title_frame = ctk.CTkFrame(self.root)self.title_frame.pack(pady=10, fill="x")# 标题标签self.title_label = ctk.CTkLabel(self.title_frame,text="三钱筮法",font=ctk.CTkFont(size=18, weight="bold"))self.title_label.pack(side="left", padx=20)# 卦序歌按钮 - 改为标签样式self.sequence_button = ctk.CTkLabel(self.title_frame,text="卦序歌",font=ctk.CTkFont(size=14),cursor="hand2" # 鼠标悬停时显示手型)self.sequence_button.pack(side="right", padx=10)self.sequence_button.bind("<Button-1>", lambda e: self.show_sequence()) # 绑定点击事件# 八卦口诀按钮 - 改为标签样式self.memory_button = ctk.CTkLabel(self.title_frame,text="八卦口诀",font=ctk.CTkFont(size=14),cursor="hand2" # 鼠标悬停时显示手型)self.memory_button.pack(side="right", padx=10)self.memory_button.bind("<Button-1>", lambda e: self.show_memory()) # 绑定点击事件# 卦名读音按钮self.pronunciation_button = ctk.CTkLabel(self.title_frame,text="卦名读音",font=ctk.CTkFont(size=14),cursor="hand2")self.pronunciation_button.pack(side="right", padx=10)self.pronunciation_button.bind("<Button-1>", lambda e: self.show_pronunciation())# 创建一个滚动容器self.scroll_frame = ctk.CTkScrollableFrame(self.root,width=600,height=700)self.scroll_frame.pack(padx=20, pady=(10, 0), fill="both", expand=True)# 将 main_frame 移动到滚动容器中self.main_frame = ctk.CTkFrame(self.scroll_frame)self.main_frame.pack(padx=10, pady=5, fill="both", expand=True)# 创建显示标签self.result_label = ctk.CTkLabel(self.main_frame,text="",font=("Microsoft YaHei", 14),justify="left",wraplength=450)self.result_label.pack(padx=20, pady=20)# 创建卦象框架self.gua_frame = ctk.CTkFrame(self.main_frame)self.gua_frame.pack(pady=20)# 只保留一个标签self.upper_gua_label = ctk.CTkLabel(self.gua_frame,text="",font=("Microsoft YaHei", 16),width=450,anchor="w",justify="left")self.upper_gua_label.pack(pady=10)self.lower_gua_label = ctk.CTkLabel(self.gua_frame,text="",font=("Microsoft YaHei", 16),width=450,anchor="w",justify="left")self.lower_gua_label.pack(pady=10)self.combined_gua_label = ctk.CTkLabel(self.gua_frame,text="",font=("Microsoft YaHei", 16),width=450,anchor="w",justify="left")self.combined_gua_label.pack(pady=10)# 按钮框架固定在底部self.button_frame = ctk.CTkFrame(self.root)self.button_frame.pack(pady=10, padx=20, fill="x", side="bottom")# 起卦按钮self.divine_button = ctk.CTkButton(self.button_frame, text="起卦",font=ctk.CTkFont(size=16),command=self.perform_divination,width=120,height=40)self.divine_button.pack(side="left", padx=10)# 退出按钮self.quit_button = ctk.CTkButton(self.button_frame,text="退出",font=ctk.CTkFont(size=16),command=self.root.quit,width=120,height=40,fg_color="#D35B58",hover_color="#C77C78")self.quit_button.pack(side="right", padx=10)# 设置初始文本self.result_label.configure(text="点击'起卦'按钮开始占卜")self.upper_gua_label.configure(text="")# 移除自动起卦# self.perform_divination()def perform_divination(self):# 获取卦象结果result, numbers, upper_gua, lower_gua, yao_symbols = auto_divination()# 在控制台打印结果以便调试print("卜卦结果:", result)print("数字序列:", numbers)print("上卦:", upper_gua)print("下卦:", lower_gua)print("爻符号:", yao_symbols)# 查找对应的卦象信息gua_info_found = Nonefor gua in gua_info['gua']:if gua['name'] == result:gua_info_found = guabreakif gua_info_found:# 显示卦象信息display_text = (f"═══════════ 卜卦结果 ═══════════\n"f"六爻数字: {' '.join(map(str, numbers))}\n"f"爻的符号: {' '.join(yao_symbols)}\n"f"═══════════ 卦象解析 ═══════════\n"f"卦象: {gua_info_found['name']}({gua_info_found['alias']})\n"f"等级: {gua_info_found['level']}\n"f"描述: {gua_info_found['description']}\n"f"诗曰: {gua_info_found['poem']}\n"f"解释: {gua_info_found['explanation']}\n"f"事业: {gua_info_found['career']}\n"f"经商: {gua_info_found['business']}\n"f"名誉: {gua_info_found['fame']}\n"f"外出: {gua_info_found['travel']}\n"f"婚姻: {gua_info_found['marriage']}\n"f"决策: {gua_info_found['decision']}")self.result_label.configure(text=display_text)# 显示卦象组成upper_symbol = gua8_symbols[gua8_hanzi.index(upper_gua)]lower_symbol = gua8_symbols[gua8_hanzi.index(lower_gua)]# 将三个标签的内容合并为一行gua_text = (f"上卦:{upper_gua} {upper_symbol} "f"下卦:{lower_gua} {lower_symbol} "f"组合卦象:{result}")# 只使用一个标签显示所有信息self.upper_gua_label.configure(text=gua_text)# 隐藏其他两个标签self.lower_gua_label.pack_forget()self.combined_gua_label.pack_forget()else:error_text = (f"未找到卦象信息:{result}\n"f"完整卜卦结果:\n"f"数字:{numbers}\n"f"爻符号:{yao_symbols}\n"f"上卦:{upper_gua}\n"f"下卦:{lower_gua}")self.result_label.configure(text=error_text)print(error_text)def show_memory(self):print("当前 memory_data:", memory_data)if "八卦口诀" in memory_data:MemoryWindow(memory_data["八卦口诀"]["title"],memory_data["八卦口诀"]["content"],self.root)else:print("未找到八卦口诀数据")def show_sequence(self):if "卦序歌" in memory_data:MemoryWindow(memory_data["卦序歌"]["title"],memory_data["卦序歌"]["content"],self.root)def show_pronunciation(self):if "卦名读音" in memory_data:MemoryWindow(memory_data["卦名读音"]["title"],memory_data["卦名读音"]["content"],self.root)def run(self):self.root.mainloop()if __name__ == "__main__":app = DivinationGUI()app.run()