OpenHarmony輕松玩轉GIF數據渲染

OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)提供了Image組件支持GIF動圖的播放,但是缺乏擴展能力,不支持播放控制等。今天介紹一款三方庫——ohos-gif-drawable三方組件,帶大家一起玩轉GIF的數據渲染,搞定GIF動圖的各種需求。

效果演示

本文將從5個小節來帶領大家使用ohos-gif-drawable這一款三方庫,其中1、2、3這3個小節,主要介紹了ohos-gif-drawable的核心能力、GIF軟解碼和GIF繪制。4和5小節主要是擴展討論,如何添加濾鏡效果和軟解碼遇到的耗時問題。

1.GIF的文件格式理論基礎

工欲善其事必先利其器。首先我們需要為自己打下理論基礎。了解GIF的數據格式,為后續解碼GIF提供理論支持。

通過學習GIF的文件格式,我們對于GIF的組成格式有了一定的了解,并且有助于理解后面GIF的解碼。

在開始介紹之前,我想讓大家了解一下整體的結構思路如下圖:

其中gifuct-js三方庫主要完成了解碼的工作。

ohos-gif-drawable三方庫則是在gifuct-js的三方庫之上,進行了封裝。并結合了OpenHarmony的Canvas繪制能力,達到了播放和控制GIF的能力。

2.GIF軟解碼:gifuct-js三方庫介紹

GIF解碼我們使用了gifuct-js這個庫,它是一個純JavaScript的GIF解碼庫。首先我們需要了解基礎用法。

2.1 參考樣例將一個文件ArrayBuffer轉換為GIF解碼后的幀數據數組。

//javascript
var gif = parseGIF(arraybuffer)
var frames = decompressFrames(gif, true)

2.2 由于OpenHarmony的Image生成PixelMap需要的數據是BGRA數據,而2.1生成的frames所有數組中的patch字段則是RGBA數據,所以我們需要使用

//javascript
var gif = parseGIF(arraybuffer)
var frames = decompressFrames(gif, false)

然后將frame目前還未生成的patch字段數據,通過generatePatch 函數,將RGBA的數據更換為BGRA即可,如下代碼所示:

//javascript
const generatePatch = image => {const totalPixels = image.pixels.lengthconst patchData = new Uint8ClampedArray(totalPixels * 4)for (var i = 0; i < totalPixels; i++) {const pos = i * 4const colorIndex = image.pixels[i]const color = image.colorTable[colorIndex] || [0, 0, 0]patchData[pos] = color[2] // BpatchData[pos + 1] = color[1]// GpatchData[pos + 2] = color[0] // RpatchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0//A}return patchData
}

generatePatch函數,在這里會根據顏色表colorTable和基于顏色表的圖像數據pixels以及透明度transparentIndex生成BGRA格式的patchData,這個數據和Canvas中getImageData獲取的ImageData數據是一致的,都是Uint8ClampedArray類型,可以直接使用putImageData讓canvas繪制。

最后,生成的patchData賦值給Frame的patch字段。

這里我們并沒有直接使用Canvas的putImageData直接繪制。為了提升擴展性,我們使用了Image的能力來生成PixelMap,這樣處理為后續濾鏡效果提供了可能,也方便后續繪制流程。

好了,到這里我們就基本上把gifuct-js庫的基礎使用簡單介紹完了。

如何使用GIF:ohos-gif-drawable三方庫的介紹。

我們先來看看整個ohos-gif-drawable組件的模型圖,通過模型圖,我們可以看到,用戶只要關注GIFComponent組件,和GIFComponent.ControllerOptions配置參數以及控制參數autoPlay和resetGif即可,非常簡單!

  1. 支持的功能列表如下

● 支持播放GIF圖片。
● 支持控制GIF播放/暫停。
● 支持重置GIF播放動畫。
● 支持調節GIF播放速率。
● 支持監聽GIF所有幀顯示完成后的回調。
● 支持設置顯示大小。
● 支持7種不同的展示類型。
● 支持設置顯示區域背景顏色。

  1. 如何使用ohos-gif-drawable

首先需要使用npm下載ohos-gif-drawable三方庫

npm install @ohos/ohos-gif-drawable --save

接下來我們需要配置一個worker給gifuct-js解碼使用。

配置worker,在應用工程的entry/src/main/ets/pages目錄下新建workers文件夾,并且創建文件 gifParseWorker.ts ,文件內容如下:

import arkWorker from '@ohos.worker';
import { handler } from '@ohos/ohos-gif-drawable/src/main/ets/components/gif/worker/GifWorker'
// handler封裝了子線程邏輯,但worker目前只能在entry中進行創建arkWorker.parentPort.onmessage = handler;

然后在entry目錄的build-profile.json5文件中,添加如下內容:

"buildOption": { 
"sourceOption": {   
"workers": [    "./src/main/ets/pages/workers/gifParseWorker.ts"
] 
}
},

到這里我們worker就配置好了。

下面就到了正式使用環節,我們只要在UI界面需要的地方寫上自定義控件GIFComponent,然后傳入GIFComponent.ControllerOptions,gifAutoPlay,gifReset這三個參數就能控制gif動畫。

import { GIFComponent, ResourceLoader } from '@ohos/ohos-gif-drawable'
// gif繪制組件用戶屬性設置
@State model:GIFComponent.ControllerOptions = new GIFComponent.ControllerOptions();
// 是否自動播放
@State gifAutoPlay:boolean = true;
// 重置GIF播放,每次取反都能生效
@State gifReset:boolean = true;
// 在ARKUI的其他容器組件中添加該組件
GIFComponent({model:$model, autoPlay:$gifAutoPlay, resetGif:this.gifReset})

舉個簡單的例子說明一下

// 創建worker
let worker = new ArkWorker.Worker('entry/ets/pages/workers/gifParseWorker.ts', {type: 'classic',name: 'loadUrlByWorker'})
// 關閉動畫     
this.gifAutoPlay = false;
// 銷毀上一次資源
this.model.destroy();
// 新創建一個modelx,用于配置用戶參數
let modelx = new GIFComponent.ControllerOptions()
modelx 
// 配置回調動畫結束監聽,和耗時監聽   
.setLoopFinish((loopTime) => {  
this.gifLoopCount++;  
this.loopHint = '當前gif循環了' + this.gifLoopCount + '次,耗時=' + loopTime + 'ms'  
}) 
// 設置組件大小   
.setSize({ width: this.compWidth, height: this.compHeight }) 
// 設置圖像和組件的適配類型 
.setScaleType(this.scaleType) 
// 設置播放速率 
.setSpeedFactor(this.speedFactor) 
// 設置背景 
.setBackgroundColor(Color.Grey)
// 加載網絡圖片,getContext(this)中的this指向page頁面或者組件都可以ResourceLoader.downloadDataWithContext(getContext(this), {   url: 'https://pic.ibaotu.com/gif/18/17/16/51u888piCtqj.gif!fwpaa70/fw/700'   }, (sucBuffer) => {   
// 網絡資源sucBuffer返回后處理  
modelx.loadBuffer(sucBuffer, () => {      console.log('網絡加載解析成功回調繪制!')   
// 開啟自動播放     
this.gifAutoPlay = true;   
// 給組件數據賦新的用戶配置參數,達到后續gif動畫效果     
this.model = modelx;   }, worker)}, (err) => {  
// 用戶根據返回的錯誤信息,進行業務處理(展示一張失敗占位圖、再次加載一次、加載其他圖片等)
})

這里ResourceLoader內置了加載網絡資源GIF,本地工程資源GIF和本地路徑資源GIF文件數據的能力。

如果你已經有了GIF文件的arraybuffer數據,也可以直接調用modelx.loadBuffer(buffer: ArrayBuffer, readyRender: (err?) => void, worker: any)進行GIF播放。

甚至你已經生成了GIF解析數據,比如調用了2.2中的解碼代碼,那么你也可以直接調用modelx.setFrames(images?: GIFFrame[])來進行gif播放。

1.控制GIF的播放與暫停:

this.gifAutoPlay = true 開啟動畫
this.gifAutoPlay = false 暫停動畫

組件內部會監聽該參數的變化,用戶只要改變值即可達到控制效果

2. 重置GIF的播放

this.gifReset = !this.gifReset 每次變化都會重置gif播放。

由于重置不需要狀態管理,所以組件內監聽到數據變化就會重置gif播放

3. 設置GIF動畫播放速度

let modelx = new GIFComponent.ControllerOptions()
modelx.setSpeedFactor(2)// 將速率提升到2倍

調用setSpeedFactor(speed: number)即可調整播放速度speed 為對比原始速率的乘積因子,比如設置0.5即為原始速率的0.5倍,設置為2即為原始速率的2倍。

4. 監聽GIF動畫播放回調(比如第一次動畫結束)和獲取動畫實際播放總時長

let modelx = new GIFComponent.ControllerOptions()
modelx.setLoopFinish((loopTime?) => {
// loopTime為GIF動畫一周期耗時,回調時間為GIF動畫一周期結束時間節點
})

調用setLoopFinish(fn: (loopTime?) => void)可以通過回調得到GIF動畫運行一周期耗時和一周期結束時間節點。

5. 顯示GIF任意一幀

let modelx = new GIFComponent.ControllerOptions()
modelx.setSeekTo(5) // 直接展示該gif第5幀圖像

調用setSeekTo(gifPosition: number)可以直接展示該gif的某一幀圖像。

到這里ohos-gif-drawable三方庫的主要能力都介紹完了,是不是很簡單呢!

6. 適配組件的大小

let modelx = new GIFComponent.ControllerOptions()

modelx.setScaleType(ScaleType.FIT_CENTER) // 將圖像縮放適配組件大小調用setScaleType(scaletype: ScaleType)可以將圖像和組件大小進行適配。

目前支持的類型如下圖所示:

GIFComponent.ScaleType

為什么要配置worker

在具體實踐過程中我們會發現,當我們按下解碼按鈕的時候,主界面會有一點卡頓的情況。特別是大的GIF文件進行解碼的時候效果更明顯。這是因為我們在主線程中進行了CPU的密集型計算,這是一個耗時且占用CPU的操作。主線程中是不能執行耗時操作的。但是JavaScript只有一個線程啊?那么解碼這一塊操作該如何處理會比較好呢?帶著疑惑,我去查閱了資料發現JavaScript雖然屬于單線程環境。但是通過引入Worker的能力,引入子線程worker,可以實現JavaScript的“多線程”技術。

OpenHarmony如何在子線程中處理耗時任務

為了爭取良好的用戶體驗,我們需要將耗時操作封裝至子線程中。

這里簡單描述一下worker的能力:

能夠讓主頁面運行的JavaScript線程中加載運行另外單獨的一個或者多個JavaScript線程,但是它的多線程編程能力區別于傳統意義上的多線程編程。主線程和Worker線程之間,不會共享任何作用域和資源,他們的通信方式是基于事件監聽機制的 message。

接下來我們參考OpenHarmony文檔下的worker能力

1. OpenHarmony環境下Worker的API接口列表

2. Worker的使用簡單案例

經過了解之后,我們可以把解碼的耗時封裝到worker中處理,避免主線程耗時操作占用CPU導致卡頓問題。提升用戶體驗。

這也是使用ohos-gif-drawable三方庫需要配置worker的原因。

擴展部分

GIF的濾鏡效果

1. 灰白濾鏡

//javascript
// 重點代碼更改 let avg = (color[0] + color[1] + color[2]) / 3patchData[pos] = avg;patchData[pos + 1] = avg;patchData[pos + 2] = avg;patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;

2. 反轉濾鏡

//javascript
// 重點代碼更改patchData[pos] = 255 - color[0];patchData[pos + 1] = 255 - color[1];patchData[pos + 2] = 255 - color[2];patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;

3. 高級濾鏡效果

假設我們這邊已經拿到了patch: Uint8ClampedArray像素數據,這里我需要先將其變換為一張PixelMap數據,參考GIFComponent中patch數據轉換為PixelMap的代碼。

//typescript
import image from "@ohos.multimedia.image"
let colorBuffer = patch.buffer
let pixelmap = await image.createPixelMap(colorBuffer, {'size': {'height': frame.dims.height as number,'width': frame.dims.width as number}
})

4. 高斯模糊

然后對PixelMap像素數據進行高斯模糊, 調用 blur(pixelmap,10,true, (outPixelMap)=>{ // 模糊后的pixelmap數據})在回調中獲取模糊后的pixelmap。以下是模糊處理的算法:

export async function blur(bitmap: any, radius: number, canReuseInBitmap: boolean, func: AsyncTransform<PixelMap>) {if (radius < 1) {func("error,radius must be greater than 1 ", null);return;}let imageInfo = await bitmap.getImageInfo();let size = {width: imageInfo.size.width,height: imageInfo.size.height}if (!size) {func(new Error("fastBlur The image size does not exist."), null)return;}let w = size.width;let h = size.height;var pixEntry: Array<PixelEntry> = new Array()var pix: Array<number> = new Array()let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());await bitmap.readPixelsToBuffer(bufferData);let dataArray = new Uint8Array(bufferData);for (let index = 0; index < dataArray.length; index+=4) {const r = dataArray[index];const g = dataArray[index+1];const b = dataArray[index+2];const f = dataArray[index+3];let entry = new PixelEntry();entry.a = 0;entry.b = b;entry.g = g;entry.r = r;entry.f = f;entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b);pixEntry.push(entry);pix.push(ColorUtils.rgb(entry.r, entry.g, entry.b));}let wm = w - 1;let hm = h - 1;let wh = w * h;let div = radius + radius + 1;let r = CalculatePixelUtils.createIntArray(wh);let g = CalculatePixelUtils.createIntArray(wh);let b = CalculatePixelUtils.createIntArray(wh);let rsum, gsum, bsum, x, y, i, p, yp, yi, yw: number;let vmin = CalculatePixelUtils.createIntArray(Math.max(w, h));let divsum = (div + 1) >> 1;divsum *= divsum;let dv = CalculatePixelUtils.createIntArray(256 * divsum);for (i = 0; i < 256 * divsum; i++) {dv[i]=(i / divsum);
}yw = yi =0;let stack = CalculatePixelUtils.createInt2DArray(div,3);let stackpointer, stackstart, rbs, routsum, goutsum, boutsum, rinsum, ginsum, binsum: number;let sir: Array<number>;let r1 = radius +1;
for(y =0; y < h; y++){rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum =0;
for(i =-radius; i <= radius; i++){p = pix[yi + Math.min(wm, Math.max(i,0))];sir = stack[i + radius];sir[0]=(p &0xff0000)>>16;sir[1]=(p &0x00ff00)>>8;sir[2]=(p &0x0000ff);rbs = r1 - Math.abs(i);rsum += sir[0]* rbs;gsum += sir[1]* rbs;bsum += sir[2]* rbs;
if(i >0){rinsum += sir[0];ginsum += sir[1];binsum += sir[2];
}else{routsum += sir[0];goutsum += sir[1];boutsum += sir[2];
}
}stackpointer = radius;for(x =0; x < w; x++){r[yi]= dv[rsum];g[yi]= dv[gsum];b[yi]= dv[bsum];rsum -= routsum;gsum -= goutsum;bsum -= boutsum;stackstart = stackpointer - radius + div;sir = stack[stackstart % div];routsum -= sir[0];goutsum -= sir[1];boutsum -= sir[2];if(y ==0){vmin[x]= Math.min(x + radius +1, wm);
}p = pix[yw + vmin[x]];sir[0]=(p &0xff0000)>>16;sir[1]=(p &0x00ff00)>>8;sir[2]=(p &0x0000ff);rinsum += sir[0];ginsum += sir[1];binsum += sir[2];rsum += rinsum;gsum += ginsum;bsum += binsum;stackpointer =(stackpointer +1)% div;sir = stack[(stackpointer)% div];routsum += sir[0];goutsum += sir[1];boutsum += sir[2];rinsum -= sir[0];ginsum -= sir[1];binsum -= sir[2];yi++;
}yw += w;
}
for(x =0; x < w; x++){rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum =0;yp =-radius * w;
for(i =-radius; i <= radius; i++){yi = Math.max(0, yp)+ x;sir = stack[i + radius];sir[0]= r[yi];sir[1]= g[yi];sir[2]= b[yi];rbs = r1 - Math.abs(i);rsum += r[yi]* rbs;gsum += g[yi]* rbs;bsum += b[yi]* rbs;if(i >0){rinsum += sir[0];ginsum += sir[1];binsum += sir[2];
}else{routsum += sir[0];goutsum += sir[1];boutsum += sir[2];
}if(i < hm){yp += w;
}
}yi = x;stackpointer = radius;
for(y =0; y < h; y++){
// Preserve alpha channel: ( 0xff000000 & pix[yi] )pix[yi]=(0xff000000& pix[Math.round(yi)])|(dv[Math.round(rsum)]<<16)|(dv[Math.round(gsum)]<<8)| dv[Math.round(bsum)];rsum -= routsum;gsum -= goutsum;bsum -= boutsum;stackstart = stackpointer - radius + div;sir = stack[stackstart % div];routsum -= sir[0];goutsum -= sir[1];boutsum -= sir[2];if(x ==0){vmin[y]= Math.min(y + r1, hm)* w;
}p = x + vmin[y];sir[0]= r[p];sir[1]= g[p];sir[2]= b[p];rinsum += sir[0];ginsum += sir[1];binsum += sir[2];rsum += rinsum;gsum += ginsum;bsum += binsum;stackpointer =(stackpointer +1)% div;sir = stack[stackpointer];routsum += sir[0];goutsum += sir[1];boutsum += sir[2];rinsum -= sir[0];ginsum -= sir[1];binsum -= sir[2];yi += w;
}
}let bufferNewData =newArrayBuffer(bitmap.getPixelBytesNumber());let dataNewArray =newUint8Array(bufferNewData);let index =0;for(let i =0; i < dataNewArray.length; i +=4){dataNewArray[i]= ColorUtils.red(pix[index]);dataNewArray[i+1]= ColorUtils.green(pix[index]);dataNewArray[i+2]= ColorUtils.blue(pix[index]);dataNewArray[i+3]= pixEntry[index].f;index++;
}await bitmap.writeBufferToPixels(bufferNewData);
if(func){
func("success", bitmap);
}
}

如果需要高級濾鏡效果可以參考ImageKnife組件的transform部分,這里僅僅展示模糊效果。

由于濾鏡效果目前ohos-gif-drawable三方庫并沒有開發接口提供出來,所以開發者可以根據實際需求重寫自定義組件GIFComponent.,只需要在生成PixelMap的代碼片段中加入濾鏡代碼,即可利用濾鏡效果開發更多精彩的應用。

為了幫助到大家能夠更有效的學習OpenHarmony 開發的內容,下面特別準備了一些相關的參考學習資料:

OpenHarmony 開發環境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源碼解析》:https://qr18.cn/CgxrRy

  • 搭建開發環境
  • Windows 開發環境的搭建
  • Ubuntu 開發環境搭建
  • Linux 與 Windows 之間的文件共享
  • ……

系統架構分析:https://qr18.cn/CgxrRy

  • 構建子系統
  • 啟動流程
  • 子系統
  • 分布式任務調度子系統
  • 分布式通信子系統
  • 驅動子系統
  • ……

OpenHarmony 設備開發學習手冊:https://qr18.cn/CgxrRy

在這里插入圖片描述

OpenHarmony面試題(內含參考答案):https://qr18.cn/CgxrRy

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/web/14816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14816.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何在JS中克隆對象

在JavaScript中&#xff0c;克隆對象并不是直接支持的操作&#xff0c;因為JavaScript中的對象是通過引用傳遞的&#xff0c;而不是通過值傳遞。但是&#xff0c;你可以使用幾種不同的方法來"克隆"或"復制"一個對象。 1. 淺拷貝&#xff08;Shallow Copy&…

二手車經營效率羅盤,用經營效率羅盤玩轉二手車生意

課程下載&#xff1a;https://download.csdn.net/download/m0_66047725/89292198 更多資源下載&#xff1a;關注我。 帶你了解不一樣的二手車圈讓二手車經營更高效 課程介紹 隨著時代不斷發展&#xff0c;二手車行業迎來了快速變革期。二手車有著一車一況、一車一價非標和價…

無人機+飛行服務:無人機飛防服務(打藥+施肥+播種)技術詳解

無人機飛防服務&#xff0c;結合了先進的無人機技術與農業實踐&#xff0c;為現代農業提供了高效、精準的打藥、施肥和播種解決方案。以下是對這些技術的詳細解析&#xff1a; 一、無人機打藥技術 無人機打藥技術利用無人機搭載噴霧設備&#xff0c;對農田進行精準施藥。通過…

【數倉系列】maxcompute、postgresql、sparksql等行轉列數據處理實戰總結(其他類型持續總結更新)

1.熟悉、梳理、總結項目研發實戰中的SQL開發日常使用中的問題、經驗總結&#xff0c;都是常用的開發技能&#xff0c;可以省去很多時間&#xff0c;時間長就忘記了 2.歡迎點贊、關注、批評、指正&#xff0c;互三走起來&#xff0c;小手動起來&#xff01; 文章目錄 1.maxcompu…

關于java.nio.file 包--用于文件和文件系統操作的核心包之一

java.nio.file 包是 Java 中用于文件和文件系統操作的核心包之一。這個包提供了許多類和接口&#xff0c;用于在 Java 虛擬機中訪問文件、管理文件屬性和操作文件系統。主要的功能包括&#xff1a; 1.訪問文件和目錄: java.nio.file.Files 類提供了許多靜態方法&#xff0c;用于…

【Crypto】看我回旋踢

文章目錄 一、看我回旋踢二、知識點什么是ROT13&#xff1f;工作原理分析字符串格式 解題感悟 一、看我回旋踢 關鍵詞回旋&#xff0c;盲猜ROT13 因為以 synt{ 開頭&#xff0c;并以 } 結束&#xff0c;基本可以判斷是ROT13 小小flag&#xff0c;拿下&#xff01; 二、知識點 …

抖音極速版:抖音輕量精簡版本,新人享大福利

和快手一樣&#xff0c;抖音也有自己的極速版&#xff0c;可視作抖音的輕量精簡版&#xff0c;更專注于刷視頻看廣告賺錢&#xff0c;收益比抖音要高&#xff0c;可玩性更佳。 抖音極速版簡介 抖音極速版是一個提供短視頻創業和收益任務的平臺&#xff0c;用戶可以通過觀看廣…

leetcode-560 和為k的數組

一、題目描述 給你一個整數數組 nums 和一個整數 k &#xff0c;請你統計并返回 該數組中和為 k 的子數組的個數 。 子數組是數組中元素的連續非空序列。 注意&#xff1a;nums中的元素可為負數 輸入&#xff1a;nums [1,1,1], k 2 輸出&#xff1a;2輸入&#xff1a;num…

裝飾乙級資質延期申請的注意事項

在申請裝飾乙級資質延期時&#xff0c;企業需要注意以下幾個重要事項&#xff1a; 提前了解政策和要求&#xff1a; 企業應提前了解并熟悉資質延期的相關政策和要求&#xff0c;包括所需材料、辦理流程、時間限制等。這有助于企業做好充分準備&#xff0c;避免在申請過程中出現…

【機器學習聚類算法實戰-5】機器學習聚類算法之DBSCAN聚類、K均值聚類算法、分層聚類和不同度量的聚集聚類實例分析

&#x1f3a9; 歡迎來到技術探索的奇幻世界&#x1f468;?&#x1f4bb; &#x1f4dc; 個人主頁&#xff1a;一倫明悅-CSDN博客 ?&#x1f3fb; 作者簡介&#xff1a; C軟件開發、Python機器學習愛好者 &#x1f5e3;? 互動與支持&#xff1a;&#x1f4ac;評論 &…

MYSQL課堂練習

學生表:studentsno&#xff0c;sname&#xff0c;ssex&#xff0c;sage&#xff0c;sdept)學號&#xff0c;姓名&#xff0c;性別&#xff0c;年齡&#xff0c;所在系 sno為主鍵課程表:Course(cno&#xff0c;cname&#xff0c;) 課程號&#xff0c;課程名 cno為主鍵 學生選課…

申報照明工程設計乙級資質關于財務審計報告的要求

申報照明工程設計乙級資質時&#xff0c;關于財務審計報告的要求通常包括以下幾點&#xff1a; 審計報告類型&#xff1a;需要提供由具有法定資質的會計師事務所出具的財務審計報告。如果是企業已經運營滿一個會計年度&#xff0c;應提交最近一個完整會計年度的年度審計報告。如…

Rust:struct 與字節序列的相互轉換

在 Rust 中&#xff0c;將結構體&#xff08;struct&#xff09;與字節序列&#xff08;Vec<u8>&#xff09;相互轉換的常見方法是使用序列化和反序列化庫。Rust 有一個流行的序列化庫叫做 serde&#xff0c;它支持多種數據格式。為了將結構體轉換為字節序列&#xff0c;…

保障餐飲場所安全:定期送檢可燃氣體報警器

在餐飲行業&#xff0c;火災隱患一直備受關注。餐廳、茶飲店等場所常常使用燃氣設備&#xff0c;而這些設備帶來了潛在的安全隱患。 為了及時發現并預防可燃氣體泄漏&#xff0c;可燃氣體報警器的定期送檢顯得尤為重要。那么&#xff0c;為什么可燃氣體報警器需要定期送檢呢&a…

【MySQL精通之路】系統變量-動態系統變量

許多服務器系統變量是動態的&#xff0c;可以在運行時進行設置。 參見“變量賦值的SET語法”。 有關設置系統變量的權限要求的描述&#xff0c;請參閱“系統變量權限” 【MySQL精通之路】系統變量-系統變量權限-CSDN博客 下表列出了所有適用于mysqld的動態系統變量。 該表列出…

java 設計模式(三)-- 單例模式

引言 單例模式是一種常用的軟件設計模式&#xff0c;它確保一個類只有一個實例&#xff0c;并提供一個全局訪問點來獲取這個實例。單例模式在Java中實現起來相對簡單&#xff0c;但實現方式有多種&#xff0c;每種方式都有其特點和適用場景。 一、單例模式的實現方式 1. 懶漢…

【Linux】從零開始認識進程間通信 —— 管道

送給大家一句話&#xff1a; 人要成長&#xff0c;必有原因&#xff0c;背后的努力與積累一定數倍于普通人。所以&#xff0c;關鍵還在于自己。 – 楊絳 從零開始認識進程間通信 1 為什么要進程間通信2 進程如何通信3 進程通信的常見方式4 管道4.1 什么是管道4.2 管道通信的系…

交叉編譯程序,提示 incomplete type “struct sigaction“ is not allowed

問題描述 incomplete type "struct sigaction" is not allowed解決辦法 在代碼的最頂端添加如下代碼即可 #define _XOPEN_SOURCE此定義不是簡單的宏定義&#xff0c;是使程序符合系統環境的不可缺少的部分 _XOPEN_SOURCE為了實現XPG&#xff1a;The X/Open Porta…

零一萬物Yi-1.5開源,34B/9B/6B多尺寸,34B超Qwen1.5-72B

前言 近年來&#xff0c;大型語言模型&#xff08;LLM&#xff09;在各個領域展現出驚人的能力&#xff0c;為人們的生活和工作帶來了巨大的改變。然而&#xff0c;大多數開源 LLM 的性能仍然無法與閉源模型相媲美&#xff0c;這限制了 LLM 在科研和商業領域的進一步應用。為了…

element-plus表格的表單校驗如何實現,重點在model和prop

文章目錄 vue&#xff1a;3.x element-plus&#xff1a;2.7.3 重點&#xff1a; 1) tableData放到form對象里 2) form-item的prop要寫成tableData.序號.屬性 <!--table-表單校驗--> <template><el-form ref"forms" :model"form"><e…