diff --git a/.gitignore b/.gitignore
index 18c1ceb..fdbfbff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@ downloaded_files/
testing/
cropped.png
backup/
+__pycache__/
+page.html
diff --git a/coin.ui b/coin.ui
index ed8eaa5..c7b9668 100644
--- a/coin.ui
+++ b/coin.ui
@@ -6,7 +6,7 @@
0
0
- 1280
+ 1695
720
@@ -58,7 +58,7 @@
0
0
- 594
+ 802
644
@@ -101,7 +101,14 @@
-
-
+
+
+ Save to file
+
+
+
+ -
+
Save to database
diff --git a/gui.py b/gui.py
index e0a1f7e..286d90f 100644
--- a/gui.py
+++ b/gui.py
@@ -9,20 +9,30 @@ from PyQt6.QtWidgets import *
from PyQt6 import uic
from PyQt6 import QtCore, QtGui, QtWidgets
import glob
+import upc
+from upc import get_name_from_upc
# [!!!] Change this if tesseract or coin can't find any data
MODELS_PATH = '/usr/share/tessdata/'
# [!!!] Sane values for scaling
-SCALE_MIN = 0.3
-SCALE_MAX = 4.0
+SCALE_MIN = 0.3
+SCALE_MAX = 4.0
# How much (in decimal) to change the scale for every degree
SCALE_DELTA = 8 * 360 * 1
# [!!!] Sane values for recognition
-WIDTH_MIN = 10
+WIDTH_MIN = 10
HEIGHT_MIN = 10
+# Column Information
+TABLE_COL_CNT = 5
+TABLE_COL_GET = 0
+TABLE_COL_DEL = 1
+TABLE_COL_UPC = 2
+TABLE_COL_NAME = 3
+TABLE_COL_PRICE = 4
+
# Automatically finds all available language models
models_list = glob.glob(MODELS_PATH + "*.traineddata")
model_names = []
@@ -34,7 +44,6 @@ if len(model_names) == 0:
print("No tesseract models found at", MODELS_PATH, "!", file=sys.stderr)
sys.exit(0)
-
class coin_app(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
@@ -42,6 +51,10 @@ class coin_app(QtWidgets.QMainWindow):
self.image = None
self.ui.openButton.clicked.connect(self.open)
+ self.ui.getNameButton.clicked.connect(self.get_all)
+ self.ui.saveFileButton.clicked.connect(self.save_file)
+ self.ui.saveDbButton.clicked.connect(self.save_database)
+
self.rubberBand = QRubberBand(QRubberBand.Shape.Rectangle, self)
self.ui.photo.setMouseTracking(True)
self.ui.photo.installEventFilter(self)
@@ -53,23 +66,52 @@ class coin_app(QtWidgets.QMainWindow):
self.models.currentTextChanged.connect(self.update_now)
self.models.setCurrentIndex(model_names.index(self.model))
- self.items = QStandardItemModel()
- self.itemsTable.setModel(self.items)
- self.items.setColumnCount(1)
-
self.scale = 1.0
+ self.items = None
+
def update_now(self, value):
self.model = value
print("Model selected as:", self.model)
+
+ def set_table_header(self):
+ item = QStandardItem(" Get ")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ item.setEditable(False)
+ self.items.setItem(0, TABLE_COL_GET, item)
+ item = QStandardItem(" Del ")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ item.setEditable(False)
+ self.items.setItem(0, TABLE_COL_DEL, item)
+ item = QStandardItem("UPC")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ item.setEditable(False)
+ self.items.setItem(0, TABLE_COL_UPC, item)
+ item = QStandardItem("Item Name")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ item.setEditable(False)
+ self.items.setItem(0, TABLE_COL_NAME, item)
+ item = QStandardItem("Price")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ item.setEditable(False)
+ self.items.setItem(0, TABLE_COL_PRICE, item)
+ self.itemsTable.resizeColumnsToContents()
def open(self):
filename = QFileDialog.getOpenFileName(self, 'Select File')
+ if filename[0] == "":
+ print("No file was selected or opened!")
+ return None
+
+ if self.items is not None:
+ self.items.deleteLater()
+
print("Opening file: ", filename[0])
self.image = cv2.imread(str(filename[0]))
- if self.image.size == 0:
+ if self.image is None:
print("Error: image is empty!", file=sys.stderr)
+ return None
frame = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
image = QImage(frame, frame.shape[1], frame.shape[0],
@@ -81,20 +123,123 @@ class coin_app(QtWidgets.QMainWindow):
print("Updated photo!")
print("Photo scale set to:", self.scale)
+ self.items = QStandardItemModel()
+ self.itemsTable.setModel(self.items)
+ self.items.setColumnCount(TABLE_COL_CNT)
+
+ self.set_table_header()
+ print("Initialized new table!")
+
+ self.statusBar().showMessage("Opened file: " + filename[0])
+
+ return None
+
+ def save_file(self):
+ filename = QFileDialog.getSaveFileName(self, 'Select File')
+
+ if filename[0] == "":
+ print("No file was selected or opened!")
+ return None
+
+ out = open(filename[0], "w")
+
+ for i in range(self.items.rowCount()):
+ print(self.items.item(i,TABLE_COL_UPC).text(), file=out, end=", ")
+ name = self.items.item(i,TABLE_COL_NAME).text()
+ name = name.replace("\n", " ").replace('"', '""')
+ print(name, file=out, end=", ")
+ print(self.items.item(i,TABLE_COL_PRICE).text().replace("\n", " "),
+ file=out)
+ out.close()
+
+ self.statusBar().showMessage("Saved to: " + filename[0])
+
+ def save_database(self):
+ print("Not implemented!")
+
def scale_update(self):
self.ui.photo.setPixmap(self.pixmap.scaled(
int(self.scale * self.pixmap.width()),
int(self.scale * self.pixmap.height())))
print("Photo scale set to:", self.scale)
+ self.statusBar().showMessage("Photo scale set to: " + str(self.scale))
+
def image_to_text(self, cropped_image):
gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 1)
crop = Image.fromarray(gray)
- text = pytesseract.image_to_string(crop, lang=self.model)
+ text = pytesseract.image_to_string(crop, lang=self.model).strip()
print("Text selected:", text)
return text
+ def get_row(self):
+ button = self.sender()
+ row = self.itemsTable.indexAt(button.pos()).row()
+ upc = self.items.item(row, TABLE_COL_UPC).text()
+ print("Started getting name of UPC:", upc)
+ name = get_name_from_upc(upc, max_tries=3)
+ print("Got name", name, "from UPC:", upc)
+ item = QStandardItem(name)
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.items.setItem(row, TABLE_COL_NAME, item)
+ self.itemsTable.resizeColumnToContents(TABLE_COL_NAME)
+ self.itemsTable.resizeRowToContents(row)
+
+ def get_all(self):
+ for i in range(1, self.items.rowCount()):
+
+ if self.items.item(i, TABLE_COL_NAME) is None or self.items.item(i, TABLE_COL_NAME).text() == "N/A":
+ upc = self.items.item(i, TABLE_COL_UPC).text()
+ print("Started getting name of UPC:", upc)
+ self.statusBar().showMessage("Started getting name of UPC: " + upc)
+ name = get_name_from_upc(upc, wait_interval=1, max_tries=30)
+ print("Got name", name, "from UPC:", upc)
+ item = QStandardItem(name)
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.items.setItem(i, TABLE_COL_NAME, item)
+ self.itemsTable.resizeColumnToContents(TABLE_COL_NAME)
+ self.itemsTable.resizeRowsToContents()
+
+ def del_row(self):
+ button = self.sender()
+ index = self.itemsTable.indexAt(button.pos())
+ self.items.removeRow(index.row())
+ print("Removed row", index.row())
+
+ def populate_buttons(self):
+ del_button = QPushButton("Del")
+ self.itemsTable.setIndexWidget(
+ self.items.index(self.items.rowCount() - 1, TABLE_COL_DEL),
+ del_button)
+ del_button.clicked.connect(self.del_row)
+
+ get_button = QPushButton("Get")
+ self.itemsTable.setIndexWidget(
+ self.items.index(self.items.rowCount() - 1, TABLE_COL_GET),
+ get_button)
+ get_button.clicked.connect(self.get_row)
+
+ def add_upc(self, text: str):
+ if text == "":
+ print("No text was recognized, will not add a new item!")
+ return None
+ self.statusBar().showMessage("Text selected: " + text)
+ item = QStandardItem(text)
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.items.setItem(self.items.rowCount(), TABLE_COL_UPC, item)
+
+ item = QStandardItem("N/A")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.items.setItem(self.items.rowCount() - 1, TABLE_COL_NAME, item)
+ item = QStandardItem("N/A")
+ item.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.items.setItem(self.items.rowCount() - 1, TABLE_COL_PRICE, item)
+
+ self.populate_buttons()
+ self.itemsTable.resizeColumnToContents(TABLE_COL_UPC)
+ return None
+
def eventFilter(self, source, event):
# Handling the selection box
if event.type() == QEvent.Type.MouseButtonPress and source is self.ui.photo:
@@ -132,8 +277,9 @@ class coin_app(QtWidgets.QMainWindow):
if width >= WIDTH_MIN and height >= HEIGHT_MIN and self.image is not None:
print("Cropped image:", self.x1, self.y1, self.x2, self.y2)
self.crop = self.image[self.y1:self.y2, self.x1:self.x2]
- cv2.imwrite("cropped.png", self.crop)
+ # cv2.imwrite("cropped.png", self.crop)
self.text = self.image_to_text(self.crop)
+ self.add_upc(self.text)
##self.ui.textEdit()
return True
diff --git a/upc.py b/upc.py
index 18a0939..c73488d 100644
--- a/upc.py
+++ b/upc.py
@@ -4,27 +4,37 @@ from seleniumbase import Driver
driver = Driver(uc=True)
driver.implicitly_wait(5)
-def get_name_from_upc(upc: str, wait_interval=10) -> str:
+def get_name_from_upc(upc: str, wait_interval=3, max_tries=10) -> str:
url = "https://stocktrack.ca/wm/index.php?s=wm&upc=" + upc
driver.get(url)
+ upc = upc.lstrip("0")
+
+ print("Removed leading 0's:", upc)
+
# Change the cookies here to match that of yours when you lookup
# any item on stocktrack.ca to bypass the Cloudflare checks
driver.add_cookie({
"name": "PHPSESSID",
- "value": "835ttgvbgncf4uq82dpagmk688",
+ "value": "9n1ic479r1bteiv758gm9hk65p",
"path": "/",
"domain": "stocktrack.ca"
})
driver.add_cookie({
"name": "cf_chl_3",
- "value": "1fad2e046cb9148",
+ "value": "1d706187484b25c",
"path": "/",
"domain": "stocktrack.ca"
})
driver.add_cookie({
"name": "cf_clearance",
- "value": "LUZoWRwn3n1wBEv7bY1XinLQbX0ZTAnggLIfkoy2S60-1708745781-1.0-AXqZtJSIQrq/6/H03NLlHwbpLA1tJmPjgjMJhzaczZjGabhR6ow16wMK5pTwilZiqK7C7fdcYvM0qsbAARkLGEU=",
+ "value": "Wp8tAMUKLdS3a4Y9AT09BIlZKx4x120uC1QzBQTUluQ-1710517775-1.0.1.1-hMEP8oeggZHBkylkwkQfi2p57H6zUUvGG40d_M4vGqOqg2Zh7wZsg6KrGl3XkDUn3mXAqyZrTqlQfd5pgHCZWQ",
+ "path": "/",
+ "domain": "stocktrack.ca"
+ })
+ driver.add_cookie({
+ "name": "fp",
+ "value": "26f4acb9b23415f921bba6977b68d55f",
"path": "/",
"domain": "stocktrack.ca"
})
@@ -32,25 +42,29 @@ def get_name_from_upc(upc: str, wait_interval=10) -> str:
driver.refresh()
name = ""
str_s = "target=\"_blank\">"
- str_t = "
UPC: " + upc + "
"
+ str_t = "
UPC: " + upc + "
"
+ str_tt = "
SKU:"
times = 0
- while True:
+ while times < max_tries:
if __debug__:
print("Iteration No. ", times)
- times = times + 1
+
+ times = times + 1
time.sleep(wait_interval)
page = str(driver.execute_script(
"return document.getElementsByTagName('html')[0].innerHTML"))
t = page.find(str_t)
s = page.rfind(str_s, 0, t)
+ tt = page.rfind(str_tt, 0, t)
if __debug__:
- print(page[s + len(str_s) : t - 1])
+ p = open("page.html", "w")
+ print(page, file=p)
if t == -1 or s == -1:
continue
else:
- name = page[s + len(str_s) : t - 1]
+ name = page[s + len(str_s) : tt]
break
- return name
+ return name.replace(" ", "\n")