2019年2月27日 星期三

線上 Julia 編譯器 Juliabox

今天在 "Beginning Julia Programming (Apress)" 這本書裡看到作者介紹了 線上 Julia 編譯器 Juliabox, 這是 Julia 語言三位創辦人 Jeff Bezanson, Stefan Karpinski 與 Viral B. Shah 教授出來開的雲端計算公司-Julia Computing 所提供的計算服務, 對於一般計算有免費方案, 學術研究則有五折優惠, 參見官網下方的 "Subscription Models" :

https://www.juliabox.com/




可見免費客戶享有最多 3 核心 CPU, 2GB 硬碟, 12GB DRAM, 90 分鐘連線時間的運算環境, 對初學者或小型專案應該綽綽有餘. 不足可購買付費方案, 但目前不論哪個方案都尚未提供 GPU 資源 (即將提供).

首先按官網右上方的 Login 按鈕登入系統, 我採用註冊 E-mail 方式 :





輸入 email 按 Register 鈕註冊, 它會發出認證信函到信箱, 點信中的認證超連結會顯示密碼設定頁面, 設定後即完成註冊 :


點 "Log in" 超連結輸入帳密會詢問是否同意隱私權協約, 勾選同意後會出現用戶資訊填寫頁面 (必填), 完成後即登入 Dashboard 頁面 :





按 "Launch" 鈕即進入檔案系統, 在 Tutorials 目錄下有教學文件可參考 :




按右上角的 "New" 按鈕即開啟 Jupyter 介面, 可開始輸入 Julia 指令, 例如輸入 versioninfo() 顯示目前執行環境的版本 :




可見目前版本是 Julia 1.0.3, 跑在 2.3GHz, Linux 64 位元的機器上. 這樣只要能連上 Internet 就可以測試學習 Julia 了, 不需要在本機安裝編譯器與套件.




基本語法測試 OK.



矩陣運算測試 OK.

Julia 語言創建者獲頒 2019 年威爾金森獎

Julia 語言的三位創建者,MIT Julia 實驗室的 Jeff Bezanson, Stefan Karpinski Viral B. Shah 教授共同獲得 2019 年的 Wilkinson 獎 (每四年頒發一次),以表彰他們在科學計算領域的貢獻。

Wilkinson 獎是為了紀念 1986  年辭世的英國數學家 James H. Wilkinson 在數值分析領域的卓越成就而設立。James H. Wilkinson 在二戰時期曾協助圖靈設計計算機,是1970 年馮紐曼獎與圖靈獎得獎人,曾任英國皇家學會院士,獲頒劍橋名譽博士,並曾于史丹佛擔任客座教授。參考 :

Julia language co-creators win James H. Wilkinson Prize for Numerical Software

2019年2月26日 星期二

月光圓舞曲

昨天休息時間聽到廣播室放了一首好聽的鋼琴曲, 馬上撥電話給一樓同事查詢曲名, 原來是 "月光圓舞曲", 是韓國音樂作曲家 Kim Yoon 同名專輯的第三首曲子, 參考 :

# 台灣代理風潮音樂 : 月光圓舞曲

Youtube 也可聽 :

https://www.youtube.com/watch?v=d-FUKsjcYNk




真的百聽不厭.

2019 年第 8 周記事

過去的一周我大部分閒暇時間放在 Django 測試, 其實我是很想把 Vue 搞定 directives 之後再鬆手, 但急著想在樹莓派上架設網站做 Fintech 實驗, 所以 Vue 就先放一邊. 打鐵應趁熱絕對沒錯, 見異思遷實在是很糟糕的特質.

上週為了報名國防大學請假帶二哥去體檢與智力測驗, 昨天 2/25 早上學測成績揭曉, 二哥英文與國文都比預期低了一個級分, 讓他似乎很緊張, 我載他去學校後直接上班, 到辦公室傳簡訊給他說, 別灰心, 沒甚麼大不了, 他回 "謝謝", 哈.

昨天他跟老師借了甄選百科回來查閱, 參考導師意見, 中字輩電機應該不行, 東華電機, 資工, 高雄大學資管比較穩一點, 海洋機械, 高雄大學電機要賭一下. 我是覺得東華也不錯, 不必非成大, 中字輩不可啦! 自己的未來是靠自己, 即使是台大畢業也不能保證甚麼. 一步一腳印老實學習才是硬道理.

上週看完熱播的韓劇 "SKY Castle" 感觸良多, 南韓菁英家庭為了孩子能上全國頂尖的三個大學 : 首爾 (Seoul), 高麗 (Korea), 以及延世 (Yonsei) 卯足全力, 一擲千金甚至不擇手段的離譜行徑, 與一樣士大夫觀念牢不可破的我們相比有過之而無不及. 我覺得南韓還比我們更重視學歷與出身. 劇中車教授一家最好玩了, 尹世雅飾演的車太太對先生的教育方式很不認同, 先是忍無可忍把先生驅逐出去, 最後乾脆帶著兒女住到秘密基地去, 威脅老公離婚. 女兒車世麗為了怕父親責罵假裝自己在哈佛學習得意, 被阿姨的一通電話戳破後車教授無法接受, 又向姊弟三人鼓吹他努力爬向金字塔頂端的意志, 車基俊說, 爸爸你自己都爬不上去, 為什麼要求我們爬上去?

難道考上明星大學, 一切問題都解決了嗎?

2019年2月24日 星期日

Python 學習筆記索引

由於學習 Python 過程中所整理記錄的筆記越來越多, 要在後續文章中列出來實在太占篇幅, 因此把它們做成索引如下, 引用時只要一筆即可, 搜尋起來也方便 :

Python 學習筆記 : 安裝執行環境與 IDLE 基本操作
Python 學習筆記 : 檔案處理
Python 學習筆記 : 日誌 (logging) 模組測試
Python 學習筆記 : 資料庫存取測試 (一) SQLite
Python 學習筆記 : 資料庫存取測試 (二) MySQL
Python Fintech 學習筆記 : 安裝技術指標套件 TA-Lib
免安裝的 Portable Python 執行環境
Python 學習筆記 : Selenium 模組瀏覽器自動化測試 (一)
Python 學習筆記 : Selenium 模組瀏覽器自動化測試 (二)
好用的 Python 編輯器 Thonny
Python 學習筆記 : 網頁擷取 (一) 使用 urllib 與 HTMLParser
Python 內建 GUI 模組 tkinter 測試 (二) : 對話框
Python 內建 GUI 模組 tkinter 測試 (三) : 版面管理員
Python 學習筆記 : 以 Gmail 寄送郵件的方法 (一)
Python 學習筆記 : 以 Gmail 寄送郵件的方法 (二)
Python 學習筆記 : 以 Hinet 寄送郵件的方法
# Python Fintech 學習筆記 : Google Finance 無法下載歷史資料
Python Fintech 學習筆記 : 從 Yahoo Finance 擷取台股成交資料
Python Fintech 學習筆記 : 用 twder 模組擷取台銀外匯報價
Python 學習筆記 : PortablePython 安裝模組的方法
# Python 學習筆記 : SSH 模組 paramiko 測試
Python 學習筆記 : pexpect 模組測試
Python 學習筆記 : telnetlib 模組測試

2019年2月23日 星期六

Python 學習筆記 : Django 2 測試 (一) : 請求與回應處理

Django 是以 Python 語言所設計, 用來架設中大型網站的框架, 源自 2003 年於美國堪薩斯州 Lawrence 新聞集團旗下報紙 Journal-World 服務的 Adrian Holovaty 與 Simon Willison, 用來管理該集團的新聞網站. 兩位開發者於 2005 年將 Django 以 BSD 授權開放原始碼, 並以比利時吉他手 Django Reinhardt 之名命名, 並於 2008 年成立 Django 軟體基金會來管理開發工作, 目前已有至少 1000 名貢獻者, 同時擁有超過 3000 個專為 Django 設計的套件.

Django 創建的目的是讓資料庫驅動的網站開發變簡單, 它不僅可與傳統關聯式資料庫一起運作, 也能與 NoSQL 資料庫如 MongoDB 等一起運作. Django 採用了一套設計原則讓開發者能快速打造出一個資料庫驅動的網站系統, 可說是最有生產力的架站框架. 其主要精神是 :
  1. 元件可重用 (DRY 原則-Don't Repeat Yourself )
  2. 堅持明確性 (Being explicit)
  3. 弱耦合結構 (Loosely coupled structure)
  4. 敏捷開發 (Agile)
明確性是指 Django 中不論處理請求或將運算結果回應給模版都使用明確的模式, 這樣對於學習者或維護者而言成本都最低; 反觀採用隱含模式 (implicit) 的 Ruby, 雖然程式碼看起來簡短, 但對整個架構還不十分了解的初學者來說學習曲線較陡; 而且很多系統互動都是在背後自動進行, 使得追蹤錯誤較為困難, 雖然隱含模式開發速度更快, 但可能耗費更多時間在維護上.

Django 的 MTV 架構是一種弱耦合架構, 各部件之間沒有很強的相依性, HTML 模版, 商業邏輯方法, 以及資料庫模型是分拆開來的部件.

參考 :

https://zh.wikipedia.org/wiki/Django

Django 使用 MTV (相對於 MVC) 架構將視圖 (Template) 與資料庫模型 (Model) 以及控制邏輯分割開來, 因此前端網頁設計師與後端程式設計師便可真正分工. 其次, 通訊協定細節被封裝起來, 資料庫也透過模型化與抽象化使存取更方便. 總之, 只要熟悉 django 結構便能快速建構一個專業的網站.

MVC 是一種軟體架構的理念, 並非一種技術, 其架構源自挪威奧斯陸大學教授 Trygve Reenskaug 於 1979 年在全錄 Xerox 實驗室做訪問研究時為 GUI 軟體設計所提出的設計模式, 最初用在程式語言 Smalltalk 的軟體架構上以實現動態的程式設計, 目的是可簡化軟體未來之維護與擴充以及重複利用程式碼並使專案開發更有效率. 參考 :

https://zh.wikipedia.org/wiki/MVC
什麼是MVC?What's Model-View-Controller?

在網頁設計中, MVC 架構如下圖所示 :


視圖負責產出使用者介面, 模型負責資料庫介接, 而控制器負責實作商業邏輯. 在網站系統中, 視圖用來生成網頁代碼以便讓瀏覽器渲染成像. Django 的設計基本上遵循 MVC 的功能分工精神, 不過, 在 Django 中 MVC 被稱為 MTV 架構, 兩者的對應如下 :


 MVC 架構 MTV 架構 說明
 M=Model (模型) M=Model (模型) 負責資料存取
 V=View (視圖) T=Template (模版)  負責資料呈現
 C=Controller (控制器) V=View (視圖) 負責訊息處理


這裡容易讓人搞混的是, Django 的 View (視圖) 是控制器功能 (具體而言即 views.py 的函數), 而資料呈現是由模版負責 (具體而言即 template 資料夾下的 HTML 檔案), 模型部分則意義相同. 總之, MVC 與 MTV 裡面的 V 指的是完全不同的功能. MTV 架構如下圖所示 :




Model 指的是抽象化的資料庫模型, 將資料庫操作抽象化的好處是不需要直接去操作各種資料庫, 而是以統一的方式操作模型. Template 是呈現給使用者的視覺畫面, 具體而言基本上就是 HTML 與 CSS 組成, 尚待控制器填空的 html 檔, 這部分被抽離出來有利於前端網頁設計師獨立作業. View 負責控制與整合, 即操作模型, 取出資料經運算後填入 Template 後回應給客戶端. 注意, 視圖也是可以不經過模版 Template 直接透過伺服器輸出網頁, 但這樣比較麻煩且變化性不大.

本篇測試參考了下面幾本書中的範例加以改寫 :
  1. It's django - 用 Python 迅速打造 Web 應用 (楊孟穎, 袁克倫, 碁峰)
  2. Python 新手使用 django 架站的 16 堂課 (何敏煌, 博碩)
  3. Python 新手使用 django 架站技術實作 (何敏煌, 林亮昀, 博碩)
  4. Python 網頁程式交易 App 實作 (林萍珍, 博碩)
  5. Beginning Django- (Daniel Rubio, Apress)
  6. 一次搞定所有 Python Web 框架開發百科全書 (佳魁, 劉長龍) 
  7. 科班出身的MVC網頁開發 : 使用Python + Django (佳魁, 王友釗)
以上書籍除了 3 外都是針對 1.x 版 (3 是 2 的第二版, 納入 django 2.0), Django 2 的中文書籍較少, 英文書可參考下列網站 :

Best Django Books (2019)

另外下列網站也是學習 Django 2.x 的好教材 :

Django 2 By Example 全书翻译、踩坑及教程 (電子書)
iT 幫鐵人賽 : From Django 1.11 to Django 2.1 系列

本篇則使用最新版的 Django 2 進行測試, Django 2.x 版不再支援 Python 2, 最低必須是 Python 3.4+ 版, 安裝前要先用 python --version 指令查詢電腦內所安裝的 Python 版本 :

C:\Users\User>python --version 
Python 3.7.2 

Django 與所支援與搭配的 Python 版本如下表 :


 Django 版本 搭配之 Python 版本
 1.11 2.7, 3.4, 3.5, 3.6
 2.0 3.4, 3.5, 3.6
 2.1, 2.2 3.5, 3.6, 3.7


Django 2 與 1.x 版有許多用法差異, 有部分用法不相容, 若在 2.x 版上沿用舊法可能導致錯誤, 參考 :

What is the difference between Django 1.11 and Django 2?

其中最主要的差異是 urls.py 中指派路由的寫法, 在 Django 1.x 使用正規表達式來解析網址; 而 Django 2.0 以後改用路由字串來解析, 但仍保留正規表達式用法, 但解析 URL 的函數已經改為 re_path() 而非 url() 了.

Django 使用 URLconf 模組負責解析網址, 它會在路由程式  urls.py 中的  urlpatterns 串列變數裡搜尋網址所對應之 views.py 處理函數. 當我們以 django-admin startproject mysite 建立一個 mysite 專案後, 在第二層專案目錄下的設定檔 settings.py 中就會被設定為 mysites.urls, 表示路由指派程式預設為 urls.py :

ROOT_URLCONF = 'mysite.urls'

在 Django 1.x 版的 urls.py 中需從 django.conf.urls 模組匯入 url() 函數, 然後利用呼叫 url() 並且使用正規表示法來規範 URL 的格式來建立 urlpatterns 串列, 例如 :

from django.conf.urls import url
from mysite imort views
urlpatterns = [
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), ]

但在 2.x 版則改為呼叫新增的 django.urls.path() 方法且使用比較簡化的 URL 描述方式, 因此要從 django.urls 模組匯入 path() 函數 :

from django.urls import path
from mysite imort views
urlpatterns = [
path('articles/<int:year>/', views.year_archive), ]

Django 2.x 版的網址解析寫法使用尖括號來取得 URL 中的參數, 這種用法比較寬鬆, 且包含了資料類型自動轉換, 不需要再呼叫 int() 或 str() 等函數 (如果沒有指定類型預設為字串), 比較直觀易用.

Django 2.x 保留了正規表達式路由解析法, 但它是另外提供 django.urls.re_path() 函數來支援傳統的正規表達式用法, 參考 :

URL dispatcher
django2笔记:路由path语法

路由解析程式 urls.py 可同時使用新舊版解析方式, 這時要同時匯入 re_path() 與 path() 兩個函數 :

from django.urls import path
from django.urls import re_path
from mysite imort views

urlpatterns = [
  re_path(r'^$', views.home),
  path('articles/<int:year>/', views.year_archive),
  ]

注意, 使用正規表達式解析首頁 (即 "/") 時要用 r'^$' 而非 r'^/$', 在開頭 ^ 與結尾 $ 符號中間多一個斜線 / 反而會出現錯誤.

另外一個語法上的改變是使用者認證函數 user.is_authenticated() 與 user.is_anonomous() 改為 user.is_authenticated 與 user.is_anonomous 屬性, 參考 :

https://www.youtube.com/watch?v=rMJDPFox-XI

Django 2 官方教學文件參考 :

https://docs.djangoproject.com/en/2.1/

Python 學習筆記索引參考 :

Python 學習筆記索引


以下從建立一個預設專案網站, 其次簡單輸出 Hello World, 最後使用 templates 生成網頁逐步測試 Django 的基本用法 :


一. 建立一個預設專案網站 : 

1. 安裝 django 套件 :

使用 Django 架站首先用 pip 安裝 django 套件, 直接用 pip install django 會安裝最新版, 我安裝時最新是 2.1.7 版 :

D:\Python\test>pip3 install django
Collecting django
  Downloading https://files.pythonhosted.org/packages/c7/87/fbd666c4f87591ae25b7bb374298e8629816e87193c4099d3608ef11fab9/Django-2.1.7-py3-none-any.whl (7.3MB)
Requirement already satisfied: pytz in c:\python37\lib\site-packages (from django) (2018.9)
Installing collected packages: django
Successfully installed django-2.1.7

可見 django 有一個相依模組 pytz.

也可以指定 Django 版本, 指令如下 :

D:\Python\test>pip3 install django==2.1.7 

如果已經安裝過 Django 想要更新為最新版, 就在指令後面加上 -U 參數 :

pip3 install django -U

在樹莓派上安裝結果如下 :

pi@raspberrypi:~ $ pip3 install django
Collecting django
  Downloading https://files.pythonhosted.org/packages/c7/87/fbd666c4f87591ae25b7bb374298e8629816e87193c4099d3608ef11fab9/Django-2.1.7-py3-none-any.whl (7.3MB)
Collecting pytz (from django)
  Downloading https://files.pythonhosted.org/packages/61/28/1d3920e4d1d50b19bc5d24398a7cd85cc7b9a75a490570d5a30c57622d34/pytz-2018.9-py2.py3-none-any.whl (510kB)
Installing collected packages: pytz, django
Successfully installed django-2.1.7 pytz-2018.9

安裝好可用下列指令檢視 Djangle 版本 :

C:\Users\User>python -m django --version 
2.1.7 

或在 IDLE 中查詢 :

>>> import django
>>> django.VERSION
(2, 1, 7, 'final', 0)

參考 :

The Complete Guide To Install Django And Configure It (Linux & Windows)


2. 使用 django-admin startproject 建立網頁專案 : 

指令 : django-admin startproject 專案名稱

以建立網頁專案 mysite 為例 :

D:\Python\test>django-admin startproject mysite

此指令執行後會在目前資料夾下建立一個 mysite 資料夾 (上層專案目錄=網站根目錄), 其下有一個 manage.py 與另一個 mysite 資料夾 (下層專案目錄=全站設定), 結構如下 :


mysite (上層專案目錄)
     |____ manage.py
     |____ mysite  (下層案目錄)
                   |_____ __init__.py
                   |_____ settings.py
                   |_____ urls.py
                   |_____ wsgi.py


這些自動建立的網頁檔案只是一個基本架構, 預設是用來呈現一個 demo 網頁, 實際開發專案是在此架構基礎上進行增修以建構較複雜之網頁, 這些手動作業主要包括 :
  1. 在下層專案目錄下添加一個 views.py 來處理視圖之呈現
  2. 修改 urls.py 為 URL 指派 views.py 中對應之處理函數
  3. 在上層專案目錄下添加一個 templates 目錄來存放網頁模版
  4. 在上層專案目錄下添加一個 static 目錄來存放靜態檔案 (css/js/images)
最後之檔案結構如下所示 :


mysite (上層專案目錄)
     |____ manage.py
     |____ templates (模版目錄)
     |____ static (靜態檔案)
     |             |____ css          (存放 CSS 樣式檔)
     |             |____ js            (存放 Javascript 檔)
     |             |____ images   (存放圖檔)
     |____ mysite  (下層專案目錄)
                   |____ __init__.py
                   |____ settings.py
                   |____ urls.py
                   |____ wsgi.py
                   |____ views.py


注意, urls.py 與 views.py 並非強制的, 可以改用其他檔名, 但要做好配套. 如果不用 urls.py 要改用例如 routers.py, 則必須修改 settings.py, 將設定項目 ROOT_URLCONF 從 'mysite.urls' 改為 'mysite.router'; 若不用 views.py 要改用例如 renders.py, 則在路由設定程式 (預設 urls.py) 裡面就要匯入 renders.py 而非 views.py. 良心的建議是 : 用預設的就好, 別自找麻煩.

首先來測試這個預設之 demo 網頁.


3. 啟動 django 伺服器 :

先用 cd mysite 指令切換至上層專案目錄 (因為那裏才有 manage.py 程式), 再執行 runserver 啟動內建之開發伺服器, 注意, 此伺服器預設開啟 8000 埠 :

D:\Python\test>cd mysite 

D:\Python\test\mysite>python manage.py runserver 
Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
February 23, 2019 - 23:16:49
Django version 2.1.7, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000

可見此網頁伺服器預設監聽 8080 埠. 開啟瀏覽器連線 localhost:8000 或 127.0.0.1:8000 即可看到此預設網頁:




Bingo! 第一個 django 網站完成了!

上面這個 127.0.0.1 只能用在本機而已, 如果要讓區網內其他主機也可存取網頁, 則必須在啟動 Django 內建的開發伺服器時指定本機在區網中的 IP 位址, 也可以指定埠號, 不一定要用預設之 8000 埠, 例如 :

python manage.py runserver 192.168.2.105 8080 

首先用 ipconfig 查詢本機 IP, 得到例如 192.168.2.105, 然後修改專案主目錄 (此處為第二層 mysite) 下的 settings.py, 將此 IP 以字串格式放入 ALLOWED_HOST 這個串列中, 例如 :

ALLOWED_HOSTS = ['192.168.2.105'] 

這樣就可以啟動伺服器了 :

D:\Python\test\mysite>python manage.py runserver 192.168.2.105:8080 
Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
February 28, 2019 - 16:18:37
Django version 2.1.7, using settings 'mysite.settings'
Starting development server at http://192.168.2.105:8080/
Quit the server with CTRL-BREAK.

這樣在瀏覽器網址列就要輸入 192.168.2.105:8080 才會連線到此伺服器了.


二. Django 專案的結構 :

接下來進一步研究上面 django 所建立的檔案與目錄結構 :


mysite  (網站根目錄)
     |____ manage.py
     |____ mysite  (全站設定)


Django 的基本檔案架構由兩層專案目錄組成, 上層專案目錄是網站根目錄, 預設包含一個管理程式 manage.py 與下層專案目錄, 以及未來將來要建立的 app 目錄. 下層專案目錄則用來存放全站設定, 預設包括網站的設定檔 settings.py 與路由配置檔 urls.py.

上層專案目錄下的 manage.py 是這個專案的管理程式, 用途為管理網站組態與接收處理命令列指令, 明確地說功能如下 :
  1. 啟動 Django 的簡易伺服器或 Shell
  2. 建置應用程式 (App)
例如上面就用了 python manage.py runserver 啟動伺服器, 而建置應用程式則使用 python manage.py startapp app_name. 此程式主要是執行用, 不需要修改.

管理程式 manage.py 的內容如下 :

#!/usr/bin/env python
import os
import sys

if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

可見它主要是從 django 套件的核心引入 execute_from_command_line 模組.

專案內容主要是放在下層專案目錄 mysite 資料夾下, 預設會建立四個檔案, 其用途如下 :



 下層專案目錄預設檔案 說明
 __init.py__ 空檔案, 使下層專案目錄成為一個套件 (package)
 settings.py 專案的設定檔 (網站功能設定)
 ursl.py URL 路由配置檔 (網址與處理函數的對應)
 wsgi.py 網頁伺服器與 Django 之介面設定黨, Django 的入口


其中 settings.py 與 urls.py 是專案的主角, __init.py__ 是一個空檔案, 目的只是要讓下層專案目錄符合套件形式上的要求而設; wsgi.py 則只有部署到主機上時才會用到.

首先開啟路由配置檔 urls.py, 此檔案負責將伺服器收到的 URL 要求對應到控制器 views.py 中的函數進行處理, 其預設內容如下 :

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin   
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

可見預設路由指派已經有一個 URL, 即 localhost:8000/admin 了, 此網址是 Django 的資料庫後端管理頁面. 當新增一個網頁程式時需在 urlpatterns 串列變數內新增一個 URL 與對應處理函數之 tuple. 

其次開啟 settings.py, 此檔案是專案的系統設定檔, 預設內容如下 :

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '2dwazqn8dsi#9lae!c32%fv(*rk@@ygbwg0c40+d38vb(+oky@'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'mysite.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mysite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'

此設定檔內都以大寫的識別字做為設定值, 其中比較重要者如下 : 
  1. BASE_DIR : 上層專案目錄 (第一層) 的路徑, 此處為第一層 mysite.
    os.path.abspath(__file__) 會傳回 settings.py 的絕對路徑, 傳給 os.path.dirname() 後會傳回下層專案目錄, 即第二層 mysite, 再傳給 os.path.dirname() 即傳回上層專案目錄, 即第一層 mysite.
  2. DEBUG : 是否啟動偵測模式
    預設為 True, 當專案開發完成, 系統正式上線時務必改為 False 以避免資安疑慮. 
  3. ALLOWED_HOST : 指定 IP 時需以字串放在串列中
  4. INSTALLED_APPS : 已安裝之應用程式
    此即為 Django 所謂應用程式可插拔 (pluggable) 的插頭位置, 當專案新增應用程式時, 必須在此串列尾添加新 APP 名稱. 
  5. ROOT_URLCONF : 根網址設定檔
    設定網址與網頁的配對程式, 此處為 mysite 下的 urls.py. 
  6. DATABASE : 資料庫設定
    預設使用 SQLite3 資料庫 db.sqlite3. 
  7. LANGUAGE_CODE : 語系
    預設為美式英語 en-us, 台灣繁體中文語系要改為 zh-Hant, 在 Django 1.7 以前是用 zh-TW, 這在 2 版行不通. 
  8. TIME_ZONE : 時區
    預設為 UTC, 台灣時區要改為 Asia/Taipei.
以下測試 Django 的請求與回應處理, 這主要是由下層專案目錄下的 urls.py 與 views.py 這兩個程式負責, 它們屬於 MVC 架構中的控制器角色. 在 views.py 中使用 django.http.HttpResponse 類別來處理回應網頁, 而請求處理則是在 urls.py 中使用 django.urls.path 類別來解析 URL 與指配處理函數, 程序如下圖所示 :





三.  用 HttpResponse 輸出 Hello World 頁面 : 

如上所述, Django 是利用 urls.py 指配客戶端要求之 URL 至 views.py 的處理函數以產生回應之網頁, 以下以經典的輸出 Hello World 的網頁為例進行測試. 首先在下層專案目錄 (第二層 mysite) 下新增一個 views.py 如下 :

#views.py
from django.http import HttpResponse

def helloworld(request): 
    return HttpResponse('<b>Hello World! <i>您好</i></b>') 

此程式從 django.http 模組引入 HttpResponse 類別以處理 HTTP 回應, 然後定義一個自訂函數 helloworld() 來回應客戶端, 此函數有一個傳入參數 request 會從伺服器接收一個封裝了客戶端要求的 HttepRequest 物件, 雖然此例用不到還是要傳入, 因為這是必要參數. 此函數只是簡單地呼叫 HttpResponse() 物件方法傳回字串. 注意, 由於回應中含有中文, 因此須以 utf-8 格式存檔.

接著編輯下層專案目錄下的 urls.py, 先從 mysite.views 模組匯入 helloworld 函數, 然後在 urlpatterns 串列中, 於預設的 admin 底下增加一個 path() 函數呼叫, 將要求之 URL 字串 'hello/' 以及所對應之 helloworld 函數當作參數傳進去 :

#urls.py
from django.contrib import admin
from django.urls import path
from mysite.views import helloworld 

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', helloworld)
]

路由器 urls.py 也可以用下列寫法 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite import views
#或 from . imports views 亦可

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', views.helloworld),
]

差異主要是因為 import 的方式不同, 如果是匯入 views 模組, 則在 path() 內配對的目的地就必須冠上views 模組名稱.

這樣就完成網站更新, Django 開發伺服器會偵測到組態更新, 不須重開伺服器, 於瀏覽器網址列輸入 localhost:8000/hello 就會顯示如下網頁 :



Bingo! 以上便是 Django 輸出簡單網頁的做法.

上面的例子是將欲輸出的網頁直接傳給 HttpResonse(), 如果是較長的網頁內容 views.py 裡面應改用三個引號的長字串變數較易讀, 例如將上面的 views.py 改為如下效果一樣 (路由器 urls.py 不用改) :

#views.py
from django.http import HttpResponse

def helloworld(request):
    html='''
<!DOCTYPE html>
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <b>Hello World! <i>您好</i></b>
  </body>
</html>
    '''
    return HttpResponse(html)

有了 /hello 這個 URL 後雖然輸入 localhost:8000/hello 會顯示正確的網頁輸出, 但若請求根目錄卻顯示 404 page not found :




這是因為在 urls.py 與對應之 views.py 裡面沒有處理根目錄請求之故. 首先在 views.py 中添加名為 home 的函數來輸出根目錄要求之輸出 :

#views.py
from django.http import HttpResponse

def helloworld(request):
    return HttpResponse('<b>Hello World! <i>您好</i></b>')

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>') 

函數 home() 只是簡單地輸出一個字串而已. 然後修改 urls.py 如下 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', helloworld),
    path('', home),
]

在 urlpatterns 串列中添加空字串 (表示根目錄) 對應到 views.py 裡面的 home() 函數, 這樣在網址列輸入 localhost:8000 就會顯示 '歡迎來到我的首頁!' 了.

上面的範例處理了路由問題, 但能否傳遞參數呢? 可以的, Django 依循 RESTful 方式傳遞參數, 亦即參數放在 URL 的後面, 格式如下 :

route/param

例如向 Tony 打招呼的 URL 是 /hello/Tony.

在上面已提及, 在 Django 2 的路由器 urls.py 程式中, 從伺服器接收參數的方式已改為使用尖括號並同時完成資料型態轉換, 格式如下 :

path('route/<type:param>/', function_name),

將上面的 urls.py 改為如下 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/<str:name>/', helloworld),
    path('', home),
]

此處尖括號內的 str:name 表示以字串格式接收 URL 中的參數, 名稱為 name. 注意, 若無指定資料型態, 預設為字串, 參數的型態格式有下列 5 種, 稱為路徑轉換器 (path converter) :


 參數型態 說明
 int 0 或正整數
 str 非空字串 (預設), 不含路徑分隔字元 "/"
 slug 以 ASCII 字元組成之字串 (含 dash 與 underscore)
 uuid 通用唯一標示碼 uuid 字串 (字母皆小寫)
 path 非空字串 (含路徑分隔字元 "/"), 可表示完整的 URL


參考 :

https://docs.djangoproject.com/en/3.0/topics/http/urls/

因為參數型態預設為 str, 所以上面範例 urls.py 中的 :str 也可以不寫. 而在 views.py 中, 參數 name 則放在第一參數 request 後面, 例如 :

#views.py
from django.http import HttpResponse

def helloworld(request, name):
    return HttpResponse('<b>Hello World! </b>' + name)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

此處將接收到的第二參數 name 串在 HTML 碼後面回應給客戶端, 在瀏覽器網址列上輸入 localhost:8000/hello/Tony 結果如下 :



可見參數 name=Tony 已被成功地透過 URL 傳遞給伺服器並回應給客戶端. 如果要傳遞多個參數, 則這些參數就在 URL 中串接在路由的後面, 格式為 :

route/param1/param2/....

在 urls.py 的 path() 呼叫中, 依序用倒斜線串接的尖括號來接收這些參數 (可指定轉換成所要之資料型態), 例如 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/<name>/<message>/', helloworld),
    path('', home),
]

而 views.py 中所對應的處理函數要依序接收所傳進來的參數, 例如 :

#views.py
from django.http import HttpResponse

def helloworld(request, name, message):
    return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

這時在網址列輸入 localhost:8000/tony/How are you 會看到如下網頁 :




視圖處理函數 views.py 中的傳入參數也可以有預設值, 當請求的 URL 中沒有傳參數過來時就會以預設值頂替, 範例如下 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/<name>/', helloworld), 
    path('hello/<name>/<message>/', helloworld),
    path('', home),
]

此處 urls.py 需添加 'hello/<name>/' 這一筆, 否則會找不到 URL 而報錯, 並不會因為有 'hello/<name>/<message>/' 這筆而找到路由 (因為不是用正規式). 相對應的 views.py 中 helloworld() 的 message 要設定預設值如下 :

#views.py
from django.http import HttpResponse

def helloworld(request, name, message='Blablabla ...'):
    return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

注意, Python 函數的傳入參數若有預設值必須全部靠右放置 (即通通放在最後面), 否則執行時會報錯. 在瀏覽器網址列輸入 localhost:8000/tony/ 沒有傳入 message 參數便會以其預設值 "blablabla" 代替 :




以上便是 Django 對 HTTP 請求與回應之基本處理方式.


四. HttpRequest 物件 : 

Django 的請求處理流程是從 settings.py 開始. 當啟動 Django 內建的開發伺服器時, 它會讀取專案目錄下的 settings.py 以解析網站的設定資訊, 其中最重要的是 ROOT_URLCONF, 它指到一個根路由設定檔, 預設為 urls.py, 此檔案告訴 Django 在此網站中會用到哪些 Python 模組以及 URL 對應到 views.py 裡哪些函數.

當 Django 接到頁面請求時, 它會將 HTTP 請求之 metadata 打包成 HttpRequest 物件, 然後在 urls.py 中依序搜尋 URL 樣式, 當找到第一個符合的 pattern 時以 HttpRequest 物件為第一參數呼叫 views.py 中對應的函數, 此函數處理完需傳回一個 HttpResponse 物件, Django 會將其轉成 HTTP 回應給用戶端.

HttpRequest 物件的常用屬性如下 :


 HttpRequest 物件屬性 說明
 method 請求的方法 (字串), 例如 "GET", "POST"
 GET GET 方法所帶之參數 (字典)
 POST POST 方法所帶之參數 (字典)
 user 使用者物件, 可用 is_authenticated() 方法判斷是否登入
 session 連線物件 (字典)
 COOKIES Cookies 物件 (字典)
 META 請求之標頭資訊 (字典), 例如 HTTP_REFERER 等


其中 method 可用來判斷不同請求方法的處理方式, 例如 :

if request.method=="GET":
   ....
elif request.method=="POST"

屬性 user 可用來判斷使用者是否已登入 :

if request.user.is_authenticated():
   ...
else:
   ...

詳細參考 :

https://docs.djangoproject.com/en/2.1/ref/request-response/

例如在 views.py 增加一個 request_test() 的視圖函數 :

#views.py
from django.http import HttpResponse

def helloworld(request, name, message='Blablabla ...'):
    return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

def request_test(request):
    try:
        method=request.method
        http_host=request.META['HTTP_HOST']
        http_user_agent=request.META['HTTP_USER_AGENT']
        remote_addr=request.META['REMOTE_ADDR']
        return HttpResponse('[method]:%s<br>[http_host]:%s<br>\
                            [http_user_agent]:%s<br>[remote_addr]:%s'\
                            %(method, http_host, http_user_agent, remote_addr))
    except e:
        return HttpResponse('Error:%s' &e)

然後在 urls.py 裡面添加一個 request_test 路徑對應 :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld, request_test

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/<name>/', helloworld),
    path('hello/<name>/<message>/', helloworld),
    path('', home),
    path('request_test/', request_test),
]

以瀏覽器拜訪 localhost:8000/request_test/ 結果如下 :




五. HttpResponse 物件 : 

HttpRequest 物件是 Django 自動建立的, 但 HttpResponse 物件則需自行呼叫 HttpResponse() 建構子或 render(), render_to_response() 函數, 並傳入一個表示回應頁面內容的 HTML 字串來建立. 每一個 views.py 內的函數都必須傳回一個 HttpResponse 物件以便讓客戶端能看到回應結果.

HttpResponse 物件也可以用檔案串流方式操作, 先建立 HttpResponse 物件, 然後再分批呼叫其 write() 方法寫入串流後再傳回用戶端. 這種方法對設定回應標題很方便.

上面範例中 views.py 裡的 request_test() 也可以這麼寫 :

#views.py
from django.http import HttpResponse

def helloworld(request, name, message='Blablabla ...'):
    return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

def request_test(request):
    response=HttpResponse() 
    try:
        method=request.method
        http_host=request.META['HTTP_HOST']
        http_user_agent=request.META['HTTP_USER_AGENT']
        remote_addr=request.META['REMOTE_ADDR']
        response.write('[method]:%s<br>' % (method))
        response.write('[http_host]:%s<br>' % (http_host))
        response.write('[http_user_agent]:%s<br>' % (http_user_agent))
        response.write('[remote_addr]:%s' % (remote_addr))
        response['Cache-Control']='no-cache'      #設定回應標題
        return response
    except e:
        return response.write('Error:%s' % e)

此例設定回應標題 Cache-Control 為 no-cache, 這可以在 Chrome 按 F12 進入開發者模式, 在 Network/Headers 頁籤內看到 :




HttpResponse 類別也定義了一些好用的子類別, 方便處理重導向或回應 404, 500 等錯誤, 常用 HttpResponse 子類別如下表 :


 HttpResponse 子類別 說明
 HttpResponsePermanentRedirect 永久重導向至指定 URL (狀態碼 301)
 HttpResponseRedirect 重導向至指定 URL (狀態碼 302)
 HttpResponseBadRequest 請求錯誤 (狀態碼 400)
 HttpResponseForbidden 頁面不存在 (狀態碼 403)
 HttpResponseNotFound 頁面禁止  (狀態碼 404)
 HttpResponseNotAllowed 請求不允許 (狀態碼 405)
 HttpResponseServerError  (狀態碼 500)


例如下列重導向範例是在上例的 views.oy 中添加一個 redirect() 函數, 直接傳回 HttpResponseRedirect 物件重導向首頁 :

#views.py
from django.http import HttpResponse
from django.http import HttpResponseRedirect

def helloworld(request, name, message='Blablabla ...'):
    return HttpResponse('<b>Hello World! </b>' + name + ': ' + message)

def home(request):
    return HttpResponse('<b>歡迎來到我的首頁!</b>')

def request_test(request):
    response=HttpResponse()
    try:
        method=request.method
        http_host=request.META['HTTP_HOST']
        http_user_agent=request.META['HTTP_USER_AGENT']
        remote_addr=request.META['REMOTE_ADDR']
        response.write('[method]:%s<br>' % (method))
        response.write('[http_host]:%s<br>' % (http_host))
        response.write('[http_user_agent]:%s<br>' % (http_user_agent))
        response.write('[remote_addr]:%s' % (remote_addr))
        response['Cache-Control']='no-cache'
        return response
    except e:
        return response.write('Error:%s' % e)

def redirect(request):   
    return HttpResponseRedirect("/") 

然後在 urls.py 中匯入此 redirect() 函數並對應到 "redirect/" :

#urls.py
from django.contrib import admin
from django.urls import path

from mysite.views import home, helloworld, request_test
from mysite.views import redirect 

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/<name>/', helloworld),
    path('hello/<name>/<message>/', helloworld),
    path('', home),
    path('request_test/', request_test),
    path('redirect/', redirect)
]

在網址列輸入 localhost:8000/redirect 果真重導向至首頁.


2020-01-20 補充 :

Django 在 2020 年已推出第三版, 在設定 (例如語言) 上與第二版有些差異, 第二版最後一個版本是 2.2.9 版, 參考 :




What Python version can I use with Django?

如何刪除樹莓派上已安裝之軟體

昨天在 Pi 3 上安裝 Teamviewer Host 結果被判定是商業用途無法使用而變成垃圾 (其實我也希望商業用途早日到來好嗎), 但是要怎麼移除這軟體呢? Raspbian 不像 Windows 那樣在程式集中可以解除安裝, 而是要用 CLI 下命令, 參考 :

How to Remove / Uninstall Software in Raspberry Pi




首先開啟 LX 終端機, 下 dpkg --list 指令顯示目前已安裝的軟體 :

pi@raspberrypi:~ $ dpkg --list
.....
ii  tasksel        3.39         all          tool for selecting tasks for inst
ii  tasksel-data   3.39         all          official tasks used for installat
ii  tcpd           7.6.q-26     armhf        Wietse Venema's TCP wrapper utili
ii  teamviewer-host 14.1.9025    armhf        Remote control solution.
ii  timidity       2.13.2-40.5  armhf        Software sound renderer (MIDI seq
ii  tk8.6-blt2.5   2.5.3+dfsg-3 armhf        graphics extension library for Tc
ii  traceroute     1:2.1.0-2    armhf        Traces the route taken by packets
ii  tree           1.7.0-5      armhf        displays an indented directory tr
.....

找到 Teamviewer 的軟體名稱為 teamviewer-host, 解除安裝指令為帶 --purge 參數的 apt-get 指令 :

pi@raspberrypi:~ $ sudo apt-get --purge remove teamviewer-host   
正在讀取套件清單... 完成
正在重建相依關係          
正在讀取狀態資料... 完成
The following packages were automatically installed and are no longer required:
  libqt5qml5 libqt5quick5 libqt5webkit5 qml-module-qtgraphicaleffects qml-module-qtquick-controls qml-module-qtquick-dialogs
  qml-module-qtquick-layouts qml-module-qtquick-privatewidgets qml-module-qtquick-window2 qml-module-qtquick2
Use 'sudo apt autoremove' to remove them.
下列套件將會被【移除】:
  teamviewer-host*
升級 0 個,新安裝 0 個,移除 1 個,有 0 個未被升級。
此操作完成之後,會空出 47.3 MB 的磁碟空間。
是否繼續進行 [Y/n]? [Y/n] Y
(讀取資料庫 ... 目前共安裝了 133614 個檔案和目錄。)
Removing teamviewer-host (14.1.9025) ...
Removed /etc/systemd/system/multi-user.target.wants/teamviewerd.service.
Clearing assignments ...
done
Processing triggers for mime-support (3.60) ...
Processing triggers for desktop-file-utils (0.23-1) ...
Processing triggers for gnome-menus (3.13.3-9) ...
Processing triggers for hicolor-icon-theme (0.15-1) ...
(讀取資料庫 ... 目前共安裝了 133506 個檔案和目錄。)
Purging configuration files for teamviewer-host (14.1.9025) ...

最後可能還有相依模組, 要下 autoremove 指令清掉 : 

pi@raspberrypi:~ $ sudo apt autoremove   

這樣在程式集裡面就找不到任何與 Teamviewer 相關的檔案了.

樹莓派遠端連線 VNC Cloud Connect 設定

由於被 Teamviewer 認為是用在商業用途而無法免費使用 Teamviewer 後, 我想起之前有注意到, 樹莓派 Raspbian 內建的 VNC Server 有個 VNC connect 功能跟 Teamviewer 的免 IP 遠端連線類似, 測試結果真的可用, 以下紀錄設定程序 :


1. 註冊 VNC Cloud 帳號 :

連線 VNC Cloud 網站按右上角的 Sign in 進入 Sign in or Sign up 頁面, 輸入有效 email 並勾選 "我不是機器人" 按 "Next" 鈕進入 "Create an account", 輸入密碼 (至少 8 個字元), 勾選 "Personal use" 與底下已閱讀聲明之選項, 選擇台灣區域後按 "Sign up" 鈕即可 :




這時 VNC 會傳送認證信到所輸入的 Email 信箱, 收信並按下確認超連結即完成帳號申請 :




2. 樹莓派登入 VNC 帳號 :

按樹莓派的右上角的 VNC 按鈕 (或 "開始/網際網路/VNC Viewer") 執行 VNC Viewer, 按右上角人形輸入所申請的 VNC 帳號密碼按 "Sign in" 鈕登入 VNC Connect :





在 Option 欄改勾選 "Direct and cloud connectivity" 按 Next :







這樣樹莓派就完成 VNC Connect 的登入連線, 亦即讓 VNC 伺服器知道如何找到樹莓派了.


3. PC 下載 VNC Viewer 申請連線授權 :

接下來是回到 PC 到官網下載安裝 VNC Viewer :

https://www.realvnc.com/en/connect/download/viewer/

我下載的是 Standalone EXE X64, 亦即綠色免安裝版, 不過很奇怪的是檔名竟然是 VNC-Viewer-6.19.107-Windows-32bit.exe (不是 64 位元版嗎) :




執行 VNC Viewer 程式先按右邊的 Sign in 鈕, 輸入帳號密碼





這時 VNC 會發出確認信, 收信後按下信中的 "Continue sign in" 與 "Authorize sign in" 即允許此 PC 透過 VNC connect 連線其他群組內設備 :






4. 連線遠端主機 :

獲得授權後 VNC Viewer 上就會出現可連線的設備, 即樹莓派桌面 :




點按連線遠端主機, 允許 Windows 防火牆存取後輸入 VNC 帳密即可連線樹莓派桌面 :





哈! 終於解決長久以來社區網路無法連回去的問題了, 之前一直庫存的第二塊 Pi 3 也可以正式派上用場了 (物聯網 Gateway + 網路爬蟲).


2019-04-09 補充 :

今天找到一個很棒的 VNC 教學 :

# How to establish remote desktop access to the Raspberry Pi

高雄的 Pi 3 裝上 16GB SD 卡連網都沒問題, VNC 也登入了, 但用 Cloud Connect 卻出現 "VNC server is not currently listening for Cloud connection" 錯誤訊息, 不知何故?




之前 8GB SD 卡沒問題啊! 奇怪. 打算換上 32GB 新卡來試.

參考 :

How to establish remote desktop access to the Raspberry Pi

超搞笑的 Teamviewer 個人使用免費噱頭

昨晚搞定 Pi 3 區網固定 IP 設定後, 馬上安裝 Teamviewer host 以便可從外網連線回來. 由於高雄家裡使用社區網路, 沒辦法在路由器上打洞讓我連線進來, Teamviewer 似乎是可行方式, 安裝程序參考之前文章 :

樹莓派安裝 TeamViewer

但問題來了, 我用筆電的 Teamviewer 連線樹莓派的 Teamviewer Host 時卻出現如下畫面, 說這連線似乎是用在商業用途, 希望我購買 Teamviewer 商業版 :




按右下角的 "立即購買" 鈕會開啟 Teamviewer 官網價格頁面, 卡好, 最便宜的單一使用者價格是每個月 988 元 (不是一次買斷, 是月租), 超過 30 美金, 我看用搶的比較快. 



官網不是說個人使用免費嗎? 為何會被認定是商業用途呢? 官網只說它會使用各種匿名方法來偵測 :

https://www.teamviewer.com/zhtw/pricing/commercial-use/

"TeamViewer免費提供個人於非商業用途使用 ... 然而,我們會採取一些方法確認軟體未在無適當授權下,非法用於商業用途。TeamViewer包含各種匿名的偵測方法可檢查軟體是否被用於商業環境。如果您經常在那樣的環境中使用TeamViewer,您將會看到警告訊息(與您進入本頁面前顯示的警告相同)... 基本上,只要可能牽涉到任何金錢或服務就算商業用途。 大致上涵蓋所有您可能會將TeamViewer用於工作相關功能的情況。"

怪了, 它怎麼知道我這連線有涉及金錢交易? 我個人的解讀是, 它採用隨機的方式來判別, 總之就是要你付錢就對了. 商業軟體付錢購買乃天經地義, 但打著個人使用免費旗幟吸引使用者, 再技術性干擾逼迫使用者付費就有點賊了. 總之, 因為一直都無法連線, 所以決定從樹莓派中完整移除 Teamviewer Host, 掰掰!

設定樹莓派區網固定 IP

年前把手上各款樹莓派板子的 Raspbian 都升為最新版後就擱下來, 沒再進一步處理連網等設定. 為了區網內存取方便, 我都是將樹莓派的 WiFi 連網 IP 固定設為 192.168.2.192 (因我的信舟無線基地台網址是 192.168.2.1), 不使用 DHCP 指派, 這樣比較好記. 參考之前的文章 :

設定樹莓派 WiFi 無線網卡固定 IP 的方法

設定固定 IP 程序如下 :

1. 顯示原來的網路介面設定 :

pi@raspberrypi:~ $ sudo cat /etc/network/interfaces 
# interfaces(5) file used by ifup(8) and ifdown(8)

# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d 


2. 編輯 interfaces 檔 :

預設只有一行 (設定目錄). 然後用 nano 編輯一個 interfaces 檔案如下, 此檔案會存在預設工作目錄 /home/pi 底下 :

pi@raspberrypi:~ $ nano interfaces 
pi@raspberrypi:~ $ cat interfaces 
source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback
iface eth0 inet dhcp

allow-hotplug wlan0
auto wlan0
iface wlan0 inet static
address 192.168.2.192   
gateway 192.168.2.1
netmask 255.255.255.0
network 192.168.2.1
broadcast 192.168.2.255
wpa-ssid "EDIMAX-tony"
wpa-psk "blablabla"

檔案中第一行就是上面原始 /etc/network/interfaces 檔的內容, 其中 address 就是設定我們想要的區網固定 IP; gateway 與 network 同樣設為 WiFi 基地台的網址即可; 而 netmask 與 broadcast 用 ifconfig 就可查到.


3. 覆蓋預設之 /etc/network/interfaces 檔 :

將此檔案以管理員身分複製到 /etc/network/interfaces 覆蓋原來的檔案 :

pi@raspberrypi:~ $ sudo cp interfaces /etc/network/interfaces 

然後用下列指令重啟網路介面或重開機即可 :


4. 重啟網路介面或重開機 :

pi@raspberrypi:~ $ sudo /etc/init.d/networking restart 
用 ifconfig 查詢可知樹莓派區網 IP 已改為 192.168.2.192 了 :

pi@raspberrypi:~ $ ifconfig 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 11  bytes 602 (602.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11  bytes 602 (602.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.192  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::a57:ff:fe16:880c  prefixlen 64  scopeid 0x20<link>
        ether 08:57:00:16:88:0c  txqueuelen 1000  (Ethernet)
        RX packets 63  bytes 11479 (11.2 KiB)
        RX errors 0  dropped 54  overruns 0  frame 0
        TX packets 80  bytes 14188 (13.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

這樣做的好處是以後用筆電透過 WiFi 網路連線樹莓派時只要固定連線 192.168.2.192 即可, 畢竟家中連線基地台的終端設備有手機平板等, 大家從 DHCP 獲得的 IP 是動態的, 樹莓派只要重開機所獲得的 IP 每次都不一樣的話就很麻煩.


2019-03-31 補充 :

上面的作法在新版 Raspbian 可能不管用了, 參考 :

# 樹莓派 Wifi 固定 IP 的新作法

2019年2月22日 星期五

二哥智力測驗

昨天下午請兩個小時載二哥去左營國軍人才招募中心 (介壽路 20 號, 左營海軍大門再過去一點) 參加網路登記的智力測驗, 合格條件為 100 分, 雖說體檢視力能否通過還不一定, 但智力測驗登記了還是要去. 讓我很驚訝的是, 想讀居校的女生還不少耶.

2019年2月21日 星期四

大學合併的好處

昨天去母校高科大圖書館取預約書, 因已借滿校友 10 本額度, 只好找 "Python Data Science From Scratch" 這本拿去還才能取預約書, 但櫃台的學妹說不用這樣, 因為三校合併後, 校友借書額度膨脹為三倍, 也就是 30 本, 而且借書系統整合完成後, 可調借原海科大與原第一科大圖書館的書, 這真是太棒了! 合併後借書證也要換新, 所以學妹也拿我的借書證去掃描製作新證, 約下周三後可取件.

昨晚二哥跟我討論, 如果因近視無法上國防大學理工學院中科院代訓生, 那可能考慮中山大學與高雄大學, 我想高科大也可考慮.