【Godot 4.2】Tree控件與TreeItem完全解析

概述

本篇是控件完全解析系列之一,主要總結一下Tree控件與TreeItem的使用。

Tree控件是一個非常強大的控件,尤其是在編寫一些相關的程序或編輯器插件時,非常適合展示樹形組織的節點型數據。

本篇將從簡單的添加根節點,根節點子節點,設置文本和圖標等等基礎開始學習,盡量涵蓋和串聯TreeTreeItem所提供的API。

為Tree添加節點

這里的“節點”只是對TreeItem的一個通俗(但有歧義的)稱呼,Tree控件的TreeItem并不算是一個真正意義上的Godot節點,行為方法也很節點不一樣。因為它是直接繼承自Object類型的,而不是Control,所以無法直接在場景面板中查看到,也無法直接用檢視器面板添加和編輯。刪除的時候也要使用free()方法。

為了盡量減少歧義,又符合通俗描述,我會在本文中盡量少的地方用“節點”的稱呼,而是直接改用TreeItem

創建根節點

  • 我們可以使用Tree控件的create_item()方法創TreeItem
  • create_item()有一個參數,用于傳入作為父節點的TreeItem,如果為null,則創建的TreeItem作為Tree根節點被添加。
  • create_item()返回對新建的TreeItem的引用,我們可以用一個變量接收,并通過這個變量繼續設定TreeItem的屬性或調用其方法。
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")   # 設定根節點文本
  • 這里我們使用TreeItemset_text()方法設定根節點root的文本
  • Tree控件可以顯示多列信息,顯示列數由其columns屬性設定,默認為1,也就是只顯示一列。
  • set_text()第一個參數是所在列的索引,默認為0

上面代碼的效果:

image.png

創建根節點的子節點

  • 在添加根節點后,可以繼續使用Tree控件的create_item()方法創建根節點的子節點,只是需要將根節點引用root作為父節點傳入。
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點文本# 創建根節點的子節點
var itm:TreeItem = tree.create_item(root)
itm.set_text(0,"子節點1")

上面代碼的效果:

image.png

  • 除了直接用Tree控件的create_item()方法外,也可以直接利用已經創建的TreeItem(比如根節點)的create_child()方法來創建子節點。
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點文本# 創建根節點的子節點
var itm:TreeItem = root.create_child()
itm.set_text(0,"子節點1")

兩種方式得到的效果是一致的。

設定圖標

使用TreeItemset_icon()方法,可以設定圖標。同樣第一個參數指定所在列。

var icon = preload("res://icon.svg")
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")   # 設定根節點文本
root.set_icon(0,icon)     # 設定根節點圖標

上面代碼效果:

image.png

設定圖標最大寬度

  • 可以看到,單純用set_icon()方法,只能在TreeItem上顯示圖標
  • 但是很多情況下我們會遇到圖標尺寸不合適的情況,這時就需要用set_icon_max_width方法設定圖標最大寬度了。
var icon = preload("res://icon.svg")
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點文本
root.set_icon(0,icon)               # 設定根節點圖標
root.set_icon_max_width(0,30)       # 設定根節點圖標寬度
  • Godot項目默認圖標icon.svg尺寸為128×128像素,通過設定最大寬度為30,則圖標將以30×30像素進行顯示。
    image.png

使用SpriteSheet或打包紋理作為圖標來源

  • SpriteSheet打包紋理都是一種常見的2D游戲或UI美術素材存儲形式。SpriteSheet采用尺寸一致的行列劃分,每一幀大小尺寸一致。而打包紋理,則是更自由的壓縮存儲多個大小不一的小圖片。

  • 這里我使用如下的一個SpriteSheet來作為一個圖標庫,用來給不同的TreeItem顯示不同的圖標。

roller_sprites.png

  • 原圖尺寸是900×150像素,單個圖標的尺寸就是150×150像素。
  • 而我們將最終顯示在TreeItem上的圖標寬度限定為18像素,這樣最終呈現的大小就是18×18像素。
var icon = preload("res://items.png") # 使用SpriteSheet或打包紋理
var cell_size = Vector2(150,150)      # 單元格尺寸# ------ 創建根節點 ------ 
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點文本
root.set_icon(0,icon)               # 設定根節點圖標
root.set_icon_max_width(0,18)       # 設定根節點圖標寬度
var cell_pos = Vector2(0,0)         # 幀的位置
# 顯示圖片局部區域作為圖標
root.set_icon_region(0,Rect2(cell_pos * cell_size,cell_size))# ------ 為根節點創建5個子節點 ------ 
for i in range(5):var itm:TreeItem = root.create_child()itm.set_text(0,"item%d" % i)itm.set_icon(0,icon)               # 設定根節點圖標itm.set_icon_max_width(0,18)       # 設定根節點圖標寬度cell_pos = Vector2(i+1,0)         # 幀的位置itm.set_icon_region(0,Rect2(cell_pos * cell_size,cell_size))

效果:

image.png

對圖標顏色進行調制

我們連接并處理Tree控件的item_selected信號。

image.png
信號處理函數代碼如下:

# 處理Tree控件的item_selected信號
func _on_tree_item_selected():# 獲取選中節點var selected_item:TreeItem = tree.get_selected()# 恢復其他節點的圖標顏色var root:TreeItem = tree.get_root()root.set_icon_modulate(0,Color.WHITE)for itm in root.get_children():itm.set_icon_modulate(0,Color.WHITE)# 修改選中節點的圖標顏色selected_item.set_icon_modulate(0,Color.BLUE)

運行后可以看到,選中項的圖標顏色用藍色調制的效果,沒有使用額外的圖標,而是通過顏色調制,做出了選中狀態與非選中狀態的圖標差異效果。

調制圖標顏色.gif

創建多列

啟用和添加多列

Tree控件支持多列視圖,其columns屬性控制列的數目。我們可以在檢視器面板直接修改,或者使用代碼形式設定。

image.png

以下是代碼形式:

# 設定顯示兩列信息
tree.columns = 2
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點第1列文本
root.set_text(1,"第2列")             # 設定第2列文本

我們通過設定第2列文本,就可以看到如下的效果:

image.png

每一列都可以像第1列一樣設置圖標或其他。

const icon = preload("res://icon.svg")
# 設定顯示兩列信息
tree.columns = 2
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點第1列文本
root.set_icon(0,icon)               # 設定第1列圖標
root.set_icon_max_width(0,16)       # 設定第1列圖標最大寬度
root.set_text(1,"第2列")             # 設定第2列文本
root.set_icon(1,icon)               # 設定第1列圖標
root.set_icon_max_width(1,16)       # 設定第1列圖標最大寬度

上面代碼,我們為兩列都設定的圖標和圖標最大寬度。

image.png

顯示列標題

和一些其他可視化程序開發語言或數據表格軟件的表格控件一樣,Tree控件也可以顯示列標題(或者通俗叫“字段”)。

你需要用代碼或在檢視器面板手動開啟column_titles_visible

image.png

啟用后就會在每列上方顯示兩個按鈕,目前因為還沒有設置其文本,所以是不完全的形態:

image.png

通過Treeset_column_title()方法,我們可以設定所有列標題按鈕的文本:

# 設定和顯示列標題
tree.column_titles_visible = true    # 顯示列標題
tree.set_column_title(0,"第1列")     # 設定第1列列標題
tree.set_column_title(1,"第2列")     # 設定第2列列標題

上面代碼設置后的效果:

image.png

是樹型列表也是二維表格

我們為根節點添加一個子節點:

# 添加子節點
var itm:TreeItem = tree.create_item(root)
itm.set_text(0,"itm1")             # 設定根節點第1列文本
itm.set_icon(0,icon)               # 設定第1列圖標
itm.set_icon_max_width(0,16)       # 設定第1列圖標最大寬度
itm.set_text(1,"第2列")             # 設定第2列文本
itm.set_icon(1,icon)               # 設定第1列圖標
itm.set_icon_max_width(1,16)       # 設定第1列圖標最大寬度

此時的效果如下:

image.png

可以看到Tree的第一列保持了樹形列表的特征,同時每一列的單元格都可以單獨選中和交互。節點的折疊與展開也不受影響,表現出一種樹形結構+二維表格的特性。

設定列標題文本對齊方式

  • 對列標題的,也可以設定其文本對齊方式,寬度等細節。

  • set_column_title_alignment設定列標題文本的對齊方式:

tree.set_column_title_alignment(0,HORIZONTAL_ALIGNMENT_LEFT)   # 居左顯示

上面的代碼設定第一列的列標題居左顯示:

image.png

設定最小列寬

tree.set_column_custom_minimum_width(0,100)  # 設定第一列最小寬度

上面的代碼設定第一列的最小寬度為100像素。
image.png

處理列標題點擊

我們可以連接和處理Tree控件的column_title_clicked信號,來處理列標題的點擊。

# 列標題被點擊
func _on_tree_column_title_clicked(column, mouse_button_index):tree.select_mode = Tree.SELECT_MULTI # 設置多選模式# 獲取Tree的所有節點var root:TreeItem = tree.get_root()var items = [root]items.append_array(root.get_children())# 選中指定列所有單元格for itm in items:for i in range(tree.columns):if i == column:itm.select(column)else:itm.deselect(i)

等價寫法:

# 列標題被點擊
func _on_tree_column_title_clicked(column, mouse_button_index):tree.select_mode = Tree.SELECT_MULTI # 設置多選模式# 獲取Tree的所有節點var root:TreeItem = tree.get_root()var items = [root]items.append_array(root.get_children())tree.deselect_all()  # 清除所有選擇# 選中指定列所有單元格for itm in items:itm.select(column)

上面的代碼實現點擊列標題時,選中當前列的所有單元格:

處理列標題按鈕點擊.gif

設定選擇模式

Tree控件有三種選擇模式,不同模式下選中TreeItem的效果哈方式不同,并且可能會影響某些信號的觸發和處理。

檢視器面板設定:

image.png

代碼方式設定:

# 設定選擇模式
tree.select_mode = Tree.SELECT_SINGLE # 單個單元格選擇
tree.select_mode = Tree.SELECT_ROW    # 按行選擇
tree.select_mode = Tree.SELECT_MULTI  # 多選

效果:
在這里插入圖片描述

select_mode屬性值效果
SELECT_SINGLE如果有多列,則一次只能單獨選中某項的某列,也就是某個單元格
SELECT_ROW每次只能選中一行,也就是選中某項的所有列
SELECT_MULTI可以使用Ctrl或Shift鍵進行多選,一次可以選中多個單元格

創建按鈕

在每列的單元格中都可以創建1個或多個按鈕。按鈕顯示在單元格的最右側,并且只顯示一個圖標,不顯示文本。

但是可以設定其鼠標提示文本等。

這種按鈕可以作為樹控件單元格或行數據交互的一種形式。

const DIR = preload("res://custom_nodes/icons/dir.png")
# 創建根節點
var root:TreeItem = tree.create_item()
root.set_text(0,"root")             # 設定根節點第1列文本
root.set_icon(0,icon)               # 設定第1列圖標
root.set_icon_max_width(0,16)       # 設定第1列圖標最大寬度
root.add_button(0,DIR)   # 為根節點0列的單元格創建按鈕
tree.set_column_custom_minimum_width(0,200)

上面的代碼為兩項的兩列都添加了一個按鈕。

image.png

處理按鈕的點擊

我們可以連接Treebutton_clicked信號,來處理單元格中添加的按鈕的信號。
其參數返回:

  • item:按鈕所在的TreeItem
  • column:按鈕所在的列
  • id:按鈕在所在單元格按鈕中的ID或索引
  • mouse_button_index:點擊按鈕的鼠標按鍵,鼠標左鍵、中鍵、右鍵還是其他額外按鍵。
func _on_tree_button_clicked(item, column, id, mouse_button_index):if mouse_button_index == MOUSE_BUTTON_LEFT: # 鼠標左鍵match column:  # 某一列0,1:match id:   # 某按鈕0:item.set_text(column,"")

上面的信號處理代碼,可以讓我們清空所在單元格的文本:

處理按鈕點擊.gif

快速編輯單元格文本

單元格可以進入文本編輯狀態,顯示一個可以編輯的文本框,從而可以快速修改單元格的文本內容,回車后完成編輯狀態。

雙擊進入編輯狀態

Treeitem_activated()信號在TreeItem或其某列單元格雙擊時觸發。

我們通過連接此信號,創建信號處理函數,并設定如下的處理代碼:

# 雙擊單元格
func _on_tree_item_activated():tree.edit_selected(true)  # 選中單元格進入編輯狀態

此時,我們就可以通過雙擊單元格,進入單元格文本的編輯狀態,修改完成后,回車確認,退出編輯狀態:

雙擊編輯單元格文本.gif

編輯后的處理

  • Treeitem_edited()信號,在編輯完某個單元格并按回車確認后觸發
  • Treeget_edited()方法可以獲取當前編輯的TreeItem
  • 通過Treeget_edited_column()方法可以獲取剛剛被編輯的TreeItem的列。

這里我只簡單的打印輸出單元格被修改后的文本:

# 單元格編輯完成后
func _on_tree_item_edited():var edt:TreeItem = tree.get_edited()    # 獲取被編輯的TreeItemvar col:int = tree.get_edited_column()  # 被編輯的列索引print(edt.get_text(col))                # 打印修改后的單元格文本

處理TreeItem或單元格選中

  • item_selected()信號,在TreeItem選中時觸發,但僅在select_modeSELECT_ROWSELECT_SINGLE 時有效。
# TreeItem被選中或單擊
func _on_tree_item_selected():print(tree.get_selected())pass
  • select_modeSELECT_MULTISELECT_SINGLE時,則可以響應cell_selected()信號。
# TreeItem的單元格被選中或點擊
func _on_tree_cell_selected():var sel:TreeItem = tree.get_selected()    # 選中的單元格所在TreeItemvar col:int = tree.get_selected_column()  # 選中的單元格列索引print(sel.get_text(col))

總結一下就是:

select_mode屬性值item_selected()信號cell_selected()信號
SELECT_SINGLE
SELECT_ROW×
SELECT_MULTI×

用按鈕控制Tree控件項的增、刪、移動

我們創建一個如下的用戶界面場景,創建一個Tree以及多個Button

通過這些按鈕,我們實現為Tree控件添加、刪除好移動項。

image.png

為場景根節點添加如下代碼:

extends Control@onready var tree = $HBoxContainer/Tree@onready var add_btn = $HBoxContainer/VBoxContainer/addBtn
@onready var del_btn = $HBoxContainer/VBoxContainer/delBtn
@onready var moveup_btn = $HBoxContainer/VBoxContainer/moveupBtn
@onready var movedown_btn = $HBoxContainer/VBoxContainer/movedownBtn
@onready var clear_btn = $HBoxContainer/VBoxContainer/clearBtn# 添加
func _on_add_btn_pressed():var parent:TreeItem = nullif tree.get_root(): # 樹不是空的if tree.get_selected(): # 有選中項parent = tree.get_selected()else:parent = tree.get_root()var itm = tree.create_item(parent)var idx:int = parent.get_child_count() if parent else 0itm.set_text(0,"新節點%d" % idx)# 刪除
func _on_del_btn_pressed():if tree.get_root(): # 樹不是空的if tree.get_selected(): # 有選中項tree.get_selected().free()pass# 上移
func _on_moveup_btn_pressed():var sel:TreeItem = tree.get_selected()if sel and sel.get_prev(): # 有選中項sel.move_before(sel.get_prev())# 下移
func _on_movedown_btn_pressed():var sel:TreeItem = tree.get_selected()if sel and sel.get_next(): # 有選中項sel.move_after(sel.get_next())# 清空
func _on_clear_btn_pressed():tree.clear()pass

可以看到其中主要是對按鈕信號的處理代碼,實現增、刪,上下移動以及清空,效果如下動圖所示:

按鈕操控-添加.gif按鈕操控-刪除.gif
按鈕操控-上移和下移.gif按鈕操控-清空.gif

自由拖放

通過為Tree控件添加基本的控件拖放邏輯,就可以實現用鼠標對TreeItem的移動和放置。

extends Treefunc _get_drag_data(at_position):if get_tree(): # 不為空var sel:TreeItem = get_item_at_position(at_position)if sel: # 有選中項set_drag_preview(make_drag_preview(sel))return [sel]func _can_drop_data(at_position, data):# 如果拖放數據是一個TreeItem就可以放置# 拖放完畢恢復拖動標志設定drop_mode_flags = DROP_MODE_ON_ITEM | DROP_MODE_INBETWEENreturn data.size() > 0 and (data[0] is TreeItem)func _drop_data(at_position, data):# 獲取目標拖放位置,-1,0,1分別代表在某項之前、之上和之后var target_pos = get_drop_section_at_position(at_position)# 獲取鼠標位置處的TreeItemvar target_itm:TreeItem = get_item_at_position(at_position)# 如果目標位置處TreeItem是data[0]的子孫節點if target_itm in get_items(data[0]):return # 禁止移動match target_pos:-1: # 拖放到了某個TreeItem之前# 根據是否同級進行區別處理if data[0].get_parent() == target_itm.get_parent(): # 如果同級data[0].move_before(target_itm)else:data[0].get_parent().remove_child(data[0])         # 先從原來的父節點刪除target_itm.add_child(data[0])                      # 添加到目標位置的TreeItemdata[0].move_before(target_itm)0:  # 拖放到了某個TreeItem上data[0].get_parent().remove_child(data[0])         # 先從原來的父節點刪除target_itm.add_child(data[0])                      # 添加到目標位置的TreeItem1: # 拖放到了某個TreeItem之后# 根據是否同級進行區別處理if data[0].get_parent() == target_itm.get_parent(): # 如果同級data[0].move_after(target_itm)else:data[0].get_parent().remove_child(data[0])         # 先從原來的父節點刪除target_itm.add_child(data[0])                      # 添加到目標位置的TreeItemdata[0].move_after(target_itm)# 返回某個TreeItem下所有子孫節點的集合
func get_items(item:TreeItem) -> Array[TreeItem]:var arr:Array[TreeItem]if item.get_child_count()>0:arr.append_array(item.get_children())for chd in item.get_children():arr.append_array(get_items(chd))return arr# 創建拖動預覽
func make_drag_preview(itm:TreeItem) -> Button:var btn = Button.new()#btn.flat = truebtn.text = itm.get_text(0)btn.icon = itm.get_icon(0)return btn

實現效果:

自由拖放.gif

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

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

相關文章

uniapp和vue項目配置多語言,實現前端切換語言

在uniapp中配置多語言功能,實現前端切換語言,可以按照以下步驟進行: 1. 創建語言包 首先,創建一個名為 lang 的目錄,并在該目錄下為每種支持的語言創建對應的JSON或JS文件。例如: lang/en.js&#xff08…

Threejs之場景標注標簽信息CSS2DRenderer

參考資料 CSS2DRenderer(HTML標簽)…單擊按鈕關閉HTML標簽 知識點 注:基于Three.jsv0.155.0 CSS2DRenderer(HTML標簽) HTML標簽遮擋Canvas畫布事件Canvas尺寸變化(HTML標簽)標簽位置不同設置方式標簽位置(標注工廠設備)標簽指示線或箭頭指向標注點鼠標選中模型…

Microsoft PyRIT能自動化完成AI紅隊的任務

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎?訂閱我們的簡報,深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同,從行業內部的深度分析和實用指南中受益。不要錯過這個機會,成為AI領…

兩數相加的問題

題目是:給兩個非空的鏈表,表示兩個非負整數。它們每位數都是按照逆序的方式存儲,并且每一個節點只能存儲一位數字。現在兩個數相加,并且以相同的形式返回一個表示和的鏈表。 首先回顧一下,什么是鏈表?鏈表…

《異常檢測——從經典算法到深度學習》26 Time-LLM:基于大語言模型的時間序列預測

《異常檢測——從經典算法到深度學習》 0 概論1 基于隔離森林的異常檢測算法 2 基于LOF的異常檢測算法3 基于One-Class SVM的異常檢測算法4 基于高斯概率密度異常檢測算法5 Opprentice——異常檢測經典算法最終篇6 基于重構概率的 VAE 異常檢測7 基于條件VAE異常檢測8 Donut: …

使用遞歸方法和類數組兩種方法計算斐波那契數列

菲波納契數列又稱"菲波納契神奇數列",是由13世紀的意大利數學家菲波納契提出的,當時是和兔子的繁殖問題有關的,它是一個很重要的數學模型。這個問題是:有小兔一對,若第二個月它們成年,第三個月生下小兔一對&…

3333666777

? 通用計算機啟動過程 1??一個基礎固件:BIOS 一個基礎固件:BIOS→基本IO系統,它提供以下功能: 上電后自檢功能 Power-On Self-Test,即POST:上電后,識別硬件配置并對其進行自檢&#xff0c…

阿里云倉庫

倉庫服務 (aliyun.com) maven中央倉庫: Central Repository: (maven.org)

Windows10 安裝Neo4j流程

1、下載并安裝ava運行環境 官網鏈接(需要注冊Oracle賬號):https://www.oracle.com/java/technologies/downloads/ 根據自己Neo4j版本確認需要的JDK版本 百度網盤鏈接: 鏈接:鏈接:https://pan.baidu.com/s/…

靜態網頁和動態網頁的異同

靜態網頁和動態網頁是兩種不同類型的網頁。它們之間的主要異同點如下: 1. 靜態網頁: - 靜態網頁是指在服務器上預先準備好的網頁,內容固定不變。 - 靜態網頁通常由HTML、CSS和JavaScript等靜態文件組成。 - 用戶訪問靜態網頁時&#xff0c…

Sodinokibi勒索病毒最新變種,解密工具更新到2.0版本

Sodinokibi勒索病毒 Sodinokibi勒索病毒又稱REvil,自從2019年6月1日,GandCrab勒索病毒運營團伙宣布停止運營之后,Sodinokibi勒索病毒馬上接管了GandCrab的大部分傳播渠道,同時它也被稱為是GandCrab勒索病毒的“接班人”&#xff…

VMware 虛擬機安裝windows 10操作系統

先提前準備好鏡像文件 1.創建新的虛擬機 2.選擇自定義,然后下一步 v Windows 建議選擇2G以上,下一步 選擇網絡地址轉換(NAT),下一步 這里可按自己的需求來分區,也可以安裝好后再分區 選擇立即重啟&#xff…

【劍指offer】C++ 翻轉字符串里面的單詞

目錄 題目: 思路: 代碼出現 結果 題目: 給定一個字符串,逐個翻轉字符串中的每個單詞。 示例 1: 輸入: "the sky is blue" 輸出: "blue is sky the" 示例 2: 輸入: " hello world! " 輸出: "world! hello" 解釋: 輸入字符…

L1-032 Left-pad(PTA)

文章目錄 L1-032 Left-pad題目描述代碼 L1-032 Left-pad 題目描述 根據新浪微博上的消息,有一位開發者不滿NPM(Node Package Manager)的做法,收回了自己的開源代碼,其中包括一個叫left-pad的模塊,就是這個…

使用 Object.defineProperty() 來進行數據劫持有什么缺點?

使用 Object.defineProperty() 來進行數據劫持有什么缺點? (1)在對一些屬性進行操作時,使用這種方法無法攔截,比如通過下標方式修改數組數據或者給對象新增屬性,這都不能觸發組件的重新渲染,因為…

Vue組件置底方法,ElementPlus布局

問題描述 在開發網頁時使用了elementplus的el-container組件 組件里分成了main和footer兩塊&#xff0c;但是想要將兩個按鈕置底在容器底部遇到了困難 如下圖所示&#xff0c;在網頁開發者工具可見兩個按鈕與左側的圖片沒有底部對齊 此時我的代碼是這樣 <el-footer>&…

STM32自學?串口發送+接收

一、相關函數說明&#xff1a; USART_ClockInit()和USART_ClockStructInit(); 用來配置同步時鐘輸出 USART_DMACmd(); 開啟USART到DMA的觸發通道 USART_SendData(); 發送數據 USART_ReceiveData(); 接收數據 二、程序代碼 serial.c文件 #include "stm32f10x.h" #i…

文件底層的深入理解之文件輸入輸出重定向

目錄 一、文件fd的分配規則 二、對輸出重定向現象的理解 三、輸出輸入重定向的簡單實現 1、輸出重定向 2、輸入重定向 一、文件fd的分配規則 最小的沒有被使用的數組下標&#xff0c;會被分配給最新打開的文件。 二、對輸出重定向現象的理解 正如上面這段代碼所示&#xff0…

C語言實現日本某地發生了一件謀殺案

題目 猜兇手 題目內容&#xff1a; 日本某地發生了一件謀殺案&#xff0c;警察通過排查確定殺人兇手必為4個嫌疑犯的一個。 以下為4個嫌疑犯的供詞: A說&#xff1a;不是我。 B說&#xff1a;是C。 C說&#xff1a;是D。 D說&#xff1a;C在胡說 已知3個人說了真話&…