- TweenLite/Max
一套由 Jack Doyle獨自完成的Flash Tweening API,提供了許多從基本到進階的Tweening特效,付費者可以取得更完善的支援功能,官方網頁如下:
你可以先下載免費版試用看看。
一套由 Jack Doyle獨自完成的Flash Tweening API,提供了許多從基本到進階的Tweening特效,付費者可以取得更完善的支援功能,官方網頁如下:
你可以先下載免費版試用看看。
Download:HashMap.zip
原先遇到HashMap結構時我都直接使用polygonal網站上所提供的,但因為設計上的需求,需要更改HashMap裡的key/value pair的次序性,所以只好自己重新實做一個。下載包裡面包含了以下五個原始檔:
-------------------------------------------------------
-------------------------------------------------------
使用Double LinkedList Node結構比較方便之後的功能擴充
/* HashMapNode.as */
package xinyu.collection{
public class HashMapNode{
public var key:*;
public var obj:*;
public var prev:HashMapNode;
public var next:HashMapNode;
public function HashMapNode(key:* = null, obj:* = null, prev:HashMapNode = null, next:HashMapNode = null){
this.key = key;
this.obj = obj;
this.prev = prev;
this.next = next;
}
}
}
Map結構的介面,定義了一些Map結構該有的基本功能。
/* Map.as */
package xinyu.collection{
public interface Map{
function insert(key:*, obj:*):Boolean;
function find(key:*):*;
function remove(key:*):Boolean;
function keySet():Array;
function entrySet():Array;
function clear():void;
function isEmpty():Boolean;
function iterator():Iterator;
function containsValue(obj:*):Boolean;
function containsKey(key:*):Boolean;
}
}
有別於ploygonal的HashMap,我將Dictionary給移除,僅只有在entrySet()裡有使用到(回傳不重複的entrySet),其餘的操作皆以Double LinkedList結構來運作,目的是要控制key/value 的次序性,所以我還多了moveUp()跟moveDown()這兩個方法來移動entry的位置。
/* HashMap.as */
package xinyu.collection{
import flash.utils.Dictionary;
import xinyu.collection.Map;
import xinyu.collection.HashMapNode;
import xinyu.collection.Iterator;
import xinyu.collection.HashIterator;
public class HashMap implements Map {
private var head:HashMapNode = new HashMapNode();
private var tail:HashMapNode = new HashMapNode();
public function HashMap() {
head.next = tail;
tail.prev = head;
}
public function insert(key:*, obj:*):Boolean {
if (key == null || obj == null) return false;
if (searchByKey(key) != null) return false;
var node:HashMapNode = new HashMapNode(key, obj, tail.prev, tail);
node.prev.next = node;
tail.prev = node;
return true;
}
public function find(key:*):*{
var node:HashMapNode = searchByKey(key);
if(node) return node.obj;
return null;
}
public function remove(key:*):Boolean{
var node:HashMapNode = searchByKey(key);
if(node == null) return false;
node.prev.next = node.next;
node.next.prev = node.prev;
return true;
}
public function keySet():Array{
var array:Array = new Array();
var curr:HashMapNode = head.next;
while(curr!=tail){
array.push(curr.key);
curr = curr.next;
}
return array;
}
public function entrySet():Array{
var array:Array = new Array();
var node:HashMapNode = head.next;
var dict:Dictionary = new Dictionary(true);
while(node != tail){
if(dict[node.obj] == undefined){
dict[node.obj] = 1;
array.push(node.obj);
}
node = node.next;
}
return array;
}
public function clear():void{
var curr:HashMapNode = head.next;
var nextPtr:*;
while(curr!=tail){
curr.key = null;
curr.obj = null;
nextPtr = curr.next;
curr.next = curr.prev = null;
curr = nextPtr;
}
head.next = tail;
tail.prev = head;
}
public function swap(key1:*, key2:*):Boolean{
if (key1 == null||key2 == null) return false;
var node1:HashMapNode = searchByKey(key1);
var node2:HashMapNode = searchByKey(key2);
if(node1 == null || node2 == null) return false;
var temp:HashMapNode = new HashMapNode();
temp.key = node1.key;
temp.obj = node1.obj;
node1.key = node2.key;
node1.obj = node2.obj;
node2.key = temp.key;
node2.obj = temp.obj;
return true;
}
public function isEmpty():Boolean {
if (head.next == tail) return true;
return false;
}
public function moveUp(key:*):Boolean{
if(key == null) return false;
var node:HashMapNode = searchByKey(key);
if(node == null) return false;
if(node.prev == head) return false;
return swap(key, node.prev.key);
}
public function moveDown(key:*):Boolean{
if(key == null) return false;
var node:HashMapNode = searchByKey(key);
if(node == null) return false;
if(node.next == tail) return false;
return swap(key, node.next.key);
}
public function get size():int{
var count:int = 0;
var curr:HashMapNode = head.next;
while(curr!=tail){
count++;
curr=curr.next;
}
return count;
}
public function containsKey(key:*):Boolean{
if(searchByKey(key)) return true;
return false;
}
public function containsValue(obj:*):Boolean{
var curr:HashMapNode = new HashMapNode();
curr = head.next;
while(curr!=tail){
if(curr.obj === obj) return true;
curr = curr.next;
}
return false;
}
private function searchByKey(key:*):HashMapNode{
var curr:HashMapNode = new HashMapNode();
curr = head.next;
while(curr!=tail){
if(curr.key === key) return curr;
curr = curr.next;
}
return null;
}
public function iterator():Iterator{
return new HashIterator(head,tail);
}
}
}
Iterator介面的部份只有簡單定義幾個基本的功能。
/* Iterator */
package xinyu.collection{
public interface Iterator{
function hasNext():Boolean;
function next():*;
function remove():void;
function rewind():void;
}
}
HashIterator就依照著Iterator介面去實做,並沒有增加額外的功能,有別於Java的Iterator,我多了rewind()。因為我在其他設計專案中需要將iterator重新指向一開始,所以多了這個功能。
/* HashIterator */
package xinyu.collection{
import xinyu.collection.Iterator;
import xinyu.collection.HashMapNode;
public class HashIterator implements Iterator{
private var head:HashMapNode;
private var tail:HashMapNode;
private var curr:HashMapNode;
public function HashIterator(head:HashMapNode, tail:HashMapNode){
this.head = head;
this.tail = tail;
curr = head.next;
}
public function hasNext():Boolean{
return (curr != tail);
}
public function next():*{
if(curr != tail){
var obj:* = curr.obj;
curr = curr.next;
return obj;
}
return null;
}
public function rewind():void{
curr = head.next;
}
public function remove():void{
tail.prev = tail.prev.prev;
tail.prev.next = tail;
}
}
}
底下是一個很簡單的範例用來展示上面的HashMap的用法
package{
import flash.display.Sprite;
import xinyu.collection.HashMap;
import xinyu.collection.Iterator;
import xinyu.collection.HashIterator;
public class HashMapDemo extends Sprite{
public function HashMapDemo(){
var hashMap:HashMap = new HashMap();
var obj1:Object = new Object();
obj1.name = "object_1";
var obj2:Object = new Object();
obj2.name = "object_2";
hashMap.insert("1", obj1);
hashMap.insert("2", obj2);
trace("isEmpty (after insertion): "+hashMap.isEmpty());
trace("Size of hashmap: "+hashMap.size);
trace("\nKeySet (before swap): "+hashMap.keySet());
trace("Key 1's Object: "+hashMap.find("1").name);
trace("Key 2's Object: "+hashMap.find("2").name);
trace("EntrySet"+hashMap.entrySet());
hashMap.swap("1","2");
trace("\nKeySet (after swap): "+hashMap.keySet());
trace("Key 1's Object: "+hashMap.find("1").name);
trace("Key 2's Object: "+hashMap.find("2").name);
trace("\nIteration result before move up the key 1:");
var iter:Iterator = hashMap.iterator();
while(iter.hasNext()){
trace(iter.next().name);
}
hashMap.moveUp("1");
trace("\nIteration result after move up the key 1:");
iter.rewind();
while(iter.hasNext()){
trace(iter.next().name);
}
iter.remove();
trace("\nSize of hashmap(after remove a node): "+hashMap.size);
hashMap.clear();
trace("\nisEmpty (after clear): "+hashMap.isEmpty());
trace("Size of hashmap: "+hashMap.size);
}
}
}
輸出畫面如下:
Size of hashmap: 2
KeySet (before swap): 1,2
Key 1's Object: object_1
Key 2's Object: object_2
EntrySet[object Object],[object Object]
KeySet (after swap): 2,1
Key 1's Object: object_1
Key 2's Object: object_2
Iteration result before move up the key 1:
object_2
object_1
Iteration result after move up the key 1:
object_1
object_2
Size of hashmap(after remove a node): 1
isEmpty (after clear): true
Size of hashmap: 0
一套來自於羅馬尼亞的防毒軟體 - BitDefender,個人使用者可以在 EULA license的授權限制下免費使用BitDefender Antivirus Scanner for Unices,你可以從下面的網站與BitDefender官方取得授權序號和軟體。
BitDefender Antivirus Scanner for Unices
系統需求:
Linux Kernel: 2.4.x or 2.6.x (recommended)
FreeBSD: 5.4 (or newer with compat5x)
glibc: version 2.3.1 or newer, and libstdc++5 from gcc 3.2.2 or newer
Processor: x86 compatible 300 MHz; i686 500MHz; amd64(x86_64)
Minimum Memory: 64MB (128MB recommended)
Minimum Free Disk Space: 100MB
系統環境:
RedHat Enterprise Linux 3 or newer
SuSE Linux Enterprise Server 9 or newer
Fedora Core 1 or newer
Debian GNU/Linux 3.1 or newer
Slackware 9.x or newer
Mandrake/Mandriva 9.1 or newer
FreeBSD 5.4 or newer
經jensbw前輩指導之後得知以下的效果原來是因為Qt 4.5為了要保留原生的Gnome的特效採用了QGtkStyle而造成的,並不是程式上得錯誤。而QGtkStyle則是Qt 4.5所強調的新功能之一,看來我真該好好去看一下這一篇What's New in Qt 4.5。
下面的截圖就是使用 Qt4.5去編譯Qt所提供的範例程式碼的樣子(在沒有更動任何程式碼的情況之下預設是使用QGtkStyle):
我們可以透過setStyle()的方法來指定想要的style,片段範例程式碼如下:
QGroupBox *gsGroupBox;
QGroupBox *clsGroupBox;
QCleanlooksStyle* clStyle;
void setupUi(QWidget *QDemoClass){
if (QDemoClass->objectName().isEmpty())
QDemoClass->setObjectName(QString::fromUtf8("QDemoClass"));
QDemoClass->resize(440, 178);
clStyle = new QCleanlooksStyle();
gsGroupBox = new QGroupBox(QDemoClass);
gsGroupBox->setObjectName(QString::fromUtf8("gsGroupBox"));
gsGroupBox->setGeometry(QRect(10, 10, 211, 161));
clsGroupBox = new QGroupBox(QDemoClass);
clsGroupBox->setObjectName(QString::fromUtf8("clsGroupBox"));
clsGroupBox->setGeometry(QRect(230, 10, 201, 161));
clsGroupBox->setStyle(clStyle);
retranslateUi(QDemoClass);
QMetaObject::connectSlotsByName(QDemoClass);
}
範例效果如下:
對於Qt 4.4的開發者而言,你可以參考jens的文章來讓你的程式也有GtkStyle的效果
http://labs.trolltech.com/page/Projects/Styles/GtkStyle
參考資料:
http://doc.trolltech.com/4.5/qt4-5-intro.html#graphics-enhancements
http://code.google.com/p/qgtkstyle/
http://labs.trolltech.com/page/Projects/Styles/GtkStyle
http://pepper.troll.no/s60prereleases/doc/qcleanlooksstyle.html
http://pepper.troll.no/s60prereleases/doc/qgtkstyle.html
最近看到有人使用陣列元素移除的方式來產生不重複亂數,下面就是一個簡單的不重複亂數產生的範例,亂數範圍0~99,產生10個不重複亂數。
Method One (Array Splice):
var numSet:Array = new Array();
var numArr:Array = new Array();
var randNum:int;
var n:int = 0;
for (var i:int = 0; i < 100; i++) {
numSet[n++] = i;
}
for (i = 0; i < 10; i++) {
randNum = Math.floor(Math.random()* 99);
numArr[i] = numSet[randNum];
numSet.splice(randNum, 1);
}
上面的程式碼裡,只要有出現過的數字就在集合裡把他給移除,這樣的作法很直觀易懂,但是所付出的運算時間代價也比較高。下面是另一個我比較常用的手法:
Method Two:
var numSet:Array = new Array();
var numArr:Array = new Array();
var randNum:int;
for (var i:int = 0; i < 100; i++) {
numSet[i] = 0;
}
for (i = 0; i < 10; i++) {
do {
randNum = Math.floor(Math.random()* 99);
} while (numSet[randNum]);
numSet[randNum] = 1;
numArr[i] = randNum;
}
numSet裡面所存的並不是亂數數字的集合,而是對應到每個數字的flag,用來標示此數字是否已經產生,這樣的作法乍看之下好像比耗空間(例如數字集合是在10000~20000時),但其實你可以透過一些簡單的運算技巧讓空間的消耗跟數字集合數成比例。
使用第二個方法的效能會比第一個快許多,尤其當集合數增大時,差距會更明顯。
最近在做一些效能的測試需要用到比較精準的Timer,分別查了一下給Windows和Linux平台的寫法。
For Linux:
在Linux裡,則是使用timeval來計算時間,理論上精準到micro second
#include <stdio.h>
#include <sys/time.h>
int main()
{
int i;
struct timeval t1, t2;
double elapsedTime;
gettimeofday(&t1, NULL);
gettimeofday(&t2, NULL);
elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;
elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;
printf("%.3f ms\n",elapsedTime);
return 0;
}
For Windows:
Windows部份使用了QueryPerformanceFrequency()這個指令,理論上是精準到跟時脈值一樣。
#include <stdio.h>
#include <windows.h>
int main()
{
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
double elapsedTime;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&t1);
QueryPerformanceCounter(&t2);
elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
printf("%.3f ms\n", elapsedTime);
return 0;
}
原以為FileReference的物件可以像在使用save()函式時宣告在method裡,沒想到這樣子會造成FIDbg10.ocx掛掉然後連帶著瀏覽器也被關閉。下面是一個會當掉的寫法:
package{
import flash.display.Sprite;
import flash.net.FileReference;
import flash.events.Event;
import flash.events.MouseEvent;
import com.xinyu.button.TextButton;
public class LoadFileDemo extends Sprite{
private var loadBtn:TextButton;
public function LoadFileDemo(){
loadBtn = new TextButton(80,20,10,1,0xCCCCCC,"Load",0x000000);
addChild(loadBtn);
loadBtn.addEventListener(MouseEvent.CLICK, onClicked);
}
private function onClicked(event:MouseEvent):void{
//declare the FileReference object inside the method that would lead to browser crash
var file:FileReference = new FileReference();
file.addEventListener(Event.SELECT, onFileSelected);
file.addEventListener(Event.COMPLETE, onFileLoaded);
file.browse();
}
private function onFileSelected(event:Event):void{
event.target.load();
}
private function onFileLoaded(event:Event):void{
trace(event.target.size);
}
}
}
可以看到上面的範例程式碼裡,我將FileReference的物件宣告在onClicked()這個Event Handler裡,拿去編譯執行後程式就會當掉。為了要解決這個問題,寫法要改成這樣:
package{
import flash.display.Sprite;
import flash.net.FileReference;
import flash.events.Event;
import flash.events.MouseEvent;
import com.xinyu.button.TextButton;
public class LoadFileDemo extends Sprite{
private var loadBtn:TextButton;
private var file:FileReference;
public function LoadFileDemo(){
loadBtn = new TextButton(80,20,10,1,0xCCCCCC,"Load",0x000000);
addChild(loadBtn);
loadBtn.addEventListener(MouseEvent.CLICK, onClicked);
}
private function onClicked(event:MouseEvent):void{
file = new FileReference();
file.addEventListener(Event.SELECT, onFileSelected);
file.addEventListener(Event.COMPLETE, onFileLoaded);
file.browse();
}
private function onFileSelected(event:Event):void{
event.target.load();
}
private function onFileLoaded(event:Event):void{
trace(event.target.size);
}
}
}
Installation pip install orange3 Run orange python -m Orange.canvas