pyqt做出的对初学者十分友好的紫微斗数排命盘程序

先展示界面:

命盘上显示的信息比较多,特别是对于初学者来说,飞星的时候禄权科忌飞到哪里去了经常容易忘记,甚至转动两下都记不清自己在飞哪颗星,这个程序就不存在这个问题。其实我对紫微斗数并不内行,里面的飞星逻辑是不是符合规则也搞不太清楚,基本上是根据AI的回答做的,做这个主要是想学会用pyqt做一个完整的有那么点象软件的东西。☺

下面是这个程序用到的技术归纳:

1、在程序启动时从文件反序列化对象,在程序关闭时自动序列化对象。

    def showEvent(self, event):
        if not self.showed:
        # 窗体显示后载入上次关闭时的命局
            try:
                with open(palaces_file, 'rb') as file:
                    info = pickle.load(file)
                    self.fill_palaces(info)
            except Exception:
                self.fly_star_menu.setEnabled(False)
                html_file_path = os.path.join(os.getcwd(), 'htmls/note.html')
                self.web_view.load(QtCore.QUrl.fromLocalFile(html_file_path))
            self.showed = True
            event.accept()

def on_app_quit(app):
    # 程序退出时保存当前命局
    with open(palaces_file, 'wb') as file:
        obj = (app.name, app.gender, app.solar_year, app.solar_month,
         app.solar_day, app.lunar_year, app.lunar_month, app.lunar_day,
         app.year_stem, app.year_branch, app.month_stem, app.month_branch,
         app.day_stem, app.day_branch, app.time_stem, app.time_branch,
         app.is_leap_month)
        pickle.dump(obj, file)

为主窗口类定义一个处理窗口显示事件的函数showEvent。由于这个函数不但在程序启动时会执行,窗口最小化后重新显示也会执行,所以设置一个标志值showed,让反序列化对象的代码只在程序启动时执行一次。序列化和反序列化操作用的python自带模块pickle,比较简单。上面的代码在反序列化时用try-except结构处理了异常,防止反序列化失败导致程序崩溃。

2、用pandas进行二维数据表检索

程序中部分星曜安星规则使用程序逻辑实现的,也有部分是直接查表得出的。实际上所有星曜安设都可以预先制作表格通过查表进行。下面是用pandas加载csv文件并查找数据的代码片段,值得注意的是使用“index_col=0”选项后避免了pandas自动添加多余的索引列,用“dataframe.loc[]”查找元素更方便,可以直接使用第一列的值定位行:

# 加载天干四化表
stem_changes_df = pd.read_csv('stem_changes.csv', index_col=0)

#……
# 查表
changes = []
for star in self.branch_palaces[palace_index].stars:
    try:
        find = stem_changes_df.loc[star.name, stem]
    except Exception as e:
        find = None
    if find is not None:
        changes.append(find)

# 将相关对象的相关属性值设置为查到的结果
setattr(self.branch_palaces[palace_index], attr_name, changes)

3、使用lambda函数给菜单项处理函数传递参数

一般给菜单项连接处理函数时,只能传不带括号的函数名,要给处理函数传递参数,最可靠而简单的方法是使用lambda函数。下面是一个代码片段:

# 流月飞星
monthly_period_menu = self.fly_star_menu.addMenu("流月飞星")
for i in range(1, 13):
    adj_index = i - 1
    menu = monthly_period_menu .addMenu(f"{month_names[adj_index]}")
    for index, palace in enumerate(fly_palaces):
        action = QAction(palace, self)
        action.triggered.connect(lambda checked, step=adj_index, p=index:
                                 self.month_fly_star(step, p))
        menu.addAction(action)

4、取消窗口标题栏后使用鼠标事件处理函数移动和最小化窗口(里面还写了用键盘事件处理程序退出的代码,但没起作用,还没弄清楚原因——根据AI的回答,可能是QWebEngineView或其他子组件拦截了Esc键事件,可以尝试在 MainApp 窗口中设置 setFocusPolicy(Qt.StrongFocus),确保窗口接收到Esc按键事件。一试果然,AI不余欺也!):

        self._drag_position = None

        #……

        # 取消标题栏
        self.setWindowFlags(Qt.FramelessWindowHint)

        #……

    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            # 记录鼠标按下时的位置
            self._drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()
        elif event.button() == Qt.RightButton:
            # 鼠标右键按下时将窗口最小化
            self.showMinimized()
            event.accept()

    def mouseMoveEvent(self, event: QMouseEvent):
        if event.buttons() == Qt.LeftButton and self._drag_position is not None:
            # 移动窗体
            self.move(event.globalPos() - self._drag_position)
            event.accept()

    def mouseReleaseEvent(self, event: QMouseEvent):
        # 释放鼠标时重置拖动位置
        self._drag_position = None
        event.accept()

    def keyPressEvent(self, event):
        # 判断按下的键是否为 Esc 键
        if event.key() == Qt.Key_Escape:
            self.close()
        # 调用父类的 keyPressEvent 方法,确保其他按键事件能正常处理
        super().keyPressEvent(event)    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            # 记录鼠标按下时的位置
            self._drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()
        elif event.button() == Qt.RightButton:
            # 鼠标右键按下时将窗口最小化
            self.showMinimized()
            event.accept()

    def mouseMoveEvent(self, event: QMouseEvent):
        if event.buttons() == Qt.LeftButton and self._drag_position is not None:
            # 移动窗体
            self.move(event.globalPos() - self._drag_position)
            event.accept()

    def mouseReleaseEvent(self, event: QMouseEvent):
        # 释放鼠标时重置拖动位置
        self._drag_position = None
        event.accept()

    def keyPressEvent(self, event):
        # 判断按下的键是否为 Esc 键
        if event.key() == Qt.Key_Escape:
            self.close()
        # 调用父类的 keyPressEvent 方法,确保其他按键事件能正常处理
        super().keyPressEvent(event)

5、jinja2模版系统处理命盘的渲染

本程序的模版文件中使用了jinja2的循环、分支结构,以及在模版中设置变量,使用内置的loop.index变量取得索引值、使用过滤操作取得列表长度({% if palace.stars|length > 0 %})等技巧,完整代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Palaces Table</title>
    <link rel="stylesheet" href="{{css}}">
</head>
<body>
    <div class="frame">
    <!-- 划三方四正 -->
    <canvas id="myCanvas" width="902" height="902"></canvas>
    {# 渲染中央宫信息 #}
    <div class="grid-container">
        <div class="center-container large-char item0">
            <div class="emphasize center-block">{{center_palace.destiny_type}}</div><br>
            <div class="center-block">
                <div class="h-justify">
                    <span><strong>姓名:</strong>{{center_palace.name}}</span>
                    <span>{{center_palace.yin_yang}}{{center_palace.gender}}</span>
                    <span class="emphasize"><strong>{{center_palace.stem_branch[:2]}}</strong></span>
                    <span>{{center_palace.age}}岁</span>
                </div>
                <div><span><strong>公历生日:</strong>{{center_palace.solar_birthday}}</span></div>
                <div><span><strong>农历生日:</strong>{{center_palace.lunar_birthday}}</span></div>
                <div><span><strong>八字干支:</strong>{{center_palace.stem_branch}}</span></div><br>
            </div>
            <div class="center-block large-char"><strong>四化口诀</strong></div>
            <div class="center-block small-char">
                <span>甲廉破武阳,乙机梁紫阴,丙同机昌廉,丁阴同机巨,</span><br>
                <span>戊贪阴右机,己武贪梁曲,庚阳武同相,辛巨阳曲昌,</span><br>
                <span>壬梁紫左武,癸破巨阴贪。飞星问凶吉,事财命褔疾。</span><br>
            </div><br>
            <div class="center-block large-char ke"><strong>
            {% if not center_palace.fly_star_status is none %}
            {{center_palace.fly_star_status}}
            {% else %}
            天盘命局
            {% endif %}</strong></div>
            <br><div class="center-block"><span>{{center_palace.today}}</span></div>
        </div>
        {# 渲染地支宫信息 #}
        {% for palace in branch_palaces %}
            <div class="item{{loop.index}}">
                {% if palace.stars|length > 0 %} {# 输出宫中星曜信息 #}
                <div>
                    {% for star in palace.stars %}
                        {% if star.type == 0 %}
                            {% set class_name = "main-star" %}
                        {% elif star.type == 1 %}
                            {% set class_name = "auxiliary-star" %}
                        {% elif star.type == 2 %}
                            {% set class_name = "fortune-star" %}
                        {% elif star.type == 3 %}
                            {% set class_name = "valued-star" %}
                        {% elif star.type == 4 %}
                            {% set class_name = "sha-star" %}
                        {% else %}
                            {% set class_name = "" %}
                        {% endif %}
                    <div class="star-div">
                        <span class="{{class_name}}">{{ star.name }}</span>
                        <span class="star-status">{{ star.status }}</span>
                        <span>
                        {% if star.stem_change != '' %}
                            {% if star.stem_change == '禄' %}
                                <span class="circled lu middle-char">{{ star.stem_change }}</span>{{ palace.lu_status }}
                            {% elif star.stem_change == '权' %}
                                <span class="circled quan middle-char">{{ star.stem_change }}</span>{{ palace.quan_status }}
                            {% elif star.stem_change == '科' %}
                                <span class="circled ke middle-char">{{ star.stem_change }}</span>{{ palace.ke_status }}
                            {% else %}
                                <span class="circled ji middle-char">{{ star.stem_change }}</span>{{ palace.ji_status }}
                            {% endif %}
                        {% endif %}
                        </span>
                        {% if star.self_change != '' %}
                            {% if star.self_change == '禄' %}
                                <span class="circled lu rectangle middle-char">{{ star.self_change }}</span>
                            {% elif star.self_change == '权' %}
                                <span class="circled rectangle quan middle-char">{{ star.self_change }}</span>
                            {% elif star.self_change == '科' %}
                                <span class="circled rectangle ke middle-char">{{ star.self_change }}</span>
                            {% else %}
                                <span class="circled rectangle ji middle-char">{{ star.self_change }}</span>
                            {% endif %}
                        {% else %}
                            <span></span>
                        {% endif %}
                    </div>
                    {% endfor %}
                </div>
                {% endif %}
                {# 星曜信息输出完成,下面输出飞星四化 #}
                <div class="center-container">
                    <div class="change-div">
                    {% if palace.dayun_changes|length >0 %}
                        {% for dayun_change in palace.dayun_changes %}
                            {% if dayun_change == '禄' %}
                                <span class="bordered small-char lu"><strong>运{{ dayun_change }}</strong></span>
                            {% elif dayun_change == '权' %}
                                <span class="bordered small-char quan"><strong>运{{ dayun_change }}</strong></span>
                            {% elif dayun_change == '科' %}
                                <span class="bordered small-char ke"><strong>运{{ dayun_change }}</strong></span>
                            {% elif dayun_change == '忌'  %}
                                <span class="bordered small-char ji"><strong>运{{ dayun_change }}</strong></span>
                            {% endif %}
                        {% endfor %}
                    {% endif %}
                    {% if palace.flow_changes|length >0 %}
                        {% for flow_change in palace.flow_changes %}
                            {% if flow_change == '禄' %}
                                <span class="bordered small-char lu"><strong>流{{ flow_change }}</strong></span>
                            {% elif flow_change == '权' %}
                                <span class="bordered small-char quan"><strong>流{{ flow_change }}</strong></span>
                            {% elif flow_change == '科' %}
                                <span class="bordered small-char ke"><strong>流{{ flow_change }}</strong></span>
                            {% elif flow_change == '忌'  %}
                                <span class="bordered small-char ji"><strong>流{{ flow_change }}</strong></span>
                            {% endif %}
                        {% endfor %}
                    {% endif %}
                    {% if palace.fly_changes|length >0 %}
                        {% for fly_change in palace.fly_changes %}
                            {% if fly_change == '禄' %}
                                <span class="bordered small-char lu"><strong>飞{{ fly_change }}</strong></span>
                            {% elif fly_change == '权' %}
                                <span class="bordered small-char quan"><strong>飞{{ fly_change }}</strong></span>
                            {% elif fly_change == '科' %}
                                <span class="bordered small-char ke"><strong>飞{{ fly_change }}</strong></span>
                            {% elif fly_change == '忌'  %}
                                <span class="bordered small-char ji"><strong>飞{{ fly_change }}</strong></span>
                            {% endif %}
                        {% endfor %}
                    {% endif %}
                    </div>
                </div>
                {# 输出身宫及大运流年斗君等标志 #}
                <div class="flag-div">
                    {% if palace.is_self_palace %}
                        <span class="flag">身宫</span>
                    {% endif %}
                    {% if palace.is_dayun_palace %}
                        <span class="flag">大运</span>
                    {% endif %}
                    {% if palace.is_flow_palace %}
                        <span class="flag">流年</span>
                    {% endif %}
                    {% if palace.is_doujun_palace %}
                        <span class="flag">斗君</span>
                    {% endif %}
                </div>
                <div class="month-div">{{palace.flow_month}}</div>
                {# 用流年月时等飞星时的宫名 #}
                {% if not palace.fly_palace_name is none %}
                    {% if  palace.fly_palace_name == '飞命' %}
                        {% set class_name = ' destiny' %}
                    {% else %}
                        {% set class_name = '' %}
                    {% endif %}
                    <div class="center-container">{# 居中对齐容器 #}
                        <div class="fly-palace large-char center-block{{class_name}}">
                            {{palace.fly_palace_name}}
                        </div>
                    </div>
                {% endif %}
                {% if not palace.flow_palace_name is none %}
                    {% if  palace.flow_palace_name == '年命' %}
                        {% set class_name = ' destiny' %}
                    {% else %}
                        {% set class_name = '' %}
                    {% endif %}
                    <div class="center-container">{# 居中对齐容器 #}
                        <div class="flow-palace large-char center-block{{class_name}}">
                            {{palace.flow_palace_name}}
                        </div>
                    </div>
                {% endif %}
                {% if not palace.month_palace_name is none %}
                    {% if  palace.month_palace_name == '月命' %}
                        {% set class_name = ' destiny' %}
                    {% else %}
                        {% set class_name = '' %}
                    {% endif %}
                    <div class="center-container">{# 居中对齐容器 #}
                        <div class="month-palace large-char center-block{{class_name}}">
                            {{palace.month_palace_name}}
                        </div>
                    </div>
                {% endif %}
                {% if not palace.day_palace_name is none %}
                    {% if  palace.day_palace_name == '日命' %}
                        {% set class_name = ' destiny' %}
                    {% else %}
                        {% set class_name = '' %}
                    {% endif %}
                    <div class="center-container">{# 居中对齐容器 #}
                        <div class="day-palace center-block{{class_name}}">
                            {{palace.day_palace_name}}
                        </div>
                    </div>
                {% endif %}
                {% if not palace.time_palace_name is none %}
                    {% if  palace.time_palace_name == '时命' %}
                        {% set class_name = ' destiny' %}
                    {% else %}
                        {% set class_name = '' %}
                    {% endif %}
                    <div class="center-container">{# 居中对齐容器 #}
                        <div class="time-palace center-block{{class_name}}">
                            {{palace.time_palace_name}}
                        </div>
                    </div>
                {% endif %}
                {# 输出宫名干支等信息 #}
                <div class="bottom-div">
                    {% set age = palace.dayun_start_age + 9 %}
                    <span class="large-char quan">
                        {% if palace.flow_age > 0 %}
                        {{palace.flow_age}}岁
                        {% endif %}
                    </span>
                    <span class="large-char lu"><strong>{{palace.dayun_start_age}}~{{age}}</strong></span>
                    <span class="stem"><strong>{{palace.stem}}</strong></span>
                    {% if palace.dayun_palace_name != '' %}
                        {% if  palace.dayun_palace_name == '大命' %}
                            <span class="large-char destiny"><strong>{{palace.dayun_palace_name}}</strong></span>
                        {% else %}
                            <span class="large-char quan"><strong>{{palace.dayun_palace_name}}</strong></span>
                        {% endif %}
                    {% endif %}

                    <span class="palace-name">{{palace.name}}</span>
                    <span class="large-char"><strong>{{palace.branch}}</strong></span>
                </div>
            </div>
        {% endfor %}
    </div>
    </div>
    <script>
        function drawDashedLine(ctx, index) {
            p1 = (index + 4) % 12
            p2 = (index + 8) % 12
            p3 = (index + 6) % 12
            // 绘制虚线
            ctx.beginPath();
            ctx.lineTo(points[index][0], points[index][1]);
            ctx.lineTo(points[p1][0], points[p1][1]);
            ctx.lineTo(points[p2][0], points[p2][1]);
            ctx.lineTo(points[index][0], points[index][1]);
            ctx.lineTo(points[p3][0], points[p3][1]);
            ctx.stroke();
        }
        const points = {0: [580, 685], 1: [355, 685], 2: [225, 685], 3: [225, 580],
            4: [225, 325], 5: [225, 225], 6: [355, 225], 7: [580, 225],
            8: [685, 225], 9: [685, 325], 10: [685, 580], 11: [685, 685]};

        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');
        // 设置虚线样式
        ctx.setLineDash([5, 5]);
        ctx.strokeStyle = "rgb(0 0 255 / 0.5)";
        drawDashedLine(ctx, {{index}});

    </script>
</body>
</html>

配合模版的css文件:

body {
    background-color: beige;
    font-family: '微软雅黑', '宋体', Geneva, Tahoma, sans-serif;
}
div{
    background-color: transparent;
}
.center-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
.center-block {
    margin:2px auto;
    display: inline-block;
}
/* 将子元素分散对齐的容器 */
.h-justify {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
}

.star-div {
    display: grid;
    grid-template-columns: 40% 10% 30% 20%;
    place-items: center;
    width: 60%;
}
.change-div {
    position: absolute;
    top: 120px;
    display: flex;
    flex-direction: row;
}
.month-div {
    position: absolute;
    left: 180px;
    top:145px;
    font-weight: bold;
    color: blue;
}
.fly-palace {
    position: absolute;
    top: 75px;
    height: 48px;
    left: 190px;
    border: green 2px solid;
    border-radius: 15%;
    color: green;
    margin-right: 10px;
}
.flow-palace {
    position: absolute;
    top: 15px;
    height: 48px;
    left: 190px;
    border: #752f03 2px solid;
    border-radius: 15%;
    color: #752f03;
    margin-right: 10px;
}
.flag-div{
    position: absolute;
    top: 145px;
    margin-left: 2px;
}
.flag{
    border: 2px solid deeppink;
    color: deeppink;
    margin: 2px auto;
}
.bottom-div{
    position: absolute;
    top: 170px;
    width: 100%;
    border-top: 2px solid #000;
    display: grid;
    grid-template-columns: 1fr 1fr 15%; /* 定义三列 */
    grid-template-rows: repeat(2, auto); /* 定义两行,每行高度自动 */
}

.bottom-div span:nth-child(1), .bottom-div span:nth-child(4) {
    text-align: left; /* 左边的元素左对齐 */
    margin-left: 2px;
}

.bottom-div span:nth-child(2), .bottom-div span:nth-child(5) {
    text-align: center; /* 中间的元素居中对齐 */
    margin-right: 50px;
}

.bottom-div span:nth-child(3),  .bottom-div span:nth-child(6){
    text-align: right; /* 右边的元素右对齐 */
    margin-right: 5px;
    border-left: 2px solid #000;
}

.large-char{
    font-size: 1.2em;
}
.middle-char {
    font-size: 1.1em;
}
.small-char{
    font-size: 0.95em;
}
.emphasize{
    color: #ff0000;
    font-weight: bold;
    font-size: 1.2em;
}
.stem{
    font-size: 1.2em;
    color: #ff00ff;
    font-weight: bold;
}
.palace-name{
    font-size: 1.2em;
    color: #0000ff;
    font-weight: bold;
}
.destiny{
    color: #ff0000;
    border-color: red;
}
/* 化禄的颜色 */
.lu {
    color: #008000;
}
/* 化权的颜色 */
.quan {
    color: blue;
}
/* 化科的颜色 */
.ke {
    color: #ff00ff;
}
/* 化忌的颜色 */
.ji {
    color: #d76b00;
}
/* 带圆圈文字,用于年干四化 */
.circled {
    display: inline-block;
    width: 1.2em;
    height: 1.2em;
    font-size: 0.8em;
    line-height: 1.2em;
    border: 2px solid;
    border-radius: 50%;
    text-align: center;
    margin: 0.1em;
}
/* 叠加在带圆圈文字上,改为方框,用于自化 */
.rectangle {
    border-radius: 10%;
}

.bordered {
    border: 1px solid;
    margin-right: 2px;
}

.main-star{
    color: #1338a6;
    font-weight: bold;
    margin-left: 5px;
    font-size: 1.1em;
}
.auxiliary-star{
    margin-left: 15px;
    font-weight: bold;
}
.sha-star{
    color: red;
    margin-left: 15px;
    font-weight: bold;
}
.valued-star {
    margin-left: 15px;
    color: #008000;
    font-weight: bold;
}
.fortune-star {
    margin-left: 15px;
    color: #ff00ff;
    font-weight: bold;
}
.star-status{
    font-size: 0.9em;
}

.frame {
    border: 3px solid black;
    width: 908px;
    /* 容器宽度 */
    height: 908px;
    /* 容器高度 */
    padding: 5px 0 0 5px;
    position: relative;
}
/* 定义网格容器样式 */
.grid-container {
    display: grid;
    /* 定义4列(纵向5根网格线,编号从1到5),每列宽度为均分 */
    grid-template-columns: repeat(4, 1fr);
    /* 定义4行(横向5根网格线,编号从1到5),每行高度为均分 */
    grid-template-rows: repeat(4, 1fr);
    gap: 2px;
    /* 行列单元格之间的间隔 */
    width: 902px;
    /* 容器宽度 */
    height: 902px;
    /* 容器高度 */
}
/* 中央,第2行第2列,合并两行两列,列开始于2号列网格线,结束于第4号列网格线;
   行开始于2号行网格线,结束于第4号行网格线;*/
.item0 {
    grid-column: 2 / 4;
    grid-row: 2 / 4;
    position: relative;
    border: 1px solid #000;
}
/* 子,第4行第3列,列开始于3号列网格线,结束于第4号列网格线;
   行开始于4号行网格线,结束于第5号行网格线;*/
.item1 {
    grid-column: 3 / 4;
    grid-row: 4 / 5;
    position: relative;
    border: 1px solid #000;
}
/* 丑,第4行第2列,列开始于2号列网格线,结束于第3号列网格线;
   行开始于4号行网格线,结束于第5号行网格线;*/
.item2 {
    grid-column: 2 / 3;
    grid-row: 4 / 5;
    position: relative;
    border: 1px solid #000;
}
/* 寅,第4行第1列,列开始于1号列网格线,结束于第2号列网格线;
   行开始于4号行网格线,结束于第5号行网格线;*/
.item3 {
    grid-column: 1 / 2;
    grid-row: 4 / 5;
    position: relative;
    border: 1px solid #000;
}
/* 卯,第3行第1列,列开始于1号列网格线,结束于第2号列网格线;
   行开始于3号行网格线,结束于第4号行网格线;*/
.item4 {
    grid-column: 1 / 2;
    grid-row: 3 / 4;
    position: relative;
    border: 1px solid #000;
}
/* 辰,第2行第1列,列开始于1号列网格线,结束于第2号列网格线;
   行开始于2号行网格线,结束于第3号行网格线;*/
.item5 {
    grid-column: 1 / 2;
    grid-row: 2 / 3;
    position: relative;
    border: 1px solid #000;
}
/* 巳,第1行第1列,列开始于1号列网格线,结束于第2号列网格线;
   行开始于1号行网格线,结束于第2号行网格线;*/
.item6 {
    grid-column: 1 / 2;
    grid-row: 1 / 2;
    position: relative;
    border: 1px solid #000;
}
/* 午,第1行第2列,列开始于2号列网格线,结束于第3号列网格线;
   行开始于1号行网格线,结束于第2号行网格线;*/
.item7 {
    grid-column: 2 / 3;
    grid-row: 1 / 2;
    position: relative;
    border: 1px solid #000;
}
/* 未,第1行第3列,列开始于3号列网格线,结束于第4号列网格线;
   行开始于1号行网格线,结束于第2号行网格线; */
.item8 {
    grid-column: 3 / 4;
    grid-row: 1 / 2;
    position: relative;
    border: 1px solid #000;
}
/* 申,第1行第4列,列开始于4号列网格线,结束于第5号列网格线;
   行开始于1号行网格线,结束于第2号行网格线; */
.item9 {
    grid-column: 4 / 5;
    grid-row: 1 / 2;
    position: relative;
    border: 1px solid #000;
}
/* 酉,第2行第4列,列开始于4号列网格线,结束于第5号列网格线;
   行开始于2号行网格线,结束于第3号行网格线;*/
.item10 {
    grid-column: 4 / 5;
    grid-row: 2 / 3;
    position: relative;
    border: 1px solid #000;
}
/* 戌,第3行第4列,列开始于4号列网格线,结束于第5号列网格线;
   行开始于3号行网格线,结束于第4号行网格线;*/
.item11 {
    grid-column: 4 / 5;
    grid-row: 3 / 4;
    position: relative;
    border: 1px solid #000;
}
/* 亥,第4行第4列,列开始于4号列网格线,结束于第5号列网格线;
   行开始于4号行网格线,结束于第5号行网格线;*/
.item12 {
    grid-column: 4 / 5;
    grid-row: 4 / 5;
    position: relative;
    border: 1px solid #000;
}
/* 设置 canvas 为绝对定位,并通过 z-index 让其显示在上面 */
#myCanvas {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
}

程序所需的辅助数据:

# 天干列表
import pandas as pd

from ziwei_star import Star

# 天干列表
stems = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
# 地支列表
branches = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']
# 时辰时间范围及时辰名称列表
time_names = ['00:00~00:59早子', '01:00~02:59丑', '03:00~04:59寅', '05:00~06:59卯', '07:00~08:59辰',
             '09:00~10:59巳', '11:00~12:59午', '13:00~14:59未', '15:00~16:59申', '17:00~18:59酉',
             '19:00~20:59戌', '21:00~22:59亥', '23:00~23:59晚子']
# 五虎遁年干月支定月干表
month_stem_from_year_df = pd.read_csv('month_heavenly_from_year.csv', index_col=0)
# 五鼠遁日干时支定日干表
time_stem_from_day_df = pd.read_csv('time_heavenly_from_day.csv', index_col=0)
# 飞星宫位
fly_palaces = ['命宫飞星', '财帛宫飞星', '事业宫飞星', '迁移宫飞星', '福德宫飞星', '疾厄宫飞星']
# 农历月份名称
month_names = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月']
# 宫名列表
palace_names = ['命宫','兄弟','夫妻','子女','财帛','疾厄', '迁移','交友','事业','田宅','福德','父母']
# 大运宫名列表
dayun_palace_names = ['大命','大兄','大夫','大子','大财','大疾', '大迁','大交','大事','大田','大福','大父']
# 星曜四化字典
star_changes_dict = {
    '廉贞': {'甲': '禄', '丙': '忌'},
    '破军': {'甲': '权', '癸': '禄'},
    '武曲': {'甲': '科', '己': '禄', '庚': '权', '壬': '忌'},
    '太阳': {'甲': '忌', '庚': '禄', '辛': '权'},
    '天机': {'乙': '禄', '丙': '权', '丁': '科', '戊': '忌'},
    '天梁': {'乙': '权', '己': '科', '壬': '禄'},
    '紫微': {'乙': '科', '壬': '权'},
    '太阴': {'乙': '忌', '丁': '禄', '戊': '权', '癸': '科'},
    '天同': {'丙': '禄', '丁': '权', '庚': '科'},
    '文昌': {'丙': '科', '辛': '忌'},
    '巨门': {'丁': '忌', '辛': '禄', '癸': '权'},
    '贪狼': {'戊': '禄', '己': '权', '癸': '忌'},
    '右弼': {'戊': '科'},
    '文曲': {'己': '忌', '辛': '科'},
    '左辅': {'壬': '科'},
    '天相': {'庚': '忌'},
}
# 星曜类别
star_categories = {
    '主曜':['天梁','武曲','天机','紫微','太阳','太阴','天同','贪狼','文曲','天府','天相','天梁','破军','七杀'],
    '辅曜':['禄存','天马','红鸾','天喜','天姚'],
    '吉曜':['左辅','右弼','文曲','文昌'],
    '贵人':['天魁','天钺'],
    '煞星':['天刑','天空','地劫','火星','铃星','陀罗','擎羊'],
    '四化':['化禄','化权','化科','化忌']
}
# 星曜信息页面
star_info_dict = {
    '紫微': '紫微.html',
    '天机': '天机.html',
    '太阳': '太阳.html',
    '武曲': '武曲.html',
    '天同': '天同.html',
    '廉贞': '廉贞.html',
    '天府': '天府.html',
    '太阴': '太阴.html',
    '贪狼': '贪狼.html',
    '巨门': '巨门.html',
    '天相': '天相.html',
    '天梁': '天梁.html',
    '破军': '破军.html',
    '七杀': '七杀.html',
    '左辅': '左辅.html',
    '右弼': '右弼.html',
    '文曲': '文曲.html',
    '文昌': '文昌.html',
    '地劫': '地劫.html',
    '天空': '天空.html',
    '天魁': '天魁.html',
    '天钺': '天钺.html',
    '陀罗': '陀罗.html',
    '禄存': '禄存.html',
    '擎羊': '擎羊.html',
    '火星': '火星.html',
    '铃星': '铃星.html',
    '天马': '天马.html',
    '红鸾': '红鸾.html',
    '天喜': '天喜.html',
    '天姚': '天姚.html',
    '天刑': '天刑.html',
    '化禄': '化禄.html',
    '化权': '化权.html',
    '化科': '化科.html',
    '化忌': '化忌.html'
}
# 星曜
star_ziwei = Star('紫微')
star_tianji = Star('天机')
star_sun = Star('太阳')
star_wuqu = Star('武曲')
star_tiantong = Star('天同')
star_lianzhen = Star('廉贞')
star_tianfu = Star('天府')
star_moon = Star('太阴')
star_wolf = Star('贪狼')
star_gate = Star('巨门')
star_tianxiang = Star('天相')
star_beam = Star('天梁')
star_pojun = Star('破军')
star_qisha = Star('七杀')
star_left = Star('左辅', 2)
star_right = Star('右弼', 2)
star_wenqu = Star('文曲', 2)
star_wenchang = Star('文昌', 2)
star_dijie = Star('地劫', 4)
star_none = Star('天空', 4)
star_tiankui = Star('天魁', 3)
star_tianyue = Star('天钺', 3)
star_gyro = Star('陀罗', 4)
star_lucun = Star('禄存', 1)
star_qingyang = Star('擎羊', 4)
star_mars = Star('火星', 4)
star_bell = Star('铃星', 4)
star_horse = Star('天马', 1)
star_hongluan = Star('红鸾', 1)
star_happy = Star('天喜', 1)
star_tianyao = Star('天姚', 1)
star_penalty = Star('天刑', 4)
# 命局五行类型表
destiny_type_df = pd.read_csv('destiny_type.csv', index_col=0)
# 根据农历月份和时支查命宫地支表
destiny_palace_df = pd.read_csv('destiny_palace.csv', index_col=0)
# 根据农历月份和时支查身宫地支表
self_palace_df = pd.read_csv('self_palace.csv', index_col=0)
# 星曜庙旺闲平陷状态表
star_status_df = pd.read_csv('star_status.csv', index_col=0)
# 查紫微表
set_ziwei_df = pd.read_csv('set_ziwei.csv', index_col=0)
# 子年斗君表
doujun_df = pd.read_csv('子年斗君.csv', index_col=0)

更正命局五行类型判断方面的错误:原来用的生年干支,正确的做法应当是用命宫干支。将天干四化表改成以星曜名称为键的字典。修改后的的主程序完整代码如下:

import functools
import os
import shutil
import sys
import pickle

from PyQt5 import QtCore
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QAction, QFileDialog, QDialog, QMessageBox)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFontDatabase, QFont, QMouseEvent
from PyQt5.QtWebEngineWidgets import QWebEngineView
from cn2an import an2cn, cn2an
from jinja2 import FileSystemLoader, Environment
from lunarcalendar import Converter, Solar

from ziwei_day_input_dlg import InputDayDlg
from ziwei_palace import make_palaces
from ziwei_inputinfo import InputInfoDialog
from ziwei_sponsor import SponsorDialog
from ziwei_starinfo import StarInfoWindow
from ziwei_global_data import *

palaces_file = os.path.join(os.getcwd(), 'date_info.pkl')
css_name = 'grid.css'


class MainApp(QMainWindow):
    def __init__(self):
        super().__init__()
        # 初始化变量
        self.showed = False  # 窗口是否已显示过的标志
        self.name = ''
        self.gender = ''
        self.solar_year = 0
        self.solar_month = 0
        self.solar_day = 0
        self.lunar_year = 0
        self.lunar_month = 0
        self.lunar_day = 0
        self.year_stem = ''
        self.year_branch = ''
        self.month_stem = ''
        self.month_branch = ''
        self.day_stem = ''
        self.day_branch = ''
        self.time_stem = ''
        self.time_branch = ''
        self.is_leap_month = ''
        self.curr_year_stem = ''
        self.curr_year_branch = ''
        self.curr_lunar_year = 0
        self.curr_lunar_month = 0
        self.curr_lunar_day = 0
        self.branch_palaces = []
        self.center_palace = None
        self.template = 'ziwei_template.html'
        self.output_file = 'destiny.html'
        self.selected_fly_star = None
        self.selected_star_info = None
        self._drag_position = None
        self.age = 0
        self.dayun_index = None  # 当前大运天干在地支十二宫中的索引
        self.flow_index = 0  # 当前流年在地支十二宫中的索引
        self.doujun_index = 0  # 斗君宫位在地支十二宫中的索引
        self.destiny_palace_index = 0  # 天盘命宫在十二宫中的索引
        self.fly_month_index = 0  # 流月飞星时的命宫在十二宫中的索引
        self.fly_day_index = 0  # 流日飞星时的命宫在十二宫中的索引
        self.fly_time_index = 0  # 流时飞星时的命宫在十二宫中的索引
        self.time_branch_index = 0
        # 确保窗口能接收键盘事件,防止按Esc键窗口不能关闭
        self.setFocusPolicy(Qt.StrongFocus)
        self.initUI()

    def initUI(self):

        # 获取屏幕尺寸
        screen = QApplication.primaryScreen()
        screen_size = screen.size()
        height = screen_size.height() - 95
        self.width = height

        # 取消标题栏
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setGeometry(0, 0, self.width, height)

        # 创建菜单栏
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu("文件")
        save_action = QAction("保存命盘", self)
        # noinspection PyUnresolvedReferences
        save_action.triggered.connect(self.save_palaces)
        load_action = QAction("载入命盘", self)
        # noinspection PyUnresolvedReferences
        load_action.triggered.connect(self.load_palaces)
        save_html_action = QAction("保存HTML", self)
        # noinspection PyUnresolvedReferences
        save_html_action.triggered.connect(self.save_html)
        exit_action = QAction("退出", self)
        # noinspection PyUnresolvedReferences
        exit_action.triggered.connect(self.exit_app)
        file_menu.addAction(save_action)
        file_menu.addAction(load_action)
        file_menu.addAction(save_html_action)
        file_menu.addAction(exit_action)

        # 输入菜单
        input_menu = menubar.addMenu("输入命例")
        input_action = QAction("新增命例", self)
        # noinspection PyUnresolvedReferences
        input_action.triggered.connect(self.make_destiny_palaces)
        input_menu.addAction(input_action)

        # 飞星菜单
        self.fly_star_menu = menubar.addMenu("运程分析")
        self.create_fly_star_menu()

        # 星曜信息菜单
        self.star_info_menu = menubar.addMenu("看盘参考")
        self.create_star_info_menu()

        # 操作说明菜单
        operation_prompt_menu = menubar.addMenu("操作说明")
        operation_prompt_action = QAction("操作说明", self)
        # noinspection PyUnresolvedReferences
        operation_prompt_action.triggered.connect(self.show_note)
        operation_prompt_menu.addAction(operation_prompt_action)

        # 赞助菜单
        sponsor_menu = menubar.addMenu("赞助")
        sponsor_action = QAction("赞助", self)
        # noinspection PyUnresolvedReferences
        sponsor_action.triggered.connect(self.show_sponsor_dialog)
        sponsor_menu.addAction(sponsor_action)

        # 窗体主体为一个 QWebEngineView 控件
        self.web_view = QWebEngineView(self)
        self.web_view.setFixedSize(height - 50, height - 50)
        self.setCentralWidget(self.web_view)

    def exit_app(self):
        # 退出菜单项点击事件,退出程序
        QApplication.quit()

    def create_fly_star_menu(self):
        def add_fly_star_menu(parent_menu, menu_label_list, callback_func):
            if parent_menu.title() == '天盘飞星':
                for label, step_index in menu_label_list:
                    action = QAction(label, self)
                    action.triggered.connect(functools.partial(callback_func, step_index))
                    parent_menu.addAction(action)
            else:
                for label, step_index in menu_label_list:
                    menu = parent_menu.addMenu(label)
                    for index, palace in enumerate(fly_palaces):
                        action = QAction(palace, self)
                        action.triggered.connect(functools.partial(callback_func, step_index, index))
                        menu.addAction(action)

        heaven_plate_fly_star_menu = self.fly_star_menu.addMenu('天盘飞星')
        heaven_plate_menu_labels = [(palace, index) for index, palace in enumerate(fly_palaces)]
        add_fly_star_menu(heaven_plate_fly_star_menu, heaven_plate_menu_labels, self.heaven_plate_fly_star)

        major_period_menu = self.fly_star_menu.addMenu('大运运程')
        major_period_menu_labels = [(f"第{i}步大运", i - 1) for i in range(1, 13)]
        add_fly_star_menu(major_period_menu, major_period_menu_labels, self.dayun_fly_star)

        flow_period_menu = self.fly_star_menu.addMenu('年运程')
        annual_period_menu_labels = [(f"第{i}年", i - 1) for i in range(1, 11)]
        add_fly_star_menu(flow_period_menu, annual_period_menu_labels, self.year_fly_star)

        monthly_period_menu = self.fly_star_menu.addMenu('月运程')
        monthly_period_menu_labels = [(month_names[i - 1], i - 1) for i in range(1, 13)]
        add_fly_star_menu(monthly_period_menu, monthly_period_menu_labels, self.month_fly_star)

        daily_period_menu = self.fly_star_menu.addMenu('日运程')
        daily_period_menu_labels = [('今日运程', 1), ('指定日期', 2)]
        add_fly_star_menu(daily_period_menu, daily_period_menu_labels, self.day_fly_star)

        time_period_menu = self.fly_star_menu.addMenu('时运程')
        time_labels = ['23:00~00:59子'] + time_names[1:12]
        time_period_menu_labels = [(time_labels[i], i) for i in range(12)]
        add_fly_star_menu(time_period_menu, time_period_menu_labels, self.time_fly_star)

    def create_star_info_menu(self):
        for category, stars in star_categories.items():
            category_menu = self.star_info_menu.addMenu(category)
            for star in stars:
                action = QAction(star, self)
                action.triggered.connect(lambda checked, s=star_info_dict[star]: self.show_star_info(s))
                category_menu.addAction(action)

    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            # 记录鼠标按下时的位置
            self._drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()
        elif event.button() == Qt.RightButton:
            # 鼠标右键按下时将窗口最小化
            self.showMinimized()
            event.accept()

    def mouseMoveEvent(self, event: QMouseEvent):
        if event.buttons() == Qt.LeftButton and self._drag_position is not None:
            # 移动窗体
            self.move(event.globalPos() - self._drag_position)
            event.accept()

    def mouseReleaseEvent(self, event: QMouseEvent):
        # 释放鼠标时重置拖动位置
        self._drag_position = None
        event.accept()

    def keyPressEvent(self, event):
        # 判断按下的键是否为 Esc 键
        if event.key() == Qt.Key_Escape:
            self.close()
        # 调用父类的 keyPressEvent 方法,确保其他按键事件能正常处理
        super().keyPressEvent(event)

    def save_palaces(self):
        file_name, _ = QFileDialog.getSaveFileName(self, "保存命盘", "", "PKL 文件 (*.pkl)")
        if file_name:
            with open(file_name, 'wb') as file:
                obj = (self.name, self.gender, self.solar_year, self.solar_month,
                       self.solar_day, self.lunar_year, self.lunar_month, self.lunar_day,
                       self.year_stem, self.year_branch, self.month_stem, self.month_branch,
                       self.day_stem, self.day_branch, self.time_stem, self.time_branch,
                       self.is_leap_month)
                pickle.dump(obj, file)

    def load_palaces(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "载入命盘", "", "PKL 文件 (*.pkl)")
        if file_name:
            self.load_destiny_palace(file_name)

    def save_html(self):
        """
        将命盘内容保存为 HTML 文件。
        """
        def save(file_name, html):
            with open(file_name, "w", encoding="utf-8") as file:
                file.write(html)

        file_name, _ = QFileDialog.getSaveFileName(self, "保存命盘", "", "HTML 文件 (*.html)")
        if file_name:
            # 将QWebEngineView的内容保存为HTML
            self.web_view.page().runJavaScript(
                "document.documentElement.outerHTML",
                lambda html: save(file_name, html)
            )
        # 将配套css文件拷贝到保存目录,以便于打开html文件时能正常显示
        css_file = os.path.join(os.getcwd(), css_name)
        dest_path = file_name.split('/')[0:-1] + [css_name]
        dest_path = '/'.join(dest_path)
        shutil.copy2(css_file, dest_path)



    def make_destiny_palaces(self):
        dialog = InputInfoDialog(self)
        if dialog.exec() == QDialog.Accepted:
            info = list(dialog.get_info().values())
            self.fill_palaces(info)

            print(f'公历:{self.solar_year}年{self.solar_month}月{self.solar_day}日')
            print(f'农历:{self.lunar_year}年{"闰" if self.is_leap_month else ""}'
                  f'{self.lunar_month}月{self.lunar_day}日')
            print(
                f'{self.year_stem}{self.year_branch}年{self.month_stem}'
                f'{self.month_branch}月{self.day_stem}{self.day_branch}日'
                f'{self.time_stem}{self.time_branch}时')

    def fill_palaces(self, info):
        (self.name, self.gender, self.solar_year, self.solar_month,
         self.solar_day, self.lunar_year, self.lunar_month, self.lunar_day,
         self.year_stem, self.year_branch, self.month_stem, self.month_branch,
         self.day_stem, self.day_branch, self.time_stem, self.time_branch,
         self.is_leap_month) = info
        palaces = make_palaces()
        self.branch_palaces = palaces[0]
        self.center_palace = palaces[1]
        lunar_today = Converter.Solar2Lunar(Solar(
            self.center_palace.today.year, self.center_palace.today.month,
            self.center_palace.today.day))
        self.curr_lunar_year = lunar_today.year
        self.curr_lunar_month = lunar_today.month
        self.curr_lunar_day = lunar_today.day
        self.age = self.curr_lunar_year - self.lunar_year + 1
        stem_index = (lunar_today.year - 4) % 10
        branch_index = (lunar_today.year - 4) % 12
        self.curr_year_stem = stems[stem_index]
        self.curr_year_branch = branches[branch_index]
        self.fill_branch_palace()
        self.update_destiny_view(self.branch_palaces, self.destiny_palace_index)

    def get_fly_index(self, base_index, index):
        fly_index = base_index
        match index:
            case 1:
                fly_index = (base_index + 8) % 12
            case 2:
                fly_index = (base_index + 4) % 12
            case 3:
                fly_index = (base_index + 6) % 12
            case 4:
                fly_index = (base_index + 2) % 12
            case 5:
                fly_index = (base_index + 7) % 12
        self.set_fly_stem_changes(fly_index)
        return fly_index

    def heaven_plate_fly_star(self, index):
        self.center_palace.fly_star_status = f'天盘{fly_palaces[index]}'
        self.set_dayun_info(self.age)
        fly_index = self.get_fly_index(self.destiny_palace_index, index)
        for i in range(12):
            self.set_doujun(i)
            self.set_flow_year_info(self.flow_index, i)
            self.set_month_info(self.fly_month_index, i)
            self.set_day_info(self.fly_day_index, i)
            self.set_time_info(self.fly_time_index, i)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def dayun_fly_star(self, step, index):
        self.center_palace.fly_star_status = f'第{step + 1}步大运{fly_palaces[index]}'
        flag = f'{self.center_palace.yin_yang}{self.center_palace.gender}'
        # 计算飞星时的大运命宫索引
        if flag == '阳男' or flag == '阴女':
            self.dayun_index = (self.destiny_palace_index + step) % 12
        elif flag == '阴男' or flag == '阳女':
            self.dayun_index = (self.destiny_palace_index - step) % 12
        fly_index = self.get_fly_index(self.dayun_index, index)
        # 设置各地支宫以飞星大运所在宫为大运命宫是的大运宫名及各地支宫在本大运期间的流年年龄
        for i in range(12):
            self.set_change_from_stem(self.branch_palaces[self.dayun_index].stem,
                                      'dayun_changes', i)
            self.set_dayun_palace_name_flow_year(i)
            self.set_doujun(i)
            self.set_flow_year_info(self.flow_index, i)
            self.set_month_info(self.fly_month_index, i)
            self.set_day_info(self.fly_day_index, i)
            self.set_time_info(self.fly_time_index, i)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def year_fly_star(self, step, index):
        if not self.flow_index is None:
            self.branch_palaces[self.flow_index].is_flow_palace = False
        self.center_palace.fly_star_status = f'第{step + 1}年{fly_palaces[index]}'
        self.flow_index = (self.dayun_index + step) % 12
        self.doujun_index = self.get_doujun_index(self.lunar_month, self.time_branch,self.flow_index)
        month_index = (self.doujun_index + self.lunar_month - 1) % 12
        day_index = (month_index + self.lunar_day - 1) % 12
        time_index = (day_index + self.time_branch_index) % 12
        fly_index = self.get_fly_index(self.flow_index, index)
        # 设置各地支宫流年四化
        for i in range(12):
            self.set_change_from_stem(self.branch_palaces[self.flow_index].stem,
                                      'flow_changes', i)
            self.set_doujun(i)
            self.set_flow_year_info(self.flow_index, i)
            self.set_month_info(month_index, i)
            self.set_day_info(day_index, i)
            self.set_time_info(time_index, i)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def month_fly_star(self, step, index):
        self.center_palace.fly_star_status = f'{month_names[step]}{fly_palaces[index]}'
        self.fly_month_index = (self.doujun_index + step) % 12
        day_index = (self.fly_month_index + self.lunar_day - 1) % 12
        time_index = (day_index + self.time_branch_index) % 12
        for i in range(12):
            self.set_month_info(self.fly_month_index, i)
            self.set_day_info(day_index, i)
            self.set_time_info(time_index, i)
        fly_index = self.get_fly_index(self.fly_month_index, index)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def day_fly_star(self, step, index):
        if step == 1:
            age = self.curr_lunar_year - self.lunar_year + 1
            self.center_palace.fly_star_status = f'今日运程:{fly_palaces[index]}'
            self.doujun_index = self.get_doujun_index(
                self.lunar_month, self.time_branch, self.flow_index)
            self.fly_month_index = (self.doujun_index + self.curr_lunar_month - 1) % 12
            self.fly_day_index = (self.fly_month_index + self.curr_lunar_day - 1) % 12

            self.set_dayun_info(age)
            self.set_change_from_stem(self.branch_palaces[self.dayun_index].stem,
                                      'dayun_changes', self.dayun_index)
        elif step == 2:
            year = self.branch_palaces[self.flow_index].flow_age + self.lunar_year - 1
            month = month_names.index(self.branch_palaces[self.fly_month_index].flow_month) + 1
            dialog = InputDayDlg(year, month)
            if dialog.exec() == QDialog.Accepted:
                day = dialog.get_day()
                self.fly_day_index = (self.fly_month_index + day - 1) % 12
                self.center_palace.fly_star_status = f'{an2cn(day)}日运程:{fly_palaces[index]}'
        time_index = (self.fly_day_index + self.time_branch_index) % 12
        for i in range(12):
            self.set_doujun(i)
            self.set_flow_year_info(self.flow_index, i)
            self.set_month_info(self.fly_month_index, i)
            self.set_day_info(self.fly_day_index, i)
            self.set_time_info(time_index, i)
        fly_index = self.get_fly_index(self.fly_day_index, index)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def time_fly_star(self, step, index):
        self.center_palace.fly_star_status = f'{time_names[step][-1:]}时{fly_palaces[index]}'
        time_index = (self.fly_day_index + step) % 12
        for i in range(12):
            self.set_time_info(time_index, i)
        fly_index = self.get_fly_index(time_index, index)
        self.set_fly_stem_changes(fly_index)
        self.update_destiny_view(self.branch_palaces, fly_index)

    def show_star_info(self, text):
        self.selected_star_info = text
        self.show_star_info_window(text)

    def show_star_info_window(self, file_name):
        dialog = StarInfoWindow(file_name, self)
        dialog.setWindowTitle("星曜信息")
        dialog.show()

    def show_note(self):
        dialog = StarInfoWindow('htmls/note.html', self)
        dialog.setWindowTitle('操作说明')
        dialog.show()

    def show_sponsor_dialog(self):
        dialog = SponsorDialog(self)
        dialog.show()

    def fill_center_palace(self):
        self.center_palace.name = self.name
        self.center_palace.gender = self.gender
        match self.year_stem:
            case '甲' | '丙' | '戊' | '庚' | '壬':
                self.center_palace.yin_yang = '阳'
            case _:
                self.center_palace.yin_yang = '阴'
        self.center_palace.solar_birthday = (f'{self.solar_year}年{self.solar_month}'
                                             f'月{self.solar_day}日')
        cn_year = (f'{an2cn(self.lunar_year // 1000)}'
                   f'{an2cn(self.lunar_year // 100 - self.lunar_year // 1000 * 10)}'
                   f'{an2cn(self.lunar_year // 10 - self.lunar_year // 100 * 10)}'
                   f'{an2cn(self.lunar_year - self.lunar_year // 10 * 10)}')
        cn_year = cn_year.replace('零', '〇')
        match self.lunar_month:
            case 1:
                cn_month = '正'
            case 11:
                cn_month = '冬'
            case 12:
                cn_month = '腊'
            case _:
                cn_month = an2cn(self.lunar_month)
        cn_day = f'{"初" if self.lunar_day < 11 else ""}{an2cn(self.lunar_day)}'
        self.center_palace.lunar_birthday = (f'{cn_year}年{"闰" if self.is_leap_month else ""}'
                                             f'{cn_month}月{cn_day}日')
        self.center_palace.age = self.age
        self.center_palace.stem_branch = (f'{self.year_stem}{self.year_branch}年'
                                          f'{self.month_stem}{self.month_branch}月'
                                          f'{self.day_stem}{self.day_branch}日'
                                          f'{self.time_stem}{self.time_branch}时')
        self.center_palace.destiny_type = destiny_type_df.loc[self.branch_palaces[
            self.destiny_palace_index].stem, self.branch_palaces[self.destiny_palace_index].branch]

    def fill_branch_palace(self):
        self.fill_branch_palaces_stem_center_palace()
        self.find_self_palace()
        self.set_zi_fu_relations()
        self.set_stars_by_month_or_day()
        self.set_kui_yue()
        self.set_lu_ren_tuo()
        self.set_mars_bell()
        self.set_hongluan_happy()
        self.set_tianyao_penalty()
        dayun_stem = self.branch_palaces[self.dayun_index].stem
        flow_stem = self.branch_palaces[self.flow_index].stem
        self.doujun_index = self.get_doujun_index(self.lunar_month, self.time_branch, self.flow_index)
        self.fly_month_index = (self.doujun_index + self.lunar_month - 1) % 12
        self.fly_day_index = (self.fly_month_index + self.lunar_day - 1) % 12
        self.time_branch_index = branches.index(self.time_branch)
        self.fly_time_index = (self.fly_day_index + self.time_branch_index) % 12
        for i in range(12):
            self.branch_palaces[i].stars = sorted(self.branch_palaces[i].stars,
                                                  key=lambda star: star.type)

            self.set_doujun(i)
            self.set_flow_year_info(self.flow_index, i)
            self.set_month_info(self.fly_month_index, i)
            self.set_day_info(self.fly_day_index, i)
            self.set_time_info(self.fly_time_index, i)
            self.set_change_from_stem(dayun_stem, 'dayun_changes', i)
            self.set_change_from_stem(flow_stem, 'flow_changes', i)

    def update_destiny_view(self, palaces, destiny_palace_index):
        env = Environment(loader=FileSystemLoader('.'))
        template = env.get_template(self.template)
        html = template.render(branch_palaces=palaces,
                               center_palace=self.center_palace,
                               index=destiny_palace_index, css=css_name)
        with open(self.output_file, 'w', encoding='utf-8') as f:
            f.write(html)
        html_file_path = os.path.join(os.getcwd(), self.output_file)
        self.web_view.load(QtCore.QUrl.fromLocalFile(html_file_path))
        self.fly_star_menu.setEnabled(True)

    def fill_branch_palaces_stem_center_palace(self):
        """
            填充地支宫位的天干、名称、大运流年标记、以大运为命宫时的宫名、大运初始年龄及大运年龄期间
        """
        # 取得命宫地支及其地支在地支十二宫中的索引,由农历月份及时支决定
        destiny_palace_branch = destiny_palace_df.loc[self.lunar_month, self.time_branch]
        self.destiny_palace_index = branches.index(destiny_palace_branch)
        # 按五虎遁口诀查找寅宫的天干
        tiger_stem = month_stem_from_year_df.loc[self.year_stem, '寅']
        # 寅宫的天干在原始天干列表中的索引
        tiger_stem_index = stems.index(tiger_stem)
        # 拼接十二地支将要用到的天干列表, 将寅宫的天干放到最前面,并补上原始十天干列表比地支列表少的两个天干
        used_stems = (stems[tiger_stem_index:] + stems[:tiger_stem_index] +
                      stems[tiger_stem_index:tiger_stem_index + 2])

        for i in range(12):
            # 定宫干,逆时针排列。将子的索引0改为寅的索引2,戌回到子的索引
            self.branch_palaces[(i + 2) % 12].stem = used_stems[i]
            # 将地支宫在地支宫列表中的索引调整为对应宫名在宫名列表中的索引
            pindex2pnindex = (self.destiny_palace_index - i) % 12
            # 定宫名
            self.branch_palaces[i].name = palace_names[pindex2pnindex]
            # 定禄权科忌在本宫的状态
            self.branch_palaces[i].lu_status = star_status_df.loc['禄', branches[i]]
            self.branch_palaces[i].quan_status = star_status_df.loc['权', branches[i]]
            self.branch_palaces[i].ke_status = star_status_df.loc['科', branches[i]]
            self.branch_palaces[i].ji_status = star_status_df.loc['忌', branches[i]]
        # 确定各地支宫位天干即可据命宫干支确定命局五行类型
        self.fill_center_palace()
        # # 计算进入各宫时的起始年龄
        for i in range(12):
            flag = f'{self.center_palace.yin_yang}{self.center_palace.gender}'
            start_year = cn2an(self.center_palace.destiny_type[1:2])
            match (i - self.destiny_palace_index) % 12:
                case 0:
                    self.branch_palaces[i].dayun_start_age = start_year
                case 1:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        110 if flag == '阴男' or flag == '阳女' else 10)
                case 2:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        100 if flag == '阴男' or flag == '阳女' else 20)
                case 3:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        90 if flag == '阴男' or flag == '阳女' else 30)
                case 4:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        80 if flag == '阴男' or flag == '阳女' else 40)
                case 5:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        70 if flag == '阴男' or flag == '阳女' else 50)
                case 6:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        60 if flag == '阴男' or flag == '阳女' else 60)
                case 7:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        50 if flag == '阴男' or flag == '阳女' else 70)
                case 8:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        40 if flag == '阴男' or flag == '阳女' else 80)
                case 9:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        30 if flag == '阴男' or flag == '阳女' else 90)
                case 10:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        20 if flag == '阴男' or flag == '阳女' else 100)
                case 11:
                    self.branch_palaces[i].dayun_start_age = start_year + (
                        10 if flag == '阴男' or flag == '阳女' else 110)
        self.set_dayun_info(self.age)

    def set_dayun_info(self, age):
        """
            查找大运宫位和流年宫位
        """
        if age < self.branch_palaces[self.destiny_palace_index].dayun_start_age:
            self.dayun_index = self.destiny_palace_index
        for i in range(12):
            if ((age >= self.branch_palaces[i].dayun_start_age) and
                    (age - self.branch_palaces[i].dayun_start_age < 10)):
                self.dayun_index = i

            if self.branch_palaces[i].branch == self.curr_year_branch:
                self.branch_palaces[i].is_flow_palace = True
                self.flow_index = i
            else:
                self.branch_palaces[i].is_flow_palace = False
        for i in range(12):  # 确定各宫的大运宫名、流年年龄
            self.set_dayun_palace_name_flow_year(i)


    def set_dayun_palace_name_flow_year(self, palace_index):
        """
        设置大运宫名,流年年龄,标记大运宫所在位置
        :param palace_index: 地支宫索引
        """
        pindex2pdindex = (self.dayun_index - palace_index) % 12
        self.branch_palaces[pindex2pdindex].dayun_palace_name = dayun_palace_names[palace_index]
        # 设置各宫流年年龄
        if palace_index < 10:
            self.branch_palaces[(self.dayun_index + palace_index) % 12].flow_age = (
                    self.branch_palaces[self.dayun_index].dayun_start_age + palace_index)
        else:
            self.branch_palaces[(self.dayun_index + palace_index) % 12].flow_age = 0
        if self.dayun_index != palace_index:
            self.branch_palaces[palace_index].is_dayun_palace = False
        else:
            self.branch_palaces[self.dayun_index].is_dayun_palace = True

    def find_self_palace(self):
        """
            找身宫
        """
        self_palace_branch = self_palace_df.loc[an2cn(self.lunar_month), self.time_branch]
        self_palace_index = branches.index(self_palace_branch)
        self.branch_palaces[self_palace_index].is_self_palace = True

    def set_doujun(self, palace_index):
        self.branch_palaces[(self.doujun_index + palace_index
                             ) % 12].flow_month = month_names[palace_index]
        if self.doujun_index == palace_index:
            self.branch_palaces[palace_index].is_doujun_palace = True
        else:
            self.branch_palaces[palace_index].is_doujun_palace = False

    def set_flow_year_info(self, index, i):
        self.branch_palaces[i].is_flow_palace = False
        self.branch_palaces[(index - i) % 12].flow_palace_name = '年' + palace_names[i][0:1]
        self.set_change_from_stem(self.branch_palaces[i].stem, 'flow_change', i)
        if index == self.flow_index:
            self.branch_palaces[index].is_flow_palace = True
        else:
            self.branch_palaces[index].is_flow_palace = False

    def set_month_info(self, index, i):
        self.branch_palaces[(index - i) % 12].month_palace_name = '月' + palace_names[i][0:1]

    def set_day_info(self, index, i):
        self.branch_palaces[(index - i) % 12].day_palace_name = '日' + palace_names[i][0:1]

    def set_time_info(self, index, i):
        self.branch_palaces[(index - i) % 12].time_palace_name = '时' + palace_names[i][0:1]

    def set_star_status_and_changes(self, star, index):
        """
            设置星曜状态及四化,并将星曜加入所属地支宫星曜列表
            :param star: 星曜
            :param index: 星曜所在地支宫索引
        """
        stem_change = {} if star.name not in star_changes_dict else star_changes_dict[star.name]
        star.status = star_status_df.loc[star.name, self.branch_palaces[index].branch]
        star.stem_change = ''
        star.self_change = ''
        if self.year_stem in stem_change:
            star.stem_change = stem_change[self.year_stem]
        if self.branch_palaces[index].stem in stem_change:
            star.self_change = stem_change[self.branch_palaces[index].stem]
        self.branch_palaces[index].stars.append(star)

    def set_zi_fu_relations(self):
        """
            安紫府诸星14颗
        """
        def get_ziwei_index(day, dtype):
            """
            求紫微所在地支宫位的算法,参照紫微斗数一本通
            :param day: 农历日
            :param dtype: 命局类型
            :return: 紫微所在地支宫位索引
            """
            if day % dtype == 0:  # 日数除局数,商为整数则从寅宫(为1)顺数商数位
                # 地支宫位索引从0开始,寅宫索引本为2,从1数,故所数数目应为商数加2-(1-0)=1
                return (1 + day // dtype) % 12
            else:  # 日数除局数,余数不为0
                quotient = day // dtype  # 先取得商数
                poor = dtype * (quotient + 1) - day  # 求得加上某数后能整除局数的某数
                index = 2 + quotient  # 从寅宫顺数商数位
                # 某数为偶数,再顺数至某数,否则逆数至某数
                return (index + poor) % 12 if poor % 2 == 0 else (index - poor) % 12

        # 查表取得紫微在地支十二宫的位置
        ziwei_branch = set_ziwei_df.loc[self.center_palace.destiny_type, str(self.lunar_day)]
        ziwei_index = branches.index(ziwei_branch)
        # ziwei_index = get_ziwei_index(self.lunar_day, cn2an(self.center_palace.destiny_type[1:2]))
        tianji_index = (ziwei_index - 1 + 12) % 12
        sun_index = (ziwei_index - 3 + 12) % 12
        wuqu_index = (ziwei_index - 4 + 12) % 12
        tiantong_index = (ziwei_index - 5 + 12) % 12
        lianzhen_index = (ziwei_index - 8 + 12) % 12
        self.set_star_status_and_changes(star_ziwei, ziwei_index)
        self.set_star_status_and_changes(star_tianji, tianji_index)
        self.set_star_status_and_changes(star_sun, sun_index)
        self.set_star_status_and_changes(star_wuqu, wuqu_index)
        self.set_star_status_and_changes(star_tiantong, tiantong_index)
        self.set_star_status_and_changes(star_lianzhen, lianzhen_index)

        # 天府在十二宫中的索引,按安星规则与紫微索引的关系是紫微小于5时二者和为4,否则和为16
        tianfu_index = 4 - ziwei_index if ziwei_index < 5 else 16 - ziwei_index
        moon_index = (tianfu_index + 1) % 12
        wolf_index = (tianfu_index + 2) % 12
        gate_index = (tianfu_index + 3) % 12
        tianxiang_index = (tianfu_index + 4) % 12
        beam_index = (tianfu_index + 5) % 12
        pojun_index = (tianfu_index + 10) % 12
        qisha_index = (tianfu_index + 6) % 12
        self.set_star_status_and_changes(star_tianfu, tianfu_index)
        self.set_star_status_and_changes(star_moon, moon_index)
        self.set_star_status_and_changes(star_wolf, wolf_index)
        self.set_star_status_and_changes(star_gate, gate_index)
        self.set_star_status_and_changes(star_tianxiang, tianxiang_index)
        self.set_star_status_and_changes(star_beam, beam_index)
        self.set_star_status_and_changes(star_pojun, pojun_index)
        self.set_star_status_and_changes(star_qisha, qisha_index)

    def set_stars_by_month_or_day(self):
        """
            左辅右弼文昌文曲天空地劫6颗
        """
        time_index = branches.index(self.time_branch)
        left_index = (3 + self.lunar_month) % 12  # 左辅,辰时起顺数至月数
        right_index = (11 - self.lunar_month) % 12  # 右弼,戌时起逆数至月数
        wenqu_index = (4 + time_index) % 12  # 文曲,辰时起顺数至时数
        wenchang_index = (10 - time_index) % 12  # 文昌,戌时起逆数至时数
        dijie_index = (11 + time_index) % 12  # 地劫,亥时起顺数至时数
        none_index = (11 - time_index) % 12  # 天空,亥时起逆数至时数
        self.set_star_status_and_changes(star_left, left_index)
        self.set_star_status_and_changes(star_right, right_index)
        self.set_star_status_and_changes(star_wenqu, wenqu_index)
        self.set_star_status_and_changes(star_wenchang, wenchang_index)
        self.set_star_status_and_changes(star_dijie, dijie_index)
        self.set_star_status_and_changes(star_none, none_index)

    def set_kui_yue(self):
        """
            天魁天钺2颗
        """
        match self.year_stem:
            case '甲' | '戊' | '庚':
                self.branch_palaces[1].stars.append(star_tiankui)
                self.branch_palaces[7].stars.append(star_tianyue)
                star_tiankui.status = star_status_df.loc['天魁', self.branch_palaces[1].branch]
                star_tianyue.status = star_status_df.loc['天钺', self.branch_palaces[7].branch]
            case '乙' | '己':
                self.branch_palaces[0].stars.append(star_tiankui)
                self.branch_palaces[8].stars.append(star_tianyue)
                star_tiankui.status = star_status_df.loc['天魁', self.branch_palaces[0].branch]
                star_tianyue.status = star_status_df.loc['天钺', self.branch_palaces[8].branch]
            case '丙' | '丁':
                self.branch_palaces[11].stars.append(star_tiankui)
                self.branch_palaces[9].stars.append(star_tianyue)
                star_tiankui.status = star_status_df.loc['天魁', self.branch_palaces[11].branch]
                star_tianyue.status = star_status_df.loc['天钺', self.branch_palaces[9].branch]
            case '壬' | '癸':
                self.branch_palaces[3].stars.append(star_tiankui)
                self.branch_palaces[5].stars.append(star_tianyue)
                star_tiankui.status = star_status_df.loc['天魁', self.branch_palaces[3].branch]
                star_tianyue.status = star_status_df.loc['天钺', self.branch_palaces[5].branch]
            case '辛':
                self.branch_palaces[6].stars.append(star_tiankui)
                self.branch_palaces[2].stars.append(star_tianyue)
                star_tianyue.status = star_status_df.loc['天钺', self.branch_palaces[2].branch]
                star_tiankui.status = star_status_df.loc['天魁', self.branch_palaces[6].branch]

    def set_lu_ren_tuo(self):
        """
            安禄存擎羊陀罗3颗
        """
        match self.year_stem:
            case '甲':
                self.branch_palaces[1].stars.append(star_gyro)
                self.branch_palaces[2].stars.append(star_lucun)
                self.branch_palaces[3].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[1].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[2].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[3].branch]
            case '乙':
                self.branch_palaces[2].stars.append(star_gyro)
                self.branch_palaces[3].stars.append(star_lucun)
                self.branch_palaces[4].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[2].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[3].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[4].branch]
            case '丙' | '戊':
                self.branch_palaces[4].stars.append(star_gyro)
                self.branch_palaces[5].stars.append(star_lucun)
                self.branch_palaces[6].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[4].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[5].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[6].branch]
            case '丁' | '己':
                self.branch_palaces[5].stars.append(star_gyro)
                self.branch_palaces[6].stars.append(star_lucun)
                self.branch_palaces[7].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[5].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[6].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[7].branch]
            case '庚':
                self.branch_palaces[7].stars.append(star_gyro)
                self.branch_palaces[8].stars.append(star_lucun)
                self.branch_palaces[9].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[7].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[8].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[9].branch]
            case '辛':
                self.branch_palaces[8].stars.append(star_gyro)
                self.branch_palaces[9].stars.append(star_lucun)
                self.branch_palaces[10].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[8].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[9].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[10].branch]
            case '壬':
                self.branch_palaces[10].stars.append(star_gyro)
                self.branch_palaces[11].stars.append(star_lucun)
                self.branch_palaces[0].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[10].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[11].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[0].branch]
            case '癸':
                self.branch_palaces[11].stars.append(star_gyro)
                self.branch_palaces[0].stars.append(star_lucun)
                self.branch_palaces[1].stars.append(star_qingyang)
                star_gyro.status = star_status_df.loc['陀罗', self.branch_palaces[11].branch]
                star_lucun.status = star_status_df.loc['禄存', self.branch_palaces[0].branch]
                star_qingyang.status = star_status_df.loc['擎羊', self.branch_palaces[1].branch]

    def set_mars_bell(self):
        """
            火星铃星天马3颗
        """
        time_branch_index = branches.index(self.time_branch)
        match self.year_branch:
            case '申' | '子' | '辰':
                mars_index = (2 + time_branch_index) % 12
                bell_index = (10 + time_branch_index) % 12
                self.branch_palaces[mars_index].stars.append(star_mars)
                self.branch_palaces[bell_index].stars.append(star_bell)
                self.branch_palaces[2].stars.append(star_horse)
                star_mars.status = star_status_df.loc['火星', self.branch_palaces[mars_index].branch]
                star_bell.status = star_status_df.loc['铃星', self.branch_palaces[bell_index].branch]
                star_horse.status = star_status_df.loc['天马', self.branch_palaces[2].branch]
            case '寅' | '午' | '戌':
                mars_index = (1 + time_branch_index) % 12
                bell_index = (3 + time_branch_index) % 12
                self.branch_palaces[mars_index].stars.append(star_mars)
                self.branch_palaces[bell_index].stars.append(star_bell)
                self.branch_palaces[8].stars.append(star_horse)
                star_mars.status = star_status_df.loc['火星', self.branch_palaces[mars_index].branch]
                star_bell.status = star_status_df.loc['铃星', self.branch_palaces[bell_index].branch]
                star_horse.status = star_status_df.loc['天马', self.branch_palaces[8].branch]
            case '亥' | '卯' | '未':
                mars_index = (9 + time_branch_index) % 12
                bell_index = (10 + time_branch_index) % 12
                self.branch_palaces[mars_index].stars.append(star_mars)
                self.branch_palaces[bell_index].stars.append(star_bell)
                self.branch_palaces[5].stars.append(star_horse)
                star_mars.status = star_status_df.loc['火星', self.branch_palaces[mars_index].branch]
                star_bell.status = star_status_df.loc['铃星', self.branch_palaces[bell_index].branch]
                star_horse.status = star_status_df.loc['天马', self.branch_palaces[5].branch]
            case '巳' | '酉' | '丑':
                mars_index = (3 + time_branch_index) % 12
                bell_index = (10 + time_branch_index) % 12
                self.branch_palaces[mars_index].stars.append(star_mars)
                self.branch_palaces[bell_index].stars.append(star_bell)
                self.branch_palaces[11].stars.append(star_horse)
                star_mars.status = star_status_df.loc['火星', self.branch_palaces[mars_index].branch]
                star_bell.status = star_status_df.loc['铃星', self.branch_palaces[bell_index].branch]
                star_horse.status = star_status_df.loc['天马', self.branch_palaces[11].branch]

    def set_hongluan_happy(self):
        """
            红鸾天喜2颗
        """
        year_branch_index = branches.index(self.year_branch)
        hongluan_index = (3 - year_branch_index) % 12
        happy_index = (9 - year_branch_index) % 12
        self.branch_palaces[hongluan_index].stars.append(star_hongluan)
        self.branch_palaces[happy_index].stars.append(star_happy)
        star_hongluan.status = star_status_df.loc['红鸾', self.branch_palaces[hongluan_index].branch]
        star_happy.status = star_status_df.loc['天喜', self.branch_palaces[happy_index].branch]

    def set_tianyao_penalty(self):
        """
            天姚天刑2颗
        """
        tianyao_index = self.lunar_month % 12
        penalty_index = (8 + self.lunar_month) % 12
        self.branch_palaces[tianyao_index].stars.append(star_tianyao)
        self.branch_palaces[penalty_index].stars.append(star_penalty)
        star_tianyao.status = star_status_df.loc['天姚', self.branch_palaces[tianyao_index].branch]
        star_penalty.status = star_status_df.loc['天刑', self.branch_palaces[penalty_index].branch]

    def set_change_from_stem(self, stem, attr_name, palace_index):
        """
        设置四化属性
        :param stem: 四化的天干
        :param attr_name: 属性名,fly_changes,dayun_changes,flow_changes 之一
        :param palace_index: 要设置四化的当前宫位在地支宫位列表(子~亥)中的索引
        """
        changes = []
        for star in self.branch_palaces[palace_index].stars:
            try:
                find = star_changes_dict[star.name][stem]
            except Exception as e:
                find = None
            if find is not None:
                changes.append(find)
        setattr(self.branch_palaces[palace_index], attr_name, changes)

    def set_fly_stem_changes(self, index):
        """
        设置飞星宫位天干四化
        :param index: 飞星宫位索引
        """
        stem = self.branch_palaces[index].stem
        for i in range(12):
            pala_index2pala_dayun_index = (index - i) % 12
            self.branch_palaces[pala_index2pala_dayun_index].fly_palace_name = '飞' + dayun_palace_names[i][1:]
            self.branch_palaces[i].fly_changes = []
            self.set_change_from_stem(stem, 'fly_changes', i)

    def get_doujun_index(self, lunar_month, time_branch, year_index):
        zi_doujun = doujun_df.loc[lunar_month, time_branch]
        zi_doujun_index = branches.index(zi_doujun)
        return (year_index + zi_doujun_index) % 12

    def load_destiny_palace(self, filename):
        """
        加载命盘
        :param filename: 命盘文件名
        """
        try:
            with open(filename, 'rb') as file:
                info = pickle.load(file)
                self.fill_palaces(info)
        except (FileNotFoundError, pickle.UnpicklingError):
            QMessageBox.warning(self, '警告', '载入命盘文件失败!')
            self.fly_star_menu.setEnabled(False)
            html_file_path = os.path.join(os.getcwd(), 'htmls', 'note.html')
            self.web_view.load(QtCore.QUrl.fromLocalFile(html_file_path))


    def showEvent(self, event):
        if not self.showed:
            # 窗体显示后载入上次关闭时的命局
            self.load_destiny_palace(palaces_file)
            self.showed = True
            event.accept()


def on_app_quit(app):
    # 程序退出时保存当前命局输入参数,即InputInfoDialog对话框返回的参数
    with open(palaces_file, 'wb') as file:
        obj = (app.name, app.gender, app.solar_year, app.solar_month,
               app.solar_day, app.lunar_year, app.lunar_month, app.lunar_day,
               app.year_stem, app.year_branch, app.month_stem, app.month_branch,
               app.day_stem, app.day_branch, app.time_stem, app.time_branch,
               app.is_leap_month)
        pickle.dump(obj, file)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 设置程序而窗体的字体
    font_id = QFontDatabase.addApplicationFont("ExtendYaHei.ttf")
    font_families = QFontDatabase.applicationFontFamilies(font_id)
    if font_families:
        font_family = font_families[0]  # 获取字体名称
        font = QFont(font_family, 12)  # 设置字体大小为 16
        app.setFont(font)
    main_app = MainApp()

    app.aboutToQuit.connect(lambda this_app=main_app: on_app_quit(this_app))
    main_app.show()
    # 获取屏幕的几何信息
    screen_geometry = app.primaryScreen().geometry()
    # 计算窗口水平居中的位置
    x = (screen_geometry.width() - main_app.width) // 2
    # 设置窗口位置
    main_app.move(x, 20)
    app.exec()
    sys.exit(0)

在pycharm中,下面的代码:

save_action.triggered.connect(self.save_file)

总是导致编辑器右边出现一条黄色警告线,虽然并不影响程序运行,但是有点强迫症的我总想消除它,试了很多方法,结果发现添加在这行代码上方添加“ # noinspection PyUnresolvedReferences”注释是最有效的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yivifu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值