aardio 文档

aardio 范例: Qwen-Image 文生图/魔搭

相关范例:Z-Image 文生图

//相关范例:Z-Image 文生图 https://www.aardio.com/zh-cn/docs/examples/AI/image/modelscope-z-image.html

import crypt;
import inet.http;//界面线程导入 inet.http 则 plus 控件可支持从网址加载图像
import win.ui.simpleWindow;
import fonts.fontAwesome;
import web.rest.jsonClient;
import win.ui;
/*DSG{{*/
var winform = win.form(text="魔搭 - 文生图";right=1150;bottom=700;bgcolor=0xF5F5F5;border="none")
winform.add(
bkplus={cls="bkplus";text="种子 (Seed):";left=745;top=285;right=900;bottom=305;align="left";anchors={top=1;right=1};color=0x666666;z=9};
btnCopy={cls="plus";text="复制图像 / 图像文件";left=921;top=631;right=1110;bottom=680;align="left";anchors={right=1;bottom=1};bgcolor=0x27AE60;border={radius=6};color=0xFFFFFF;disabled=1;font=LOGFONT(h=-14;weight=600);iconStyle={align="left";font=LOGFONT(h=-14;name='FontAwesome');padding={left=17;top=1}};iconText='\uF0EA';notify=1;textPadding={left=37};z=18};
btnGenerate={cls="plus";text="生成图像";left=742;top=631;right=902;bottom=680;align="left";anchors={right=1;bottom=1};bgcolor=0x3498DB;border={radius=6};color=0xFFFFFF;font=LOGFONT(h=-15;weight=600);iconStyle={align="left";font=LOGFONT(h=-16;name='FontAwesome');padding={left=17;top=1}};iconText='\uF1FC';notify=1;textPadding={left=37};z=17};
btnOpenKeyUrl={cls="plus";text="设置 API 接口";left=745;top=122;right=869;bottom=145;align="left";anchors={top=1;right=1};color=0x3498DB;iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome')};iconText='\uF08E';notify=1;textPadding={left=18};z=10};
chkRandomSeed={cls="plus";text="随机种子";left=745;top=346;right=848;bottom=371;align="left";anchors={top=1;right=1};color=0x3C3C3C;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8}};iconText='\uF096';notify=1;textPadding={left=28};z=14};
comboQuality={cls="combobox";left=745;top=245;right=1105;bottom=270;anchors={top=1;right=1;bottom=1};edge=1;items={"auto","low","medium","high","xhigh","max"};mode="dropdownlist";z=12};
comboSize={cls="combobox";left=745;top=184;right=1105;bottom=209;anchors={top=1;right=1};edge=1;items={"1:1(2048x2048)","4:3(2400x1792)","3:4(1792x2400)","3:2(2528x1696)","2:3(1696x2528)","16:9(2752x1536)","9:16(1536x2752)"};mode="dropdownlist";z=11};
editPrompt={cls="edit";left=30;top=565;right=710;bottom=680;anchors={left=1;right=1;bottom=1};autohscroll=false;bgcolor=0xFFFFFF;edge=1;multiline=1;vscroll=1;z=16};
editSeed={cls="edit";text="42";left=745;top=309;right=1105;bottom=334;anchors={top=1;right=1};edge=1;multiline=1;z=13};
labelPrompt={cls="bkplus";text="提示词 (Prompt):";left=30;top=540;right=200;bottom=560;align="left";anchors={left=1;bottom=1};color=0x999999;font=LOGFONT(h=-13;weight=600);z=5};
labelQuality={cls="bkplus";text="质量 (Quality):";left=745;top=221;right=930;bottom=241;align="left";anchors={top=1;right=1};color=0x666666;z=7};
labelSize={cls="bkplus";text="尺寸 (Size):";left=745;top=160;right=930;bottom=180;align="left";anchors={top=1;right=1};color=0x666666;z=6};
labelTip={cls="plus";left=767;top=556;right=1069;bottom=607;anchors={right=1;bottom=1};border={color=0x008000;radius=16;width=1};hide=1;z=8};
picturebox={cls="plus";text="请在下方输入提示词生成图像";left=30;top=57;right=710;bottom=500;anchors={left=1;top=1;right=1;bottom=1};border={color=0xEEEEEE;radius=8;width=1};color=0x999999;font=LOGFONT(h=-16);iconStyle={font=LOGFONT(h=-60;name='FontAwesome')};iconText='\uF03E';notify=1;repeat="scale";textPadding={top=100};z=2};
progressBar={cls="plus";left=812;top=138;right=1023;bottom=349;anchors={top=1;right=1;bottom=1};bgcolor=0xECF0F1;border={radius=3};forecolor=0x3498DB;hide=1;z=15};
titleBar={cls="bkplus";left=0;top=0;right=1150;bottom=46;anchors={left=1;top=1;right=1};bgcolor=0x2C3E50;z=1};
titleIcon={cls="bkplus";text='\uF03E';left=15;top=6;right=55;bottom=42;anchors={left=1;top=1};color=0x3498DB;font=LOGFONT(h=-20;name='FontAwesome');z=4};
titleText={cls="bkplus";text="魔搭 Qwen Image 2.1 文生图";left=2;top=10;right=336;bottom=34;anchors={left=1;top=1};color=0xFFFFFF;font=LOGFONT(h=-16;weight=700);z=3}
)
/*}}*/

import fsys.table;
var configImage = fsys.table(io.appData("aardio/std/examples/AI/images/modelscope.table"),{
    edit = "text";
    comboSize = "2048x2048";  
    editPrompt = `A cinematic photo of a tiny astronaut riding a corgi on Mars, dramatic sunset, ultra detailed, 35mm lens.`;
    selItem = 1;
    itemData = [{
        url="https://api-inference.modelscope.cn/v1";
        key= "";
        model="Qwen/Qwen-Image-2.1";
        temperature=1; 
    }] 
});
var aiChatConfig = configImage.itemData[[configImage.selItem]] : {}  ;

winform.bindConfig( configImage,{
    edit = "text"; 
    combobox = "selText"; 
} );

winform.editPrompt.setPadding(5,5,5,5); 
winform.progressBar.setPieRange(0,100);
winform.progressBar.orphanWindow(true);

var tempImageDir = io.tmpname();
io.createDir(tempImageDir);

winform.lastImagePath = null;
winform.lastImageExt = "png";

winform.showTip = function(str){
    winform.labelTip.text = str;
    winform.labelTip.show(true);
    winform.setTimeout(
        function(){
            winform.labelTip.show(false);
        },5000
    );
}

winform.setBusy = function(busy){
    if(busy){
        winform.btnGenerate.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'};
        winform.btnCopy.disabled = true; 
    }
    else{
        winform.btnGenerate.disabledText = null; 
    }
}

winform.btnGenerate.oncommand = function(){
    var prompt = string.trim(winform.editPrompt.text);
    if(!#prompt){
        return winform.msgbox("请输入提示词。","提示","warn");
    }

    if(!aiChatConfig[["url"]]){
        return winform.msgbox("请先配置 API 接口。",,"warn");
    }

    var size = winform.comboSize.selText || "(2048x2048)";
    size =  string.match(size,"\((.+)\)");

    var quality = winform.comboQuality.selText || "auto";  

    var steps = ({
        low    = 15; // 快速预览
        medium = 30; // 平衡模式
        high   = 40; // 官方标准推荐
        xhigh  = 45; // 复杂高质
        max    = 50; // 魔搭 API 允许的最大上限
        auto   = 30; // 默认对齐官方标准
    })[quality] : 40; // 未指定或非法参数兜底为 40

    if(winform.chkRandomSeed.checked){
        winform.editSeed.text = ..math.random(99999)
    }
    var seed = winform.editSeed.text;

    winform.picturebox.background = null;
    winform.picturebox.foreground = null;
    winform.picturebox.iconText = '\uF03E';
    winform.picturebox.text = "正在连接魔搭图像模型...";
    winform.text = "魔搭文生图 - 生成中";
    winform.btnCopy.text = "复制图像 / 图像文件";
    winform.btnCopy.iconText = '\uF0EA';
    winform.lastImagePath = null;
    winform.lastImageExt = outputFormat;
    winform.setBusy(true);

    thread.invoke(
        function(winform, aiChatConfig, prompt, size, steps, seed ){


            import web.rest.jsonClient; 
            var http = web.rest.jsonClient(,aiChatConfig.proxy);

            import string.escape2;
            http.setAuthToken(string.escape2(aiChatConfig.key));
            http.setTimeouts(15000,300000,300000);

            http.setHeaders({
                "X-ModelScope-Async-Mode":"true",
                "X-ModelScope-Task-Type":"image_generation"
            });

            //声明 HTTP API
            var ai = http.api("https://api-inference.modelscope.cn/v1");

            winform.picturebox.text = "正在生成图像,请稍候...";

            //向服务器提交生成任务
            var imageData, err = ai.images.generations(
                model = "Qwen/Qwen-Image-2.1";
                prompt = prompt;
                size = size,
                steps = steps;
                seed = seed;
            ); 

            //获取任务 ID,直接下标 [[]] 操作符遇到 null 值返回 null 而不报错
            var taskId = imageData[["task_id"]];
            if(taskId){
                winform.progressBar.show(true); 

                for(i=1;60){ // 最多尝试 60 次(5分钟) 
                    winform.progressBar.progressPos = 40 + i;
                    thread.delay( 2000 + (3000/50 * steps) ); // 等待5秒

                    //查询任务执行结果
                    var result, err2 = ai.tasks[taskId].get();
                    err = err2;

                    var status = result[["task_status"]];
                    if(status == "SUCCEED"){
                        var url = result.output_images[1];
                        var imagePath = io.tmpname(time.format("%Y%m%d_%H%M%S") , inet.url.getFileName(url))
                        winform.picturebox.background = url;
                        winform.picturebox.text = null;
                        winform.picturebox.iconText = null;
                        winform.btnCopy.disabled = false;
                        winform.text = "魔搭文生图 - 生成成功";
                        winform.lastImagePath = imagePath;
                        winform.lastImageExt = ext;

                        if(revisedPrompt){
                            winform.showTip("已生成图像,双击图像可放大预览。魔搭 返回了优化后的提示词。");
                        }
                        else{
                            winform.showTip("已生成图像,双击图像可放大预览。");
                        }

                        //下载图像
                        http.receiveFile(imagePath).get(url);

                        winform.showTip("已生成图像,双击图像可放大预览。");
                        break
                    }
                    elseif(status == "FAILED"){
                            err = "FAILED"
                    }

                    if(err) break;
                }
            }

            winform.progressBar.show(false);
            winform.setBusy(false);

            if(err) { 
                var msg = err || resultData[["error"]][["message"]] || resultData[["message"]] || rawText || "未知错误";
                winform.picturebox.text = "生成失败";
                winform.picturebox.iconText = '\uF057';
                winform.msgboxErr(err,"魔搭 API 请求失败");       
            } 

        },winform,aiChatConfig,prompt, size, steps, seed 
    );
}

winform.btnCopy.oncommand = function(){
    var imagePath = winform.lastImagePath;
    if(!imagePath || !io.exists(imagePath)){
        return winform.showTip("请先生成图像。");
    }

    if(winform.btnCopy.text == "打开图像文件"){
        raw.explore(imagePath,"/select");
        winform.showTip("已打开图像所在位置。");
        winform.btnCopy.text = "复制图像 / 图像文件";
        winform.btnCopy.iconText = '\uF0EA';
    }
    else{
        import win.clip.bitmap;
        win.clip.bitmap.writeHtml(imagePath);

        import win.clip.file;
        win.clip.file.write(imagePath,"copy",false);
        winform.btnCopy.text = "打开图像文件";
        winform.btnCopy.iconText = '\uF1C5';
        winform.showTip("已复制图像与图像文件。");
    }
}

winform.picturebox.onMouseDoubleClick = function(wParam,lParam){
    var imagePath = winform.lastImagePath;
    if(!imagePath || !io.exists(imagePath)){
        return winform.msgboxErr("请先生成图像。","错误");
    }

    var frmPreview = win.form(text="图像预览";right=757;bottom=466;bgcolor=0xFFFFFF;border="none")
    frmPreview.add(
    btnClose={cls="close";text="×";left=725;top=3;right=753;bottom=31;bgcolor=0xCC8B94;anchors={top=1;right=1};font=LOGFONT(h=-21);notify=1;z=2};
    picturebox={cls="plus";left=0;top=0;right=758;bottom=467;bgcolor=0xFFFFFF;anchors={left=1;top=1;right=1;bottom=1}foreRepeat="scale";notify=1;repeat="scale";z=1}
    )

    frmPreview.onActivateApp = function(activated){ if(!activated) frmPreview.close(); }
    frmPreview.onCancel = function(){ frmPreview.close(); }
    frmPreview.picturebox.onMouseDoubleClick = function(){ frmPreview.close(); }
    frmPreview.picturebox.foreground = imagePath;
    frmPreview.fullscreen();
    frmPreview.btnClose.oncommand = function(){ frmPreview.close(); }
    frmPreview.btnClose.show(true);
}

winform.btnOpenKeyUrl.oncommand = function(){
    import web.rest.aiChat.settingForm;

    table.assign(configImage,{
        title = "GPT Image 2.5 - 接口设置"; // 标题  
        hyperlink1Title = "获取 key"; // key 输入框显示嵌入链接
        allowKatex = false;
    });

    if(!configImage.itemData){
        configImage.itemData = []
    }

    if(configImage.itemData[configImage.selItem]){
        configImage.itemData[configImage.selItem].reasoning = {
            effort = winform.comboQuality.selText;
        }
        configImage.save()
    }

    //打开 AI 设置窗口,参数 2 指定配置文件
    var frmSetting = web.rest.aiChat.settingForm(mainForm,configImage);
    if( frmSetting.doModal(mainForm) ){ 

        aiChatConfig = configImage.itemData[configImage.selItem];  
        if(aiChatConfig){
            effort = aiChatConfig.reasoning[["effort"]]

            if((effort==="none")||(effort==="minimal")){
                 effort = "auto";
            }

            winform.comboQuality.selText = effort
        }
    }
}

winform.onDestroy = function(){
    import fsys;
    fsys.delete(tempImageDir);
}

winform.btnGenerate.skin({
    background={
        default=0xFF3498DB;
        hover=0xFF2980B9;
        active=0xFF1F618D;
        disabled=0xFFBDC3C7;
    };
    color={
        default=0xFFFFFFFF;
        disabled=0xFF7F8C8D;
    }
});

winform.btnCopy.skin({
    background={
        default=0xFF27AE60;
        hover=0xFF229954;
        active=0xFF1E8449;
        disabled=0xFFBDC3C7;
    };
    color={
        default=0xFFFFFFFF;
        disabled=0xFF7F8C8D;
    }
});

winform.btnOpenKeyUrl.skin({
    color={
        default=0xFF3498DB;
        hover=0xFF2980B9;
        active=0xFF1F618D;
    }
});

winform.chkRandomSeed.skin({
    color={
        default=0xFF3C3C3C;
        hover=0xFF0078D4;
        active=0xFF005A9E;
        disabled=0xFFAAAAAA;
    };
    checked={ 
        iconText='\uF046'
    }
});
winform.chkRandomSeed.checked = true; 

win.ui.simpleWindow(winform);
winform.show();
win.loopMessage();
Markdown 格式