整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Python用20行代碼就能實現漂亮的網頁界面?你確定不來看看

          們在寫一個網站或者一個網頁界面的時候,需要學習很多東西,對小白來說很困難!比如我要做一個簡單的網頁交互:

          要懂后端,比如Python里面的Django或者Flask,或者是Java里面的SpringBoot

          要懂前端,現在都叫大前端了(因為很復雜),比如前端的框架Vue/React, 然后頁面的美化框架Bootstrap ,還有html ,csss 和Javascript 三駕馬車.

          天啊,聽聽頭都大呢!其實我就給老板做一個簡單的交互的頁面,而且我只會Python ,有沒有很簡單的辦法可以做到呢。

          今天小編在Github上逛的時候,找到一個非常酷的神器,名字叫remi ,目前收獲了2300個贊。

          star并不是很多, 但是這個庫可是選入2018年的十大明星庫 ,號稱是一個獨立的GUI庫,而且最牛逼的就是這個庫 竟然小于 100K ,是否很神奇呢,我們一起來體驗一下特性:

          跟其他GUI庫區別? Kivy,PyQT和PyGObject都需要主機操作系統的本機代碼,這意味著安裝或編譯大型依賴項。Remi只需要一個Web瀏覽器即可顯示您的GUI。

          我需要懂HTML嗎? 不,只需要使用Python進行編碼。

          它是開源的嗎? 當然!Remi是根據Apache許可發布的。開源,免費!

          我需要某種網絡服務器嗎? 不,自帶網絡服務器。

          1、安裝

          如何安裝呢,因為是Python 庫,直接用pip 即可

          pip install remi

          如果是沒有網絡的,或者服務器跟外網不通的,可以離線安裝。下載這個包,然后用install 安裝

          python setup.py install

          2、快速體驗

          我們來快速看一下,這個簡單的Hello world網頁。

          這里面包含了一個 2個元素:

          • 文本Hello world ;
          • 一個Press me的按鈕;

          點擊這個按鈕還會改變Hello world的文本內容,看點一下就變成了Button pressed.

          上面這個簡單的效果,其實只用了20來行Python代碼,都是原生的Python代碼,沒有用一行HTML .

          我們來看一下源碼:

          整個代碼的結構層次還很清晰的:

          • 首先聲明一個MyApp類,這個類會繼承母框架App里面的功能,可以認為是一個網頁畫布;
          • 然后在這個畫布里面有3個函數:

          init主要是做類的初始化工作;

          main主要對這個畫布布局進行設計,包含一個文本和一個按鈕。

          on_button_pressed主要是控件的相應,點擊觸發函數

          • 最后是start來啟動這個類,就會生成一個本地的網站鏈接

          3、更多復雜的網頁元素

          看完上面的設計是不是就覺得跟Python里面大名鼎鼎的tk庫很相似啊,上面的只是開胃菜,這個remi還能提供更復雜的gui界面元素。

          上面的是一個完整的demo頁面,基本上常見的控件元素的都包含了,比如有菜單欄,文本,按鈕,單選框,進度欄,下拉框,表格,彈出框,按鈕選取文件路徑,文件樹形結構,日期等幾十種控件。

          有了這個神器,是不是可以告別Python里面的tk庫,也可以省去學習HTML的時間,還等啥,趕緊下載嘗鮮一下吧。

          對Python感興趣或者是正在學習的小伙伴,可以點贊關注收藏支持一波哦, 持續更新中


          原文鏈接:https://blog.csdn.net/NNNJ9355/article/details/107319582

          、摘 要

          (OF作品展示)

          OF之前介紹了用python實現數據可視化、數據分析及一些小項目,但基本都是后端的知識。想要做一個好看的小系統,我們還要學一些前端的知識,今天OF將講解如何用pycharm(全棧開發不錯的工具)做一張好看的網頁,以后我們就可以自己開發網頁/網站放到互聯網上。

          主要內容:網頁前端布局

          適用人群:Python初學者,前端初學者

          準備軟件:pycharm

          二、pycharm操作說明

          1. 創建項目

          1) 下載完成pycharm, 點擊file-New Project...

          2) 按下圖步驟創建一個項目,目前我們選擇Pure python即可,以后我們可以學習用Django/flask等框架來做完整的系統

          2. 創建HTML文件

          1)在創建的項目空白處鼠標右鍵-New-HTML File

          2)輸入英文的網頁名字,盡量不要輸入中文/特殊字符

          3. HTML格式說明

          當雙擊打開我們創建后的HTML文件,大家會看到下面的界面

          三、網頁設計

          1. 草圖繪制

          在開始開發網頁前,我們需要自己設計下想要把網頁做成什么樣,為了節省成本OF都是自己設計的頁面樣式,可以手繪,也可以在PPT上畫。

          2. css名字定義

          我們先學習一個比較簡單的布局如下圖,大家可以看到下圖已經畫出了我們需要添加的內容,要注意的地方是比如Taylor的圖片和文字一定要用<div class=bord>框住,否則Taylor圖片與文字將會是左右的關系,而不是上下

          四、網頁開發

          1. body部分

          根據上述的css名字定義,先填充<body>內的代碼,那么我們就完成一半的工作了,代碼如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
          </head>
          <body>
          <div id="intro">
              <p class="peo">人物介紹</p>
          </div>
          <div id="pic1">
              <div class="bord">
                  <img class="img" src="pic/Taylor.png"/>
                  <p class="word">Taylor</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/yan.png"/>
                  <p class="word">東</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/song.png"/>
                  <p class="word">喬</p>
              </div>
          </div>
          </body>
          </html>

          2. 網頁測試

          1)鼠標移到代碼處,在右上角我們會看到一排瀏覽器,我們點擊其中一個運行

          2)目前看到的網頁內容從上到下顯示

          3. head部分

          首先我們簡要了解下flex布局,大家可以看到下圖中#main的style樣式中display:flex,在body部分將3個顏色內容框在<div id="main">中,運行結果是3個顏色的內容橫向展示了,而不是上下

          1)那么我們先從“人物介紹”的布局開始,“人物介紹”居中展現(用flex中justify-content:center),而且下面有一條黑線,OF準備用border樣式來實現,所以在<div id=intro>里另加了個<div class=peo>,代碼如下:

          (注:style中的#main對應body中的id=main, .main對應class=main)

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <style>
                  #intro {
                      display: -webkit-flex; /* Safari */
                      display: flex;
                      justify-content: center;
                  }
                  .peo {
                      max-width: 10rem;
                      border-bottom: 0.2rem solid #000000;
                      font-family: sans-serif;
                      font-size: 1.5rem;
                      color: #0070C0;
                      line-height: 3rem;
                  }
              </style>
          </head>
          <body>
          <div id="intro">
              <p class="peo">人物介紹</p>
          </div>
          <div id="pic1">
              <div class="bord">
                  <img class="img" src="pic/Taylor.png"/>
                  <p class="word">Taylor</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/yan.png"/>
                  <p class="word">東</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/song.png"/>
                  <p class="word">喬</p>
              </div>
          </div>
          </body>
          </html>

          運行結果:

          2)圖片部分是3個<div class=bord>橫向展示,所以要在框住它們的<div id=pic1>樣式中設置flex布局,在<style>里加入以下代碼:

          #pic1 {
              display: -webkit-flex; /* Safari */
              display: flex;
              justify-content: center;
          }

          運行結果:

          3)圖片之間靠太近,圖片大小不一致,文字沒居中,我們在<style>里加入以下代碼:

          .bord{
              padding: 1rem 2rem;
          }
          
          
          .img {
              border: 0.2rem solid #e3e3e3;
              max-width: 15rem;
              height: 22rem;
          }
          
          
          .word {
              text-align: center;
          }

          運行結果:

          五、總 結

          今天我們學會了flex布局,今后所有的網頁排版都可以實現了,祝愿大家都有所收獲,完整的代碼如下:

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <style>
                  #intro {
                      display: -webkit-flex; /* Safari */
                      display: flex;
                      justify-content: center;
          
          
                  }
          
                  .peo {
                      max-width: 10rem;
                      border-bottom: 0.2rem solid #000000;
                      font-family: sans-serif;
                      font-size: 1.5rem;
                      color: #0070C0;
                      line-height: 3rem;
                  }
          
                  #pic1 {
                      display: -webkit-flex; /* Safari */
                      display: flex;
                      justify-content: center;
                  }
          
                  .bord{
                      padding: 1rem 2rem;
                  }
          
                  .img {
                      border: 0.2rem solid #e3e3e3;
                      max-width: 15rem;
                      height: 22rem;
                  }
          
                  .word {
                      text-align: center;
                  }
              </style>
          </head>
          <body>
          <div id="intro">
              <p class="peo">人物介紹</p>
          </div>
          <div id="pic1">
              <div class="bord">
                  <img class="img" src="pic/Taylor.png"/>
                  <p class="word">Taylor</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/yan.png"/>
                  <p class="word">東</p>
              </div>
              <div class="bord">
                  <img class="img" src="pic/song.png"/>
                  <p class="word">喬</p>
              </div>
          </div>
          </body>
          </html>

          今天的知識比較基礎但非常實用,每天學會一個小技能,積少成多,以后就能成為大神。如果大家對網頁上的實現有什么不懂的,盡請留言,OF一定會一一解答的。

          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的“你好世界”

          這個例子是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

          wx.BoxSizer是最常見的布局管理器。它的常用設置有:

          (1)排列方向:wx.VERTICAL垂直排列還是wx.HORIZONTAL水平排列;

          (2)排列比例:一個布局中所包含的組件的尺寸由其比例所決定,比例為0表示在窗口尺寸變化時保持尺寸不變,其他比例系數表示組件在該布局管理器中的尺寸占比;且通常使用wx.EXPAND旗標來使得組件占據管理器分配給它的所有空間;

          (3)邊界:組件的邊界大小可以自定義設置,同時具體哪個邊界(上下左右或全部)都可以任意指定;

          (4)對齊方式:可以設定左端對齊、右端對齊、頂部對齊、底部對齊、中心對齊等多種對齊方式;

          (5)在某一級容器組件中,使用SetSizer()來為其指定布局管理器;

          (6)在布局管理器中用Add()方法來添加組件。

          wx.StaticBoxSizer是在BoxSizer周圍加上了一個靜態文本框的顯示。

          wx.GridSizer

          wx.GridSizer是網格布局管理器,可以設置幾排幾列以及橫縱的間距,網格中的組件尺寸都是相同的。

          (如果有的網格不需要添加組件,可以添加沒有內容的StaticText作為占位符)

          wx.FlexGridSizer

          wx.FlexGridSizer與wx.GridSizer類似,但其更靈活,它不要求網格中所有的組件尺寸都相同,而是在同一行中的所有組件都高度相同,而同一列中的所有組件都寬度相同。

          它還可以設置能growable的行和列,即在當前sizer中如果有空間,就將特定的行和列調整相應的大小來占據這個空間(注意將該行或列中的組件設為expandable)。

          wx.GridBagSizer

          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)
          

          Sizer常見問題

          大部分的問題出現在:

          (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'):

          主站蜘蛛池模板: 午夜福利一区二区三区在线观看 | 亚洲熟妇无码一区二区三区| 一区二区三区美女视频| 成人区人妻精品一区二区不卡| 视频一区视频二区制服丝袜| 国产成人精品一区二区三在线观看| 一区二区三区免费高清视频| 精品国产亚洲一区二区三区| 2022年亚洲午夜一区二区福利| 久久精品成人一区二区三区| 亚洲第一区精品日韩在线播放| 久久精品国产一区二区三区| 老熟妇仑乱一区二区视頻| 另类国产精品一区二区| 国产a∨精品一区二区三区不卡 | 熟女少妇精品一区二区| 日韩中文字幕精品免费一区| 污污内射在线观看一区二区少妇| 午夜视频一区二区| 国产成人久久精品一区二区三区 | 一区 二区 三区 中文字幕| 亚洲国产视频一区| 美女AV一区二区三区| 免费无码A片一区二三区| 亚洲成AV人片一区二区| 久久无码精品一区二区三区| 国产一区二区好的精华液| 国产人妖视频一区二区| 国产亚洲日韩一区二区三区| 日韩人妻无码免费视频一区二区三区 | 天天视频一区二区三区| 成人免费观看一区二区| 国产精品高清一区二区人妖| 国产精品免费一区二区三区四区| 国产一区二区精品久久岳√| 亚洲第一区在线观看| 精品一区二区无码AV| 鲁丝片一区二区三区免费| 国产一区二区三区樱花动漫| 国产一区二区三区免费在线观看 | 国产AV一区二区三区无码野战|