理解PostgreSQL的模式、表、空間、用戶間的關係

在平時的工作中,我們經常接觸到數據庫表用戶以及角色的使用,由於經常使用默認的數據庫表空間模式(Schema),所以我們往往忽略了數據庫表空間和模式的概念以及作用。

接下來,先介紹一下模式和表空間的定義以及作用。

什麼是Schema?

一個數據庫包含一個或多個已命名的模式,模式又包含表。模式還可以包含其它對象, 包括數據類型函數操作符等。同一個對象名可以在不同的模式里使用而不會導致衝突; 比如,herschemamyschema都可以包含一個名為mytable的表。 和數據庫不同,模式不是嚴格分離的:只要有權限,一個用戶可以訪問他所連接的數據庫中的任意模式中的對象。

我們需要模式的原因有好多:

  • 允許多個用戶使用一個數據庫而不會幹擾其它用戶。
  • 把數據庫對象組織成邏輯組,讓它們更便於管理。
  • 第三方的應用可以放在不同的模式中,這樣它們就不會和其它對象的名字衝突。

模式類似於操作系統層次的目錄,只不過模式不能嵌套。

什麼是表空間?

表空間是實際的數據存儲的地方。一個數據庫schema可能存在於多個表空間,相似地,一個表空間也可以為多個schema服務。

通過使用表空間,管理員可以控制磁盤的布局。表空間的最常用的作用是優化性能,例如,一個最常用的索引可以建立在非常快的硬盤上,而不太常用的表可以建立在便宜的硬盤上,比如用來存儲用於進行歸檔文件的表。

PostgreSQL表空間、數據庫、模式、表、用戶、角色之間的關係

角色與用戶的關係

PostgreSQL中,存在兩個容易混淆的概念:角色/用戶。之所以說這兩個概念容易混淆,是因為對於PostgreSQL來說,這是完全相同的兩個對象。唯一的區別是在創建的時候:

1.我用下面的psql創建了角色custom:

CREATE ROLE custom PASSWORD 'custom';

接着我使用新創建的角色custom登錄,PostgreSQL給出拒絕信息:

FATAL:role 'custom' is not permitted to log in.

說明該角色沒有登錄權限,系統拒絕其登錄

2.我又使用下面的psql創建了用戶guest:

CREATE USER guest PASSWORD 'guest';

接着我使用guest登錄,登錄成功

難道這兩者有區別嗎?查看文檔,又這麼一段說明:CREATE USER is the same as CREATE ROLE except that it implies LOGIN. —-CREATE USER除了默認具有LOGIN權限之外,其他與CREATE ROLE是完全相同的。

為了驗證這句話,修改custom的權限,增加LOGIN權限:

ALTER ROLE custom LOGIN;

再次用custom登錄,成功!那麼事情就明了了:

CREATE ROLE custom PASSWORD ‘custom’ LOGIN 等同於 CREATE USER custom PASSWORD ‘custom’.

這就是ROLE/USER的區別。

數據庫與模式的關係

模式(schema)是對數據庫(database)邏輯分割。

在數據庫創建的同時,就已經默認為數據庫創建了一個模式–public,這也是該數據庫的默認模式。所有為此數據庫創建的對象(表、函數、試圖、索引、序列等)都是創建在這個模式中的:

1.創建一個數據庫mars

CREATE DATABASE mars;

2.用custom角色登錄到mars數據庫,查看數據庫中的所有模式:\dn

显示結果只有public一個模式。

3.創建一張測試表

CREATE TABLE test(id integer not null);

4.查看當前數據庫的列表:\d;

显示結果是表test屬於模式public.也就是test表被默認創建在了public模式中。

5.創建一個新模式custom,對應於登錄用戶custom

CREATE SCHEMA custom;

ALTER SCHEMA custom OWNER TO custom;

6.再次創建一張test表,這次這張表要指明模式

CREATE TABLE custom.test (id integer not null);

7.查看當前數據庫的列表: \d

显示結果是表test屬於模式custom.也就是這個test表被創建在了custom模式中。

得出結論是:數據庫是被模式(schema)來切分的,一個數據庫至少有一個模式,所有數據庫內部的對象(object)是被創建於模式的。用戶登錄到系統,連接到一個數據庫后,是通過該數據庫的search_path來尋找schema的搜索順序,可以通過命令SHOW search_path;具體的順序,也可以通過SET search_path TO 'schema_name'來修改順序。

官方建議是這樣的:在管理員創建一個具體數據庫后,應該為所有可以連接到該數據庫的用戶分別創建一個與用戶名相同的模式,然後,將search_path設置為$user,即默認的模式是與用戶名相同的模式。

表空間與數據庫的關係

數據庫創建語句:

CREATE DATABASE dbname;

默認的數據庫所有者是當前創建數據庫的角色,默認的表空間是系統的默認表空間pg_default

為什麼是這樣的呢?

因為在PostgreSQL中,數據的創建是通過克隆數據庫模板來實現的,這與SQL SERVER是同樣的機制。由於CREATE DATABASE dbname並沒有指明數據庫模板,所以系統將默認克隆template1數據庫,得到新的數據庫dbname。(By default, the new database will be created by cloning the standard system database template1)

template1數據庫的默認表空間是pg_default,這個表空間是在數據庫初始化時創建的,所以所有template1中的對象將被同步克隆到新的數據庫中。

相對完整的語法應該是這樣的:

CREATE DATABASE dbname TEMPLATE template1 TABLESPACE tablespacename;
ALTER DATABASE dbname OWNER TO custom;

1.連接到template1數據庫,創建一個表作為標記:

CREATE TABLE test(id integer not null);

向表中插入數據

INSERT INTO test VALUES (1);

2.創建一個表空間:

CREATE TABLESPACE tsmars OWNER custom LOCATION '/tmp/data/tsmars';

在此之前應該確保目錄/tmp/data/tsmars存在,並且目錄為空。

3.創建一個數據庫,指明該數據庫的表空間是剛剛創建的tsmars

CREATE DATABASE dbmars TEMPLATE template1 OWNERE custom TABLESPACE tsmars;
ALTER DATABASE dbmars OWNER TO custom;

4.查看系統中所有數據庫的信息:\l+

可以發現,dbmars數據庫的表空間是tsmars,擁有者是custom;

仔細分析后,不難得出結論:

在PostgreSQL中,表空間是一個目錄,裏面存儲的是它所包含的數據庫的各種物理文件

總結

表空間是一個存儲區域,在一個表空間中可以存儲多個數據庫,儘管PostgreSQL不建議這麼做,但我們這麼做完全可行。一個數據庫並不知直接存儲表結構等對象的,而是在數據庫中邏輯創建了至少一個模式,在模式中創建了表等對象,將不同的模式指派該不同的角色,可以實現權限分離,又可以通過授權,實現模式間對象的共享,並且還有一個特點就是:public模式可以存儲大家都需要訪問的對象。

表空間用於定義數據庫對象在物理存儲設備上的位置,不特定於某個單獨的數據庫。數據庫是數據庫對象的物理集合,而schema則是數據庫內部用於組織管理數據庫對象的邏輯集合,schema名字空間之下則是各種應用程序會接觸到的對象,比如表、索引、數據類型、函數、操作符等。

角色(用戶)則是數據庫服務器(集群)全局範圍內的權限控制系統,用於各種集群範圍內所有的對象權限管理。因此角色不特定於某個單獨的數據庫,但角色如果需要登錄數據庫管理系統則必須連接到一個數據庫上。角色可以擁有各種數據庫對象。

關注公眾號:JAVA九點半課堂,這裡有一批優秀的技術大牛,為你提供方向,提供資源!加入我們,一起探討技術,共同進步!回復“資料”獲取 2T 行業最新資料!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

不放心東奧食安 南韓擬自運食材、偵測核食

摘錄自2019年12月5日公共電視報導

明年東京奧運,南韓奧會考量到食品安全,計畫購買輻射偵測器,針對代表團在奧運期間可能吃到的食物偵測,也打算運送自家食材前往日本。讓選手不必擔心吃到2011年福島核災可能遭污染的在地食品。

南韓執政共同民主黨議員申東坤說:「我們將從南韓盡可能帶很多食材過去,也因可能過境會面臨困難,例如肉品跟魚。我們會用各種形式帶食物,即使我們獲得在地韓國餐廳支援,我們還是會檢測輻射採取補充措施,供應安全的食品。」       

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

熊出沒注意! 日本熊類攻擊人類概況與對策

文:宋瑞文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

Nissan砸2,650萬英鎊 投資電動車用電池

日本車商Nissan宣布將投資2,650萬英鎊,在英國桑德蘭廠發展電動車引擎電池,主要用意為改善旗下Nissan Leaf電動車電池產品。

Nissan Leaf是全球電動車銷量常勝軍,同時也是英國第一款量產電動車。Nissan於2011年時募資1.89億英鎊開始在英國生產Nissan Leaf電動車與鋰電池,目前年電池產能約六萬顆。

截至目前為止,Nissan Leaf在英國的投資、生產製造與銷售等工作,已在英國創造兩千多份工作。新增的2,650萬英鎊投資將可確保該廠300份職缺。同時,這份投資也反映Nissan繼續發展零碳排引擎的承諾。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

南投搬家前需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

小白學 Python(21):生成器基礎

人生苦短,我選Python

前文傳送門

生成器

我們前面聊過了為什麼要使用迭代器,各位同學應該還有印象吧(說沒有的就太過分了)。

列表太大的話會佔用過大的內存,可以使用迭代器,只拿出需要使用的部分。

生成器的設計原則和迭代器是相似的,如果需要一個非常大的集合,不會將元素全部都放在這個集合中,而是將元素保存成生成器的狀態,每次迭代的時候返回一個值。

比如我們要生成一個列表,可以採用如下方式:

list1 = [x*x for x in range(10)]
print(list1)

結果如下:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

如果我們生成的列表非常的巨大,比如:

list2 = [x*x for x in range(1000000000000000000000000)]

結果如下:

Traceback (most recent call last):
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <module>
    list2 = [x*x for x in range(1000000000000000000000000)]
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <listcomp>
    list2 = [x*x for x in range(1000000000000000000000000)]
MemoryError

報錯了,報錯信息提示我們存儲異常,並且整個程序運行了相當長一段時間。友情提醒,這麼大的列表創建請慎重,如果電腦配置不夠很有可能會將電腦卡死。

如果我們使用生成器就會非常方便了,而且執行速度嗖嗖的。

generator1 = (x*x for x in range(1000000000000000000000000))
print(generator1)
print(type(generator1))

結果如下:

<generator object <genexpr> at 0x0000014383E85B48>
<class 'generator'>

那麼,我們使用了生成器以後,怎麼讀取生成器生成的數據呢?

當然是和之前的迭代器一樣的拉,使用 next() 函數:

generator2 = (x*x for x in range(3))
print(next(generator2))
print(next(generator2))
print(next(generator2))
print(next(generator2))

結果如下:

Traceback (most recent call last):
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 14, in <module>
    print(next(generator2))
StopIteration

直到最後,拋出 StopIteration 異常。

但是,這種使用方法我們並不知道什麼時候會迭代結束,所以我們可以使用 for 循環來獲取每生成器生成的具體的元素,並且使用 for 循環同時也無需關心最後的 StopIteration 異常。

generator3 = (x*x for x in range(5))
for index in generator3:
    print(index)

結果如下:

0
1
4
9
16

generator 非常的強大,本質上, generator 並不會取存儲我們的具體元素,它存儲是推算的算法,通過算法來推算出下一個值。

如果推算的算法比較複雜,用類似列表生成式的 for 循環無法實現的時候,還可以用函數來實現。

比如我們定義一個函數,emmmmmm,還是簡單點吧,大家領會精神:

def print_a(max):
    i = 0
    while i < max:
        i += 1
        yield i

a = print_a(10)
print(a)
print(type(a))

結果如下:

<generator object print_a at 0x00000278C6AA5CC8>
<class 'generator'>

這裏使用到了關鍵字 yieldyieldreturn 非常的相似,都可以返回值,但是不同的是 yield 不會結束函數。

我們調用幾次這個用函數創建的生成器:

print(next(a))
print(next(a))
print(next(a))
print(next(a))

結果如下:

1
2
3
4

可以看到,當我們使用 next() 對生成器進行一次操作的時候,會返回一次循環的值,在 yield 這裏結束本次的運行。但是在下一次執行 next() 的時候,會接着上次的斷點接着運行。直到下一個 yield ,並且不停的循環往複,直到運行至生成器的最後。

還有一種與 next() 等價的方式,直接看示例代碼吧:

print(a.__next__())
print(a.__next__())

結果如下:

5
6

接下來要介紹的這個方法就更厲害了,不僅能迭代,還能給函數再傳一個值回去:

def print_b(max):
    i = 0
    while i < max:
        i += 1
        args = yield i
        print('傳入參數為:' + args)

b = print_b(20)
print(next(b))
print(b.send('Python'))

結果如下:

1
傳入參數為:Python
2

上面講了這麼多,可能各位還沒想到生成器能有什麼具體的作用吧,這裏我來提一個——協程。

在介紹什麼是協程之前先介紹下什麼是多線程,就是在同一個時間內可以執行多個程序,簡單理解就是你平時可能很經常的一邊玩手機一邊聽音樂(毫無違和感)。

協程更貼切的解釋是流水線,比如某件事情必須 A 先做一步, B 再做一步,並且這兩件事情看起來要是同時進行的。

def print_c():
    while True:
        print('執行 A ')
        yield None
def print_d():
    while True:
        print('執行 B ')
        yield None

c = print_c()
d = print_d()
while True:
    c.__next__()
    d.__next__()

結果如下:

...
執行 A 
執行 B 
執行 A 
執行 B 
執行 A 
執行 B 
執行 A 
執行 B 
執行 A 
執行 B 
...

因為 while 條件設置的是永真,所以這個循環是不會停下來的。

這裏我們定義了兩個生成器,並且在一個循環中往複的調用這兩個生成器,這樣看起來就是兩個任務在同時執行。

最後的協程可能理解起來稍有難度,有問題可以在公眾號後台問我哦~~~

示例代碼

本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便大家取用。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

安利一個繪製指引線的JS庫leader-line

前言

之前看到一篇推薦這個搜索引擎的新聞,對於這個搜索引擎是否好用咱們不予置評,但是我在這個搜索引擎上面發現了一個好玩的前端功能。

如上圖,將鼠標浮動到學習來源上時,會展示一堆指引線。

本博客的右側文章目錄也集成了這個功能,諸位可以玩一玩。

當時覺得這個功能很好玩,而且前端領域其實這種指引線還是有很多用處的,比如新手指引,功能指引,腦圖之類的功能。

鑒於以後很可能需要用到,當時就調試了一下這個網站,發現使用了這個庫。

然後百度了一下,發現網上也沒什麼人介紹這個庫,所以這裏寫個安利文吧。

LeaderLine

這個庫在Github上的介紹很簡單:

Draw a leader line in your web page.

意思就是在網頁上畫指引線。

使用起來也非常方便:

<script src="leader-line.min.js"></script>
<script>
  new LeaderLine(
    document.getElementById('start'),
    document.getElementById('end')
  );
</script>

new一個LeaderLine對象即可,只需要輸入兩個dom元素節點而已。

當然也可以輸入更多的參數來繪製各種各樣的指引線:

具體的使用方法可以去查看lead-line的,這裏就不贅述了。

而且這個庫本身就提供了hover繪製指引線的功能,並且能偏移起始點和結束點的位置,同時當起始點和結束點變動時,也可以實時調整指引線。

這兩個功能可以將鼠標hover到右側的文章目錄上,然後滾動鼠標輪來查看效果。

原理

這個庫的實現原理其實很簡單,根據提供的兩個dom元素,找到這兩個dom元素的位置,然後通過svg在body下繪製一條指引線。

這個庫雖然只是個js,但是在引入後會將一些樣式寫到一個id為leader-line-defs的svg元素內。

這些指引線使用了一個叫leader-line的樣式class,如果繪製指引線時出現遮擋情況,可以通過調整這個樣式class的z-index或者position來處理。

可以預想一下,這些指引線都是position:absolute的,因為position:fixed的元素在滾動時肯定會存在問題。

原理都講了,所以諸位請在頁面有fixed元素或者absolute元素時,仔細查看指引線是否會與這些元素產生遮擋。

示例代碼

這裏就以我博客右側目錄集成的指引線功能作為示例代碼:

// 生成目錄上的指引線
function createCatalogLeaderLine($h2Arr) { // $h2Arr是一個dom元素集合,注意不是數組哦
  // lines的目的是為了保留leader-line變量,方便重繪
  var lines = {};
  var options = {
    color: '#5bf', // 指引線顏色
    endPlug: "disc", // 指引線結束點的樣式
    size: 2, // 線條尺寸
    startSocket: "left", //在指引線開始的地方從元素左側開始
    endSocket: "right", //在指引線開始的地方從元素右側結束
    hide:true // 繪製時隱藏,默認為false,在初始化時可能會出現閃爍的線條
  };
  [].slice.call($h2Arr).forEach(function (item) {
    var anchor = LeaderLine.mouseHoverAnchor(document.getElementById('catalog' + item.id), 'draw', {
      // 指引線動效
      animOptions: {
        duration: 500
      },
      // 清除默認的hover樣式
      hoverStyle:{
        backgroundColor: null
      },
      // 起始點樣式,這裏為了清除默認樣式
      style: {
        paddingTop: null,
        paddingRight: null,
        paddingBottom: null,
        paddingLeft: null,
        cursor: null,
        backgroundColor: null,
        backgroundImage: null,
        backgroundSize: null,
        backgroundPosition: null,
        backgroundRepeat: null
      },
      // 當起始點被hover時調用的事件
      onSwitch: function (event) {
        var line = lines[item.id]
        // 浮動上去就重繪
        if (event.type == "mouseenter") {
          line.position();
        }
      }
    });
    lines[item.id] = new LeaderLine(
      anchor,
      document.getElementById(item.id),
      options
    );
  })
  // 滾動時重繪指引線
  $(window).scroll(function () {
    for (var key in lines) {
      lines[key].position()
    }
  })
}

其中LeaderLine.mouseHoverAnchor為leader-line提供的api,顧名思義即可。

代碼就不講了,關鍵點都有註釋。

總結

沒什麼好總結的,這裏發一個小吐槽。

其實我博客集成這個功能時,最開始是直接把這個庫的js複製粘貼到了博客園的自定義js代碼中,沒想到博客園這方面做了大小限制。

所以我就把Magi這個搜索引擎的引用地址拿來用了,萬一哪天這個搜索引擎不能用了或者js地址變了那麼我目錄的指引功能可能就掛了。

N年之後你看到這篇文章,也許功能失效了,到時候別忘了給我發個短消息提醒我一下。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

※別再煩惱如何寫文案,掌握八大原則!

日本風災後核廢棄物外流 空拍機搜索仍大批失蹤

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

遷移桌面程序到MS Store(11)——應用SVG圖標

在傳統桌面程序中,對圖標的使用大多是直接嵌入JPG或者PNG的圖片。在祖傳的1366×768分辨率下,並沒有什麼問題。相對於手機硬件的突飛猛進,也側面反映了PC行業的落寞和桌面程序開發的不思進取。用360衛士的群眾並不能倒推PC行業的升級。反倒是水果公司雙高的利潤和口碑讓人很是眼饞。加之某軟跳出來教豬隊友做硬件。現在倒是有些起色,1080p的屏幕已是標配,4k也算常見。那麼傳統桌面程序在升級過程中,就會遇到今天要討論的,如何解決高分辨率下圖標模糊的問題。

一種解決方案是按最高的分辨率提供圖片。這種適合較大的圖片,比如背景啥的。另一種就是今天要討論的,針對當前流行的、扁平化圖標的解決方案。

 從本篇的標題可以看出,我們希望應用SVG矢量圖來適應各種分辨率的情況。以WPF程序為例,首先要面對的問題是,WPF並不支持像嵌入JPG/PNG圖標這樣,直接使用SVG圖標。大動干戈的引用第三方library通過自定義類型來支持SVG並不是本文的目的。這裏我們要介紹如何通過字體文件,進而在WPF或UWP中使用SVG圖標的方式。

雖然WPF不支持直接使用SVG文件,但是Windows是支持矢量字體的。而我們的目的就是要將圖標以字體的形式在WPF程序中显示。具體使用的字體TrueType,則是由微軟和蘋果共同開發的字體類型標準,該字體文件的擴展名是.ttf。

接下來我們依然是通過Sample工程來說明。首先給出GitHub的地址:

首先我們打開WpfAppWithPNGs工程,圖標的使用代碼如下:

        <Image Grid.Row="0" Grid.Column="0"  Width="32" Height="32" Source="Resources/Airplane_Off.png" ></Image>
        <Image Grid.Row="0" Grid.Column="1"  Width="64" Height="64" Source="Resources/Airplane_On.png" ></Image>
        <Image Grid.Row="0" Grid.Column="2"  Width="96" Height="96" Source="Resources/Bluetooth_Off.png"  ></Image>
        <Image Grid.Row="0" Grid.Column="3"  Width="128" Height="128" Source="Resources/Bluetooth_On.png"  ></Image>

 

這裏主要有兩個問題,因為我們默認提供的是32×32的圖標,因此除了第一列Width和Height設置為32的圖標,其他的圖標都存在模糊的問題。第二個問題是針對圖標的每一種顏色,都需要對應提供不同的圖標文件(圖中的例子需要有灰色和藍色兩份文件)。相對的SVG圖標僅僅需要一份文件。即可在程序中動態設定不同的顏色了。

這裏先給出最終WPF項目中,對SVG圖標的引用的代碼,然後我們再進行詳細解釋。對應的工程名為WpfAppWithFontIcons。

        <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Static local:FontIcons.airplane_mode_circ}"   Foreground="Gray"  FontSize="32" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Static local:FontIcons.airplane_mode_circ}"  Foreground="{StaticResource dellBlue}"  FontSize="64" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Static local:FontIcons.bluetooth_inactive}"   Foreground="Orange"  FontSize="96" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Static local:FontIcons.bluetooth_inactive}"    Foreground="Brown"  FontSize="128" ></TextBlock>

代碼最大的不同應該是由<Image/>標籤更改為<TextBlock/>標籤,這是因為我們是通過ttf字體文件,曲線救國的方式來使用SVG圖標。

具體的步驟如下:

準備SVG圖標文件,將這些文件打包成一整個ttf字體文件。打包的方式有很多種,通常我使用的是IcoMoon的免費解決方案。地址如下:

通過這個網站選擇SVG圖標文件上傳,打包生成一個zip文件。解壓後文件夾結構如下圖:

ttf文件在fonts文件夾中,實際使用時,需要作為資源文件,添加到WPF工程中。點擊圖中的demo.html會打開一個本地網頁,可用於查找ttf文件中包含的SVG圖標,以及對應的unicode。實際我們是通過對unicode的引用來显示SVG圖標的。

完整的project結構如下圖,Fonts文件夾是手動添加用來放置ttf文件。ttf文件名字都是根據項目需要來取,並不固定。

ttf字體文件需要以<FontFamily/>的形式添加到項目的<Resources/>節點中。然後再通過<Style/>指定給<TextBlock/>。當然不在<Resources/>節點定義Style,而是在每個<TextBlock/>中指定FontFamily屬性也是可以的。有關XAML的語法細節,回字的四種寫法什麼的,這裏略過不提。

    <Window.Resources>
        <FontFamily x:Key="Fonticon">/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2</FontFamily>
        <Style TargetType="TextBlock">
            <Setter Property="FontFamily" Value="{StaticResource Fonticon}" ></Setter>
        </Style>
        <SolidColorBrush x:Key="dellBlue">#007DB8</SolidColorBrush>
    </Window.Resources>

這裏說明一下“/Fonts/rcc-fonticon-ribbon-v2.ttf#rcc-fonticon-ribbon-v2”值的定義,#前面的是文件路徑,#後面的是font name,查看的方法是雙擊ttf文件,參考下圖。

在定義好FontFamily之後,我們並不推薦直接將unicode寫到XAML或.cs文件中。因為在XAML中,你需要如下編寫:

<TextBlock Grid.Row="0" Grid.Column="0" Text="&#xe900;" Foreground="Gray"  FontSize="32" ></TextBlock>

而在C#代碼中,又需要以下面這種格式:

textBlockAirplane.Text = "\ue900"

兩種不統一的格式會在將來修改時帶來極大的困難,特別是圖標被多處引用時,全局的查找替換根本就是噩夢。此外,毫無意義的unicode值的可讀性根本等於0。正常人類無法將”&#xe900;”,”\ue900″和Airplane的圖標聯繫起來。

我推薦的做法是生成一個FontIcons Class,以string類型屬性的形式暴露出來。這樣可以獲得IDE智能語法提示的支持,更新時也僅需修改這個Class,Find All Reference更是方便無比。同時無論在XAML文件,還是C#代碼中,我們看到的都是統一的“FontIcons.airplane_mode_circ”。

    public static class FontIcons
    {
        public static string airplane_mode_circ { get; } = "\ue900";
        public static string bluetooth_inactive { get; } = "\ue901";
        public static string brightness { get; } = "\ue902";
        public static string brightness_inactive { get; } = "\ue903";
        public static string browse_inactive { get; } = "\ue904";
        public static string camera { get; } = "\ue905";
    }

那麼我們是不是需要手工來編寫FontIcons Class呢?大哥我們是能把午飯(我不愛喝咖啡)轉換成Code的生物啊!當然是寫個小工具來自動生成了。在Sample庫中,參考IcoMoonReader工程,只需將IcoMoon生成的.svg文件(icomoon.zip解壓后的fonts文件夾里)丟在IconMoonReader.exe同級目錄,即可生成相應代碼。

 其實只有一個方法啦,使用時需要注意具體的文件名是否正確。

            using (var stream = new FileStream("rcc-fonticon-ribbon-v2.svg", FileMode.Open))
            {
                using (var reader = new StreamReader(stream))
                {
                    var pattern = "unicode(\\S)*\\sglyph-name(\\S)*\"";
                    var input = reader.ReadToEnd();
                    foreach (Match match in Regex.Matches(input, pattern))
                    {
                        pattern = "\"\\S*\"";
                        var list = new List<string>();
                        foreach (var result in Regex.Matches(match.Value, pattern))
                        {
                            list.Insert(0, result.ToString());
                        }
                        var name = list[0].Replace("\"", "").Replace("-","_");
                        var code = list[1].Replace("&#x", "\\u").Replace(";", "");
                        Console.WriteLine($"public static string {name} {{ get; }} = {code};");
                    }
                }
            }

把生成的C#字符串定義貼到具體工程的FontIcons Class(名字隨意)。

這樣一個優秀的解決方案如果僅支持WPF,那又談何遷移到MS Store呢?實際上這套機制放到UWP工程中也是可以的。雖然UWP可以通過SvgImageSource屬性原生支持SVG了,但我們的這套方案在圖標的應用方面毫不遜色,甚至可以說更為方便。具體的例子可以參考AppWithFontIcon工程。在這個UWP的工程中,除了放ttf文件的位置我換到了現成的Assets文件夾,幾乎沒有改變。

        <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind local:FontIcons.airplane_mode_circ}"   Foreground="Gray"  FontSize="32" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind local:FontIcons.airplane_mode_circ}"  Foreground="{StaticResource dellBlue}"  FontSize="64" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="{x:Bind local:FontIcons.bluetooth_inactive}"   Foreground="Orange"  FontSize="96" ></TextBlock>
        <TextBlock Grid.Row="0" Grid.Column="3" Text="{x:Bind local:FontIcons.bluetooth_inactive}"    Foreground="Brown"  FontSize="{x:Bind DynamicFontSize(),Mode=OneWay,FallbackValue=128}" ></TextBlock>

因為UWP沒有了x:static關鍵字,所以我換成了x:Bind。換成x:Bind之後甚至可以動態的響應值的變化。比如我在這裏把FontSize做了一個x:bind到DynamicFontSize()方法,讓字體隨着界面改變,動態的變大變小。雖然並沒有什麼卵用……但是Demo的時候可以增加點噱頭……

本篇到此結束,照例貼上Github地址:

感謝耐着性子看到這裏的同學!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

北海道核電站少報一半污染

摘錄自2019年12月26日共同社報導

日本北海道電力公司周二(24日)發布消息稱,公司自1988年起就一直向周邊地方政府少報「泊核電站」向大氣排放的放射性氣體廢棄物數值,長期只報告實際數值的一半。

「泊核電站」位於北海道古宇郡泊村,自1988年至2012年間運作。日本北海道電力公司會定期向日本原子能規制廳、北海道政府及泊村等報告放射性氣體廢棄物排放量。不過,該公司在煙囪採集樣本時,一直忽略樣本會因混入空氣而被稀釋一半,結果一直採用錯誤數值。

共同社報導,這是因該公司的操作手冊中,遺漏了需要修正數值的記述。北海道電力公司副社長阪井一郎為事件道歉,但公司稱排放仍未超出安全標準。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

建立通用MRV機制 讓碳排計算公平且透明

文:范馨心(InnoEnergy永續能源工程與管理碩士學程)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享