Commit 1adc9318 authored by Robin Trioux's avatar Robin Trioux
Browse files

Virtual card

parent 82d2a222
......@@ -111,13 +111,17 @@ class Client(metaclass=ClientSingleton):
kwargs['amount'] = packMoney(kwargs['amount'])
refillingRequest = com_pb2.RefillingRequest(**kwargs)
refillingReply = self.stub.Refill(refillingRequest)
self.now = unpackTime(refillingReply.now)
newBalance = unpackMoney(refillingReply.customer_balance)
refilling = unpackRefilling(refillingReply.refilling)
refilling.setNewBalance(newBalance)
return refilling
if refillingReply.status == com_pb2.RefillingReply.SUCCESS:
self.now = unpackTime(refillingReply.now)
newBalance = unpackMoney(refillingReply.customer_balance)
refilling = unpackRefilling(refillingReply.refilling)
refilling.setNewBalance(newBalance)
return refilling
else:
printE("Unable to refill")
return None
except RpcError:
printE("Unable to refill")
printE("RPC: Unable to refill")
return None
def requestHistory(self, **kwargs):
......@@ -136,17 +140,20 @@ class Client(metaclass=ClientSingleton):
try:
productsRequest = com_pb2.ProductsRequest(**kwargs)
productsReply = self.stub.Products(productsRequest)
self.now = unpackTime(productsReply.now)
# Fill product List
pbProductList = productsReply.products # get protobuff products
for pb_product in pbProductList:
newProduct = unpackProduct(pb_product)
productList.append(newProduct)
return productList
if productsReply.status == com_pb2.ProductsReply.SUCCESS:
self.now = unpackTime(productsReply.now)
# Fill product List
pbProductList = productsReply.products # get protobuff products
for pb_product in pbProductList:
newProduct = unpackProduct(pb_product)
productList.append(newProduct)
return productList
else:
printE("Unable to get product list")
return None
except RpcError:
printE("Unable to get product list")
printE("RPC: Unable to get product list")
return None
return None
......
......@@ -5,6 +5,7 @@ from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import *
from Euro import *
# Specialized models ...
class QProductSelectorModel(QTreeModel):
......
......@@ -67,16 +67,15 @@ class QCardObserver(QObject, CardObserver, metaclass=QCardObserverSingleton):
cardInserted = pyqtSignal()
cardRemoved = pyqtSignal()
__hasCard = False
cardUID = toHexString([0,0,0,0]) # now it's always a string
errorCode = []
cardBalance = None
def __init__(self):
# should it be super(QObject), super(CardObserver) ?
QObject.__init__(self)
CardObserver.__init__(self)
self.cardReader = getReader()
self.cardUID = toHexString([0,0,0,0]) # now it's always a string
self.__hasCard = False
def update(self, observable, actions):
......@@ -125,15 +124,11 @@ class QCardObserver(QObject, CardObserver, metaclass=QCardObserverSingleton):
def hasCard(self):
return self.__hasCard
def virtualCardInsert(self):
self.cardUID = toHexString([0x1, 0x2, 0x3, 0x4])
self.__hasCard = True
self.cardInserted.emit()
#DEBUG
#For virtual card...
def setCardState(self,state):
self.__hasCard = state
def virtualCardRemove(self):
self.cardUID = toHexString([0, 0, 0, 0])
self.__hasCard = False
self.cardRemoved.emit()
# Allow the user to connect and disconect the reader whenerver he wants
......
......@@ -44,3 +44,15 @@ class QNFCManager(QObject, metaclass=QNFCManagerSingleton):
def hasCard(self):
return self.cardObserver.hasCard()
def virtualCardInsert(self,uid):
cardObserver = QCardObserver()
cardObserver.cardUID = uid
cardObserver.setCardState(True)
cardObserver.cardInserted.emit()
def virtualCardRemove(self):
cardObserver = QCardObserver()
cardObserver.cardUID = toHexString([0, 0, 0, 0])
cardObserver.setCardState(False)
cardObserver.cardRemoved.emit()
\ No newline at end of file
......@@ -13,7 +13,30 @@ from QUIManager import QUIManager
import copy
# Basicaly, this file is the heart of the GUI since there are trees everywhere
# The tree model/view paradigm is NOT easy to handle at first, but the most difficult
# functions are already implemented...
#The key to understand what's going here is to check the Qt's Model/View paradigm
#https://doc.qt.io/qt-5/model-view-programming.html
#It's a really powerfull way to handle trees in UIs
#IN A FEW WORDS
#The main idea behind model/view paradigm, is break the tree into two main parts:
#The model (What's inside my tree ?) and View (How does it look ?)
#Both are completly INDEPENDENTS and exchange information through some standards functions
#It's a client/server relationship, the tree ask elements to the model and the model respond
#Because both are independants, you can use the same model for different treeView or the other way
#Pay attention to the object you're handling, basicaly there are three layers from the UI to the data:
# QModelIndex index -> What the view (and the model) handle
# ItemTree item -> What the model (and only the model) handle internally
# QAtom qAtom -> What the user handle (and the dev)
class TreeItem:
""" This class is is in charge to store the architecture/data inside the QT Tree Model.
It behave like a basic chained list where each node is a QAtom"""
def __init__(self, data, parent=None):
self.parentItem = parent
self.childItems = []
......@@ -118,7 +141,6 @@ class TreeItem:
return True
# never tested, should works ....
# remove n_columns from position
def removeColumns(self, position, n_columns):
if position < 0 or position + n_columns > len(self.data.getTexts()):
......@@ -159,6 +181,11 @@ class TreeItem:
texts[index] = str(text)
def __parseMagicString(self):
"""Magic strings are strings that must be evaluated as a mathematical expressions
All magic strings start with either '=' or '@'
The variable used in the expression must be part of the QAtoms's attributes
for instance with a product '@ (2*price)**2' return (2 x price)² where price is
an a property existing in the product."""
atomDict = vars(self.data) #python built-in function to turn an object into dictionary
texts = self.data.getTexts()
......@@ -169,18 +196,29 @@ class TreeItem:
expression = text.replace('=','').replace('@','')
try:
parsedTextList[index] = str(eval(expression,atomDict)) #no rage still pythoninc <3
#eval(expr,dict), basically it searchs variable defined in 'dict' and replace them in expr.
except:
printW("Failed to parse the expression {}".format(expression))
return parsedTextList
class QTreeModel(QAbstractItemModel):
"""Base class for all tree models
QTreeModel inherit from QAbstractItemModel which is a pure Qt class.
Abstract means that we need to overload some standard function in order to make
it works correctly.
Most of the method implemented below are standard methods the TreeView need to work"""
def __init__(self, headers, data=None, parent=None):
super().__init__(parent)
self.rootItem = TreeItem(headers)
def data(self, index, role):
"""A model don't store only the text to display, it can also store the colors,
Icons etc...
Depending on what the View want to know, the model must provide the correct information,
hence the 'role'...
"""
uim = QUIManager()
if not index.isValid():
......@@ -234,6 +272,9 @@ class QTreeModel(QAbstractItemModel):
return None
def index(self, row, column, parent=QModelIndex()):
"""index is a Qt object that represent a particular node in the model.
Despite a tree is technically a 2D object (row/column) an index has actually
3 coordinates. The 3rd one is the parent of the node. """
if not self.hasIndex(row, column, parent):
return QModelIndex()
......@@ -313,6 +354,11 @@ class QTreeModel(QAbstractItemModel):
return result
def setData(self, index, value, role=Qt.EditRole):
"""You should use this method as much as possible to modify your tree's contend.
Notice the use of 'dataChanged.emit' this notify the 'view' that it should be updated.
If you don't use this method, ensure that dataChanged is correctly emited, otherwise you'll never
see anything"""
if role != Qt.EditRole:
return False
......@@ -329,6 +375,7 @@ class QTreeModel(QAbstractItemModel):
return result
# Tool
# Basicaly, all these functions are not required by Qt, but are requiered by our own needs..
def getItem(self, index):
if index.isValid():
......
......@@ -317,3 +317,40 @@ class QNFCInfo(QWidget):
cardUID = nfcm.getCardUID()
balance = client.requestUserBalance(customer_id=cardUID)
self.rowInfo.setRow(1,1,balance)
class QVirtualCard(QWidget):
virtualCardInserted = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
uim = QUIManager()
nfcm = QNFCManager()
#Definition
self.mainLayout = QGridLayout()
self.inputLine = QLineEdit()
self.showCardButton = QPushButton()
self.removeCardButton = QPushButton()
#Layout
self.mainLayout.addWidget(self.inputLine,0,0,1,2)
self.mainLayout.addWidget(self.showCardButton,1,0,1,1)
self.mainLayout.addWidget(self.removeCardButton,1,1,1,1)
self.setLayout(self.mainLayout)
center(self)
self.showCardButton.clicked.connect(self.virtualCardInsert)
self.virtualCardInserted[str].connect(nfcm.virtualCardInsert)
self.removeCardButton.clicked.connect(nfcm.virtualCardRemove)
#Setup
self.inputLine.setText("00 01 02 03")
self.showCardButton.setText("Lire carte")
self.removeCardButton.setText("Retirer carte")
self.setWindowIcon(uim.getWindowIcon("nfc-card"))
self.setWindowTitle("Carte NFC Virtuelle")
self.setFixedSize(300,100)
def virtualCardInsert(self):
self.virtualCardInserted.emit(self.inputLine.text())
\ No newline at end of file
from GUI import *
# TODO: ENSURE BALANCE CAN'T HAVE ABSURDS VALUES (eg: 16.33333...)
# TODO: ENSURE BALANCE CAN'T HAVE ABSURDS VALUES (eg: 16.33333...) [OK Money class + string]
# TODO: FIX MULTI-USER PANEL
# TODO: UNIFY KEY NAME BITWEEN THE SERVER AND THE CLIENT
# TODO: UNIFY KEY NAME BITWEEN THE SERVER AND THE CLIENT [OK GRPC]
# TODO: MAKE AUTOMATIC GRAPH
# TODO: FIND A EASIER WAY TO HANDLE TREEMODELS AND STUFF...
# TODO: FIND A EASIER WAY TO HANDLE TREEMODELS AND STUFF... [OK QATOMS]
# TODO: ADD WARNING WHEN ADD HUGE AMOUNT ON CARDS
# TODO: HANDLE TRANSLATION
# TODO: ADD A TOOLBAR STATUS SINGLETON HANDLER [OK VIA QCONNECTOR]
......@@ -16,10 +16,10 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainMenu()
# FakeCard = QFakeCard()
VirtualCard = QVirtualCard()
# la fenêtre est rendue visible
# MainWindow.showMaximized()
MainWindow.show()
# FakeCard.show()
VirtualCard.show()
app.exec_()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment