2019年3月31日 星期日

2019 年第 13 周記事

時序來到 3 月底初春, 新的一年已過四分之一了. 春分之後太陽軌跡迅速北移, 每天休息時練八段錦就感受到, 陽光已經不會曬到我平常練功站立的位置了.

三月芒果花謝了, 逐漸成長的青芒果目前約 1~2 個雞蛋大. 週六 3/30 下午去買茄子苗時順路到資材行買芒果套袋小號的, 買兩包 100 個共 120 元, 今年結果率高的芒果樹約 3 顆, 其他都稀稀疏疏, 100 個應該夠. 一個半小時只能搞定一顆樹, 其餘下周再戰, 因買回的 10 株茄子必須在天黑前種下去. 小舅現在忙著當外公, 沒有餘力顧菜園, 未來一年我得自己來種菜.




花了三周時間整理的 jQuery UI 測試筆記, 總算大致完成了, 也必須要完成, 因為四月份應該要整理邏輯設計簡報, 4 月底又要開始授課了.

今天中午載二哥去高雄展覽館看自動化工業展, 大部分是機械產業, 例如機器手臂, 機密機械, 雷射掃描應用等. 印象比較深刻的是一家中和的廠商, 透過協力軟體業者為機具加上 AI 影像辨識, 應用在例如空拍機識別病蟲害嚴重或養分不良區域做重點噴灑農藥要肥料, 或辨識乳牛聚集區域以便集中灑水促進泌乳等, 可見 AI 正向各行業滲透.




在展場看到一家賣低周波商品的廠家, 推銷自家研發的耳穴, 鼻翼, 眼窩專用取穴工具, 另外還有特殊布料做成的手套與襪子均可用類似耳機插孔的接點連接低周波產生器, 特價 3600, 本想吃過午飯再決定是否購買, 但在總圖附近找不到停車位, 二哥說回家算了就沒回去買了.

菁菁的 OPPO R9S 壞了, 她說網拍衣服想用 iPhone 8 Plus, 上週去亞太, 遠傳, 台哥大逛一圈後, 決定亞太的 396 月租專案 (限速 21MB 但不限量 iPhone 8 Plus 20900 元), 今晚再去亞太找小菲預定手機, 結果說 1/4 開始有更優惠的 368 專案, 每個月有 3GB 不限速, 達 3GB 後才限速 21MB, 當然用新方案. 明天要先去中華將預付卡過戶給菁菁再辦攜出.

樹莓派 Wifi 固定 IP 的新作法

最近發現前陣子裝好的新版 Rasbian 竟然無法連網, 右上角的網路狀態 (電波符號) 消失, 換成兩條垂直打叉叉的棒棒 (表示網路流量為 0), 滑鼠移到上面顯示 "No wireless interfaces found".

奇怪, 上個月下載新版 Raspbian 後按照下面我這篇之前的文章更新 /etc/network/interfaces 檔, 將 wlan0 的 IP 固定為靜態的 192.168.2.192 後運作都正常, 為何一個月之後就不行了?

設定樹莓派區網固定 IP

我將原始的 interfaces 覆蓋回去, 重新開機還是不行? 經搜尋資訊找到下面這篇文章 :

RPI 3 B+ No wireless interfaces found

我試了其中時間點為 Wed Dec 26, 2018 2:26 pm 的這篇回答, 作者是將 interfaces.dpkg-old 用 mv 覆蓋 interfaces, 我則是用 cp (目的是保留 interfaces.dpkg-old, 因為用 mv 是將其直接改名為 interfaces), 重開機確實恢復藍芽與 WiFi 連線了 :

$ cd /etc/network 
$ sudo cp interfaces.dpkg-old interfaces 
$ sudo reboot   

螢幕右上方的 WiFi 電波符號果然又出現了 :




顯然之前更改 /etc/network/interfaces 設定來取得固定 IP 的作法現在失靈了, 不能改 interfaces 要改哪裡呢? 我找到下面這篇文章 :

Headless Raspberry Pi - Setup WiFi and Static IP Address

其中的 Step 6 提到了 static IP 的新作法, 原來是要修改 /etc/dhcpcd.conf 這個檔案, 先備份原始檔再用 nano 開啟 dhcpcd.conf, 其原始內容都是 remark :

$ cd /etc
$ sudo cp dhcpcd.conf dhcpcd.conf.old
$ sudo nano /etc/dhcpcd.conf

加入下面內容 :

interface wlan0 
static ip_address=192.168.2.192 
static routers=192.168.2.1 
static domain_name_servers=192.168.2.1

其中 192.168.2.192 是我想要取得的固定 IP, 而 192.168.2.1 則是我的無線基地台網址, 這兩個 IP 都要視自己的情況去修改. 按 Ctrl+O 存檔後按 Ctrl+X 跳出 nano, 然後重開機 :

$ sudo reboot

用 ifconfig 可知果然順利取得想要的固定 IP :

pi@raspberrypi:~ $ ifconfig
eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether b8:27:eb:63:2e:7b  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

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 9  bytes 524 (524.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9  bytes 524 (524.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::1775:4d8c:b01f:725  prefixlen 64  scopeid 0x20<link>
        ether b8:27:eb:36:7b:2e  txqueuelen 1000  (Ethernet)
        RX packets 105  bytes 22193 (21.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 124  bytes 23623 (23.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

弄了一整個晚上終於搞定了.

2019年3月30日 星期六

市圖還書 1 本

本周有人預約下面這本, 必須先還 :
  1. 超圖解Arduino互動設計入門
此書是 Arduino 入門好書, 書中好幾個實驗我都還沒時間做. 我看我要很久之後才有時間再玩物聯網了.

樹莓派安裝 Nginx 伺服器失敗

下午嘗試在樹莓派安裝 Nginx 伺服器, 結果失敗, 過程紀錄如下 :

1. 更新 apt-get 列表 :

pi@raspberrypi:~ $ sudo apt-get update

2. 安裝 Nginx 伺服器 :

pi@raspberrypi:~ $ sudo apt-get install nginx 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  nginx-common nginx-full
Suggested packages:
  fcgiwrap nginx-doc
The following NEW packages will be installed:
  nginx nginx-common nginx-full
0 upgraded, 3 newly installed, 0 to remove and 294 not upgraded.
Need to get 535 kB of archives.
After this operation, 1,263 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main nginx-common all 1.6.2-5+deb8u6 [88.4 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main nginx-full armhf 1.6.2-5+deb8u6 [374 kB]
Get:3 http://mirrordirector.raspbian.org/raspbian/ jessie/main nginx all 1.6.2-5+deb8u6 [72.9 kB]
Fetched 535 kB in 3s (146 kB/s)
Preconfiguring packages ...
Selecting previously unselected package nginx-common.
(Reading database ... 135208 files and directories currently installed.)
Preparing to unpack .../nginx-common_1.6.2-5+deb8u6_all.deb ...
Unpacking nginx-common (1.6.2-5+deb8u6) ...
Selecting previously unselected package nginx-full.
Preparing to unpack .../nginx-full_1.6.2-5+deb8u6_armhf.deb ...
Unpacking nginx-full (1.6.2-5+deb8u6) ...
Selecting previously unselected package nginx.
Preparing to unpack .../nginx_1.6.2-5+deb8u6_all.deb ...
Unpacking nginx (1.6.2-5+deb8u6) ...
Processing triggers for man-db (2.7.0.2-5) ...
Processing triggers for systemd (215-17+deb8u6) ...
Setting up nginx-common (1.6.2-5+deb8u6) ...
Setting up nginx-full (1.6.2-5+deb8u6) ...
Job for nginx.service failed. See 'systemctl status nginx.service' and 'journalctl -xn' for details.
invoke-rc.d: initscript nginx, action "start" failed.
dpkg: error processing package nginx-full (--configure):
 subprocess installed post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of nginx:
 nginx depends on nginx-full (>= 1.6.2-5+deb8u6) | nginx-light (>= 1.6.2-5+deb8u6) | nginx-extras (>= 1.6.2-5+deb8u6); however:
  Package nginx-full is not configured yet.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.
 nginx depends on nginx-full (>> 1.6.2-5+deb8u6.1~) | nginx-light (>> 1.6.2-5+deb8u6.1~) | nginx-extras (>> 1.6.2-5+deb8u6.1~); however:
  Package nginx-full is not configured yet.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.

dpkg: error processing package nginx (--configure):
 dependency problems - leaving unconfigured
Processing triggers for systemd (215-17+deb8u6) ...
Errors were encountered while processing:
 nginx-full
 nginx
E: Sub-process /usr/bin/dpkg returned an error code (1)

看起來是安裝失敗, 下 start 指令出現錯誤 :

pi@raspberrypi:~ $ sudo service nginx start
Job for nginx.service failed. See 'systemctl status nginx.service' and 'journalctl -xn' for details.

我搜尋到下面這篇 :


作者的做法是用 wget 去 Nginx 官網下載最新版 : 


我挑選 stable 的 1.14.2 版來試 :

1. 下載 tar.gx 檔 : 

pi@raspberrypi:~ $ sudo wget http://nginx.org/download/nginx-1.14.2.tar.gz  
--2019-03-30 14:12:43--  http://nginx.org/download/nginx-1.14.2.tar.gz
Resolving nginx.org (nginx.org)... 62.210.92.35, 95.211.80.227, 2001:1af8:4060:a004:21::e3
Connecting to nginx.org (nginx.org)|62.210.92.35|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1015384 (992K) [application/octet-stream]
Saving to: ‘nginx-1.14.2.tar.gz’

nginx-1.14.2.tar.gz 100%

2019-03-30 14:12:47 (379 KB/s) - ‘nginx-1.14.2.tar.gz’ saved [1015384/1015384]

2. 解壓縮 : 

pi@raspberrypi:~ $ sudo tar zxvf nginx-1.14.2.tar.gz   
nginx-1.14.2/
nginx-1.14.2/auto/
nginx-1.14.2/conf/
nginx-1.14.2/contrib/
.......

3. 下載安裝相依套件 :

pi@raspberrypi:~ $ sudo apt-get install libpcre3* libssl-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Note, selecting 'libpcre-ocaml-dev-ypu03' for regex 'libpcre3*'
Note, selecting 'libpcre3-dbg' for regex 'libpcre3*'
Note, selecting 'libpcre3-dev' for regex 'libpcre3*'
......

Setting up libssl1.0.0:armhf (1.0.1t-1+deb8u11) ...
Setting up nginx-full (1.6.2-5+deb8u6) ...
Job for nginx.service failed. See 'systemctl status nginx.service' and 'journalctl -xn' for details.
invoke-rc.d: initscript nginx, action "start" failed.
dpkg: error processing package nginx-full (--configure):
 subprocess installed post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of nginx:
 nginx depends on nginx-full (>= 1.6.2-5+deb8u6) | nginx-light (>= 1.6.2-5+deb8u6) | nginx-extras (>= 1.6.2-5+deb8u6); however:
  Package nginx-full is not configured yet.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.
 nginx depends on nginx-full (>> 1.6.2-5+deb8u6.1~) | nginx-light (>> 1.6.2-5+deb8u6.1~) | nginx-extras (>> 1.6.2-5+deb8u6.1~); however:
  Package nginx-full is not configured yet.
  Package nginx-light is not installed.
  Package nginx-extras is not installed.

dpkg: error processing package nginx (--configure):
 dependency problems - leaving unconfigured
Setting up libpcre++0:armhf (0.9.5-6) ...
Setting up ocaml-base-nox (4.01.0-5+rpi1) ...
Setting up ledit (2.03-2) ...
update-alternatives: using /usr/bin/ledit to provide /usr/bin/readline-editor (readline-editor) in auto mode
Setting up libfindlib-ocaml (1.4.1-1) ...
Setting up libpcre-ocaml (7.0.4-1) ...
Setting up ocaml-findlib (1.4.1-1) ...
Setting up libssl-dev:armhf (1.0.1t-1+deb8u11) ...
Setting up libssl-doc (1.0.1t-1+deb8u11) ...
Setting up libpcre++-dev (0.9.5-6) ...
Setting up libpcre3-dbg:armhf (2:8.35-3.3+deb8u4) ...
Setting up ocaml-compiler-libs (4.01.0-5+rpi1) ...
Setting up ocaml-interp (4.01.0-5+rpi1) ...
Setting up ocaml-nox (4.01.0-5+rpi1) ...
Setting up camlp4 (4.01.0-5+rpi1) ...
Setting up libfindlib-ocaml-dev (1.4.1-1) ...
Setting up libpcre-ocaml-dev (7.0.4-1) ...
Processing triggers for libc-bin (2.19-18+deb8u7) ...
Errors were encountered while processing:
 nginx-full
 nginx
E: Sub-process /usr/bin/dpkg returned an error code (1)

問題是一樣的.

查詢 Raspbian Jessie 的 Dabian 版本 : 

pi@raspberrypi:~ $ cat /etc/debian_version
8.0

難道 Jessie 已經太舊了嗎?

2019年3月29日 星期五

如何在樹莓派 Apache 伺服器執行 Python 程式

今天看到 "樹哥的樹莓派(PI)" 分享了如何在樹莓派的 Apache 伺服器順利執行 Python 程式的秘訣 :

在Apache 以Python 視為 CGI 來源來執行

這篇主要是講為何照網路上說明去設定 Apache 的預設網頁設定檔 /etc/apache2/sites-available/000-default.conf 後, 網頁卻顯示程式原始碼, 根本沒執行的原因是, 在重啟 Apache 伺服器前應先下 sudo a2enmod cgi  指令以便在 /etc/apache2/mods-enabled/ 下產生一個 SymLink : cgid.load. 但沒提到如何修改 000-default.conf 設定檔.

這個 000-default.conf 設定檔原始內容如下, 有作用的部分只有兩行 :

pi@raspberrypi:~ $ sudo cat /etc/apache2/sites-available/000-default.conf 
<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

我照樹哥文中圖片內容修改 000-default.conf, 加入如下內容 (藍色部分) :

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ScriptAlias /cgi-bin/ /var/www/cgi-bin/
        <Directory "/var/www/cgi-bin">
            Options +ExecCGI
            AddHandler cgi-script .cgi .py
            Options FollowSymLinks
            Require all granted
        </Directory>

我使用 nano 編輯設定檔 :

pi@raspberrypi:~ $ sudo nano /etc/apache2/sites-available/000-default.conf
pi@raspberrypi:~ $ sudo cat /etc/apache2/sites-available/000-default.conf 
<VirtualHost *:80>
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
        ScriptAlias /cgi-bin/ /var/www/cgi-bin/
        <Directory "/var/www/cgi-bin">
            Options +ExecCGI
            AddHandler cgi-script .cgi .py
            Options FollowSymLinks
            Require all granted
        </Directory>


        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with "a2disconf".
        #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

接著到 /www/var/ 底下新增 cgi-bin 子目錄, 然後在 cgi-bin 下編輯測試程式 test.py :

pi@raspberrypi:~ $ cd /var/www  
pi@raspberrypi:/var/www $ sudo mkdir cgi-bin  
pi@raspberrypi:/var/www $ ls  
cgi-bin  html
pi@raspberrypi:/var/www $ cd cgi-bin  
pi@raspberrypi:/var/www/cgi-bin $ nano test.py    

測試程式 test.py 內容如下 :

pi@raspberrypi:/var/www/cgi-bin $ cat test.py
#!/usr/bin/python 
print('Content-type: text/html\n\n')
print('<b>Hello World</b>')

就是簡單輸出 Hello World 而已. 注意, 第一行的 #!/usr/bin/python 不可少, 否則會因無法呼叫 Python 解譯器來執行檔案而造成 Internal Error. 

接著要用 chmod 將此 Python 程式之權限改為可執行 : 

pi@raspberrypi:/var/www/cgi-bin $ sudo chmod +x test.py  

最後, 重點來了, 要先下 sudo a2enmod cgi 建立連接, 再重啟伺服器 : 

pi@raspberrypi:/var/www/cgi-bin $ sudo a2enmod cgi   
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Enabling module cgi.
To activate the new configuration, you need to run:
  service apache2 restart 
pi@raspberrypi:/var/www/cgi-bin $ sudo service apache2 restart 

開啟樹莓派瀏覽器, 網址輸入 localhost/cgi-bin/test.py 即可看到網頁輸出 Hello World :



如果沒有先下 sudo a2enmod cgi 就重啟伺服器的話, 將看到程式原始碼 :




雖然計畫使用 Nginx + Python, 沒有打算用 Apache, 但還是測試看看 Apache+Python 是否可行. 但前提是樹莓派要先安裝 Apache, 指令如下 :

$ sudo apt-get install apache2
參考 :

在樹莓派上架設 PHP+MySQL 網站伺服器
在 Windows 下安裝 Apache + Python
安裝 Apache2 + Python + MySQL
Run python script as cgi under apache2 server
Apache Web Server with cgi for python scripts

好書 : 一次搞定所有 Python Web 框架開發百科全書

這本書是從母校高科大圖書館調借原第一科大藏書, 因有人預約, 這幾天趕緊將我想看的 Django 部分看完並記錄簡單摘要如下 :

一次搞定所有 Python Web 框架開發百科全書 (佳魁, 劉長龍)


Source : 博客來


此書主要介紹四個 Python 網站架站框架 : Django, Flask, Tornado, 以及 Twisted. 在過去 20 幾年裡, Python 社群出現了幾十種網站框架, 目前還持續開發維護的主要就是這四種主流架構 :
  1. Twisted (2000 年) :
    老牌的事件驅動, 非同步模型之高性能程式設計架構, 與前三種專注於 HTTP 應用的框架不同之處是, Twisted 適用於從傳輸層到自訂應用協定的所有網路程式開發, 在不同作業系統採用不同之底層技術以實現高效能網路通訊, 例如在 Windows 使用 IOCP, 在 Linux 使用 epoll.
  2. Django (2003 年) :
    Python 最成熟最負盛名之企業級網站框架, 擁有完整之線上文件與開發社群, 其個模組之間耦合較緊密. 
  3. Tornado (2009 年) :
    支援非同步 I/O, 逾時事件處理, 與程式碼協同作業, 搭配內建高效能可擴充的 HTTP 伺服器可平行處理網頁請求, 在高網路流量時表現強健. 支援 HTML5 標準之 WebSocket, 可實現伺服器與瀏覽器間的雙向即時通訊. 
  4. Flask  (2010 年) :
    以 Werkzeug 與 Jinja2 為基礎發展而成的輕量級可擴充微框架, 訴求可快速佈署微小型專案. 與 Django 不同的是, Flask 預設並未安裝任何 ORM 資料庫元件, 開發者需自行選擇安裝資料庫外掛程式. 
我原本對 Flask 很有興趣, 但由於以前玩 GAE 的關係, 對 Django 的語法較熟悉, 因此還是專心學好 Django 吧! 此書分三篇, 第一篇是前四章, 對 Python, 網頁技術 (HTML, CSS, Javascript, jQuery), 與 ORM 做了摘要介紹, 後面四章則是實戰的專案開發, 是很有編輯概念的一本好書.

參考 :

Python Twisted介绍

Linux 安裝 Nginx+uWSGI 設定 :

$ sudo apt-get install nginx
$ sudo services nginx start (啟動伺服器)
$ sudo services nginx stop  (停止伺服器)
$ sudo services nginx status  (檢視狀態)
$ sudo services nginx restart  (重啟伺服器)

Nginx 相關檔案路徑 :
  1. 程式檔案 : /usr/sbin/nginx
  2. 全域設定檔 : /etc/nginx/nginx.conf
  3. 存取記錄檔 : /var/log/nginx/access.log
  4. 錯誤記錄檔 : /var/log/nginx/error.log
  5. 網站設定檔 : /etc/nginx/sites-enabled/default
Nginx 已預設方式啟動後, 在開發偵錯過程中可能需要調整執行參數, 這些參數分布在全域設定檔 (例如檔案上傳限制, 允許最大用戶連接數, CPU 核心數目等) 與網站設定檔 (例如根目錄預設頁面檔名, 監聽的 IP, 錯誤頁面, 轉發頁面等) 中.

安裝 uWSGI :

pip install uwsgi

2019年3月28日 星期四

jQuery UI 學習筆記 (十一) : 選單 (Menu)

今天繼續來測試 jQuery UI 的選單 (menu). Menu 是個分門別類管理資料的好用工具, 可以大大節省版面空間並使資料井井有條, 例如用來導覽公司網頁中的產品型錄等等, 記得以前都要用一堆 CSS 與 Javascript 才能做出這種功能, 現在用 jQuery UI 很簡單就能實現了.

Menu API 與教學文件參考 :

https://jqueryui.com/menu/
http://api.jqueryui.com/menu/

本篇測試參考了如下書籍 :
  1. jQuery UI 使用者介面設計 (歐萊里, Studio Tib. 譯)
  2. jQuery UI 與 Plugin 開發實戰 (悅知文化, 吳哲穎譯)
  3. jQuery 全能權威指南 (上奇, 張亞飛)
  4. Pro jQuery 2.0 second eddition (Apress, Freeman Adam)
jQuery UI 的選單主要使用 HTML 的清單結構 (即 ul 與 li 元素) 組成, ul 是選單的容器, li 則是選項的容器. 每一個選項都必須用一個 div 元素包起來 (這是必要的, 因渲染後產生的部分樣式是要套用在此 div 上), 例如 :

  <ul id="menu-1">
    <li>
      <div>submenu-1</div>
      <ul>
        <li>
          <div>item-1-1</div>
        </li>
        <li>
          <div>item-1-2</div>
        </li>
      </ul>
    </li>
    <li>
      <div>submenu-2</div>
      <ul>
        <li>
          <div>item-2-1</div>
        </li>
        <li>
          <div>item-2-2</div>
        </li>
      </ul>
    </li>
    <li>
      <div>item-1</div>
    </li>
    <li>
      <div>item-2</div>
    </li>
  </ul>

其中主選單 menu-1 含有兩個子選單 submenu-1 與 submenu-2, 以及兩個選項 item-1 與 item-2, 階層可以一直延伸下去, 只要在 li 內再放 ul 即可. 例如 :


測試 1 : 書訊導覽 [看原始碼]

注意, 此例在 style 元素中添加了 class 為 ui-menu 的樣式, 用來將選單及其子選單都限制為同一寬度.

CSS 部分 :

<style>
.ui-menu {width: 100px;}
</style>

HTML 部分 :

  <ul id="books">
    <li>
      <div>網路書店</div>
      <ul>
        <li>
          <div>博客來</div>
        </li>
        <li>
          <div>金石堂</div>
        </li>
        <li>
          <div>誠品</div>
        </li>
      </ul>
    </li>
    <li>
      <div>圖書館</div>
      <ul>
        <li>
          <div>高科大</div>
        </li>
        <li>
          <div>高雄市圖</div>
        </li>
      </ul> 
    </li>
  </ul>

jQuery 部分 :

$("#books").menu();




此例第一層兩個選項均有子選單, 滑鼠移至項目上時會展開子選單. 在 Chrome 按 F12 進入開發者模式, 切到 Element 頁籤可檢視經 jQuery UI 渲染後的 HTML 如下 :




可見選單的每一個 ul 元素都被添加了許多樣式 class, 其中第一個樣式是 ui-menu, 此 class 可用來限定選單與所有子選單之寬度.

如果把上面測試 1 的 ui-menu 的寬度設定拿掉, 則主選單會占滿父元素的寬度, 如果是在網頁的最上層, 則會占滿全螢幕, 可能會破壞整個版面, 例如 :


測試 1-1 : 書訊導覽 (沒有設定 ui-menu 樣式之寬度) [看原始碼]

其次, 每個選項都必須用 div 包起來, 否則主題布景無法套用, 例如將上面測試 1 中各選項的 div 都拿掉的話, padding 與主題布景等樣式就不見了 :


測試 1-2 : 書訊導覽 (沒有 div) [看原始碼]






可見製作 jQuery UI 選單有兩個必要條件 :
  1. 一定要為選單設定寬度樣式.
  2. 每個選項都必須用 div 包起來.
注意, ui-menu 的樣式會套用到主選單與所有子選單, 如果不想一體套用, 也可以在代表選單的 ul 元素中用 in-line 樣式進行寬度設定 :

<ul id="menu-1" style="width:100px;">

好處是可以根據各選單中最長選項的長度來調整寬度, 而非套用同樣的寬度. 例如 :


測試 2 : 為各別選單設置寬度 [看原始碼]

HTML 部分 :

  <ul id="books" style="width: 100px;">
    <li>
      <div>網路書店</div>
      <ul style="width: 80px;">
        <li>
          <div>博客來</div>
        </li>
        <li>
          <div>金石堂</div>
        </li>
        <li>
          <div>誠品</div>
        </li>
      </ul>
    </li>
    <li>
      <div>圖書館</div>
      <ul style="width: 100px;">
        <li>
          <div>高科大</div>
        </li>
        <li>
          <div>高雄市圖</div>
        </li>
      </ul> 
    </li>
  </ul>

jQuery 部分 :

$("#books").menu();




此例去除了測試 1 中 style 元素內的 ui-menu 等級寬度設定, 轉而為代表選單的 ul 元素各別設定 style 屬性, 可見子選單 "網路書店" 因為項目之最長字數為 3, 寬度被設成較短的 80px.

如果要為選項加上超連結, 只要用 a 元素包裹選項 Label 即可, 例如 :


測試 3 : 為各選項加上超連結 [看原始碼]

HTML 部分 :

  <ul id="books">
    <li>
      <div>
        <a>網路書店</a>
      </div>
      <ul>
        <li>
          <div>
            <a href="https://www.books.com.tw" target="_blank">博客來</a>
          </div>
        </li>
        <li>
          <div>
            <a href="https://www.kingstone.com.tw" target="_blank">金石堂</a>
          </div>
        </li>
        <li>
          <div>
            <a href="http://www.eslite.com.tw" target="_blank">誠品</a>
          </div>
        </li>
      </ul>
    </li>
    <li>
      <div>
        <a>圖書館</a>
      </div>
      <ul>
        <li>
          <div>
            <a href="https://nkust.primo.exlibrisgroup.com" target="_blank">高科大</a>
          </div>
        </li>
        <li>
          <div>
            <a href="https://www.ksml.edu.tw" target="_blank">高雄市圖</a>
          </div>
        </li>
      </ul> 
    </li>
  </ul>

jQuery 部分 :

$("#books").menu();




此例中每一個被 a 元素包起來的 li 選項都有一個底線表示此為超連結, 似乎有點破壞視覺純淨感, 這可以為 a 元素加上 text-decoration:none 樣式設定解決, 例如 :



CSS 樣式 :

    .ui-menu {width: 100px;}
    a {text-decoration:none;}

HTML 與測試 3 相同. 

以上是選單的基本用法. 下面則針對 menu 常用的選項, 方法, 與事件進行測試. 常用屬性如下表 :

 常用 option 說明
 disabled 不可用狀態 (true/false, 預設 false)
 icons 指定選單上的按鈕 (字串, 預設 "ui-icon-carat-1-e")

常用方法如下表 :

 常用 method 說明
 option 取得或設定選項之值
 disable 禁能 (等於 disabled 屬性=True)
 enable 致能 (等於 disabled 屬性=False)
 refresh 重新渲染元件
 collapseAll 關閉所有開啟之子選單

常用事件如下表 :

 常用 event 說明
 create 建立物件時觸發
 blur 項目失去焦點時觸發
 select 項目被選擇時觸發
 focus 項目取得焦點時觸發

下列範例綜合測試了 icons, disabled 等屬性, 以及 option, disable, enable, open, close 等方法 :



HTML 部分 :

  <ul id="books" style="width: 100px;">
    <li>
      <div>網路書店</div>
      <ul style="width: 80px;">
        <li>
          <div>博客來</div>
        </li>
        <li>
          <div>金石堂</div>
        </li>
        <li>
          <div>誠品</div>
        </li>
      </ul>
    </li>
    <li>
      <div>圖書館</div>
      <ul style="width: 100px;">
        <li>
          <div>高科大</div>
        </li>
        <li>
          <div>高雄市圖</div>
        </li>
      </ul> 
    </li>
  </ul><br>
  <button id="enable-disable">Enable</button>
  <button id="collapseAll">collapseAll</button><br><br>
  <textarea id="output"></textarea>

jQuery 部分 :

      var config={
        disabled: true,
        icons: {submenu: "ui-icon-circle-triangle-e"},
        create: function(e, ui) {
          var html="觸發 create 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        blur: function(e, ui) {
          var html="觸發 blur 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        focus: function(e, ui) {
          var html="觸發 focus 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        select: function(e, ui) {
          var html="觸發 select 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          }
        };
      $("#books").menu(config);
      $("#enable-disable").button();
      $("#enable-disable").on("click", function() {
        var str=$("#enable-disable").html();
        if (str=="Enable") {
          $("#books").menu("option", "disabled", false);
          //$("#books").menu("enable");  //另一個做法
          $("#enable-disable").html("Disable");
          }
        else {
          $("#books").menu("disable");
          $("#enable-disable").html("Enable");
          }
        });
      $("#collapseAll").button();
      $("#collapseAll").on("click", function() {
        $("#books").menu("collapseAll");
        });





此例在 menu 物件建立時將表單設為不可用, 並用 icons 選項設定新的子選單圖像, 然後監視 create, focus, select, blur 等事件.

爸換人工水晶體

今天下午請假回鄉下, 載爸去劉眼科做人工水晶體置換術, 這次是換左眼水晶體, 因白內障越來越模糊, 前年先換右眼, 效果還不錯, 都用健保的, 因為阿泉伯的朋友蕭主任換好幾萬元的, 太高級了, 結果比較怕強光, 換了好幾年了, 出外都盡量戴太陽眼鏡, 否則太刺眼, 不知道的朋友都以為他很愛耍派頭, 裝帥. 參考 :

白內障手術,植入哪種人工水晶體比較好?

因明早九點還要去拆紗布換藥, 所以下午就不回高雄了, 明早再請兩小時假.

二哥申請入學第一階段放榜

周三早上申請入學第一階段放榜, 二哥再次失望了, 原本期望可過的東華電機沒上, 剛好過門檻的高雄大學電機也沒上, 申請五家過三家, 都是物理系與電機電子或光電相關的 :
  1. 東華物理系光電組
  2. 高雄大學應用物理
  3. 嘉義大學電子物理
東華資工可上卻不敢申請 (因為覺得太熱門), 高雄大學電機系 APCS 只有三個名額也不敢報, 結果顯示如果勇敢申請應該可以上. 問他有否考慮指考, 他說想去讀東華物理光電, 努力修學分寒假再申請轉系. 物理也是有興趣, 但工作機會可能較少. 二類學測數學沒考好選擇性就少了.

接下來就是寫完備審資料了. 讀書計畫我擬了幾個電機電子資工自修必備技能給他去寫 :
  1. 大一上 : 微積分, Python 基礎, 網頁前端 (HTML+Javascript+jQuery)
  2. 大一下 : Python 應用, 網頁後端 (Django), Arduino+Raspberry Pi
  3. 大二上 : Python 資料科學 (Numpy + Scipy + Pandas)
東華暑期大一新生先修課程有 Python 與 R 語言線上遠距教學 (學校這樣要求是有遠見的), 我建議全修.

2019年3月26日 星期二

jQuery UI 學習筆記 (十) : 下拉式選單 (Selectmenu)

選單 selectmenu 就是加上主題布景的下拉式選單, 其實在測試主題布景切換時就已經用過了, 參考 :

jQuery UI 學習筆記 (一) : 主題布景 (Themes)

本篇是針對其選項, 方法, 與事件做較詳細之測試.

Selectmenu 的 API 與教學文件參考 :

https://jqueryui.com/selectmenu/
http://api.jqueryui.com/selectmenu/

本篇測試參考了如下書籍 :
  1. jQuery UI 使用者介面設計 (歐萊里, Studio Tib. 譯)
  2. jQuery UI 與 Plugin 開發實戰 (悅知文化, 吳哲穎譯)
  3. jQuery 全能權威指南 (上奇, 張亞飛)
Selectmenu 基本上就是 select/option 元素, 以選擇器取得 jQuery 物件後, 呼叫 selectmenu() 方法即可, 其 jQuery 的事件要用 on() 綁定 selectmenuchange 事件, 例如 :


測試 1 : Selectmenu 測試 : 預設選單 [看原始碼]

HTML 部分 :

  <select id="language">
    <option value="">最擅長的程式語言</option>
    <option value="C">C</option>
    <option value="R">R</option>
    <option value="Java">Java</option>
    <option value="Python">Python</option>
    <option value="Javascript">Javascript</option>
  </select>

jQuery 部分 : 

      $("#language").selectmenu();
      $('#language').on('selectmenuchange', function() {
        var language=$(this).val();
        alert("您擅長的程式語言是 : " + language);
        });




此例呼叫 selectmenu() 並未傳入選項物件, 因此預設的選單寬度似乎有點大, 這可用 width 選項加以設定.

常用選項如下 :

 常用 option 說明
 disabled 不可用狀態 (true/false, 預設 false)
 width  指定選單寬度 (單位 px), 預設 false
 icons 指定選單上的按鈕 (字串, 預設 "ui-icon-triangle-1-s")

常用方法如下 :

 常用 method 說明
 option 取得或設定選項之值
 disable 禁能 (等於 disabled 屬性=True)
 enable 致能 (等於 disabled 屬性=False)
 refresh 重新渲染元件
 open 開啟 (拉下) 選單
 close 關閉 (縮合) 選單

常用事件如下 :

 常用 event 說明
 create 建立物件時觸發
 change 選擇之項目改變時觸發
 select 項目被選擇時觸發
 focus 項目取得焦點時觸發
 open 選單開啟 (拉下) 時觸發
 close 選單關閉 (縮合) 時觸發

下列範例綜合測試了 width, icons, disabled 等屬性, 以及 option, disable, enable, open, close 等方法 :


Selectmenu 測試 2 : 選項與方法綜合測試 [看原始碼]

HTML 部分 :

  <select id="language">
    <option value="">最擅長的程式語言</option>
    <option value="C">C</option>
    <option value="R">R</option>
    <option value="Java">Java</option>
    <option value="Python">Python</option>
    <option value="Javascript">Javascript</option>
  </select>
  <button id="open-close">Open</button>
  <button id="enable-disable">Enable</button>
  <p id="output"></p>

jQuery 部分 :

      var config={
        width: 180,
        disabled: true,
        icons: {button: "ui-icon-circle-triangle-s"}
        };
      $("#language").selectmenu(config);
      $('#language').on('selectmenuchange', function() {
        var language=$(this).val();
        $("#output").html("最擅長的程式語言是 : " + language);
        });
      $("#open-close").button();
      $("#open-close").on("click", function() {
        var str=$("#open-close").html();
        if (str=="Open") {
          $("#language").selectmenu("open");
          $("#open-close").html("Close");
          }
        else {
          $("#language").selectmenu("close");
          $("#open-close").html("Open");
          }
        });
      $("#enable-disable").button();
      $("#enable-disable").on("click", function() {
        var str=$("#enable-disable").html();
        if (str=="Enable") {
          $("#language").selectmenu("option", "disabled", false);
          //$("#language").selectmenu("enable");  //另一個做法
          $("#enable-disable").html("Disable");
          }
        else {
          $("#language").selectmenu("disable");
          $("#enable-disable").html("Enable");
          }
        });




此例設置了 open-close 與 enable-disable 兩個按鈕, 分別控制 selectmenu 選單的 "開啟/關閉" 與 "可用/不可用" 狀態切換. 這兩個按鈕透過其 label 來判斷選單目前之狀態而反覆切換. 初始化時選單是不可用的 (disabled: true), 因此 enable-disable 按鈕上的 label 預設是 Enable; 同理, 選單一般初始為關閉狀態, 因此 open-close 按鈕上的 label 預設是 Open.

其次, 由測試 1 可知, selectmenu 選單上預設是一個向下的小三角形圖案, 此例用 icons 選項將其設定為外面有圓圈的小三角形圖案 "ui-icon-circle-triangle-s", 還有更多 icon 可選, 參考 :


另外利用 width 選項將選單寬度做適當調整, 視覺效果較佳.

下列範例則是測試事件 :


Selectmenu 測試 3 : 六個事件測試 [看原始碼]

HTML 部分 :

  <table style="border-width:0px;">
    <tr>
      <td style="vertical-align: top;">
        <select id="language">
          <option value="">最擅長的程式語言</option>
          <option value="C">C</option>
          <option value="R">R</option>
          <option value="Java">Java</option>
          <option value="Python">Python</option>
          <option value="Javascript">Javascript</option>
        </select>
      </td>
      <td>
        <textarea id="output"></textarea>
      </td>
    </tr>
  </table>

jQuery 部分 :

      var config={
        create: function(e, ui) {
          $("#language").selectmenu("option", "width", 180);
          $("#language").selectmenu("open");
          $("#output").html("觸發 create 事件 : " + $(this).val());
          },
        change: function(e, ui) {
          var html="觸發 change 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        select: function(e, ui) {
          var html="觸發 select 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        focus: function(e, ui) {
          var html="觸發 focus 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        open: function(e, ui) {
          var html="觸發 open 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          },
        close: function(e, ui) {
          var html="觸發 close 事件 : " +  $(this).val() + "\n" +
                   $("#output").html();
          $("#output").html(html);
          }
        };
      $("#language").selectmenu(config);




此例將顯示輸出的元素改為 textarea, 且為了版面好看將 selectmenu 與 textarea 分別放在 1*2 表格的 cell 中. 在設定物件 config 中為六種事件綁定了處理函數, 其中 create 會在 selectmenu 物件建立時觸發, 於此事件中利用 option 方法設定了選單的寬度 width, 然後呼叫 open 方法開啟選單, 所以網頁初始化時選單是打開的.

每一個事件被觸發時, 會先取得 textarea 的內容, 然後將此事件的資訊冠在其前, 再更新 textarea 內容, 因此最新的事件總是在前面. 注意, 取得 selectmenu 的被選取值要用 $(this).val(), 不是用 ui.value.

好站 : 用 PVC 管製作小型風力發電機

我在 Youtube 上看到下面這個小型風力發電機的組裝方法, 原來用 PVC 管就能搞定, 這種巧思實在令我讚嘆! 參考 :

How to Make Wind Turbine Generator - Clean Energy




不過這個製作方式還是沒有解決電線纏繞問題, 當風機一直朝單方向旋轉時, 電線會捲在管柱上, 這一般都是使用集電環來解決. 我以前也製作過一個會隨風向轉向的小型風力發電機, 但最後被我改成固定式的, 原因就是纏繞問題.

我想到的一個辦法是可以改用中空的牙管, 這樣電線就可以從中間穿過去拉下來了 :

【不囉嗦24H送出】M10 空心牙管 牙管 螺紋管 穿線管 長度60-100mm 均一價

或者找一個管徑與滾珠軸承內徑相同的鐵管也可以, 但不好找.

20210305 補充 :

上面那個賣完了, 但有新上架的 :


先拍下照片本周去小漢問問看有無此品項 (M10 是搭配螺帽之編號, M10*1.2 表示牙距是 1.2mm, 必須與螺帽牙距相同才鎖得進去). 另外, 轉動軸下方可套上編碼盤用來偵測風速與風向 (可用紅外線反射感測器).

市圖還書 1 本

下面這本書本周有人預約, 先還再說 :
  1. 7天學會Git版本控制
Git 我看了好久還是一直沒時間去全盤掌握, 有點苦惱. 

2019年3月25日 星期一

jQuery UI 學習筆記 (九) : 單選與複選按鈕 (CheckboxRadio)

上一篇測試 "jQuery UI 學習筆記 (八) : 按鈕群組 (Buttonset) " 以 buttonset() 將 radio/checkbox 的選項轉變成按鈕群組, 使選項具有主題布景色彩; 但是可能需在提示文字中加註 "單選" 或 "複選", 避免被誤認為是一般按鈕.

可否在這些選項按鈕上保留 radio 單選圓鈕與 checkbox 多選方塊的圖樣, 以便與一般按鈕有明顯區隔呢? 可以的, 可呼叫 checkboxradio() 使 radio/checkbox 轉變成具有圓鈕與核取方塊的按鈕群組.

Checkboxradio 的API 與教學文件參考 :

https://jqueryui.com/checkboxradio/
http://api.jqueryui.com/checkboxradio/

本篇測試參考了如下書籍 :
  1. jQuery UI 使用者介面設計 (歐萊里, Studio Tib. 譯)
  2. jQuery UI 與 Plugin 開發實戰 (悅知文化, 吳哲穎譯)
  3. jQuery 全能權威指南 (上奇, 張亞飛)
將 radio/checkbox 轉變成 CheckboxRadio 按鈕元件只要用選擇器選取全部 input 元素, 再呼叫 checkboxradio() 方法即可 :

$("input").checkboxradio()

這樣雖然會選取全部 input 元素, 不過沒有關係, jQuery UI 會自動找尋 type=radio 與 type=checkbox 的元素進行渲染, 其他 input 元素不會受到影響, 例如 :


測試 1 : CheckboxRadio 測試 : checkbox 勾勾無顯示 [看原始碼]

HTML 部分 :

  <p>請選擇最討厭的水果 (單選) :</p>
  <div>
    <input type="radio" name="hatest_fruit" id="h1" value="芭樂">
    <label for="h1">芭樂</label></input>
    <input type="radio" name="hatest_fruit" id="h2" value="香蕉">
    <label for="h2">香蕉</label></input>
    <input type="radio" name="hatest_fruit" id="h3" value="葡萄">
    <label for="h3">葡萄</label></input>
    <input type="radio" name="hatest_fruit" id="h4" value="榴槤">
    <label for="h4">榴槤</label></input>
    <input type="radio" name="hatest_fruit" id="h5" value="木瓜">
    <label for="h5">木瓜</label></input>
  </div>
  <p>請選擇最常吃的水果 (複選) :</p>
  <div>
    <input type="checkbox" name="often_fruit" id="o1" value="芭樂">
    <label for="o1">芭樂</label></input>
    <input type="checkbox" name="often_fruit" id="o2" value="香蕉">
    <label for="o2">香蕉</label></input>
    <input type="checkbox" name="often_fruit" id="o3" value="葡萄">
    <label for="o3">葡萄</label></input>
    <input type="checkbox" name="often_fruit" id="o4" value="榴槤">
    <label for="o4">榴槤</label></input>
    <input type="checkbox" name="often_fruit" id="o5" value="木瓜">
    <label for="o5">木瓜</label></input>
  </div><br>
  <button id="ok">確定</button>

jQuery 部分 :

      $("#ok").button();
      $("input").checkboxradio();
      $("#ok").click(function(e) {
        var hatest_fruit=$("[name=hatest_fruit]:radio:checked").val();
        var arr=[];
        $("[name=often_fruit]:checkbox:checked").each(function(){
          arr.push($(this).val());
          });
        var often_fruit=arr.join();
        var msg="您最討厭的水果是 : " + hatest_fruit + "\n" +
                "您最常吃的水果是 : " + often_fruit;
        alert(msg);
        });




注意, checkbox 按鈕有點奇怪, 勾選後按鈕變成紅色, 但打勾卻消失, 只有滑鼠 hover 滑到選項按鈕上面時才會出現勾勾. 但這是因為選用 jQuery UI 主題背景 hot-sneaks 的關係, 若改用其他主題就不會, 例如用 base 或 eggplant 等主題就不會 :

  <link id="theme" href="themes/hot-sneaks/jquery-ui.min.css" rel="stylesheet">

測試 2 : CheckboxRadio 測試 : checkbox 勾勾有顯示 [看原始碼]

<link href="themes/base/jquery-ui.min.css" rel="stylesheet">




事實上, jQuery UI 25 個預設主題布景中有 16 個有勾勾被蓋掉情形, 下列範例使用下拉式選單切換主題布景來測試, 這是透過給樣式主題的 link 元素設 id, 然後用 SelectMenu 下拉式選單的 selectmenuchange 事件來切換主題名稱, 參考 :

jQuery UI 學習筆記 (一) : 主題布景 (Themes)


測試 3 : CheckboxRadio 測試 : 主題布景切換 [看原始碼]

  <link id="theme" href="themes/eggplant/jquery-ui.min.css" rel="stylesheet">




這些按鈕看起來似乎是獨立的, 可以選取這些選項呼叫 buttonset() 將這些選項按鈕都變成按鈕群組, 參考 :

# jQuery UI 學習筆記 (八) : 按鈕群組 (Buttonset)

例如 :

測試 4 : CheckboxRadio 測試 : 呼叫 buttonset 形成按鈕群組 [看原始碼]

HTML 部分 :

  <p>請選擇最討厭的水果 (單選) :</p>
  <div>
    <input type="radio" name="hatest_fruit" id="h1" value="芭樂">
    <label for="h1">芭樂</label></input>
    <input type="radio" name="hatest_fruit" id="h2" value="香蕉">
    <label for="h2">香蕉</label></input>
    <input type="radio" name="hatest_fruit" id="h3" value="葡萄">
    <label for="h3">葡萄</label></input>
    <input type="radio" name="hatest_fruit" id="h4" value="榴槤">
    <label for="h4">榴槤</label></input>
    <input type="radio" name="hatest_fruit" id="h5" value="木瓜">
    <label for="h5">木瓜</label></input>
  </div>
  <p>請選擇最常吃的水果 (複選) :</p>
  <div>
    <input type="checkbox" name="often_fruit" id="o1" value="芭樂">
    <label for="o1">芭樂</label></input>
    <input type="checkbox" name="often_fruit" id="o2" value="香蕉">
    <label for="o2">香蕉</label></input>
    <input type="checkbox" name="often_fruit" id="o3" value="葡萄">
    <label for="o3">葡萄</label></input>
    <input type="checkbox" name="often_fruit" id="o4" value="榴槤">
    <label for="o4">榴槤</label></input>
    <input type="checkbox" name="often_fruit" id="o5" value="木瓜">
    <label for="o5">木瓜</label></input>
  </div><br><br>
  <button id="ok">確定</button>

jQuery 部分 :

      $("#ok").button();
      $("input").checkboxradio();
      $("div").buttonset();
      $("#ok").click(function(e) {
        var hatest_fruit=$("[name=hatest_fruit]:radio:checked").val();
        var arr=[];
        $("[name=often_fruit]:checkbox:checked").each(function(){
          arr.push($(this).val());
          });
        var often_fruit=arr.join();
        var msg="您最討厭的水果是 : " + hatest_fruit + "\n" +
                "您最常吃的水果是 : " + often_fruit;
        alert(msg);
        });




此例結合了 checkboxradio 與 buttonset 功能, 讓 radio/checkbox 能在美化視覺效果外還能保持原始 HTML 介面.

Checkradio 提供選項, 方法, 以及事件對 radio/checkbox 進行操控, 常用選項如下表 :

 常用 option 說明
 classes 對 ui-checkbox 樣式 class 指定額外樣式
 disabled 不可用狀態 (true/false, 預設 false)
 icon 是否要顯示 radio/checkbox 圖像 (true/false, 預設 false) 

常用方法如下表 :

 常用 method 說明
 option 取得或設定選項之值
 disable 禁能 (等於 disabled 屬性=True)
 enable 致能 (等於 disabled 屬性=False)
 refresh 更新元件之視覺狀態 (checked 或 disabled)

常用方法如下表 :

 常用 event 說明
 create 建立物件時觸發

例如 :


測試 5 : CheckboxRadio 測試 : 選項與方法 [看原始碼]

HTML 部分 :

  <p>請選擇最討厭的水果 (單選) :</p>
  <div>
    <input type="radio" name="hatest_fruit" id="h1" value="芭樂">
    <label for="h1">芭樂</label></input>
    <input type="radio" name="hatest_fruit" id="h2" value="香蕉">
    <label for="h2">香蕉</label></input>
    <input type="radio" name="hatest_fruit" id="h3" value="葡萄">
    <label for="h3">葡萄</label></input>
    <input type="radio" name="hatest_fruit" id="h4" value="榴槤">
    <label for="h4">榴槤</label></input>
    <input type="radio" name="hatest_fruit" id="h5" value="木瓜">
    <label for="h5">木瓜</label></input>
  </div>
  <p>請選擇最常吃的水果 (複選) :</p>
  <div>
    <input type="checkbox" name="often_fruit" id="o1" value="芭樂">
    <label for="o1">芭樂</label></input>
    <input type="checkbox" name="often_fruit" id="o2" value="香蕉">
    <label for="o2">香蕉</label></input>
    <input type="checkbox" name="often_fruit" id="o3" value="葡萄">
    <label for="o3">葡萄</label></input>
    <input type="checkbox" name="often_fruit" id="o4" value="榴槤">
    <label for="o4">榴槤</label></input>
    <input type="checkbox" name="often_fruit" id="o5" value="木瓜">
    <label for="o5">木瓜</label></input>
  </div><br><br>
  <button id="ok">確定</button>
  <button id="usable">Enable</button>
  <button id="icon">Show Icon</button>

jQuery 部分 :

      var config={
        disabled: true, 
        icon: false,
        classes: {"ui-checkboxradio": "highlight"}
        };
      $("input").checkboxradio(config);      
      $("#ok").button();
      $("#usable").button();
      $("#usable").click(function(e) {
        var isDisabled=$("input").checkboxradio("option", "disabled");
        if (isDisabled) { //現在是不可用狀態
          $("input").checkboxradio("enable");
          $("#usable").html("Disable");
          }
        else {  //現在是可用狀態
          $("input").checkboxradio("disable");
          $("#usable").html("Enable");
          }
        });
      $("#icon").button();
      $("#icon").click(function(e) {
        var haveIcon=$("input").checkboxradio("option", "icon");
        if (haveIcon) { //現在有圖樣
          $("input").checkboxradio("option", "icon", false);
          $("#icon").html("Show icon");
          }
        else {  //現在無圖樣
          $("input").checkboxradio("option", "icon", true);
          $("#icon").html("Hide icon");
          }
        });
      $("#ok").button();
      $("#ok").click(function(e) {
        var hatest_fruit=$("[name=hatest_fruit]:radio:checked").val();
        var arr=[];
        $("[name=often_fruit]:checkbox:checked").each(function(){
          arr.push($(this).val());
          });
        var often_fruit=arr.join();
        var msg="您最討厭的水果是 : " + hatest_fruit + "\n" +
                "您最常吃的水果是 : " + often_fruit;
        alert(msg); 
        });







此例設置了 id=usable 與 id=icon 兩個按鈕, 在選項物件 config 中將選項 disable 設為 true, icon 設為 false, 故預設 radio/checkbox 均無圖像且均不可用, 故 usable 按鈕預設顯示 "Enable", 而 icon 按鈕預設為 "Show icon", 按下這兩個按鈕會使 radio/checkbox 恢復為可用, 以及顯示圖像, 同時 usable 與 icon 按鈕上的文字也會隨之反覆變化.