1、建立环境

windows下

cd .\cards-backend\
python -m venv venv
.\venv\Scripts\Activate.ps1

老样子,记得切到venv下面去

=======================================================

2、安装界面库

pip3 install nicegui

先装nicgui

from nicegui import ui

ui.label('Hello NiceGUI!')

ui.run()

测试用代码

好了,没问题了

=======================================================

3、安装数据库ORM库,小乌龟

 pip3 install tortoise-orm

定义模型:models.py

from tortoise.models import Model
from tortoise import fields


class Cards(Model):
    id = fields.IntField(pk=True)
    study_id = fields.TextField()
    front_cover_image = fields.TextField()
    front_cover_llm_prompt = fields.TextField()
    cn_text = fields.TextField()
    fr_text = fields.TextField()
    pinyin_text = fields.TextField()
    create_date = fields.DatetimeField(auto_now_add=True)
    update_date = fields.DatetimeField(auto_now=True)


from tortoise import Tortoise, run_async
from models import Cards

async def init():
    # Here we create a SQLite DB using file "db.sqlite3"
    #  also specify the app name of "models"
    #  which contain models from "app.models"
    await Tortoise.init(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['models']}
    )
    # Generate the schema
    await Tortoise.generate_schemas()

    # Create instance by save
    card = Cards(study_id='1.1.1',
                 front_cover_image="\\images\\p2888293790.jpeg",
                 front_cover_llm_prompt="",
                 cn_text="你好!你好!",
                 fr_text="Bonjour!Bonjour!",
                 pinyin_text="/nǐ/hǎo! /nǐ/hǎo!")
    await card.save()

    await Tortoise.close_connections()

# run_async is a helper function to run simple async Tortoise scripts.
run_async(init())

做一个数据库的初始化的脚本

接着,初始化数据库本身:

python init_db.py

用Dbearver打开

很好,用数据库来管理就会方便很多


=======================================================

4、移植一个最基础版本的编辑界面:

from typing import List

import models
from tortoise import Tortoise

from nicegui import app, ui

async def init_db() -> None:
    await Tortoise.init(db_url='sqlite://db.sqlite3', modules={'models': ['models']})

async def close_db() -> None:
    await Tortoise.close_connections()

app.on_startup(init_db)
app.on_shutdown(close_db)

@ui.refreshable
async def list_of_cards() -> None:
    async def delete(card: models.Cards) -> None:
        await card.delete()
        list_of_cards.refresh()

    cards: List[models.Cards] = await models.Cards.all()
    print(cards)
    for card in reversed(cards):
        with ui.card():
            with ui.row().classes('items-center'):
                ui.input('study_id', on_change=card.save) \
                    .bind_value(card, 'study_id').on('blur', list_of_cards.refresh)
                ui.input('front_cover_image', on_change=card.save) \
                    .bind_value(card, 'front_cover_image').on('blur', list_of_cards.refresh).classes('w-20')
                ui.input('prompt', on_change=card.save) \
                    .bind_value(card, 'front_cover_llm_prompt').on('blur', list_of_cards.refresh)
                ui.input('cn_text', on_change=card.save) \
                    .bind_value(card, 'cn_text').on('blur', list_of_cards.refresh)
                ui.input('fr_text', on_change=card.save) \
                    .bind_value(card, 'fr_text').on('blur', list_of_cards.refresh)
                ui.input('pinyin_text', on_change=card.save) \
                    .bind_value(card, 'pinyin_text').on('blur', list_of_cards.refresh)
                ui.button(icon='delete', on_click=lambda u=card: delete(u)).props('flat')


@ui.page('/')
async def index():
    async def create() -> None:
        await models.Cards.create(name=name.value, front_cover_image='')
        name.value = ''
        front_cover_image.value = None
        list_of_cards.refresh()

    with ui.column().classes('mx-auto'):
        with ui.row().classes('w-full items-center px-4'):
            name = ui.input(label='Name')
            front_cover_image = ui.input(label='front_cover_image').classes('w-20')
            ui.button(on_click=create, icon='add').props('flat').classes('ml-auto')
        await list_of_cards()

ui.run()

=======================================================

5、引入模板生成机制,生成静态的html5页面

pip3 install Jinja2

然后建立模板:

        {% for card in cards %}
          <!-- Slides -->
          <div class="swiper-slide">
            <img src="{{ card.front_cover_image }}" alt="Card 1 Image">
            <span class="row">
                <span class="lang">🇨🇳</span>
                <p class="in-speak">{{ card.cn_text }}</p>
            </span>
            <span class="row">
                <span class="lang">🇫🇷</span>
                <p class="in-speak">{{ card.fr_text }}</p>
            </span>
          </div>
          {% endfor %}

昨天的工作成果里,我使用了swiper这个库来建立上下滑动的东西

然后今天我把html这部分模板化掉

from jinja2 import Environment, FileSystemLoader

# 加载模板文件
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('index_template.html')

python里加载模板文件

在这里加入新的按钮

按钮对应新的渲染逻辑:

    async def build_html() -> None:
        print("I am in build_html()")
        cards: List[models.Cards] = await models.Cards.all()
        print(cards)
        # 渲染模板
        html_output = template.render(cards=cards)
        # 将渲染后的结果写入文件(可选)
        with open('index.html', 'w') as file:
            file.write(html_output)

加载数据库的所有卡片,并且渲染新的index.html

报错:'gbk' codec can't encode character '\U0001f1e8' in position 1763: illegal multibyte sequence

 https://stackoverflow.com/questions/3218014/unicodeencodeerror-gbk-codec-cant-encode-character-illegal-multibyte-sequen 

修改open部分:

        with open('index.html', 'w',encoding='utf-8') as file:
            file.write(html_output)

问题解决

看一下生成的index.html

卧槽,效果真好

OK,这样以后就不用手动去管理这个东西了

==========================================

6、工作流

先维护数据库:

生成好对应的图片

进入页面点击最右边的生成页面

进入刚才生成的index.html刷新查看结果

之后再写一个发布的脚本就可以了

=============================================

7、待优化

豆包那边生成图片到这个具体的句子的关联的过程,以及翻译的过程,是可以将存入数据库的过程再自动化一些的

这样就可以制作上千条句子的对应五六个国家语言的场景卡片了