2019年12月10日 星期二

#桌面上的計算機(二)--建立計算機

--使用PyQt套件

#原文出處

#摘要紀錄與說明
--python的GUI套件,有 Tkinter, wxPython, PyQt, PySide2...等等
--PyQt5 是基於Qt5而來

--請直接下載檔案執行如果是複製網頁上的程式,貼上後請注意縮排問題。

#一步一步建立「py小計算機」
        1. #01-建立外殼  pycalc01.py
        2. import sys

        3. from PyQt5.QtWidgets import QApplication  #qt應用程式
        4. from PyQt5.QtWidgets import QMainWindow  #qt主視窗
        5. from PyQt5.QtWidgets import QWidget  #qt總部件
        6. #from PyQt5 import QtGui  #為了加一個window icon,這裡可以省略。

        7. #這是原作者,感謝。
        8. __version__ '0.1'
        9. __author__ 'Leodanis Pozo Ramos'

        10. #定義主視窗,繼承自QMainWindow
        11. class PyCalcUi(QMainWindow):
        12.     #初始化,訂定視窗外觀
        13.     def __init__(self):
        14.         super().__init__()
        15.         self.setWindowTitle('py小計算機')
        16.         self.setFixedSize(235235#固定大小,免得計算機變形。
        17.         # 建立中央部件
        18.         self._centralWidget QWidget(self)  #先建立總部件實體(self相當於parent=self,top level的物件沒有parent了,所以不能省略。)
        19.         self.setCentralWidget(self._centralWidget)  #再設定成中央部件
        20.         #self.setWindowIcon(QtGui.QIcon('icon.png'))  #這行將icon放上去。這行可以省略。

        21. # 主程式
        22. def main():
        23.     pycalc QApplication(sys.argv)  #建立app
        24.     view PyCalcUi()  #建立主視窗實體
        25.     
        26.     view.show()  
        27.     sys.exit(pycalc.exec())

        28. if __name__ == '__main__':
        29.     main()
          1. #02-建立外觀  pycalc02.py
          2. import sys

          3. from PyQt5.QtWidgets import QApplication  #qt應用程式
          4. from PyQt5.QtWidgets import QMainWindow  #qt主視窗
          5. from PyQt5.QtWidgets import QWidget  #qt總部件
          6. from PyQt5 import QtGui  #為了加一個window icon,這裡可以省略。
          7. from PyQt5.QtCore import Qt  #一般性質的設定
          8. from PyQt5.QtWidgets import QGridLayout  #格狀排列
          9. from PyQt5.QtWidgets import QLineEdit   #單行文字
          10. from PyQt5.QtWidgets import QPushButton  #按鈕
          11. from PyQt5.QtWidgets import QVBoxLayout  #垂直排列

          12. #這是原作者,感謝。
          13. __version__ '0.1'
          14. __author__ 'Leodanis Pozo Ramos'

          15. #定義主視窗,繼承自QMainWindow
          16. class PyCalcUi(QMainWindow):
          17.     #初始化,訂定視窗外觀
          18.     def __init__(self):
          19.         super().__init__()
          20.         self.setWindowTitle('py小計算機')
          21.         self.setFixedSize(235235#固定大小,免得計算機變形。
          22.         self.generalLayout QVBoxLayout() #整體的部件是垂直排列
          23.         # 建立中央部件
          24.         self._centralWidget QWidget(self)  #先建立總部件實體(self相當於parent=self,top level的物件沒有parent了,所以不能省略。)
          25.         self.setCentralWidget(self._centralWidget)  #再設定成中央部件
          26.         self._centralWidget.setLayout(self.generalLayout)  #中央部件就設定為整體垂直排列
          27.         self._createDisplay() #計算機顯示數字的部件
          28.         self._createButtons() #計算機顯示按鈕的部件

          29.         #self.setWindowIcon(QtGui.QIcon('icon.png'))  #這行將icon放上去。這行可以省略。
          30.     
          31.     #計算機顯示數字的部件
          32.     def _createDisplay(self):
          33.         self.display QLineEdit() #顯示只要一行
          34.         self.display.setFixedHeight(35)  #顯示幕的高度
          35.         self.display.setAlignment(Qt.AlignRight)  #靠右顯示
          36.         self.display.setReadOnly(True)  #設定唯讀
          37.         
          38.         self.generalLayout.addWidget(self.display#將以上設定加入整體垂直設定中
          39.         
          40.     def _createButtons(self):
          41.         self.buttons = {}
          42.         buttonsLayout QGridLayout()
          43.         #在字典中,設定按鈕顯示的數字和在格狀排列中的座標
          44.         buttons = {'7': (00),
          45.                    '8': (01),
          46.                    '9': (02),
          47.                    '/': (03),
          48.                    'C': (04),
          49.                    '4': (10),
          50.                    '5': (11),
          51.                    '6': (12),
          52.                    '*': (13),
          53.                    '(': (14),
          54.                    '1': (20),
          55.                    '2': (21),
          56.                    '3': (22),
          57.                    '-': (23),
          58.                    ')': (24),
          59.                    '0': (30),
          60.                    '00': (31),
          61.                    '.': (32),
          62.                    '+': (33),
          63.                    '=': (34),
          64.                   }
          65.         #取出字典中的文字和座標,一一擺進格狀排列中
          66.         for btnTextpos in buttons.items():
          67.             self.buttons[btnText] = QPushButton(btnText)  #設定按鈕實體,以及顯示的文字
          68.             self.buttons[btnText].setFixedSize(4040)    #設定按鈕大小
          69.             buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1])  #擺進格狀排列
          70.             
          71.           self.generalLayout.addLayout(buttonsLayout)#將以上設定加入整體垂直設定中
          72. # 主程式
          73. def main():
          74.     pycalc QApplication(sys.argv)  #建立app
          75.     view PyCalcUi()  #建立主視窗實體
          76.     
          77.     view.show()  
          78.     sys.exit(pycalc.exec())

          79. if __name__ == '__main__':
          80.     main()
  • 處理view的部份--也就是計算機面板上的訊號-- pycalc03.py
  1. 面板顯示
  2. 計算式
  3. 按鍵狀態
          1. #03-建立Controller,處理view訊號,只剩=還沒處理。  pycalc03.py
          2. import sys

          3. from PyQt5.QtWidgets import QApplication  #qt應用程式
          4. from PyQt5.QtWidgets import QMainWindow  #qt主視窗
          5. from PyQt5.QtWidgets import QWidget  #qt總部件
          6. from PyQt5 import QtGui  #為了加一個window icon,這裡可以省略。
          7. from PyQt5.QtCore import Qt  #一般性質的設定
          8. from PyQt5.QtWidgets import QGridLayout  #格狀排列
          9. from PyQt5.QtWidgets import QLineEdit   #單行文字
          10. from PyQt5.QtWidgets import QPushButton  #按鈕
          11. from PyQt5.QtWidgets import QVBoxLayout  #垂直排列
          12. from functools import partial  #有關function的工作

          13. #這是原作者,感謝。
          14. __version__ '0.1'
          15. __author__ 'Leodanis Pozo Ramos'

          16. #定義主視窗,繼承自QMainWindow
          17. class PyCalcUi(QMainWindow):
          18.     #初始化,訂定視窗外觀
          19.     def __init__(self):
          20.         super().__init__()
          21.         self.setWindowTitle('py小計算機')
          22.         self.setFixedSize(235235#固定大小,免得計算機變形。
          23.         self.generalLayout QVBoxLayout() #整體的部件是垂直排列
          24.         # 建立中央部件
          25.         self._centralWidget QWidget(self)  #先建立總部件實體(self相當於parent=self,top level的物件沒有parent了,所以不能省略。)
          26.         self.setCentralWidget(self._centralWidget)  #再設定成中央部件
          27.         self._centralWidget.setLayout(self.generalLayout)  #中央部件就設定為整體垂直排列
          28.         self._createDisplay() #計算機顯示數字的部件
          29.         self._createButtons() #計算機顯示按鈕的部件

          30.         #self.setWindowIcon(QtGui.QIcon('icon.png'))  #這行將icon放上去。這行可以省略。
          31.     
          32.     #計算機顯示數字的部件
          33.     def _createDisplay(self):
          34.         self.display QLineEdit() #設定display,顯示只要一行
          35.         self.display.setFixedHeight(35)  #顯示幕的高度
          36.         self.display.setAlignment(Qt.AlignRight)  #靠右顯示
          37.         self.display.setReadOnly(True)  #設定唯讀
          38.         
          39.         self.generalLayout.addWidget(self.display#將以上設定加入整體垂直設定中
          40.         
          41.     def _createButtons(self):
          42.         self.buttons = {}
          43.         buttonsLayout QGridLayout()
          44.         #在字典中,設定按鈕顯示的數字和在格狀排列中的座標
          45.         buttons = {'7': (00),
          46.                    '8': (01),
          47.                    '9': (02),
          48.                    '/': (03),
          49.                    'C': (04),
          50.                    '4': (10),
          51.                    '5': (11),
          52.                    '6': (12),
          53.                    '*': (13),
          54.                    '(': (14),
          55.                    '1': (20),
          56.                    '2': (21),
          57.                    '3': (22),
          58.                    '-': (23),
          59.                    ')': (24),
          60.                    '0': (30),
          61.                    '00': (31),
          62.                    '.': (32),
          63.                    '+': (33),
          64.                    '=': (34),
          65.                   }
          66.         #取出字典中的文字和座標,一一擺進格狀排列中
          67.         for btnTextpos in buttons.items():
          68.             self.buttons[btnText] = QPushButton(btnText)  #設定按鈕實體,以及顯示的文字
          69.             self.buttons[btnText].setFixedSize(4040)    #設定按鈕大小
          70.             buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1])  #擺進格狀排列
          71.             
          72.             self.generalLayout.addLayout(buttonsLayout)#將以上設定加入整體垂直設定中
          73.     #補PyCalcUi的3個methods,在PyCalcCtrl中被呼叫。
          74.     def setDisplayText(selftext):
          75.         """Set display's text."""
          76.         self.display.setText(text)
          77.         self.display.setFocus()

          78.     def displayText(self):
          79.         """Get display's text."""
          80.         return self.display.text()

          81.     def clearDisplay(self):
          82.         """Clear the display."""
          83.         self.setDisplayText("")
          84.             
          85. #建立Controller
          86. class PyCalcCtrl:
          87.     def __init__(selfview):
          88.         self._view view  #初始化參數view(PyCalcUi實體)
          89.         self._connectSignals() #呼叫方法
          90.     
          91.     #顯示幕上的文字處理
          92.     def _buildExpression(selfsub_exp):
          93.         expression self._view.displayText() + sub_exp
          94.         self._view.setDisplayText(expression)
          95.     
          96.     #連結事件和處理程式
          97.     def _connectSignals(self):
          98.         #掃描所有按鍵,有click,就呼叫_buildExpression。
          99.         for btnTextbtn in self._view.buttons.items():
          100.             if btnText not in {'=''C'}:
          101.                 btn.clicked.connect(partial(self._buildExpressionbtnText)) #有click,才會呼叫_buildExpression

          102.         self._view.buttons['C'].clicked.connect(self._view.clearDisplay)
          103.     
          104. # 主程式
          105. def main():
          106.     pycalc QApplication(sys.argv)  #建立app
          107.     view PyCalcUi()  #建立主視窗實體
          108.     view.show()  #顯示
          109.     
          110.     #處理按鍵和顯示幕的文字
          111.     PyCalcCtrl(view=view)

          112.     sys.exit(pycalc.exec())

          113. if __name__ == '__main__':
          114.     main()
  • 最後的計算及按錯時的處理。  pycalc04.py
  1. #04-完整程式。
  2. #處理「=」「enter」事件及計算答案。  pycalc04.py

  3. import sys

  4. from PyQt5.QtWidgets import QApplication  #qt應用程式
  5. from PyQt5.QtWidgets import QMainWindow  #qt主視窗
  6. from PyQt5.QtWidgets import QWidget  #qt總部件
  7. from PyQt5 import QtGui  #為了加一個window icon,這裡可以省略。
  8. from PyQt5.QtCore import Qt  #一般性質的設定
  9. from PyQt5.QtWidgets import QGridLayout  #格狀排列
  10. from PyQt5.QtWidgets import QLineEdit   #單行文字
  11. from PyQt5.QtWidgets import QPushButton  #按鈕
  12. from PyQt5.QtWidgets import QVBoxLayout  #垂直排列
  13. from functools import partial  #有關function的工作

  14. #這是原作者,感謝。
  15. __version__ '0.1'
  16. __author__ 'Leodanis Pozo Ramos'

  17. ERROR_MSG '算式錯誤'
  18. #定義主視窗,繼承自QMainWindow
  19. class PyCalcUi(QMainWindow):
  20.     #初始化,訂定視窗外觀
  21.     def __init__(self):
  22.         super().__init__()
  23.         self.setWindowTitle('py小計算機')
  24.         self.setFixedSize(235235#固定大小,免得計算機變形。
  25.         self.generalLayout QVBoxLayout() #整體的部件是垂直排列
  26.         # 建立中央部件
  27.         self._centralWidget QWidget(self)  #先建立總部件實體(self相當於parent=self,top level的物件沒有parent了,所以不能省略。)
  28.         self.setCentralWidget(self._centralWidget)  #再設定成中央部件
  29.         self._centralWidget.setLayout(self.generalLayout)  #中央部件就設定為整體垂直排列
  30.         self._createDisplay() #計算機顯示數字的部件
  31.         self._createButtons() #計算機顯示按鈕的部件

  32.         #self.setWindowIcon(QtGui.QIcon('icon.png'))  #這行將icon放上去。這行可以省略。
  33.     
  34.     #計算機顯示數字的部件
  35.     def _createDisplay(self):
  36.         self.display QLineEdit() #設定display,顯示只要一行
  37.         self.display.setFixedHeight(35)  #顯示幕的高度
  38.         self.display.setAlignment(Qt.AlignRight)  #靠右顯示
  39.         self.display.setReadOnly(True)  #設定唯讀
  40.         
  41.         self.generalLayout.addWidget(self.display#將以上設定加入整體垂直設定中
  42.         
  43.     def _createButtons(self):
  44.         self.buttons = {}
  45.         buttonsLayout QGridLayout()
  46.         #在字典中,設定按鈕顯示的數字和在格狀排列中的座標
  47.         buttons = {'7': (00),
  48.                    '8': (01),
  49.                    '9': (02),
  50.                    '/': (03),
  51.                    'C': (04),
  52.                    '4': (10),
  53.                    '5': (11),
  54.                    '6': (12),
  55.                    '*': (13),
  56.                    '(': (14),
  57.                    '1': (20),
  58.                    '2': (21),
  59.                    '3': (22),
  60.                    '-': (23),
  61.                    ')': (24),
  62.                    '0': (30),
  63.                    '00': (31),
  64.                    '.': (32),
  65.                    '+': (33),
  66.                    '=': (34),
  67.                   }
  68.         #取出字典中的文字和座標,一一擺進格狀排列中
  69.         for btnTextpos in buttons.items():
  70.             self.buttons[btnText] = QPushButton(btnText)  #設定按鈕實體,以及顯示的文字
  71.             self.buttons[btnText].setFixedSize(4040)    #設定按鈕大小
  72.             buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1])  #擺進格狀排列
  73.             
  74.         self.generalLayout.addLayout(buttonsLayout)#將以上設定加入整體垂直設定中
  75.     
  76.     def setDisplayText(selftext):
  77.         """Set display's text."""
  78.         self.display.setText(text)
  79.         self.display.setFocus()

  80.     def displayText(self):
  81.         """Get display's text."""
  82.         return self.display.text()

  83.     def clearDisplay(self):
  84.         """Clear the display."""
  85.         self.setDisplayText("")

  86. #這是一個獨立的functions,就是model部份        
  87. def evaluateExpression(expression):
  88.     try:
  89.         result str(eval(expression, {}, {})) #eval(expressionglobals=Nonelocals=None)全域變數和區域變數
  90.     except Exception:
  91.         result ERROR_MSG

  92.     return result
  93.     
  94. #建立Controller
  95. class PyCalcCtrl:
  96.     #Control溝通model和view:view處理所有的顯示,model處理所有的後端計算,Control就是二者的交會處。
  97.     def __init__(self,modelview):
  98.         self._evaluate model #model就是evaluateExpression
  99.         self._view view  #初始化參數view(PyCalcUi實體)
  100.         self._connectSignals() #呼叫方法
  101.     
  102.     #計算答案,間接承接自model的function,來算答案。
  103.     def _calculateResult(self):
  104.         result self._evaluate(expression=self._view.displayText())
  105.         self._view.setDisplayText(result)

  106.     #顯示幕上的文字處理
  107.     def _buildExpression(selfsub_exp):
  108.         #先檢查,如果之前是錯誤的訊息,就先清除
  109.         if self._view.displayText() == ERROR_MSG:
  110.             self._view.clearDisplay()

  111.         expression self._view.displayText() + sub_exp
  112.         self._view.setDisplayText(expression)
  113.     
  114.     #連結事件和處理程式
  115.     def _connectSignals(self):
  116.         #掃描所有按鍵,有click,就呼叫_buildExpression。
  117.         for btnTextbtn in self._view.buttons.items():
  118.             if btnText not in {'=''C'}:
  119.                 btn.clicked.connect(partial(self._buildExpressionbtnText)) #有click,才會呼叫_buildExpression
  120.                 
  121.         self._view.buttons['='].clicked.connect(self._calculateResult#按=就算答案
  122.         self._view.display.returnPressed.connect(self._calculateResult#按enter就算答案

  123.         self._view.buttons['C'].clicked.connect(self._view.clearDisplay)
  124.     
  125. # 主程式
  126. def main():
  127.     pycalc QApplication(sys.argv)  #建立app
  128.     view PyCalcUi()  #建立主視窗實體
  129.     view.show()  #顯示
  130.     
  131.     #處理按鍵和顯示幕的文字,model算出答案
  132.     model evaluateExpression
  133.     PyCalcCtrl(model=modelview=view)

  134.     sys.exit(pycalc.exec())

  135. if __name__ == '__main__':
  136.     main()

沒有留言:

張貼留言