前段时间,闲来无事,就用ChatGPT写了一个PythonQT排盘工具,经过不断测试和优化,终于有了一个雏形,GPT果然NB,coding过程如行云流水般顺畅,感觉挺好玩的。
代码运行后展示效果:
附上源码:
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QPushButton, QTextEdit, QDateEdit
from PyQt5.QtCore import QDate
from lunarcalendar import Converter, Solar
from datetime import datetime, timedelta
import swisseph as swe
# 天干和地支的列表
TIANGAN = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
DIZHI = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
# 十神表
TEN_GODS = {
"甲": ["比肩", "劫财", "食神", "伤官", "偏财", "正财", "七杀", "正官", "偏印", "正印"],
"乙": ["劫财", "比肩", "伤官", "食神", "正财", "偏财", "正官", "七杀", "正印", "偏印"],
"丙": ["偏印", "正印", "比肩", "劫财", "食神", "伤官", "偏财", "正财", "七杀", "正官"],
"丁": ["正印", "偏印", "劫财", "比肩", "伤官", "食神", "正财", "偏财", "正官", "七杀"],
"戊": ["七杀", "正官", "偏印", "正印", "比肩", "劫财", "食神", "伤官", "偏财", "正财"],
"己": ["正官", "七杀", "正印", "偏印", "劫财", "比肩", "伤官", "食神", "正财", "偏财"],
"庚": ["偏财", "正财", "七杀", "正官", "偏印", "正印", "比肩", "劫财", "食神", "伤官"],
"辛": ["正财", "偏财", "正官", "七杀", "正印", "偏印", "劫财", "比肩", "伤官", "食神"],
"壬": ["食神", "伤官", "偏财", "正财", "七杀", "正官", "偏印", "正印", "比肩", "劫财"],
"癸": ["伤官", "食神", "正财", "偏财", "正官", "七杀", "正印", "偏印", "劫财", "比肩"]
}
# 藏干表
HIDDEN_STEMS = {
"子": ["癸"],
"丑": ["己", "癸", "辛"],
"寅": ["甲", "丙", "戊"],
"卯": ["乙"],
"辰": ["戊", "乙", "癸"],
"巳": ["丙", "庚", "戊"],
"午": ["丁", "己"],
"未": ["己", "丁", "乙"],
"申": ["庚", "壬", "戊"],
"酉": ["辛"],
"戌": ["戊", "辛", "丁"],
"亥": ["壬", "甲"]
}
def get_tiangandizhi(year, month, day, hour, minute):
# 动态获取立春时间
li_chun_time = get_li_chun_time(year)
print(f"立春时间: {li_chun_time}")
# 将公历日期转换为农历日期
solar_date = Solar(year, month, day)
lunar_date = Converter.Solar2Lunar(solar_date)
print(f"农历日期: {lunar_date}")
# 确定年柱
if datetime(year, month, day, hour, minute) >= li_chun_time:
solar_year = solar_date.year
else:
solar_year = solar_date.year - 1
year_gan = TIANGAN[(solar_year - 4) % 10]
year_zhi = DIZHI[(solar_year - 4) % 12]
# 月柱(假设使用正统的月份推算法)
year_gan_index = (lunar_date.year - 3) % 10
month_gan = TIANGAN[(year_gan_index * 2 + lunar_date.month - 1) % 10]
month_zhi = DIZHI[(lunar_date.month + 1) % 12]
# 日柱(假设使用正统的日干支推算法,通常需要查万年历)
base_date = datetime(1900, 1, 31).date()
days_passed = (solar_date.to_date() - base_date).days
day_gan_index = (days_passed + 40) % 10
day_gan = TIANGAN[(days_passed + 40) % 10]
day_zhi = DIZHI[(days_passed + 40) % 12]
# 时柱
total_hours = hour + minute / 60.0
hour_index = int((total_hours + 1) // 2) % 12
hour_gan_index = (day_gan_index * 2 + hour_index) % 10
hour_gan = TIANGAN[hour_gan_index]
hour_zhi = DIZHI[hour_index]
return {
"年柱": year_gan + year_zhi,
"月柱": month_gan + month_zhi,
"日柱": day_gan + day_zhi,
"时柱": hour_gan + hour_zhi
}
def get_stem_yinyang(stem):
yang_stems = ["甲", "丙", "戊", "庚", "壬"]
return 1 if stem in yang_stems else 0
def calculate_fortune_years(bazi, is_male):
birth_year_stem = bazi["年柱"][0]
birth_month_stem = bazi["月柱"][0]
birth_month_branch = bazi["月柱"][1]
stem_yinyang = get_stem_yinyang(birth_year_stem)
# 只有阳年干男生和阴年干女生,大运顺序排列
is_forward = (stem_yinyang == 1 and is_male) or (stem_yinyang == 0 and not is_male)
month_stem_index = TIANGAN.index(birth_month_stem)
month_branch_index = DIZHI.index(birth_month_branch)
fortune_years = []
for i in range(1, 11):
if is_forward:
gan_index = (month_stem_index + i) % 10
zhi_index = (month_branch_index + i) % 12
else:
gan_index = (month_stem_index - i) % 10
zhi_index = (month_branch_index - i) % 12
fortune_years.append(TIANGAN[gan_index] + DIZHI[zhi_index])
return fortune_years
def get_ten_god(day_master, stem):
return TEN_GODS[day_master][TIANGAN.index(stem)]
def get_hidden_stems_and_gods(day_master, branch):
hidden_stems = HIDDEN_STEMS[branch]
hidden_gods = [get_ten_god(day_master, stem) for stem in hidden_stems]
return hidden_stems, hidden_gods
def find_geju(bazi, day_master):
month_branch = bazi['月柱'][1]
hidden_stems = HIDDEN_STEMS[month_branch]
geju = []
# Check if any hidden stems appear in the heavenly stems
for hidden_stem in hidden_stems:
for pillar in ['年柱', '月柱', '日柱', '时柱']:
tgan = bazi[pillar][0]
# 比肩、劫财不入格
if hidden_stem in tgan and (get_ten_god(day_master, hidden_stem) not in ("比肩","劫财")):
return [get_ten_god(day_master, hidden_stem)]
# 循环月支所有的藏干是否天干透出
# geju.append(get_ten_god(day_master, hidden_stem))
return geju
def get_li_chun_time(year):
jd_start = swe.julday(year, 2, 3, 0) # Start search from February 3rd
jd_end = swe.julday(year, 2, 5, 0) # End search on February 5th
for jd in range(int(jd_start), int(jd_end) + 1):
for minute in range(1440): # 1440 minutes in a day
jd_current = jd + minute / 1440.0 # Increment by minutes
lon, _ = swe.calc_ut(jd_current, swe.SUN)
if lon[0] >= 315: # Check if the longitude is 315 degrees (立春)
gregorian_date = swe.revjul(jd_current, swe.GREG_CAL)
year, month, day, hour = gregorian_date[:4]
minute = int((hour - int(hour)) * 60)
second = int(((hour - int(hour)) * 60 - minute) * 60)
return datetime(year, month, day, int(hour), minute, second) + timedelta(hours=8)
return None # If no transit found
class BaziCalculator(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
date_layout = QHBoxLayout()
date_label = QLabel('出生日期:')
self.date_edit = QDateEdit(QDate.currentDate())
self.date_edit.setCalendarPopup(True)
date_layout.addWidget(date_label)
date_layout.addWidget(self.date_edit)
time_layout = QHBoxLayout()
hour_label = QLabel('出生时间:')
self.hour_combo = QComboBox()
self.hour_combo.addItems([f'{i:02d}' for i in range(24)])
self.minute_combo = QComboBox()
self.minute_combo.addItems([f'{i:02d}' for i in range(60)])
time_layout.addWidget(hour_label)
time_layout.addWidget(self.hour_combo)
time_layout.addWidget(self.minute_combo)
gender_layout = QHBoxLayout()
gender_label = QLabel('性别:')
self.gender_combo = QComboBox()
self.gender_combo.addItems(['男', '女'])
gender_layout.addWidget(gender_label)
gender_layout.addWidget(self.gender_combo)
self.calculate_button = QPushButton('计算')
self.calculate_button.clicked.connect(self.calculate_bazi)
self.result_text = QTextEdit()
self.result_text.setReadOnly(True)
layout.addLayout(date_layout)
layout.addLayout(time_layout)
layout.addLayout(gender_layout)
layout.addWidget(self.calculate_button)
layout.addWidget(self.result_text)
self.setLayout(layout)
self.setWindowTitle('排盘工具')
self.resize(400, 500)
def calculate_bazi(self):
birth_date = self.date_edit.date().toPyDate()
birth_hour = int(self.hour_combo.currentText())
birth_minute = int(self.minute_combo.currentText())
gender = self.gender_combo.currentText()
bazi = get_tiangandizhi(birth_date.year, birth_date.month, birth_date.day, birth_hour, birth_minute)
day_master = bazi['日柱'][0] # 日主
if gender == '男':
result = "八字乾造:\n\n"
else:
result = "八字坤造:\n\n"
# Displaying the BaZi in vertical format with Ten Gods and Hidden Stems
result += "年柱\t月柱\t日柱\t时柱\n\n"
result += f"{get_ten_god(day_master, bazi['年柱'][0])}\t{get_ten_god(day_master, bazi['月柱'][0])}\t{gender}\t{get_ten_god(day_master, bazi['时柱'][0])}\n"
result += f"{bazi['年柱'][0]}\t{bazi['月柱'][0]}\t{bazi['日柱'][0]}\t{bazi['时柱'][0]}\n"
result += f"{bazi['年柱'][1]}\t{bazi['月柱'][1]}\t{bazi['日柱'][1]}\t{bazi['时柱'][1]}\n\n"
# Get hidden stems and gods for each pillar
hidden_stems_gods = {'年柱': [], '月柱': [], '日柱': [], '时柱': []}
for pillar in hidden_stems_gods.keys():
branch = bazi[pillar][1]
hidden_stems, hidden_gods = get_hidden_stems_and_gods(day_master, branch)
hidden_stems_gods[pillar] = list(zip(hidden_stems, hidden_gods))
# Determine the maximum number of hidden stems for proper alignment
max_hidden = max(len(v) for v in hidden_stems_gods.values())
for i in range(max_hidden):
for pillar in ['年柱', '月柱', '日柱', '时柱']:
if i < len(hidden_stems_gods[pillar]):
stem, god = hidden_stems_gods[pillar][i]
result += f"{stem} {god}\t"
else:
result += "\t"
result += "\n"
result += '\n'
# Find and display 格局
geju = find_geju(bazi, day_master)
if geju:
result += "格局: " + ", ".join(geju) + "\n"
is_male = (gender == '男')
fortune_years = calculate_fortune_years(bazi, is_male)
result += "\n大运:\n"
for i, fortune in enumerate(fortune_years):
result += f"第{i*10+1}-{(i+1)*10}年: {fortune}\n"
self.result_text.setText(result)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = BaziCalculator()
ex.show()
sys.exit(app.exec_())