用c#实现单链表(程序代码已经验证,完全正确)
C#開發EyeLink眼動儀的實驗程序
【題外話】
Eyelink眼動儀是SR Research推出的一款眼動儀,很多高校都在使用其做實驗。其官方提供了COM的接口,所以支持COM接口的開發平臺都可以開發使用。官方甚至提供了一個C#的樣例供參考,不過這個樣例相比起其他的VC++的樣例而言功能過于簡單,程序本身也比較亂,再加上國內關于EyeLink的資料又比較少,所以這里我簡要寫下我使用EyeLink眼動儀的開發框架,方便大家開發。
本文地址:http://www.cnblogs.com/mayswind/p/3417211.html
【文章索引】
準備工作
EyeLink實驗的操作流程
EyeLink的基本使用方法
EyeLink的高級使用方法
【一、準備工作】
雖然SR Research提供的大多數樣例都是基于VC++的,但由于C#開發快速高效,同時在實現復雜功能時C#更容易開發,所以我還是選擇了使用C#開發實驗程序,而不是VC++。SR Research比較有意思的是,官方雖然提供了所有的說明文檔以及開發工具包,但是這些都需要從官網的論壇才可以下載,而且官網論壇的注冊是需要人工審核的,所以注冊一定千萬不要著急。好在審核也不會慢,去除時差的原因沒有多久就能通過審核。
注冊完成以后,首先需要下載開發工具包,該工具包里包括了SDK文件以及幾個說明文檔還有幾個示例程序。地址見下:
https://www.sr-support.com/showthread.php?6-Windows-Display-Software
程序員開發手冊(EyeLink Programmers Guide.pdf)就在安裝后的目錄中的Docs中,雖然是C語言版本的而且也不是使用的COM接口,但對于了解SDK還是有幫助的。當然也可以單獨下載,地址見下:
https://www.sr-support.com/showthread.php?4-EyeLink-C-Programmers-Guides
所有的自帶樣例程序都在安裝目錄下的SampleExperiments目錄,COM接口的都在com目錄下,其中還有一個C#的樣例程序。而EyeLink提供的COM接口文件位于Libs目錄下,文件名為“SREyeLink.dll”,直接在C#里引用即可。
【二、EyeLink實驗的操作流程】
一般使用EyeLink進行實驗的都要遵循以下的流程:
1、程序開始界面,可能介紹本實驗的情況及提示接下來按什么鍵進入實驗等等。
2、一般正式進入實驗流程后首先按C鍵進入Calibration界面進行眼睛的校準,一般為9點校準(可以自行設置),如果一個點校準失敗會在所有點校準后重新校準。
3、眼睛校準成功后按V鍵進入Validation界面進行剛才校準結果的驗證。
4、驗證成功后開始實驗,一個實驗可能有多個Trail組成,在所有Trial之前或每個Trial之前可能需要Drift使受試者注視屏幕中心(位置可自定義)以繼續。
5、如果有多個Trial將依次執行。
【三、EyeLink的基本使用方法】
EyeLink提供的COM接口總體還是很友好的,常見的有以下幾個類:
1、EyeLink:與EyeLink操作主要的類,提供連接Host PC、創建EDF文件、向Host PC發送命令和向EDF中記錄信息等等,常見的方法如下:
| 方法名 | 含義 |
| void open("Host PC IP", 0); | 連接到指定Host PC,Host PC IP通常為100.1.1.1 |
| void close(); | 關閉指定連接 |
| void openDataFile("fileName"); | 在Host PC創建指定文件名的EDF文件以供記錄 |
| void receiveDataFile("fileName", "localPath"); | 將Host PC記錄的指定文件名的EDF傳回到本地路徑 |
| void startRecording(true, true, true, true); | 開始記錄注視點信息(四個選項可以設置是否記錄指定內容) |
| void stopRecording(); | 結束記錄注視點信息 |
| bool isRecording(); | 獲取是否正在記錄注視點信息 |
| void sendCommand("command"); | 向Host PC發送指定的指令 |
| void sendMessage("message"); | 向Host PC發送指定的信息,記錄在EDF文件中 |
| void doTrackerSetup(); | 進入實驗流程,允許用戶按C和V執行相應操作或按Enter顯示鏡頭內容 |
| void doDriftCorrect(x, y, true, true); | 要求受試者看住指定的(x, y)點以繼續 |
| void setOfflineMode(); | Host PC進入Offline Mode,通常實驗結束后要執行該操作 |
| shorteyeAvailable(); | 獲取攝像頭跟蹤到的眼睛(可以轉換為EL_EYE枚舉) |
| shortgetLastButtonPress(out pressTime); | 獲取按鍵信息(比如可以獲取連接在Host PC上的手柄的按鍵) |
| Sample getNewestSample(); | 獲取注視點信息 |
2、EyeLinkUtil:提供了幾個常用工具方法,如提供當前時間、進入實時模式等等。
| 方法名 | 含義 |
| void beginRealTimeMode(time); | 開始指定時間(毫秒)的實時模式 |
| voidendRealTimeMode(); | 結束實時模式 |
| double currentTime(); | 獲取當前時間(毫秒) |
| ELGDICal getGDICal(); | 獲取EyeLink GDICal |
3、ELGDICal:通過將窗口的Handle傳給該類,實現由EyeLink SDK控制直接在界面上繪制Calibration、Validation以及Drift等界面,同時開發者也可以直接在界面線程上執行邏輯或繪制相應內容,而無需擔心界面無響應而創建子線程等等。
| 方法名 | 含義 |
| void setCalibrationColors(foreColor, backColor); | 設置Calibration、Validation、Drift等界面的前景色和背景色 |
| voidsetCalibrationWindow(hWnd); | 設置上述界面的窗口Handle |
| voidenableKeyCollection(true/false); | 設置是否允許通過鍵盤控制EyeLink SDK操作(如C、V、Enter) |
EyeLink基本使用非常簡單,就如同給出的C#的樣例一樣,在這里給出一個簡單的使用,比如直接在界面線程上執行代碼(this這里就是指當前窗體):
1 EyeLink eyeLink = new EyeLink();//創建EyeLink對象
2 eyeLink.open("100.1.1.1", 0);//連接到Host PC
3 eyeLink.openDataFile("test.edf");//在Host PC創建指定文件名的EDF以供記錄
4 eyeLink.sendCommand("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON");//設置事件或記錄的內容,下同
5 eyeLink.sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS");
6 eyeLink.sendCommand("link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON");
7 eyeLink.sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS");
8 eyeLink.sendMessage(String.Format("DISPLAY_COORDS {0} {1} {2} {3}", this.Left, this.Top, this.Width, this.Height));//設置窗口的范圍
9
10 EyeLinkUtil eyeLinkUtil = new EyeLinkUtil();//創建EyeLinkUtil對象
11 ELGDICal gdiCal = eyeLinkUtil.getGDICal();//創建EyeLinkGDICal對象
12
13 gdiCal.setCalibrationWindow(this.Handle.ToInt32());//將窗體句柄傳給EyeLink SDK
14 gdiCal.enableKeyCollection(true);
15 eyeLink.doTrackerSetup();//開始進行設置階段(Calibration、Vadation等)
16 gdiCal.enableKeyCollection(false);
17
18 gdiCal.enableKeyCollection(true);
19 eyeLink.doDriftCorrect((Int16)(this.Width / 2), (Int16)(this.Height / 2), true, true);//所有Trial前或每個Trial前讓受試者注視指定位置以開始Trial
20 gdiCal.enableKeyCollection(false);
21
22 eyeLink.startRecording(true, true, true, true);//開始記錄EDF文件
23 Double st = eyeLinkUtil.currentTime();
24
25 while ((st + 20000) > eyeLinkUtil.currentTime())//循環20秒程序結束
26 {
27 EL_EYE eyeUsed = (EL_EYE)eyeLink.eyeAvailable();//獲取當前眼睛使用情況
28 Sample sample = eyeLink.getNewestSample();//獲取當前注視點
29
30 if (sample == null || eyeUsed == EL_EYE.EL_EYE_NONE)
31 {
32 continue;
33 }
34
35 if (eyeUsed == EL_EYE.EL_BINOCULAR)
36 {
37 eyeUsed = EL_EYE.EL_LEFT;//如果兩只眼睛同時被捕捉到則使用左眼
38 }
39
40 Single x = sample.get_gx(eyeUsed);//獲取注視點相對窗口的橫坐標
41 Single y = sample.get_gy(eyeUsed);//獲取注視點相對窗口的縱坐標
42 Single pa = sample.get_pa(eyeUsed);//獲取瞳孔大小
43
44 if (x == (Single)EL_CONSTANT.EL_MISSING_DATA || y == (Single)EL_CONSTANT.EL_MISSING_DATA || pa <= 0)
45 {
46 continue;
47 }
48
49 //TODO: 在這里處理x、y坐標等
50 }
51
52 eyeLink.sendCommand("close_data_file");//關閉Host PC上的EDF文件
53 eyeLink.receiveDataFile("test.edf", "本機文件路徑");//將Host PC上的EDF文件傳回到測試機
54
55 eyeLink.setOfflineMode();//將Host PC設置為離線狀態(非實驗進行狀態)
56 eyeLink.stopRecording();//結束記錄EDF文件
57 eyeLink.close();//關閉與Host PC的連接
58
59 eyeLink = null;
60 eyeLinkUtil = null;
【四、EyeLink的高級使用方法】
有了上述這些還不夠,比如:
1、雖然上述我們能記錄EDF文件,但是如果有多個Trial的話我們還希望能在EDF文件中予以區分。所以在每個Trial前后我們還需要這樣去做:
1 eyeLink.sendCommand(String.Format("record_status_message 'TRIAL {0}'", trialIndex));
2 eyeLink.sendMessage(String.Format("TRIALID {0}", trialIndex));
3 eyeLink.startRecording(true, true, true, true);
4
5 //Trial邏輯
6
7 eyeLink.stopRecording();
2、如果我們在Trial中顯示了圖片,同時我們希望EDF Viewer在查看時也能將對應的圖片顯示出來,那么我們還需要在startRecording之前執行這樣一句話,去讓EDF Viewer打開指定路徑下的圖片。
1 eyeLink.sendMessage(String.Format("!V IMGLOAD FILL {0}", imagePath));
3、如果我們希望受測者能夠使用按鍵進行Trial的操作(比如按某鍵表示完成或跳過該Trial之類的),我們還需要獲取按鍵的信息,其中如果我們還需要在EDF文件中顯示出按鍵的情況,我們還需要向Host PC發送一條信息(下列代碼中第6行)。
1 Double pressTime = 0;
2 Int16 buttonCode = eyeLink.getLastButtonPress(out pressTime);
3
4 if (buttonCode != 0)
5 {
6 eyeLink.sendMessage(String.Format("ENDBUTTON {0}", buttonCode));
7 //TODO: 執行相關按鍵的邏輯
8 }
4、如果我們希望在Drift的過程中,受測者在將眼睛注視到指定的點后還需要按指定的鍵才能繼續的話,我們還需要在開始的配置過程中寫如下的代碼(這里使用的是按鍵5):
1 eyeLink.sendCommand("button_function 5 'accept_target_fixation'");
附一個我寫的C#上使用EyeLink的框架(MIT LICENSE):http://files.cnblogs.com/mayswind/DotMaysWind.EyeLink.rar
【相關鏈接】
1、SR Research Support Site:https://www.sr-support.com/forums/
|
如果您覺得本文對您有所幫助,不妨點擊下方的“推薦”按鈕來支持我! 本文及文章中代碼均基于“署名-非商業性使用 3.0”,文章歡迎轉載,但請您務必注明文章的作者和出處鏈接,如有疑問請私信我聯系! 我在參加51CTO的博客比賽,如果您覺得我還不錯,請投我一票,謝謝!http://blog.51cto.com/contest2013/1788535 |
分類:C#
標簽:.NET,C#,Winform,SR Research,EyeLink,眼動儀,實驗
用c#實現單鏈表(程序代碼已經驗證,完全正確)
1.程序的大致結構如下圖:
2.下面依次列出各個類的代碼
①ILISTDs.cs 這是一個接口類,列出單鏈表的方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 單鏈表
{
public interface IListDs<T>
{
int GetLength();//求長度
void Clear();//清空操作
bool IsEmpty();//判斷線性表是否為空
void Append(T item);//附加操作
void Insert(T item,int i);//插入操作
T Delete(int i);//刪除操作
T GetElem(int i);//取表元
int Locate(T value);//按值查找
}
}
②LinkList.cs 單鏈表的實現類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 單鏈表
{
public class LinkList<T> : IListDs<T>
{
private Node<T> head;//單鏈表的頭引用
//頭引用的屬性
public Node<T> Head
{
get
{
return head;
}
set
{
head = value;
}
}
//構造器
public LinkList()
{
head = null;
}
//求單鏈表的長度
public int GetLength()
{
Node<T> p = head;
int len = 0;
while (p != null)
{
p = p.Next;
len++;
}
return len;
}
//清空單鏈表
public void Clear()
{
head = null;
}
//判斷是否為空
public bool IsEmpty()
{
if (head == null)
{
return true;
}
else
{
return false;
}
}
//在單鏈表的末尾添加新元素
public void Append(T item)
{
Node<T> q = new Node<T>(item);
Node<T> p = new Node<T>();
if (head == null)
{
head = q;
return;
}
p = head;
while (p.Next != null)
{
p = p.Next;
}
p.Next = q;
}
//在單鏈表第i個位置前面插入一個值為item的節點
public void Insert(T item, int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine("鏈表為空或者位置錯誤");
return;
}
if (i == 1)
{
Node<T> q = new Node<T>(item);
q.Next = head;
head = q;
return;
}
Node<T> p = head;
Node<T> r = new Node<T>();
int j = 1;
while (p.Next != null && j < i)
{
r = p;
p = p.Next;
j++;
}
if (j == i)
{
Node<T> q = new Node<T>(item);
Node<T> m = r.Next;
r.Next = q;
q.Next = m;
}
}
//在單鏈表第i個位置后面插入一個值為item的節點
public void InsertPost(T item, int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine("鏈表為空或者位置錯誤");
return;
}
if (i == 1)
{
Node<T> q = new Node<T>(item);
q.Next = head.Next;
head.Next = q;
return;
}
Node<T> p = head;
Node<T> r = new Node<T>();
int j = 1;
while (p.Next != null && j <= i)
{
r = p;
p = p.Next;
j++;
}
if (j == i+1)
{
Node<T> q = new Node<T>(item);
Node<T> m = r.Next;
r.Next = q;
q.Next = m;
}
else
{
Console.WriteLine("插入位置過大,error");
}
}
public T Delete(int i)
{
if (IsEmpty() || i < 1)
{
Console.WriteLine("鏈表為空或者位置錯誤");
return default(T);
}
Node<T> q = new Node<T>();
if (i == 1)
{
q = head;
head = head.Next;
return q.Data;
}
Node<T> p = head;
int j = 1;
while (p.Next != null && j < i)
{
q = p;
p = p.Next;
j++;
}
if (j == i)
{
q.Next = p.Next;
return p.Data;
}
else
{
Console.WriteLine("位置不正確");
return default(T);
}
}
//獲得單鏈表第i個元素
public T GetElem(int i)
{
if (IsEmpty())
{
Console.WriteLine("鏈表是空鏈表");
return default(T);
}
Node<T> p = new Node<T>();
p = head;
int j=1;
while(p.Next!=null&&j<i)
{
p = p.Next;
j++;
}
if (j == i)
{
return p.Data;
}
else
{
Console.WriteLine("位置不正確!");
}
return default(T);
}
//在單鏈表中查找值為value的節點
public int Locate(T value)
{
if (IsEmpty())
{
Console.WriteLine("鏈表是空鏈表!");
return -1;
}
Node<T> p = new Node<T>();
p = head;
int i = 1;
while (((p.Next!=null)&&(!p.Data.Equals(value))))
{
p = p.Next;
i++;
}
if (p == null)
{
Console.WriteLine("不存在這樣的節點。");
return -1;
}
else
{
return i;
}
}
}
}
③ Node.cs 節點類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 單鏈表
{
public class Node<T>
{
private T data;//數據域
private Node<T> next;//引用域
//構造器
public Node(T val, Node<T> p)
{
data = val;
next = p;
}
//構造器
public Node(Node<T> p)
{
next = p;
}
//構造器
public Node(T val)
{
data = val;
}
//構造器
public Node()
{
data = default(T);
next = null;
}
//數據域屬性
public T Data {
get {
return data;
}
set {
data = value;
}
}
//引用域屬性
public Node<T> Next {
get {
return next;
}
set {
next = value;
}
}
}
}
④Program.cs 主程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 單鏈表
{
class Program
{
static void Main(string[] args)
{
LinkList<string> link = new LinkList<string>();
link.Append("123");
link.Append("567");
link.Append("jqk");
link.Insert("abc",2);
link.InsertPost("def",2);
int length = link.GetLength();
int k=link.Locate("567");
string m=link.GetElem(3);
Console.WriteLine("567的位置為"+k);
Console.WriteLine("位置為3的值為"+m);
Console.WriteLine("鏈表的長度為"+length);
Node<string> n = link.Head;
while (n != null)
{
Console.WriteLine(n.Data);
n = n.Next;
}
}
}
}
⑤運行結果如下圖,和預測結果完全一致
總結
以上是生活随笔為你收集整理的用c#实现单链表(程序代码已经验证,完全正确)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十万个为什么儿童版_把中国科技传播至阿语
- 下一篇: 把你的树莓派打造成一个NAS,并实现多硬