者:科研貓 | 老丁
責編:科研貓 | 依米
如果我們在PubMed中輸入檢索關鍵詞,往往會出現海量的文獻。盡管都是經過同行評議、理論上可信的科研成果,但其質量也是良莠不齊。如何在其中挑選出高質量的文獻,有一條捷徑就是看文獻所在雜志的影響因子。高影響因子的雜志,往往文章審核機制更為健全、科研成果更為可信、權威。
今天我們來講講,如何在新版PubMed中實現基于影響因子分值的文獻篩選,需要用到PubMed里面的自定義過濾器(Custom Filter)的功能。
步驟1:
PubMed現在已經關掉了老版本入口,現在的新版本要求,必須先進行注冊、登錄后才能進行篩選條件的設置。
登錄后點擊右上角的用戶名,會有下拉菜單。點擊Dashboard,這里就是老版本的MyNCBI入口,可以進行多項篩選項目的設置。
MyNCBI里面的內容很多,我們需要找到Filters(篩選器),點擊Manage Filters選型進行設置。
點擊Manage Filters選型后,我們就可以進行Create custom filter的設置了。
步驟2:
Custom Filter的本質是二次過濾,是對用戶輸入關鍵詞(例如:cancer and drug、PCOS、HIV等)后,搜索到的文獻進行二次篩選,更加快速精準的找到滿足用戶需要的文獻。
自定義過濾器包含對特定期刊的搜索,這是IF過濾條件的形成由來。例如,設置過濾條件IF>20,即把IF在20以上的期刊名都列出來,通過OR連接詞,組成搜索語句,從已搜索到的文獻進行二次過濾,把IF在10以上的期刊文獻羅列出來。
有兩點需要特別注意一下。就是新版的PubMed,對自定義過濾器的字符進行了限制,單個自定義的過濾器字符不超過4000,所有自定義過濾器包含的字符數不能超過10000。也就是說,自定義過濾器須同時滿足這兩個條件,才能實現自定義的檢索。
進過科研貓的試驗發現,基于期刊ISSN號而構成的IF過濾器,使得自定義過濾器在不超過字符數限制的情況下可以檢索到更多的文獻。原因是,期刊名的字符數要遠遠多于ISSN號。
建議IF過濾區間可以分為IF≥20,20≥IF≥10,10≥IF≥7,7≥IF≥6,6≥IF≥5(僅供參考),可自主設置。 隨著影響因子的降低,5分以下每1分的影響因子間隔,它所包含的期刊ISSN號檢索組合都會超過4000。所以,如果非常需要對影響因子進行更小分值的劃分,可自行試驗區間。
步驟3:
安裝Scholarscope插件:Scholarscope它能自動加載 PubMed 期刊的影響因子,幫助用戶篩選有用的期刊;添加文獻下載鏈接,在校外也能一鍵下載文獻(基于Sci-Hub)。有了Scholarscope插件,盡管不能對5分以下的期刊進行自定義篩選設置,也可以在瀏覽檢索結果的過程中,直接看到對應影響因子。
插件下載地址:
https://www.scholarscope.cn/
該插件的安裝教程詳見:
http://blog.scholarscope.cn/how-to-install/
不是對所有瀏覽器都支持,僅支持chrome、火狐、360、QQ瀏覽器等,當然也僅能在此瀏覽器的PubMed頁面中才能加載影響因子,同一電腦的另一未安裝插件的瀏覽器是不能加載的。
以火狐瀏覽器為例:點擊下載后,會陸續有兩個彈窗;點擊添加。
檢驗是否安裝成功了呢?打開PubMed頁面輸入任意關鍵詞,看檢索出來的文獻是否帶有影響因子標志。
步驟4:
在Scholarscope生成PubMed的影響因子Filter,直接打開網站,https://www.scholarscope.cn/tools/issn.html
輸入IF范圍,沒有無效提示的情況下,點擊「確定」即可生成,「復制到剪貼板」,備用后面的設置。根據步驟1中操作進入Create custom filter的設置。
在下圖中Query terms中填入「復制到剪貼板」中的內容,在Save filter as中填入影響因子區間,保存即可。
給Active框打勾,才可激活Filter,choose another database中會顯示激活的篩選條目個數。
我們可以輸入任意關鍵詞搜索內容,可以看到自定義設置的IF過濾器位于屏幕的左側。為什么只選擇了4個篩選條目,沒有加上“6≥IF≥5”呢,那是因為,這樣的話,總的字符數會超過10000,無法進行檢索。所以,可以進行分批次的影響因子自定義篩選。
注意事項:
1)注意檢查NCBI賬號是否已登陸。
2)單個自定義過濾器的字符數是否超過4000,所有自定義過濾器包含的字符數是否超過10000。
3)創建filter,是否已經點Active。無法一次性全部active 的時候,不要忘記進行分批。
參考資料:
[1]https://www.nlm.nih.gov/pubs/techbull/jf18/jf18_pm_filters.html
[2]https://www.ncbi.nlm.nih.gov/books/NBK53591/
科研貓小平臺,大功能。本公眾號旨在傳播生物醫學科研技能和生物信息學基礎知識及應用技巧,助您在大數據時代精準挖掘科研數據,讓您輕輕松松學知識,順順利利發文章。
xPython是一個開發桌面端圖形界面的跨平臺函數庫,開發語言為Python,它是基于C++的函數庫wxWidgets的封裝。
私信小編01即可獲取Python學習資料
wxpython有大量組件,它們可以從邏輯上(注意是邏輯上)這樣劃分:
(1)基礎組件
這些組件為其所派生的子組件提供基礎功能,通常不直接使用。
(2)頂層組件
?
這些組件相互獨立存在。
(3)容器
?
這些組件包含其他組件。
(4)動態組件
?
這些組件可以被用戶所交互編輯。
(5)靜態組件
?
這些組件用來展示信息,無法被用戶所交互編輯。
(6)其他組件
?
這些組件包括狀態欄、工具欄、菜單欄等。
除了邏輯上的劃分,各個組件之間還存在著繼承關系,以一個button組件為例:
?
Button是一個小window,具體地,它是繼承自wx.Control這一類的window(有些組件是window,但不是繼承自wx.Control,比如wx.Dialog,更具體來說,controls這類組件是可放置在containers這類組件上的組件)。同時所有的windows都可以響應事件,button也不例外,因此它還繼承自wx.EvtHandler。最后,所有的wxpython對象都繼承自wx.Object類。
這個例子是wxPython的最小可用例子,用來say hello to the world:
import wx
app=wx.App()
frame=wx.Frame(None, title='Hello World')
frame.Show()
app.MainLoop()
麻雀雖小五臟俱全,該例子包含了最基本的代碼和組件:
(1)首先導入wxPython庫:
import wx
wx可視為一個命名空間,后面所有的函數和類都以它開頭。
(2)創建應用實例:
app=wx.App()
每一個wxPython程序都必須有一個應用實例。
(3)創建應用框架并顯示:
frame=wx.Frame(None, title='Hello World')
frame.Show()
這里創建了一個wx.Frame對象。wx.Frame是一個重要的“容器”組件,它用來承載其他組件,它本身沒有父組件(如果我們給組件的parent參數設為None,即代表該組件沒有父組件)。創建該對象后,還需調用Show方法才能顯示出來。
wx.Frame的構造函數一共有七個參數:
wx.Frame(wx.Window parent, int id=-1, string title='', wx.Point pos=wx.DefaultPosition,
wx.Size size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, string name="frame")
除了第一個parent參數需要顯式指定,其余六個都有默認值,包括ID、名稱、位置、尺寸和樣式等。因此,可以通過改變這些參數來進一步地對該frame進行個性化定制。
(4)啟動程序主循環:
app.MainLoop()
程序的主循環是一個無限循環模式,它捕獲并分發程序生命周期內的所有事件。
菜單欄主要由三部分組成:wx.MenuBar、wx.Menu和wx.MenuItem。
在菜單欄MenuBar中可以添加菜單Menu,在菜單Menu中又可以添加菜單項MenuItem。
添加完后不要忘了使用SetMenuBar來將菜單欄加入到框架中。
進一步地,在某個菜單Menu中,還可以添加子菜單SubMenu,然后繼續添加菜單項。
還可以給菜單設置圖標、快捷鍵、對wx.EVT_MENU事件的動作、菜單樣式(打勾、單選)等。
上下文菜單有時叫做“彈出菜單”,比如右鍵某個位置,出現上下文選項。
工具欄的添加也是類似流程:先添加工具欄CreateToolBar,然后在上面添加工具AddTool。
別忘了使用toolbar.Realize()使之呈現出來(這一步與操作系統有關,Linux上不強制使用,Windows必須使用,為了跨平臺性,最好將這一步明確寫出)。
對于某個工具,可以設置邏輯使之Enable或Disable,常見的比如undo和redo,這兩個按鈕不是一直可以點的,在最開始時redo就必須是disabled,因為沒有歷史操作,所以可以設置具體的邏輯使之disable掉。
狀態欄即底部顯示當前狀態的狀態條。
布局可以分為絕對布局和布局管理器sizer。絕對布局有很多缺點,比如:
(1)組件的尺寸和位置不隨窗口的改變而改變;
(2)不同平臺上應用程序可能顯示不同;
(3)字體的改變可能破壞布局;
(4)如果想改變布局,必須將之前的全部推翻。
因此,推薦使用布局管理器sizer來管理布局。
wxPython常用的sizer有:wx.BoxSizer、wx.StaticBoxSizer、wx.GridSizer、wx.FlexGridSizer、wx.GridBagSizer。
wx.BoxSizer是最常見的布局管理器。它的常用設置有:
(1)排列方向:wx.VERTICAL垂直排列還是wx.HORIZONTAL水平排列;
(2)排列比例:一個布局中所包含的組件的尺寸由其比例所決定,比例為0表示在窗口尺寸變化時保持尺寸不變,其他比例系數表示組件在該布局管理器中的尺寸占比;且通常使用wx.EXPAND旗標來使得組件占據管理器分配給它的所有空間;
(3)邊界:組件的邊界大小可以自定義設置,同時具體哪個邊界(上下左右或全部)都可以任意指定;
(4)對齊方式:可以設定左端對齊、右端對齊、頂部對齊、底部對齊、中心對齊等多種對齊方式;
(5)在某一級容器組件中,使用SetSizer()來為其指定布局管理器;
(6)在布局管理器中用Add()方法來添加組件。
wx.StaticBoxSizer是在BoxSizer周圍加上了一個靜態文本框的顯示。
wx.GridSizer是網格布局管理器,可以設置幾排幾列以及橫縱的間距,網格中的組件尺寸都是相同的。
(如果有的網格不需要添加組件,可以添加沒有內容的StaticText作為占位符)
wx.FlexGridSizer與wx.GridSizer類似,但其更靈活,它不要求網格中所有的組件尺寸都相同,而是在同一行中的所有組件都高度相同,而同一列中的所有組件都寬度相同。
它還可以設置能growable的行和列,即在當前sizer中如果有空間,就將特定的行和列調整相應的大小來占據這個空間(注意將該行或列中的組件設為expandable)。
wx.GridBagSizer是wxPython中最靈活的sizer(不僅僅是wxPython,其他函數庫也有類似的配置),它可以顯式地指定sizer中組件所占據的區域,比如橫跨幾行幾列等。
它的構造函數很簡單:
wx.GridBagSizer(integer vgap, integer hgap)
只需設定間距,然后通過Add()方法添加組件:
Add(self, item, tuple pos, tuple span=wx.DefaultSpan, integer flag=0,
integer border=0, userData=None)
pos參數指定組件在這個虛擬網格中的起始位置,(0, 0)就代表左上角,span就指定它橫跨幾行幾列,比如(3, 2)代表占據3行2列。
如果想組件可以隨窗口伸縮,別忘了設置expandle屬性,及:
AddGrowableRow(integer row)
AddGrowableCol(integer col)
大部分的問題出現在:
(1)設置比例proportional錯誤,只有需要隨窗口變化的組件和sizer才需要設置為非0,其他都設置為0。且sizer和里面的組件可分別設置,比如下面的:
self.panel=wx.Panel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
vbox=wx.BoxSizer( wx.VERTICAL )
hbox1=wx.BoxSizer( wx.HORIZONTAL )
self.st1=wx.StaticText( self.panel, wx.ID_ANY, u"Class Name", wx.DefaultPosition, wx.DefaultSize, 0 )
self.st1.Wrap( -1 )
hbox1.Add( self.st1, 0, wx.RIGHT, 8 )
self.tc=wx.TextCtrl( self.panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
hbox1.Add( self.tc, 1, 0, 5 )
vbox.Add( hbox1, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, 10 )
在vbox中添加了hbox1,hbox1中又添加了靜態文本框st1和輸入框tc,hbox1的比例為0,代表在vbox這一垂直排列的管理器變化時,hbox1尺寸不變化,但tc的比例又為1,所以vbox在垂直變化時,tc按著hbox1不變化,但vbox水平變化時,tc就會隨著變化。這樣就有非常高的適應性。
(總結起來:看是否expandable要看組件所在的sizer!!)
(2)邊界border尺寸設置不統一,導致對不齊
(3)Expandable屬性和proportion兩個中有一個忘了設置,導致組件不能隨窗口伸縮。
這個sizer的編寫此處可以借助wxFormBuilder工具來進行設計,實現所想即所得。(wxFormBuilder能夠實現即時的改變,但此處遇到一個小問題,在wxGridBagSizer設置了某列進行可伸縮后,在wxFormBuilder中卻不能正確伸縮,反而generate code后直接調用能正確伸縮,所以也不能完全相信,但可以99%相信,實在調不通后可以換種運行方式接著調)
事件是一個圖形界面程序的核心部分,任何圖形界面程序都是事件驅動的。
事件可以有多重產生方式,大部分是用戶觸發的,也有可能由其他方式產生,比如網絡連接、窗口管理和計時器調用等。
關于事件,這里面有幾個過程和要素:
事件循環Event Loop(比如wxPython的MainLoop()方法),它一直在尋找和捕獲事件Event(比如wx.EVT_SIZE、wx.EVT_CLOSE等);當捕獲到事件后,就通過分發器dispatcher將事件分發到事件句柄Event Handler(事件句柄是對事件進行響應的動作方法);事件本身與事件句柄的映射由Event Binder來完成(即Bind()方法)。
對用戶編程來說,最常打交道的就是Bind()方法:
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
溯源起來,該Bind()方法是定義在EvtHandler類中,而EvtHandler又派生了Window類,Window類又是絕大多數組件的父類,因此可以在組件中直接使用該方法(如果想將事件解綁,則可以調用Unbind()方法,其參數跟下面的參數相同)。
event參數就是事件對象,它指定了事件類型;
handler就是對該事件的響應方法,這個通常要由編程自定義完成;
source是指該事件來自于哪個組件,比如很多組件都能產生同樣的事件,就需要指定具體的來源,比如很多button都能產生鼠標點擊事件。這里面就有一個很tricky的地方,假設self是一個panel,該panel上有很多buttons,名為bt1、bt2,那么self.Bind(event, handler, source=self.bt1)和self.bt1.Bind(event, handler)有什么區別呢?兩者看起來的效果是相同的, 這里有一個帖子詳細說明了兩者的區別 ;
id是通過ID來指定事件來源,而上面的source是通過直接指定實例,兩者目的相同;關于組件的ID,主要有兩種創建方式:
(1)讓系統自動創建:即使用-1或wx.ID_ANY,系統自動創建的ID都是負數,因此用戶自己創建的ID都應該是正數,此種情況通常用于不用改變狀態的組件。可以使用GetId()來獲取該隱形id;
(2)標準ID:wxPython提供了一些標準IDs,比如wx.ID_SAVE、wx.ID_NEW等;
id2是指定多個IDs,上面的id是一次只能指定單個ID。
這里面有個很好玩的用法,如果想批量給多個同類組件綁定事件,可以用lambda函數,比如:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
__author__='huangbinghe@gmail.com'
import wx
class TestFrm(wx.Frame):
"""TestFrm"""
def __init__(self, *arg, **kw):
super().__init__(*arg, **kw)
panel=wx.Panel(self, -1)
box=wx.BoxSizer(wx.VERTICAL)
for i in range(5):
btn=wx.Button(panel, -1, label="test-{}".format(i))
btn.Bind(wx.EVT_BUTTON, lambda e, mark=i: self.on_click(e, mark))
box.Add(btn, 0, wx.LEFT)
panel.SetSizer(box)
def on_click(self, event, mark):
wx.MessageDialog(self, 'click mark:{}'.format(
mark), 'click btn', wx.ICON_INFORMATION).ShowModal()
if __name__=='__main__':
app=wx.App()
frm=TestFrm(None, title="hello world")
frm.Show()
app.MainLoop()
有兩種類型的事件:basic events和command events。它們兩者的區別在于是否傳播上。事件的傳播是指事件從觸發該事件的子組件開始,傳遞給其父組件,并觀察其響應。Basic events不傳播,而command events傳播。比如wx.CloseEvent就是一個basic event,它不傳播,因為如果傳播給父組件就很沒有道理。
默認情形下,在事件句柄中的事件是阻止傳播的,如果想讓它繼續傳播,需要調用skip()方法(這個也解釋了上面的self.Bind(event, handler, source=self.bt1)和self.bt1.Bind(event, handler)的區別)。
窗口移動事件:wx.EVT_MOVE
窗口銷毀事件:wx.EVT_CLOSE,發生在點擊工具欄的關閉按鈕、Alt+F4或從開始菜單關閉計算機時(注意銷毀窗口是destroy()方法)
按鈕事件:wx.EVT_BUTTON,點擊一個按鈕時
菜單事件:wx.EVT_MENU,點擊一個菜單時
繪圖事件:wx.EVT_PAINT,改變窗口尺寸或最大化窗口時(最小化窗口時不會產生該事件)
焦點事件:wx.EVT_SET_FOCUS,當某組件成為焦點時;wx.EVT_KILL_FOCUS,當某組件失去焦點時
鍵盤事件:wx.EVT_KEY_DOWN,鍵盤按下;wx.EVT_KEY_UP,鍵盤彈起;wx.EVT_CHAR,這個應該是為了兼容非英語字符。
對話框是一種非常重要的人機交互的手段,可以使得用戶輸入數據、修改數據、更改程序配置等。
消息對話框是為了向用戶展示消息,可以通過一些預定義的旗標來定制消息對話框的按鈕和圖標,如下圖所示:
?
若想自定義對話框,只需繼承wx.Dialog即可。
wxPython提供了大量基礎組件,如:
基礎按鈕Button;
圖形按鈕BitmapButton;
切換按鈕ToggleButton(有兩種狀態可以切換:按下和未按下);
靜態文本框StaticText(展示一行或多行只讀文本);
文本輸入框TextCtrl;
富文本輸入框RichTextCtrl可以加入圖像、文字色彩等效果;
帶格式文本輸入框StyledTextCtrl;
超鏈接HyperLinkCtrl;
靜態位圖:StaticBitmap;
靜態分割線StaticLine(可垂直可水平);
靜態框StaticBox(為了裝飾用,將多個組件組合在一起顯示);
下拉列表框ComboBox;
可編輯的下拉列表框Choice;
復選框CheckBox(有兩個狀態:勾選或未勾選);
單選按鈕RadioButton(單選按鈕是從一組選項中只能選擇一個,將多個單選按鈕組合成一個選項組時,只需設定第一個單選按鈕style為wx.RB_GROUP,后面跟著的那些單選按鈕就自動跟它一組,如果想另開一組,只需再將另一組的第一個單選按鈕的style設置為wx.RB_GROUP);
進度條Gauge;
滑動條Slider;
整數數值調節鈕SpinCtrl;
浮點數數值調節鈕SpinCtrlDouble;
滾動條ScrollBar。
列表框ListBox:是對一組選項的展示和交互,它有兩個主要的事件,一個是wx.EVT_COMMAND_LISTBOX_SELECTED,即鼠標單擊某一項時產生;另一個是wx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED,即鼠標雙擊某一項時產生。
列表視圖ListCtrl:也是用來展示一組選項,與ListBox不同的是,ListBox僅能展示一列,而ListCtrl能展示多列。ListCtrl有三種視圖模式:list、report和icon。向ListCtrl中插入數據需要使用兩種方法:首先使用InsertItem()方法獲得行號,然后再在當前行中使用SetItem()方法在列中插入數據。
Mixins:Mixins增強了ListCtrl的功能,它們都在wx.lib.mixins.listctrl這個模塊中,一共有六種Mixins:
(1)wx.ColumnSorterMixin:使得在report視圖中對列進行排序;
(2)wx.ListCtrlAutoWidthMixin:自動調整最后一列的寬度來占據剩余的空間;
(3)wx.ListCtrlSelectionManagerMix:定義了與系統無關的選擇策略;
(4)wx.TextEditMixin:使得可以編輯文本;
(5)wx.CheckListCtrlMixin:給每一行增加了一個復選框;
(6)wx.ListRowHighlighter:候選行自動背景高亮。
wx.html.HtmlWindow:用來展示HTML頁面。
wx.SplitterWindow:包含兩個子窗口(如果使用wxFormBuilder,注意手動添加上兩個panel)
另外還有比如:
樹狀結構TreeCtrl;
表格Grid;
搜索框SearchCtrl;
調色板ColourPickerCtrl;
字體設置器FontPickerCtrl;
文件選擇器FilePickerCtrl;
文件目錄選擇器DirPickerCtrl;
文件樹選擇器GenericDirCtrl;
日期選擇器DatePickerCtrl;
日歷CalenderCtrl。
wxPython的繪圖之前寫過,參見以下兩篇:
ImagePy解析:6 — wxPython GDI繪圖和FloatCanvas
ImagePy解析:11 — 使用wxPython設備上下文繪圖
如上,wxPython的常用組件已經有很多,但仍然不能涵蓋真實情況下的千奇百怪的需求,這時候就要根據自己的需求自定義組件。
自定義組件有兩種方式:一種是在現有組件的基礎上修改或增強,這種方式仍然有一定的限制;另一種是結合wxPython的GDI繪圖,自己從頭創建組件,這種方式就具有極大的靈活性。
從頭繪制組件一般都是在wx.Panel基礎上進行創建。
下面給了一個俄羅斯方塊的游戲程序代碼,可以說是一個使用wxPython編寫GUI程序的集大成者:
#!/usr/bin/python
"""
ZetCode wxPython tutorial
This is Tetris game clone in wxPython.
author: Jan Bodnar
website: www.zetcode.com
last modified: April 2018
"""
import wx
import random
class Tetris(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, size=(180, 380),
style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX)
self.initFrame()
def initFrame(self):
self.statusbar=self.CreateStatusBar()
self.statusbar.SetStatusText('0')
self.board=Board(self)
self.board.SetFocus()
self.board.start()
self.SetTitle("Tetris")
self.Centre()
class Board(wx.Panel):
BoardWidth=10
BoardHeight=22
Speed=300
ID_TIMER=1
def __init__(self, *args, **kw):
# wx.Panel.__init__(self, parent)
super(Board, self).__init__(*args, **kw)
self.initBoard()
def initBoard(self):
self.timer=wx.Timer(self, Board.ID_TIMER)
self.isWaitingAfterLine=False
self.curPiece=Shape()
self.nextPiece=Shape()
self.curX=0
self.curY=0
self.numLinesRemoved=0
self.board=[]
self.isStarted=False
self.isPaused=False
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER)
self.clearBoard()
def shapeAt(self, x, y):
return self.board[(y * Board.BoardWidth) + x]
def setShapeAt(self, x, y, shape):
self.board[(y * Board.BoardWidth) + x]=shape
def squareWidth(self):
return self.GetClientSize().GetWidth() // Board.BoardWidth
def squareHeight(self):
return self.GetClientSize().GetHeight() // Board.BoardHeight
def start(self):
if self.isPaused:
return
self.isStarted=True
self.isWaitingAfterLine=False
self.numLinesRemoved=0
self.clearBoard()
self.newPiece()
self.timer.Start(Board.Speed)
def pause(self):
if not self.isStarted:
return
self.isPaused=not self.isPaused
statusbar=self.GetParent().statusbar
if self.isPaused:
self.timer.Stop()
statusbar.SetStatusText('paused')
else:
self.timer.Start(Board.Speed)
statusbar.SetStatusText(str(self.numLinesRemoved))
self.Refresh()
def clearBoard(self):
for i in range(Board.BoardHeight * Board.BoardWidth):
self.board.append(Tetrominoes.NoShape)
def OnPaint(self, event):
dc=wx.PaintDC(self)
size=self.GetClientSize()
boardTop=size.GetHeight() - Board.BoardHeight * self.squareHeight()
for i in range(Board.BoardHeight):
for j in range(Board.BoardWidth):
shape=self.shapeAt(j, Board.BoardHeight - i - 1)
if shape !=Tetrominoes.NoShape:
self.drawSquare(dc,
0 + j * self.squareWidth(),
boardTop + i * self.squareHeight(), shape)
if self.curPiece.shape() !=Tetrominoes.NoShape:
for i in range(4):
x=self.curX + self.curPiece.x(i)
y=self.curY - self.curPiece.y(i)
self.drawSquare(dc, 0 + x * self.squareWidth(),
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
self.curPiece.shape())
def OnKeyDown(self, event):
if not self.isStarted or self.curPiece.shape()==Tetrominoes.NoShape:
event.Skip()
return
keycode=event.GetKeyCode()
if keycode==ord('P') or keycode==ord('p'):
self.pause()
return
if self.isPaused:
return
elif keycode==wx.WXK_LEFT:
self.tryMove(self.curPiece, self.curX - 1, self.curY)
elif keycode==wx.WXK_RIGHT:
self.tryMove(self.curPiece, self.curX + 1, self.curY)
elif keycode==wx.WXK_DOWN:
self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
elif keycode==wx.WXK_UP:
self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
elif keycode==wx.WXK_SPACE:
self.dropDown()
elif keycode==ord('D') or keycode==ord('d'):
self.oneLineDown()
else:
event.Skip()
def OnTimer(self, event):
if event.GetId()==Board.ID_TIMER:
if self.isWaitingAfterLine:
self.isWaitingAfterLine=False
self.newPiece()
else:
self.oneLineDown()
else:
event.Skip()
def dropDown(self):
newY=self.curY
while newY > 0:
if not self.tryMove(self.curPiece, self.curX, newY - 1):
break
newY -=1
self.pieceDropped()
def oneLineDown(self):
if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
self.pieceDropped()
def pieceDropped(self):
for i in range(4):
x=self.curX + self.curPiece.x(i)
y=self.curY - self.curPiece.y(i)
self.setShapeAt(x, y, self.curPiece.shape())
self.removeFullLines()
if not self.isWaitingAfterLine:
self.newPiece()
def removeFullLines(self):
numFullLines=0
statusbar=self.GetParent().statusbar
rowsToRemove=[]
for i in range(Board.BoardHeight):
n=0
for j in range(Board.BoardWidth):
if not self.shapeAt(j, i)==Tetrominoes.NoShape:
n=n + 1
if n==10:
rowsToRemove.append(i)
rowsToRemove.reverse()
for m in rowsToRemove:
for k in range(m, Board.BoardHeight):
for l in range(Board.BoardWidth):
self.setShapeAt(l, k, self.shapeAt(l, k + 1))
numFullLines=numFullLines + len(rowsToRemove)
if numFullLines > 0:
self.numLinesRemoved=self.numLinesRemoved + numFullLines
statusbar.SetStatusText(str(self.numLinesRemoved))
self.isWaitingAfterLine=True
self.curPiece.setShape(Tetrominoes.NoShape)
self.Refresh()
def newPiece(self):
self.curPiece=self.nextPiece
statusbar=self.GetParent().statusbar
self.nextPiece.setRandomShape()
self.curX=Board.BoardWidth // 2 + 1
self.curY=Board.BoardHeight - 1 + self.curPiece.minY()
if not self.tryMove(self.curPiece, self.curX, self.curY):
self.curPiece.setShape(Tetrominoes.NoShape)
self.timer.Stop()
self.isStarted=False
statusbar.SetStatusText('Game over')
def tryMove(self, newPiece, newX, newY):
for i in range(4):
x=newX + newPiece.x(i)
y=newY - newPiece.y(i)
if x < 0 or x >=Board.BoardWidth or y < 0 or y >=Board.BoardHeight:
return False
if self.shapeAt(x, y) !=Tetrominoes.NoShape:
return False
self.curPiece=newPiece
self.curX=newX
self.curY=newY
self.Refresh()
return True
def drawSquare(self, dc, x, y, shape):
colors=['#000000', '#CC6666', '#66CC66', '#6666CC',
'#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
light=['#000000', '#F89FAB', '#79FC79', '#7979FC',
'#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
dark=['#000000', '#803C3B', '#3B803B', '#3B3B80',
'#80803B', '#803B80', '#3B8080', '#806200']
pen=wx.Pen(light[shape])
pen.SetCap(wx.CAP_PROJECTING)
dc.SetPen(pen)
dc.DrawLine(x, y + self.squareHeight() - 1, x, y)
dc.DrawLine(x, y, x + self.squareWidth() - 1, y)
darkpen=wx.Pen(dark[shape])
darkpen.SetCap(wx.CAP_PROJECTING)
dc.SetPen(darkpen)
dc.DrawLine(x + 1, y + self.squareHeight() - 1,
x + self.squareWidth() - 1, y + self.squareHeight() - 1)
dc.DrawLine(x + self.squareWidth() - 1,
y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
dc.SetPen(wx.TRANSPARENT_PEN)
dc.SetBrush(wx.Brush(colors[shape]))
dc.DrawRectangle(x + 1, y + 1, self.squareWidth() - 2,
self.squareHeight() - 2)
class Tetrominoes(object):
NoShape=0
ZShape=1
SShape=2
LineShape=3
TShape=4
SquareShape=5
LShape=6
MirroredLShape=7
class Shape(object):
coordsTable=(
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
((0, -1), (0, 0), (1, 0), (1, 1)),
((0, -1), (0, 0), (0, 1), (0, 2)),
((-1, 0), (0, 0), (1, 0), (0, 1)),
((0, 0), (1, 0), (0, 1), (1, 1)),
((-1, -1), (0, -1), (0, 0), (0, 1)),
((1, -1), (0, -1), (0, 0), (0, 1))
)
def __init__(self):
self.coords=[[0,0] for i in range(4)]
self.pieceShape=Tetrominoes.NoShape
self.setShape(Tetrominoes.NoShape)
def shape(self):
return self.pieceShape
def setShape(self, shape):
table=Shape.coordsTable[shape]
for i in range(4):
for j in range(2):
self.coords[i][j]=table[i][j]
self.pieceShape=shape
def setRandomShape(self):
self.setShape(random.randint(1, 7))
def x(self, index):
return self.coords[index][0]
def y(self, index):
return self.coords[index][1]
def setX(self, index, x):
self.coords[index][0]=x
def setY(self, index, y):
self.coords[index][1]=y
def minX(self):
m=self.coords[0][0]
for i in range(4):
m=min(m, self.coords[i][0])
return m
def maxX(self):
m=self.coords[0][0]
for i in range(4):
m=max(m, self.coords[i][0])
return m
def minY(self):
m=self.coords[0][1]
for i in range(4):
m=min(m, self.coords[i][1])
return m
def maxY(self):
m=self.coords[0][1]
for i in range(4):
m=max(m, self.coords[i][1])
return m
def rotatedLeft(self):
if self.pieceShape==Tetrominoes.SquareShape:
return self
result=Shape()
result.pieceShape=self.pieceShape
for i in range(4):
result.setX(i, self.y(i))
result.setY(i, -self.x(i))
return result
def rotatedRight(self):
if self.pieceShape==Tetrominoes.SquareShape:
return self
result=Shape()
result.pieceShape=self.pieceShape
for i in range(4):
result.setX(i, -self.y(i))
result.setY(i, self.x(i))
return result
def main():
app=wx.App()
ex=Tetris(None)
ex.Show()
app.MainLoop()
if __name__=='__main__':
main()
效果如圖:
?
不過我在運行上述代碼時,出現了無法使用箭頭鍵來控制方塊的情形,解決方式在Board這個panel中設置一個旗標:
super(Board, self).__init__(*args, **kw, style=wx.WANTS_CHARS)
該問題的討論在:
how to catch arrow keys ?
Stumped: arrows/tab kills keyboard focus
另外,捕獲keycode,如果是判斷字母,最好是大小寫形式都判斷,即里面:
if keycode==ord('P') or keycode==ord('p'):
胖科技,(爭取)每日為您帶來科技背后不一樣的故事!
周一至周五每日6:00更新,周六周日不定時更新,我們不見不散!
今天,有一則爆料,想必大家都已經知道了,即:安卓版手機QQ中存在“惡意彩蛋”,只要輸入菜刀表情和心碎表情中間再加一個特殊的英文符號,發出去以后就會變成罵人的話。
當天晚上手機QQ官微就給出回應:只是產品BUG,預計當晚能修復。
“彩蛋”這個詞,想必對大家來說并不陌生。二胖作為漫威迷和電影迷,對彩蛋總是懷著不斷探索之心。
二胖認為,菜單更多的是,隱藏著的寶藏/結尾的寶藏,是對資生粉絲的饋贈。不強求所有人看懂,卻在發現之后能夠令人欣喜若狂。
其實,科技公司的程序員們,也是對“彩蛋”有著深深執念的一群人,接下來,二胖將給大家帶來一些互聯網巨頭的“程序猿”們,帶來的那些有意思的彩蛋。
網易云音樂的程序員們設置的彩蛋真的很深。二胖查閱資料后,也親身試了一下。沒想到真的成功了,雖然這個彈出框的寫的真的談不上美觀,但是讀完內容,二胖真的有點感動。
網易云音樂在音樂界的地位可以說真的很高。只要是朋友圈分享歌曲,網易必然占據80%左右。這個彩蛋大家可以試試:
打開網易云音樂——點擊左上角菜單欄——點擊設置——點擊關于網易云音樂——點擊圖標四秒——等待十秒左右——就會跳出來啦~
從這些簡單的詞藻中,能看出來網易云程序員對他自身的產品真的像對待自己的孩子一樣,使得這個產品變得有溫度,有故事,有深度。
作為微軟的經典及老牌產品:windows系統,其實藏著一個巨大的秘密!這個秘密藏在現在已經幾乎沒人使用的XP系統中。
點擊左下角的“開始”里找到“運行”,輸入telnet towel.blinkenlights.nl 等待幾秒鐘 你將會看到一部精彩的用字符做出來的《星球大戰》電影!注意:此程序是微軟公司特意設計的,對系統是沒有任何影響的!(只有XP系統哦)
Win7有很多軟件里面都有制作者內置在軟件里面的小彩蛋,excel系列的辦公軟件也有隱藏的彩蛋,比如Excel95的可以打開一個3D場景的小游戲,里面可以找制作人的照片,還有Excel2000的賽車小游戲等。
今天,二胖給打家帶來Excel中賽車小游戲的打開方法,這個真的是,小學電腦課無聊時候經常玩,簡直是二胖的回憶殺。
首先我們打開Excel 2000,隨便新建一頁,然后點擊“文件--另存為Web頁”;然后點擊發布后再將“添加交互對象”打勾,在把它儲存為html格式文件;然后在IE中打開剛剛保存的文件,找到第2000行整行選取,使用Tab鍵將光標至于WC列;然后同時按住鍵盤“Crtl+Alt+Shift”組合鍵,接著點擊表格左上角的office圖標;然后就進入賽車小游戲了,方向鍵進行控制,H鍵開關燈,SPACE空格鍵射擊,O鍵可以放油。
很明顯,第2000行代表的就是Excel2000了,而WC列似乎就是程序猿的“小惡作劇”了~是不是也是有點意思?
好了,今天,那些互聯網公司程序猿們的背后設計的彩蛋就先寫到這里。今天我們的話題就是“我最喜歡的彩蛋”或“我對彩蛋的看法”,歡迎與二胖分享。
歡迎點擊上面的關注按鈕關注二胖哦!二胖科技,(爭取)每日為您帶來科技背后不一樣的故事!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。