AI-GC-手把手教你写一个小说推文生成器(UI界面实现-part01)

前言👀

项目地址:小汐创作助手: 小汐创作助手 (gitee.com)

为了能够快速完成页面的开发,提高开发速度降低开发成本。因此,我们整个项目,没有直接采用原来的前后端分离方案,而是直接使用streamlit完成搭建。streamlit是一款基于Python编写的前端机器学习组件库。简单高效,内置丰富的组件,同时我们也可以使用css对部分样式进行改造处理。 docs.streamlit.io/ 那么很显然,我们本篇博文的目的就是如何快速来实现,我们先前预设的UI效果。

首页: 对话助手 小说视频生成 设置页面 这里我们会简单介绍一下,我们在项目当中使用到的基本组件。当然streamlit本身还有非常丰富的组件,可以去查看官方文档探索,虽然是英文的,但是可以使用浏览器翻译来快速阅览,查找文档。文档当中有丰富的示例,因此可以直接看到效果,然后拿过来进行修改。

页面编写⚽

那么废话不多说,我们来看到我们整个页面是如何开始完成编写的。这里我们按照模块去进行拆分,希望,通过本篇博文,读者应该可以,将我们的整体页面跑起来,这样的话,我们接下来就只需关系到我们的功能实现即可了。

页面侧边导航

在开始之前,我们需要简单了解一下关于streamlit的一些机制的问题,首先的话,streamlit是支持热加载的。整个执行过程大致是这样的:1. 页面展示 2. 监听事件 3.执行事件函数 4. 重新刷新页面 因此这里要注意,在重新刷新页面的时候,是相当于把你写的代码重新执行了一遍的,因此,你先前保留在内存当中的变量都是会被重置的,因此如果要进行状态的保存的话,需要使用到它提供的session机制来进行保存。 那么这个事件的触发,执行,包括:按钮的点击,文本框的触发。而我们的侧边栏导航的实现,是通过单选框来进行实现的,因此每一次选择的话,其实都相当于重新运行了一下界面,只是我们会把一些状态进行保存而已。因此这一点需要特别注意🍡

整个侧边导航的实现非常简单:

python 复制代码
    def page_content(page):
        if page == ':rainbow[首页]':
            index()
        if page == ':green[novel助手]':
            assistant = AssistantNovel()
            assistant.page()
        elif page == ':blue[novel生成]':
            novelGenerate = NovelGenerate()
            novelGenerate.page()
        elif page == ':red[设置]':
            novelSettings = NovelSettings()
            novelSettings.page()


    custom_css = """
        <style>
        .block-container.st-emotion-cache-gh2jqd.ea3mdgi5 {
            width: 100%;
            margin: 0 auto;
            max-width: 1200px;
        }
        .st-emotion-cache-1i41fkg.e1f1d6gn2{
            height: 600px;
            overflow-y: scroll; /* 添加垂直滚动条 */
        }
        </style>
    """
    st.markdown(custom_css, unsafe_allow_html=True)

    selected_page = st.sidebar.radio(
        'Select Page which you want 👇',
        [":rainbow[首页]", ":green[novel助手]", ":blue[novel生成]",":red[设置]"],
        captions=["HomePage👻", "Novel Assistant😊", "Novel generation🤑","Settings😶"]
    )


    page_content(selected_page)

在切换单选按钮时,执行不同的页面展示函数进行实现。同时为了满足我们的要求,我们这里重新修改了一下css。这块你可能比较好奇,为什么这个css的选择器那么奇怪。其实这个是streamit渲染之后,对应的容器的div的class。这个可以通过控制台进行获取。为了让内容显示可以有更大的空间,这里我们做了这样的修改:

python 复制代码
  custom_css = """
        <style>
        .block-container.st-emotion-cache-gh2jqd.ea3mdgi5 {
            width: 100%;
            margin: 0 auto;
            max-width: 1200px;
        }
        .st-emotion-cache-1i41fkg.e1f1d6gn2{
            height: 600px;
            overflow-y: scroll; /* 添加垂直滚动条 */
        }
        </style>
    """

首页🐱‍🏍

首页的话,主要是对项目的介绍,因此页面的形式和内容比较简单。这里的话,我们是直接使用st.markdown来实现文字的展示的。

python 复制代码
def index():

    st.markdown("*Novel-Video创作助手* is **really** ***cool*** --v0.1beta( ̄︶ ̄)↗ .")
    st.markdown('''
        :red[自带] :orange[小汐] :green[创作助手] :blue[完成文档润色] :violet[流水线]
        :gray[内容生成] :rainbow[解放双手].''')
    st.markdown("Welcome to here! &mdash;\
                :tulip::cherry_blossom::rose::hibiscus::sunflower::blossom:")
    current_file_path = os.path.abspath(__file__)
    current_dir = os.path.dirname(current_file_path)
    st.image(current_dir+r"/assert/img/bg.jpg",width=800)

当然,在这里,我们使用到一些配置相关的东西,但是这里没有涉及到具体的功能实现,因此我们不需要太关心。

到此,我们把我们最开始需要运行的main.py文件进行了实现。这将作为我们的整个程序的主入口。完整代码如下:

python 复制代码
"""
@Author:Huterox
@Description:Go For It
@Time:2024/4/16 8:58
@Copyright:©2018-2024 awesome!
"""


from webui.streamlit.novelAssistant import AssistantNovel
from webui.streamlit.novelGenerate import NovelGenerate
from webui.streamlit.novelSettings import NovelSettings
import os

import streamlit as st

def index():

    st.markdown("*Novel-Video创作助手* is **really** ***cool*** --v0.1beta( ̄︶ ̄)↗ .")
    st.markdown('''
        :red[自带] :orange[小汐] :green[创作助手] :blue[完成文档润色] :violet[流水线]
        :gray[内容生成] :rainbow[解放双手].''')
    st.markdown("Welcome to here! &mdash;\
                :tulip::cherry_blossom::rose::hibiscus::sunflower::blossom:")
    current_file_path = os.path.abspath(__file__)
    current_dir = os.path.dirname(current_file_path)
    st.image(current_dir+r"/assert/img/bg.jpg",width=800)

if __name__ == '__main__':

    def page_content(page):
        if page == ':rainbow[首页]':
            index()
        if page == ':green[novel助手]':
            assistant = AssistantNovel()
            assistant.page()
        elif page == ':blue[novel生成]':
            novelGenerate = NovelGenerate()
            novelGenerate.page()
        elif page == ':red[设置]':
            novelSettings = NovelSettings()
            novelSettings.page()


    custom_css = """
        <style>
        .block-container.st-emotion-cache-gh2jqd.ea3mdgi5 {
            width: 100%;
            margin: 0 auto;
            max-width: 1200px;
        }
        .st-emotion-cache-1i41fkg.e1f1d6gn2{
            height: 600px;
            overflow-y: scroll; /* 添加垂直滚动条 */
        }
        </style>
    """
    st.markdown(custom_css, unsafe_allow_html=True)

    selected_page = st.sidebar.radio(
        'Select Page which you want 👇',
        [":rainbow[首页]", ":green[novel助手]", ":blue[novel生成]",":red[设置]"],
        captions=["HomePage👻", "Novel Assistant😊", "Novel generation🤑","Settings😶"]
    )


    page_content(selected_page)

对话助手

我想在这块,你应该注意到了,我们还导入了如下包:

python 复制代码
from webui.streamlit.novelAssistant import AssistantNovel
from webui.streamlit.novelGenerate import NovelGenerate
from webui.streamlit.novelSettings import NovelSettings

实际上,这个就是我们接下来需要逐一实现的页面。 那么这块我们还是先来看到我们的对话助手。注意,在我们本篇文章里面,还是先讨论我们如何实现这个页面,而不涉及到我们具体的实现,具体的实现,我们将在具体的章节讲到。 那么关于对话助手的实现的话,还是比较简单的,这里我们使用了streamlit提供的专门的对话组件,所以实现很快,并且实现效果还是很不错的。这个风格我也挺喜欢的,当然这里还是没有做历史话题记录,因为如果要做的话,那么我们这里最好还是要设计一套用户系统,这样的话,开发事件又上去了。 那么对话助手的实现如下:

python 复制代码
class AssistantNovel(object):

    def __init__(self):
        self.chat = ChatBotHandler()

    def get_response(self,prompt, history):
        return self.chat.signChat(history)

    def clear_chat_history(self):
        st.session_state.messages = [{"role": "assistant", "content": "🍭🍡你好!我是全能创作助手~小汐🥰,可以帮助您完善补充文案细节?🧐"}]

    def page(self):
        # 主聊天对话窗口
        prompt = st.chat_input(placeholder="请输入对话")

        if "messages" not in st.session_state.keys():
            st.session_state.messages = [{"role": "assistant", "content": "🍭🍡你好!我是全能创作助手~小汐🥰,可以帮助您完善补充文案细节?🧐"}]

        for message in st.session_state.messages:
            with st.chat_message(message["role"]):
                st.write(message["content"])
        if prompt:
            st.session_state.messages.append({"role": "user", "content": prompt})
            with st.chat_message("user"):
                st.write(prompt)

        if st.session_state.messages[-1]["role"] != "assistant":
            with st.chat_message("assistant"):
                with st.spinner("Thinking..."):
                    try:
                        response = "我是返回的结果,你好"
                    except Exception as e:
                        print(e)
                        response = "哦┗|`O′|┛ 嗷~~,出错了,请稍后再试!😥"
                    placeholder = st.empty()
                    full_response = ''
                    for item in response:
                        full_response += item
                        time.sleep(0.01)
                        placeholder.markdown(full_response)
                    placeholder.markdown(full_response)
            message = {"role": "assistant", "content": full_response}
            st.session_state.messages.append(message)
        st.button('清空历史对话', on_click=self.clear_chat_history)

那么在这块,我们的重点(后面实现功能的时候)就是:response = "我是返回的结果,你好" 这块代码的返回结果需要配合到我们的真正的AI返回。当然这块为了简便,我们也是实现了打字机特效,只是这个特效的前提不是以流式调用openai得到的,而是一次性拿到结果,然后再本地实现打字机特效进行加载的。这样的话,实现更加简答,而且一次性传输,虽然总体上慢一点,但是开销也小一点。

设置页面🍭

这块的话,我们先聊哪个小说视频页面是怎么实现的,因为这个的话还是比较复杂的。但是我们可以先聊一聊,这个设置页面,这个页面相对简单。 那么在这里的话,我们也是用到了streamlit的布局系统: streamlit默认是垂直布局的,也就是所有的元素默认往下放置的。那么当我们要实现刚刚的这样的效果的时候,就不得不使用到水平布局,使用水平布局非常方便:

python 复制代码
c1,c2 = st.columns(2)
with c1:
	pass
with c2:
	pass

这样一来就直接完成了布局,这默认是1:1,所以你还可以这样传递参数:

python 复制代码
c1,c2 = st.columns([1,2])
with c1:
	pass
with c2:
	pass

这样的话,左边比右边就是1:2布局。

okey,那么接下来的话,我们直接看到总体代码吧:

python 复制代码
class NovelSettings(object):

    def __init__(self):
        self.current_file_path = os.path.abspath(__file__)
        self.current_dir = os.path.dirname(self.current_file_path)

    def __temp_save_config(self,openai_key,base_url,default_model,mj_api_key,global_temperature):
        Config.settings["openai_api_key"] = openai_key
        Config.settings["openai_api_base"] = base_url
        Config.settings["default_model"] = default_model
        Config.settings["image_api_key"] = mj_api_key
        Config.settings["temperature"] = global_temperature
        st.session_state.settings = Config.settings

    def page(self):
        # 左侧边栏还是设置,右侧还是音频展示
        col1,col2 = st.columns([1, 2])
        gap = "50px"
        # 为列之间的间隔添加CSS样式
        st.markdown(f"<style>.streamlit-column{{ padding-right: {gap}; }}</style>", unsafe_allow_html=True)
        with col1:

            st.markdown('''
                :red[设置仅当前有效哟😎],\n
                :green[刷新后回复默认设置😝]''')
            # 创建文本输入框
            openai_key = st.text_input("请输入你的OpenAI key", value=Config.settings.get("openai_api_key"))
            base_url = st.text_input("请输入你的Base Url", value=Config.settings.get("openai_api_base"))
            default_model = st.text_input("请输入你的Default Model", value=Config.settings.get("default_model"))
            mj_api_key = st.text_input("请输入你的绘画API Key", value=Config.settings.get("image_api_key"))
            # 创建数字输入框
            global_temperature = st.number_input("设置全局temperature", min_value=0.1, max_value=1.0, step=0.1,
                                                 value=Config.settings.get("temperature"))
            save_button = st.button("保存设置")
            # 这里可以根据需要添加保存设置的逻辑
            if save_button:
                self.__temp_save_config(openai_key, base_url, default_model, mj_api_key, global_temperature)
                show_popup = st.empty()
                with st.expander("提示🍡", expanded=True):
                    st.write(":green[nice,仅当前有效哟~😊。]")
                    # 添加一个按钮来关闭弹窗
                    if st.button("关闭"):
                        show_popup.empty()
        with col2:
            st.markdown('''
                 :blue[语音试听🍳]''')

            c01,c02,c03 = st.columns(3)
            with c01:
                st.text("小艺")
                st.audio(self.current_dir+r"\..\..\assert\audio\test01.mp3")
                st.text("云建")
                st.audio(self.current_dir+r"\..\..\assert\audio\test02.mp3")
                st.text("云溪")
                st.audio(self.current_dir + r"\..\..\assert\audio\test03.mp3")
            with c02:
                st.text("云霞")
                st.audio(self.current_dir+r"\..\..\assert\audio\test04.mp3")
                st.text("云阳")
                st.audio(self.current_dir + r"\..\..\assert\audio\test05.mp3")
                st.text("小北")
                st.audio(self.current_dir + r"\..\..\assert\audio\test06.mp3")
            with c03:
                st.text("小妮")
                st.audio(self.current_dir+r"\..\..\assert\audio\test07.mp3")

这里的话,我们用到了关于config的信息,那么关于config的话,其实无法也就这几条:

python 复制代码
{
    "openai_api_key": "key",
    "openai_api_base": "https://api.moonshot.cn/v1",
    "language": "auto",
    "hide_history_when_not_logged_in": true,
    "default_model": "moonshot-v1-8k",
    "temperature": 0.5,
    "multi_api_key": false,
    "server_name": "0.0.0.0",
    "server_port": 9090,
    "autobrowser": false,
    "share": false,
    "system_xiaoxi": "你是一个全能小助手,你的名字叫小汐,尤其擅长写作和故事改编。",
    "image_api_key": "key"
}

只不过是这样读取了一下而已:

python 复制代码
def getConfig()->dict:
    try:
        with open(r"./config.json", 'r', encoding="utf-8") as f:
            config = json.load(f)
    except Exception as e:
        try:
            current_file_path = os.path.abspath(__file__)
            current_dir = os.path.dirname(current_file_path)
            with open(current_dir + "/../config.json", 'r', encoding="utf-8") as f:
                config = json.load(f)
        except Exception as e:
            current_file_path = os.path.abspath(__file__)
            current_dir = os.path.dirname(current_file_path)
            with open(current_dir + "/config.json", 'r', encoding="utf-8") as f:
                config = json.load(f)
    if ("settings" in st.session_state.keys()):
        config = st.session_state.settings
    else:
        st.session_state.settings = config
    return config
# config 变为全局变量

class Config(object):
    settings = getConfig()

因为我们设置还是需要将配置里面的一些信息进行展示的😃

视频生成页面🦁

之后的话,就是我们的视频生成页面了,这个部分确实还是比较麻烦的。而且有几个小细节需要注意。当然,总体的页面实现还是不复杂的,我们开始依次进行拆解。

页面布局😃

毫无以为,我们还是先来看到我们的页面布局吧: 框起来的都是意味着,我们使用到了对应的水平布局的:

所以这里废话不多少,我们先来看到我们是怎么吧我们的组件给布置上去的,先不讨论,怎么实现功能。这里我们看到我们专门的page方法。

python 复制代码
 def page(self):
        self.col1,self.col2 = st.columns([1,2])
        with self.col1:
            gen_data = self.__get_gen_data()
            self.novel_text = st.text_area(label="文本输入",placeholder="请输入小说文本🎈",
                                           height=380,value=gen_data.get("novel_text"))
            self.temperature = st.slider("temperature",min_value=0.2,max_value=1.0,step=0.1,
                                         value=gen_data.get("temperature"),)
            self.b_c0,self.b_c1 = st.columns([1,2])
            with self.b_c0:
                self.template = st.button("模板生成",type="primary")
            if(self.template):
                self.__gen_model_fn()
        with self.col2:
            st.markdown("当前版本可直接生成视频,也可导出素材至剪映等剪辑软件ヾ(≧▽≦*)o")
            c1,c2,c3,c4,c5,c6 = st.columns(6)
            with c1:
                self.batch_gen = st.button("批量生成",type="primary",on_click=self.__batch_gen_fn)
            with c2:
                # self.export_button_jianying = st.button("导出剪映",type="primary",on_click=self.__export_jianying_fn)
                self.export_button_jianying = st.button("导出素材",type="primary",on_click=self.__export_source_fn)
            with c3:
                self.export_button_video = st.button("导出视频",type="primary",on_click=self.__export_video_fn)
            with c4:
                self.audio_select = st.selectbox("音频选择",("小艺","云建","云溪","云霞","云阳","小北","小妮"))
                if self.audio_select:
                    self.gen_data["audio_select"] = self.audio_select
                    st.session_state.gen_data = self.gen_data
            with c5:
                self.language_select = st.selectbox("语言选择",("中文","英文"))
                if self.language_select:
                    self.gen_data["language_select"] = self.language_select
                    st.session_state.gen_data = self.gen_data
            with c6:
                self.add_button  = st.button("添加",type="primary",on_click=self.__current_add)
            # 展示数据的容器
            st.markdown("----------------->生成内容👻🦅👇")
            self.data_container = st.container(height=400)
            data = self.gen_data
            for index in range(len(data.get("data"))):
                with self.data_container:
                    # 创建一行多列的布局,现在拿到当前行的数据
                    c01, c02, c03, c04,c06,c07 = st.columns([4, 3, 2, 2, 2,2])
                    current_line_data = data.get("data")[index]
                    with c01:
                        # 加载图片
                        if(current_line_data["图片"] != self.default_img):
                            if(current_line_data["图片"]!=-1):
                                st.image(current_line_data["图片"] ,caption="生成-成功",)
                            else:
                                image = Image.open(self.default_img)
                                st.image(image, caption="生成-失败")
                        else:
                            image = Image.open(self.default_img)
                            st.image(image,  caption="示例")
                    with c02:
                        # 加载音频

                        if (current_line_data["音频"] != self.default_audio):
                            if(current_line_data["音频"]!=-1):
                                st.text("生成-成功")
                                st.audio(current_line_data["音频"])
                            else:
                                st.text("生成-失败")
                                st.audio(self.default_audio)
                        else:
                            st.text("示例")
                            st.audio(self.default_audio)

                    with c03:
                        # 查看文本-提示词
                        st.button("提示"+str(index),on_click=self.c03_button_click,args=(index,current_line_data,))

                    with c04:
                        # 查看文本-分段
                        st.button("分段" + str(index), on_click=self.c04_button_click,args=(index,current_line_data,))

                    with c06:
                        if st.button("删除"+str(index)):
                            self.__current_delete(index)

                    with c07:
                        if st.button("生成"+str(index),type="primary"):
                            self.__current_gen(index)

当然在这里我们还绑定了一些函数,这些函数我们先默认实现都是空,先不管。

数据加载

现在我们看到了我们完成了基本的页面布局,并且把我们的组件都搞上去了。但是细心的你应该是发现了,那就是这部分是动态加载的数据。 那么这块的话,就不得不说回到我们先前说的关于streamlit的特性,那就是我们的streamlit再执行一个方法,按钮(被触发)之后,我们的整个页面会重新刷新,所以如果,你执行完毕之后,能够把数据存起来,那么他会自动刷新页面,然后我们加载的组件又是根据数据来的,这样的话,就完成了我们的数据的动态的添加操作了。那么在这块的话,我们主要看到我们数据张什么样子:

python 复制代码
self.gen_data = {
            "novel_text":"",
            "temperature":0.4,
            "audio_select": "小艺",
            "language_select":"中文",
            "data":[
                    {
                        "提示词":"prompt0",
                        "分段":"part0",
                        "图片":self.default_img,
                        "音频":self.default_audio
                    },
                    {
                        "提示词": "prompt1",
                        "分段": "part1",
                        "图片": self.default_img,
                        "音频": self.default_audio
                    },
                    ]
        }

那么重点是看到这一块:

那么这里的话,其实就是我们需要唯一注意的点,在页面的展示阶段。当然我们接下来还有几个细节需要注意。

注意细节☠

我们刚刚谈到,关于streamlit的执行机制的问题,因此的话,虽然这个执行机制非常的好,可以帮助我们及时将数据刷新过来,这一点有点类似于vue的响应式。(说到这个,最近也有研究,有机会可以聊聊如何通过proxy+观察者模式实现这个数据的双向绑定)

  1. text_area 获取输入值,这里我们获取输入值,需要使用这里面绑定的key 至于是为啥,因为这个组件默认就是触发的,所以只能通过这种方式来拿到值,具体的可以思考思考😊并不难想到
  2. 函数的绑定,同样由于刷新机制的问题,在弹窗当中的按钮绑定事件,只能使用on_click来绑定,才能保证触发。为什么同样是刚刚的原因。总结就是:同一时刻(只能有一个方法运行(按钮))on_click绑定类似于call_back的方式,所以一定会触发。总结就是,当你不确定的时候,直接使用on_click 绑定事件。而不是if来判断触发

那么到这块这边的细节就这些了,在实际实现的时候,还有细节,我们到时候再说。

完整代码🐨

python 复制代码
import streamlit as st
from streamlit_modal import Modal
modal = Modal(key="Data",title="view😀")
class NovelGenerate:
    def __init__(self):
        # 在这里定义到我们的数据

        self.current_file_path = os.path.abspath(__file__)
        self.current_dir = os.path.dirname(self.current_file_path)
        self.storyBoardHandler = StoryBoardHandler()
        self.default_img = self.current_dir+r"/../../assert/img/wait.jpg"
        self.default_audio = self.current_dir + r"\..\..\assert\audio\test01.mp3"
        self.sources = self.current_dir+"/../../resource/sources"
        self.video_builder = VideoBuilder()
        self.exportSource = ExportSource()
        self.zip_file_path = self.sources+"/default.zip"
        self.gen_data = {
            "novel_text":"",
            "temperature":0.4,
            "audio_select": "小艺",
            "language_select":"中文",
            "data":[
                    {
                        "提示词":"prompt0",
                        "分段":"part0",
                        "图片":self.default_img,
                        "音频":self.default_audio
                    },
                    {
                        "提示词": "prompt1",
                        "分段": "part1",
                        "图片": self.default_img,
                        "音频": self.default_audio
                    },
                    ]
        }

        if("gen_data" in st.session_state.keys()):
            self.gen_data = st.session_state.gen_data
        else:
            st.session_state.gen_data = self.gen_data

    """
    -- v0.2.5 版本暂不考虑此方案
    """
    def __export_jianying_fn(self):
        # 导出剪映模板
        pass

    def __export_source_do(self):
		pass

    def __export_source_fn(self):
        # 导出媒体资源
   		pass


    def __export_video_fn(self):
       pass

    # 保证我们当前的gen_data和在session里面的是一致的
    def __get_gen_data(self):
        if ("gen_data" in st.session_state.keys()):
            self.gen_data = st.session_state.gen_data
        else:
            st.session_state.gen_data = self.gen_data
        return self.gen_data



    def __current_delete(self,index):
        # print("删除-当前元素",index)
		pass

    def __current_add(self):
       pass

    def __batch_gen_fn(self):
       pass

    def __current_gen_batch(self,index,condition:list):

       pass

    def __current_gen(self,index):

        pass

    def __gen_model_fn(self):

    	pass

    def page(self):
        self.col1,self.col2 = st.columns([1,2])
        with self.col1:
            gen_data = self.__get_gen_data()
            self.novel_text = st.text_area(label="文本输入",placeholder="请输入小说文本🎈",
                                           height=380,value=gen_data.get("novel_text"))
            self.temperature = st.slider("temperature",min_value=0.2,max_value=1.0,step=0.1,
                                         value=gen_data.get("temperature"),)
            self.b_c0,self.b_c1 = st.columns([1,2])
            with self.b_c0:
                self.template = st.button("模板生成",type="primary")
            if(self.template):
                self.__gen_model_fn()
        with self.col2:
            st.markdown("当前版本可直接生成视频,也可导出素材至剪映等剪辑软件ヾ(≧▽≦*)o")
            c1,c2,c3,c4,c5,c6 = st.columns(6)
            with c1:
                self.batch_gen = st.button("批量生成",type="primary",on_click=self.__batch_gen_fn)
            with c2:
                # self.export_button_jianying = st.button("导出剪映",type="primary",on_click=self.__export_jianying_fn)
                self.export_button_jianying = st.button("导出素材",type="primary",on_click=self.__export_source_fn)
            with c3:
                self.export_button_video = st.button("导出视频",type="primary",on_click=self.__export_video_fn)
            with c4:
                self.audio_select = st.selectbox("音频选择",("小艺","云建","云溪","云霞","云阳","小北","小妮"))
                if self.audio_select:
                    self.gen_data["audio_select"] = self.audio_select
                    st.session_state.gen_data = self.gen_data
            with c5:
                self.language_select = st.selectbox("语言选择",("中文","英文"))
                if self.language_select:
                    self.gen_data["language_select"] = self.language_select
                    st.session_state.gen_data = self.gen_data
            with c6:
                self.add_button  = st.button("添加",type="primary",on_click=self.__current_add)
            # 展示数据的容器
            st.markdown("----------------->生成内容👻🦅👇")
            self.data_container = st.container(height=400)
            data = self.gen_data
            for index in range(len(data.get("data"))):
                with self.data_container:
                    # 创建一行多列的布局,现在拿到当前行的数据
                    c01, c02, c03, c04,c06,c07 = st.columns([4, 3, 2, 2, 2,2])
                    current_line_data = data.get("data")[index]
                    with c01:
                        # 加载图片
                        if(current_line_data["图片"] != self.default_img):
                            if(current_line_data["图片"]!=-1):
                                st.image(current_line_data["图片"] ,caption="生成-成功",)
                            else:
                                image = Image.open(self.default_img)
                                st.image(image, caption="生成-失败")
                        else:
                            image = Image.open(self.default_img)
                            st.image(image,  caption="示例")
                    with c02:
                        # 加载音频

                        if (current_line_data["音频"] != self.default_audio):
                            if(current_line_data["音频"]!=-1):
                                st.text("生成-成功")
                                st.audio(current_line_data["音频"])
                            else:
                                st.text("生成-失败")
                                st.audio(self.default_audio)
                        else:
                            st.text("示例")
                            st.audio(self.default_audio)

                    with c03:
                        # 查看文本-提示词
                        st.button("提示"+str(index),on_click=self.c03_button_click,args=(index,current_line_data,))

                    with c04:
                        # 查看文本-分段
                        st.button("分段" + str(index), on_click=self.c04_button_click,args=(index,current_line_data,))

                    # with c05:
                    #     st.selectbox("音频" + str(index),
                    #                  ("小艺", "云建", "云溪", "云霞", "云阳", "小北", "小妮"))

                    with c06:
                        if st.button("删除"+str(index)):
                            self.__current_delete(index)

                    with c07:
                        if st.button("生成"+str(index),type="primary"):
                            self.__current_gen(index)

    def c03_button_click(self,index,current_line_data):
		pass

    def c04_button_click(self,index,current_line_data):
		pass

总结

最后,这里还需要注意一点,那就是在项目当中: webui有两个实现,一个是gradio,一个是streamlit。对应gradio是先前的废弃方案,所以请不要关注那个部分😑

相关推荐
一只老虎10 分钟前
基于 OpenCV 和 dlib 方法进行视频人脸检测的研究
人工智能·opencv·音视频
全域观察15 分钟前
开源,一天200star,解锁视频字幕生成新方式——一款轻量级开源字幕工具,免费,支持花字,剪映最新会员模式吃相太难看了
人工智能·新媒体运营·开源软件·内容运营·程序员创富
不去幼儿园29 分钟前
【SSL-RL】自监督强化学习: 好奇心驱动探索 (CDE)算法
大数据·人工智能·python·算法·机器学习·强化学习
SaNDJie33 分钟前
24.11.13 机器学习 特征降维(主成份分析) KNN算法 交叉验证(K-Fold) 超参数搜索
人工智能·算法·机器学习
只怕自己不够好2 小时前
Tensorflow基本概念
人工智能·tensorflow
vvw&2 小时前
如何在 Ubuntu 上安装 Jupyter Notebook
linux·人工智能·python·opencv·ubuntu·机器学习·jupyter
deflag2 小时前
第T7周:Tensorflow实现咖啡豆识别
人工智能·tensorflow·neo4j
CV-King3 小时前
AI生成字幕模型whisper介绍与使用
人工智能·opencv·计算机视觉·whisper
BestSongC5 小时前
基于YOLOv8模型的安全背心目标检测系统(PyTorch+Pyside6+YOLOv8模型)
人工智能·pytorch·深度学习·yolo·目标检测·计算机视觉
冻感糕人~6 小时前
大模型研究报告 | 2024年中国金融大模型产业发展洞察报告|附34页PDF文件下载
人工智能·程序人生·金融·llm·大语言模型·ai大模型·大模型研究报告