diff --git a/brmbar3/brmbar-cli.py b/brmbar3/brmbar-cli.py
index cb6cb20..4e75325 100755
--- a/brmbar3/brmbar-cli.py
+++ b/brmbar3/brmbar-cli.py
@@ -15,7 +15,6 @@ active_credit = None
for line in sys.stdin:
barcode = line.rstrip()
- # TODO $neco
if barcode[0] == "$":
credits = {'$02': 20, '$05': 50, '$10': 100, '$20': 200, '$50': 500, '$1k': 1000}
credit = credits[barcode]
@@ -25,7 +24,7 @@ for line in sys.stdin:
print("CREDIT " + str(credit))
active_inv_item = None
active_credit = credit
- continue;
+ continue
if barcode == "SCR":
print("SHOW CREDIT")
diff --git a/brmbar3/brmbar-gui-qt4.py b/brmbar3/brmbar-gui-qt4.py
new file mode 100755
index 0000000..3f53876
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python3
+
+import sys
+import psycopg2
+
+from PySide import QtCore, QtGui, QtDeclarative
+
+import brmbar
+
+
+class ShopAdapter(QtCore.QObject):
+ """ Interface between QML and the brmbar package """
+ def __init__(self):
+ QtCore.QObject.__init__(self)
+
+ @QtCore.Slot(str, result='QVariant')
+ def barcodeInput(self, barcode):
+ """ Evaluate barcode received on input
+
+ Normally, we would return just the account object, but
+ passing that to QML appears to be very non-trivial.
+ Therefore, we construct a map that we can pass around easily.
+ We return None on unrecognized barcode. """
+ barcode = str(barcode)
+ if barcode and barcode[0] == "$":
+ credits = {'$02': 20, '$05': 50, '$10': 100, '$20': 200, '$50': 500, '$1k': 1000}
+ credit = credits[barcode]
+ if credit is None:
+ return None
+ return { "acctype": "recharge", "amount": str(credit)+".00" }
+ acct = brmbar.Account.load_by_barcode(db, barcode)
+ if acct is None:
+ return None
+ if acct.acctype == 'debt':
+ map = acct.__dict__.copy()
+ map["balance"] = str(acct.balance())
+ map["negbalance"] = str(-acct.balance())
+ map["negbalance_str"] = acct.negbalance_str()
+ return map
+ elif acct.acctype == "inventory":
+ buy, sell = acct.currency.rates(currency)
+ map = acct.__dict__.copy()
+ map["price"] = str(sell)
+ return map
+ else:
+ return None
+
+ @QtCore.Slot('QVariant', 'QVariant', result='QVariant')
+ def sellItem(self, itemid, userid):
+ user = brmbar.Account.load(db, id = userid)
+ shop.sell(item = brmbar.Account.load(db, id = itemid), user = user)
+ return user.negbalance_str()
+
+ @QtCore.Slot('QVariant', 'QVariant', result='QVariant')
+ def chargeCredit(self, credit, userid):
+ user = brmbar.Account.load(db, id = userid)
+ shop.add_credit(credit = credit, user = user)
+ return user.negbalance_str()
+
+db = psycopg2.connect("dbname=brmbar")
+shop = brmbar.Shop.new_with_defaults(db)
+currency = shop.currency
+
+
+app = QtGui.QApplication(sys.argv)
+view = QtDeclarative.QDeclarativeView()
+
+ctx = view.rootContext()
+ctx.setContextProperty('shop', ShopAdapter())
+
+view.setSource('brmbar-gui-qt4/main.qml')
+
+view.show()
+app.exec_()
diff --git a/brmbar3/brmbar-gui-qt4/BarButton.qml b/brmbar3/brmbar-gui-qt4/BarButton.qml
new file mode 100644
index 0000000..a202d4c
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/BarButton.qml
@@ -0,0 +1,34 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+Rectangle {
+ id: rectangle1
+ width: 240
+ height: 83
+ color: "#000000"
+ border.color: "#ffffff"
+
+ property string text: "Button"
+ property int fontSize: 46
+
+ signal buttonClick
+ onButtonClick: { /* Supplied by component user. */ }
+
+ Text {
+ id: text1
+ color: "#ffffff"
+ text: parent.text
+ font.pointSize: 44
+ scale: if (!mousearea1.pressed) { 1 } else { 0.95 }
+ horizontalAlignment: Text.AlignHCenter
+ anchors.fill: parent
+ smooth: true
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: mousearea1
+ anchors.fill: parent
+ onClicked: buttonClick()
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/BarClock.qml b/brmbar3/brmbar-gui-qt4/BarClock.qml
new file mode 100644
index 0000000..2840857
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/BarClock.qml
@@ -0,0 +1,27 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+Rectangle {
+ id: clock
+ width: 320
+ height: 65
+ property variant now: new Date()
+ property variant textColor: "#000000"
+ property variant textSize: 12
+ Timer {
+ id: clockUpdater
+ interval: 1000 // update clock every second
+ running: true
+ repeat: true
+ onTriggered: {
+ parent.now = new Date()
+ }
+ }
+ Text {
+ id: clockLabel
+ anchors.centerIn: parent
+ text: Qt.formatDateTime(parent.now, "hh:mm:ss")
+ color: parent.textColor
+ font.pointSize: parent.textSize
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/BarTextHint.qml b/brmbar3/brmbar-gui-qt4/BarTextHint.qml
new file mode 100644
index 0000000..4ef4e92
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/BarTextHint.qml
@@ -0,0 +1,38 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+Item {
+ id: main_hint
+ width: 894
+ height: 80
+
+ property variant hint_goal: ""
+ property variant hint_action: ""
+
+ Text {
+ id: text1
+ x: 0
+ y: 0
+ color: "#ffffff"
+ text: parent.hint_goal
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 0
+ anchors.top: parent.top
+ anchors.topMargin: 0
+ font.pointSize: 44
+ }
+
+ Text {
+ id: text2
+ x: 11
+ color: "#40ff5d";
+ text: parent.hint_action;
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 0
+ anchors.top: parent.top
+ anchors.topMargin: 0
+ anchors.right: parent.right
+ anchors.rightMargin: 0
+ transformOrigin: Item.Center; smooth: true; font.bold: false; wrapMode: Text.NoWrap; font.pointSize: 44;horizontalAlignment: Text.AlignHCenter
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/BarcodeInput.qml b/brmbar3/brmbar-gui-qt4/BarcodeInput.qml
new file mode 100644
index 0000000..908902e
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/BarcodeInput.qml
@@ -0,0 +1,18 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+TextInput {
+ id: barcode
+ x: 433
+ y: 65
+ width: 80
+ height: 100
+ color: "#ff6464"
+ text: ""
+ transformOrigin: Item.Center
+ visible: true
+ opacity: 0
+ font.pixelSize: 12
+ focus: true
+ validator: RegExpValidator { regExp: /..*/ } /* non-empty strings; barcode readers send empty lines, ignore these */
+}
diff --git a/brmbar3/brmbar-gui-qt4/BasePage.qml b/brmbar3/brmbar-gui-qt4/BasePage.qml
new file mode 100644
index 0000000..074c045
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/BasePage.qml
@@ -0,0 +1,80 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+import QtQuick 1.0
+
+Rectangle {
+ width: 1024
+ height: 768
+ color: "#000000"
+ Text {
+ id: title
+ x: 65
+ y: 35
+ color: "#71cccc"
+ text: "brmbar v3"
+ font.pointSize: 36
+ }
+ BarClock {
+ id: clock
+ x: 328
+ y: 35
+ color: "#000000"
+ textColor: "#71cccc"
+ textSize: 36
+ }
+
+ Image {
+ id: image1
+ x: 688
+ y: 41
+ height: 65
+ smooth: true
+ fillMode: Image.PreserveAspectFit
+ source: "brmlab.svg"
+ }
+
+ property alias status_text: status_text_id
+ Text {
+ id: status_text_id
+ x: 65
+ y: 112
+ width: 894
+ color: "#ff4444"
+ text: ""
+ horizontalAlignment: Text.AlignHCenter
+ //anchors.horizontalCenter: clock.horizontalCenter
+ font.pointSize: 36
+
+ state: "HIDDEN"
+ opacity: 0
+
+ states: [
+ State {
+ name: "HIDDEN"
+ PropertyChanges { target: status_text; opacity: 0 }
+ },
+ State {
+ name: "VISIBLE"
+ PropertyChanges { target: status_text; opacity: 100 }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "VISIBLE"
+ to: "HIDDEN"
+ NumberAnimation { property: "opacity"; duration: 12000; easing.type: Easing.InOutCubic }
+ }
+ ]
+
+ function setStatus(statusText, statusColor) {
+ text = statusText
+ color = statusColor
+ state = "VISIBLE"
+ state = "HIDDEN"
+ }
+ function hideStatus() {
+ state = "HIDDEN"
+ }
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/ChargeCredit.qml b/brmbar3/brmbar-gui-qt4/ChargeCredit.qml
new file mode 100644
index 0000000..71d277e
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/ChargeCredit.qml
@@ -0,0 +1,113 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+import QtQuick 1.0
+
+Item {
+ id: page
+ anchors.fill: parent
+
+ property variant username: ""
+ property variant userdbid: ""
+ property variant amount: ""
+
+ Text {
+ id: item_name
+ x: 65
+ y: 156
+ width: 537
+ height: 160
+ color: "#ffffff"
+ text: parent.username ? parent.username : "Credit charge"
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 44
+ }
+
+ Text {
+ id: text3
+ x: 611
+ y: 156
+ height: 160
+ width: 348
+ color: "#ffff7c"
+ text: parent.amount
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 90
+ }
+
+ BarTextHint {
+ x: 65
+ y: 430
+ hint_goal: parent.amount ? (parent.username ? "Charge now?" : "Charge user:") : "Charge credit:"
+ hint_action: !(parent.amount && parent.userdbid) ? "Scan barcode now" : ""
+ }
+
+ BarcodeInput {
+ color: "#00ff00" /* just for debugging */
+ onAccepted: {
+ var acct = shop.barcodeInput(text)
+ text = ""
+ if (typeof(acct) == "undefined" || (parent.username && acct.acctype != "recharge") || (parent.amount && acct.acctype != "debt")) {
+ status_text.setStatus("Unknown barcode", "#ff4444")
+ return
+ }
+ if (acct.acctype == "debt") {
+ username = acct.name
+ userdbid = acct.id
+ } else {
+ amount = acct.amount
+ }
+ if (username && amount) {
+ parent.chargeCredit()
+ }
+ }
+ }
+
+ BarButton {
+ id: charge_button
+ x: 65
+ y: 582
+ width: 360
+ text: "Charge"
+ fontSize: 44
+ visible: parent.amount && parent.userdbid
+ onButtonClick: {
+ parent.chargeCredit()
+ }
+ }
+
+ BarButton {
+ id: cancel
+ x: 599
+ y: 582
+ width: 360
+ text: "Cancel"
+ onButtonClick: {
+ status_text.setStatus("Charging cancelled", "#ff4444")
+ loadPage("MainPage")
+ }
+ }
+
+ Text {
+ id: text1
+ x: 112
+ y: 333
+ width: 800
+ height: 80
+ color: "#ffffff"
+ text: "Put "+amount+" Kč in the money box now."
+ visible: amount ? true : false
+ anchors.horizontalCenterOffset: 0
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ font.pointSize: 36
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ function chargeCredit() {
+ var balance = shop.chargeCredit(amount, userdbid)
+ status_text.setStatus("Charged! "+username+"'s credit is "+balance+".", "#ffff7c")
+ loadPage("MainPage")
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/ItemInfo.qml b/brmbar3/brmbar-gui-qt4/ItemInfo.qml
new file mode 100644
index 0000000..3445e7d
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/ItemInfo.qml
@@ -0,0 +1,90 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+import QtQuick 1.0
+
+Item {
+ id: page
+ anchors.fill: parent
+
+ property variant name: ""
+ property variant dbid: ""
+ property variant price: ""
+
+ Text {
+ id: item_name
+ x: 65
+ y: 156
+ width: 537
+ height: 160
+ color: "#ffffff"
+ text: parent.name
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 44
+ }
+
+ Text {
+ id: text3
+ x: 611
+ y: 156
+ height: 160
+ width: 348
+ color: "#ffff7c"
+ text: parent.price
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 90
+ }
+
+ BarTextHint {
+ x: 65
+ y: 430
+ hint_goal: "Buy on credit:"
+ hint_action: "Scan barcode now"
+ }
+
+ BarcodeInput {
+ color: "#00ff00" /* just for debugging */
+ onAccepted: {
+ var acct = shop.barcodeInput(text)
+ text = ""
+ if (typeof(acct) == "undefined") {
+ status_text.setStatus("Unknown barcode", "#ff4444")
+ return
+ }
+ if (acct.acctype != "debt") {
+ loadPageByAcct(acct)
+ return
+ }
+ var balance = shop.sellItem(dbid, acct.id)
+ status_text.setStatus("Sold! "+acct.name+"'s credit is "+balance+".", "#ffff7c")
+ loadPage("MainPage")
+ }
+ }
+
+ BarButton {
+ id: pay_cash
+ x: 65
+ y: 582
+ width: 360
+ text: "Pay by cash"
+ fontSize: 44
+ onButtonClick: {
+ // TODO
+ status_text.setStatus("Sold! Put " + price + " Kč in the money box.", "#ffff7c")
+ loadPage("MainPage")
+ }
+ }
+
+ BarButton {
+ id: cancel
+ x: 599
+ y: 582
+ width: 360
+ text: "Cancel"
+ onButtonClick: {
+ status_text.setStatus("Transaction cancelled", "#ff4444")
+ loadPage("MainPage")
+ }
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/MainPage.qml b/brmbar3/brmbar-gui-qt4/MainPage.qml
new file mode 100644
index 0000000..b93576d
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/MainPage.qml
@@ -0,0 +1,63 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+import QtQuick 1.0
+
+Item {
+ id: page
+ anchors.fill: parent
+
+ BarTextHint {
+ x: 65
+ y: 234
+ hint_goal: "Buy item:"
+ hint_action: "Scan barcode now"
+ }
+
+ BarcodeInput {
+ onAccepted: {
+ var acct = shop.barcodeInput(text)
+ text = ""
+ if (typeof(acct) == "undefined") {
+ status_text.setStatus("Unknown barcode", "#ff4444")
+ return
+ }
+ loadPageByAcct(acct)
+ }
+ }
+
+ BarButton {
+ id: select_item
+ x: 65
+ y: 430
+ width: 360
+ text: "Select Item"
+ fontSize: 44
+ }
+
+ BarButton {
+ id: select_credit_user
+ x: 599
+ y: 430
+ width: 360
+ text: "Credit"
+ onButtonClick: {
+ loadPage("ChargeCredit")
+ }
+ }
+
+ BarButton {
+ id: stock_manager
+ x: 65
+ y: 582
+ width: 360
+ text: "Stock Mgmt"
+ }
+
+ BarButton {
+ id: user_manager
+ x: 599
+ y: 582
+ width: 360
+ text: "User Mgmt"
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/UserInfo.qml b/brmbar3/brmbar-gui-qt4/UserInfo.qml
new file mode 100644
index 0000000..f54f04b
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/UserInfo.qml
@@ -0,0 +1,77 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+Item {
+ id: page
+ anchors.fill: parent
+
+ property variant name: ""
+ property variant dbid: ""
+ property variant negbalance: ""
+
+ Text {
+ id: item_name
+ x: 65
+ y: 156
+ width: 337
+ height: 160
+ color: "#ffffff"
+ text: parent.name
+ wrapMode: Text.WordWrap
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 44
+ }
+
+ Text {
+ id: text3
+ x: 411
+ y: 156
+ height: 160
+ width: 548
+ color: "#ffff7c"
+ text: parent.negbalance
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ font.pointSize: 90
+ }
+
+ BarcodeInput {
+ onAccepted: {
+ var acct = shop.barcodeInput(text)
+ text = ""
+ if (typeof(acct) == "undefined") {
+ status_text.setStatus("Unknown barcode", "#ff4444")
+ return
+ }
+ if (acct.acctype == "recharge") {
+ loadPage("ChargeCredit", { "username": name, "userdbid": dbid, "amount": acct.amount })
+ return
+ }
+
+ loadPageByAcct(acct)
+ }
+ }
+
+ BarButton {
+ id: charge_credit
+ x: 65
+ y: 582
+ width: 360
+ text: "Charge"
+ fontSize: 44
+ onButtonClick: {
+ loadPage("ChargeCredit", { "username": name, "userdbid": dbid })
+ }
+ }
+
+ BarButton {
+ id: cancel
+ x: 599
+ y: 582
+ width: 360
+ text: "Main Screen"
+ onButtonClick: {
+ loadPage("MainPage")
+ }
+ }
+}
diff --git a/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject
new file mode 100644
index 0000000..2f5a47a
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject
@@ -0,0 +1,20 @@
+/* File generated by Qt Creator, version 2.5.0 */
+
+import QmlProject 1.1
+
+Project {
+ mainFile: "brmbar-gui-qt4.qml"
+
+ /* Include .qml, .js, and image files from current directory and subdirectories */
+ QmlFiles {
+ directory: "."
+ }
+ JavaScriptFiles {
+ directory: "."
+ }
+ ImageFiles {
+ directory: "."
+ }
+ /* List of plugin directories passed to QML runtime */
+ // importPaths: [ "../exampleplugin" ]
+}
diff --git a/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user
new file mode 100644
index 0000000..eeee7ab
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/brmbar-gui-qt4.qmlproject.user
@@ -0,0 +1,121 @@
+
+
+
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ System
+ false
+ 4
+ false
+ true
+ 1
+ true
+ 0
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ QML Viewer
+ QML Viewer
+ QmlProjectManager.QmlTarget
+ -1
+ -1
+ 0
+ 0
+ 0
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ 25
+
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+
+ QML Viewer
+ QmlProjectManager.QmlRunConfiguration
+ CurrentFile
+
+ 2
+
+ 3768
+ false
+ true
+ false
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.EnvironmentId
+ {a277f310-b549-4ad7-87ca-cd03f76f19ff}
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 11
+
+
diff --git a/brmbar3/brmbar-gui-qt4/brmlab.svg b/brmbar3/brmbar-gui-qt4/brmlab.svg
new file mode 100644
index 0000000..d6803f6
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/brmlab.svg
@@ -0,0 +1,189 @@
+
+
diff --git a/brmbar3/brmbar-gui-qt4/main.qml b/brmbar3/brmbar-gui-qt4/main.qml
new file mode 100644
index 0000000..5c43528
--- /dev/null
+++ b/brmbar3/brmbar-gui-qt4/main.qml
@@ -0,0 +1,28 @@
+// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5
+import QtQuick 1.1
+
+BasePage {
+ id: canvas
+
+ property variant page: Qt.createComponent("MainPage.qml").createObject(canvas)
+ function loadPage(name, properties) {
+ status_text.hideStatus()
+ name += ".qml"
+ page.destroy();
+ if (typeof(properties) == "undefined") {
+ page = Qt.createComponent(name).createObject(canvas)
+ } else {
+ page = Qt.createComponent(name).createObject(canvas, properties)
+ }
+ }
+
+ function loadPageByAcct(acct) {
+ if (acct.acctype == "inventory") {
+ loadPage("ItemInfo", { name: acct["name"], dbid: acct["id"], price: acct["price"] })
+ } else if (acct.acctype == "debt") {
+ loadPage("UserInfo", { name: acct["name"], dbid: acct["id"], negbalance: acct["negbalance"] })
+ } else if (acct.acctype == "recharge") {
+ loadPage("ChargeCredit", { amount: acct["amount"] })
+ }
+ }
+}