0% found this document useful (0 votes)
1 views

Code_Save

The document contains a Flask application that integrates with the ZaloPay API for processing payments, handling callbacks, and checking order statuses. It includes routes for payment processing, callback handling, and order status retrieval, as well as a GUI application built with PyQt5 for displaying revenue graphs and importing data from Excel. The application uses HMAC for security and logs various events for debugging purposes.

Uploaded by

quang vinh
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views

Code_Save

The document contains a Flask application that integrates with the ZaloPay API for processing payments, handling callbacks, and checking order statuses. It includes routes for payment processing, callback handling, and order status retrieval, as well as a GUI application built with PyQt5 for displaying revenue graphs and importing data from Excel. The application uses HMAC for security and logs various events for debugging purposes.

Uploaded by

quang vinh
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 56

CODE POSTMAN

# coding=utf-8

from time import time


from datetime import datetime
import json, hmac, hashlib, urllib.request, urllib.parse, random
from flask import Flask, request, jsonify
from collections import OrderedDict # Để giữ thứ tự các trường trong JSON

app = Flask(__name__)
config = {
"app_id": 2554,
"key1": "sdngKKJmqEMzvh5QQcdD2A9XBSKUNaYn",
"key2": "trMrHtvjo6myautxDUiAcYsVtaeQ8nhf",
"endpoint": "https://sb-openapi.zalopay.vn/v2/create"
}

# Define the payment route


@app.route("/payment", methods=["POST"])
def payment():
transID = random.randrange(1000000)
order = {
"app_id": config["app_id"],
"app_trans_id": "{:%y%m%d}_{}".format(datetime.today(), transID), #
mã giao dich có định dạng yyMMdd_xxxx
"app_user": "user123",
"app_time": int(round(time() * 1000)), # miliseconds
"embed_data": json.dumps({}),
"item": json.dumps([{}]),
"amount": 50000,
"description": "Lazada - Payment for the order #" + str(transID),
"bank_code": "zalopayapp"
}

# app_id|app_trans_id|app_user|amount|apptime|embed_data|item
data = "{}|{}|{}|{}|{}|{}|{}".format(order["app_id"],
order["app_trans_id"], order["app_user"],
order["amount"], order["app_time"],
order["embed_data"], order["item"])

order["mac"] = hmac.new(config['key1'].encode(), data.encode(),


hashlib.sha256).hexdigest()
print(data)

try:
response = urllib.request.urlopen(url=config["endpoint"],
data=urllib.parse.urlencode(order).encode())
result = json.loads(response.read().decode('utf-8')) # Decode
response to ensure proper handling of Unicode

# Sắp xếp các trường theo thứ tự mong muốn


ordered_result = OrderedDict([
("return_code", result.get("return_code")),
("return_message", result.get("return_message")),
("sub_return_code", result.get("sub_return_code")),
("sub_return_message", result.get("sub_return_message")),
("zp_trans_token", result.get("zp_trans_token")),
("order_url", result.get("order_url")),
("order_token", result.get("order_token"))
])

print(json.dumps(ordered_result, indent=4,
ensure_ascii=False)) # Log the result in terminal
with proper Unicode handling
return jsonify(ordered_result) # Return the ordered result as JSON
except Exception as e:
return jsonify({"error": str(e)}), 500

# CALLBACK
@app.route('/callback', methods=['POST'])
def callback():
result = {}

try:
cbdata = request.json
mac = hmac.new(config['key2'].encode(), cbdata['data'].encode(),
hashlib.sha256).hexdigest()

# kiểm tra callback hợp lệ (đến từ ZaloPay server)


if mac != cbdata['mac']:
# callback không hợp lệ
result['return_code'] = -1
result['return_message'] = 'mac not equal'
else:
# thanh toán thành công
# merchant cập nhật trạng thái cho đơn hàng
dataJson = json.loads(cbdata['data'])
print("update order's status = success where app_trans_id = " +
dataJson['app_trans_id'])

result['return_code'] = 1
result['return_message'] = 'success'
except Exception as e:
result['return_code'] = 0 # ZaloPay server sẽ callback lại (tối đa 3
lần)
result['return_message'] = str(e)

# thông báo kết quả cho ZaloPay server


return jsonify(result)

# Order status route


@app.route('/order-status/<app_trans_id>', methods=['POST'])
def order_status(app_trans_id):
params = {
"app_id": config["app_id"],
"app_trans_id": app_trans_id
}

data = "{}|{}|{}".format(config["app_id"], params["app_trans_id"],


config["key1"]) # app_id|app_trans_id|key1
params["mac"] = hmac.new(config['key1'].encode(), data.encode(),
hashlib.sha256).hexdigest()
try:
status_endpoint = "https://sb-openapi.zalopay.vn/v2/query"
response = urllib.request.urlopen(url=status_endpoint,
data=urllib.parse.urlencode(params).encode())
result = json.loads(response.read().decode('utf-8')) # Decode
response to ensure proper handling of Unicode

print(json.dumps(result, indent=4, ensure_ascii=False)) # Log the


result in terminal with proper Unicode handling
return jsonify(result) # Return the result as JSON
except Exception as e:
return jsonify({"error": str(e)}), 500

if __name__ == "__main__":
app.run(debug=True)

Test image:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableWidgetItem,
QPushButton, QMessageBox, QFileDialog
from PyQt5 import uic
from PyQt5.QtCore import QDateTime, QDate, QTime
from collections import defaultdict
import matplotlib.pyplot as plt
import pandas as pd

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()

# Load the UI file


uic.loadUi('Opencv_PiDash.ui', self)

self.setWindowTitle("Revenue Graph")
self.setGeometry(100, 100, 800, 600)

self.dateEditStart.setDisplayFormat("dd-MM-yyyy")
self.dateEditStart.setDate(QDate(2023, 1, 1))
self.dateEditStart.setCalendarPopup(True)
self.dateEditStart.dateChanged.connect(self.update_table_only)

self.dateEditEnd.setDisplayFormat("dd-MM-yyyy")
self.dateEditEnd.setDate(QDate(2024, 12, 31))
self.dateEditEnd.setCalendarPopup(True)
self.dateEditEnd.dateChanged.connect(self.update_table_only)

self.comboBox.addItems(["Daily", "Monthly", "Yearly"])


self.comboBox.currentIndexChanged.connect(self.update_graph)

self.tableWidget_History.setColumnCount(5)
self.tableWidget_History.setHorizontalHeaderLabels(["Name", "Phone",
"License Plate", "Time", "Money"])

self.import_button_history = self.findChild(QPushButton,
'Import_history')
self.import_button_history.clicked.connect(self.import_excel_history)

def clear_table(self):
self.tableWidget_History.setRowCount(0)

def update_table_history(self, data):


self.clear_table()
for rowData in data:
rowPosition = self.tableWidget_History.rowCount()
self.tableWidget_History.insertRow(rowPosition)
for column, value in enumerate(rowData):
if column == 4: # Money column
# Remove " VND" from the end of the string
money_str = str(value).replace(" VND", "")
item = QTableWidgetItem(f"{money_str} VND")
else:
item = QTableWidgetItem(str(value))
self.tableWidget_History.setItem(rowPosition, column, item)
self.tableWidget_History.resizeColumnsToContents()
self.tableWidget_History.resizeRowsToContents()

def filter_data(self):
start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()
filtered_data = []

for row in range(self.tableWidget_History.rowCount()):


name = self.tableWidget_History.item(row, 0).text()
phone = self.tableWidget_History.item(row, 1).text()
license_plate = self.tableWidget_History.item(row, 2).text()
time_str = self.tableWidget_History.item(row, 3).text()
money_str = self.tableWidget_History.item(row, 4).text().replace("
VND", "")
money = int(money_str)

date = QDateTime.fromString(time_str, 'hh:mm:ss dd-MM-


yyyy').date()
if start_date <= date <= end_date:
filtered_data.append([name, phone, license_plate, time_str,
money])

self.update_table_history(filtered_data)
return filtered_data

def update_table_only(self):
self.filter_data()

def update_graph(self):
self.show_revenue_graph()
def show_revenue_graph(self):
try:
# Get selected time period
selected_option = self.comboBox.currentText()
date_revenue = defaultdict(int)

start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()

# Calculate revenue based on selected time period


for row in range(self.tableWidget_History.rowCount()):
money_str = self.tableWidget_History.item(row,
4).text().replace(" VND", "")
money = int(money_str)
date_str = self.tableWidget_History.item(row, 3).text()
date = QDateTime.fromString(date_str, 'hh:mm:ss dd-MM-
yyyy').date()

if start_date <= date <= end_date:


if selected_option == "Daily":
date_revenue[date] += money
elif selected_option == "Monthly":
month_year = QDate(date.year(), date.month(), 1)
date_revenue[month_year] += money
elif selected_option == "Yearly":
year = QDate(date.year(), 1, 1)
date_revenue[year] += money

dates = sorted(date_revenue.keys())
revenues = [date_revenue[date] / 1_000_000 for date in dates] #
Convert to million VND

if not dates:
QMessageBox.warning(self, 'Thông báo', 'Không có dữ liệu để vẽ
đồ thị.', QMessageBox.Ok)
return

# Clear previous plot if needed


plt.figure(figsize=(10, 6))

if selected_option == "Monthly":
x_labels = [d.toString('MM-yyyy') for d in dates] # Change x-
axis label format for "Monthly"
elif selected_option == "Yearly":
x_labels = [d.toString('yyyy') for d in dates] # Change x-
axis label format for "Yearly"
else:
x_labels = [d.toString('dd-MM-yyyy') for d in dates]

plt.bar(x_labels, revenues)
plt.xlabel('Date')
plt.ylabel('Revenue (Million VND)') # Update y-axis label
plt.title(f'Revenue Over Time ({selected_option})')
plt.xticks(rotation=45)
plt.gca().get_yaxis().get_major_formatter().set_scientific(False)
# Disable scientific notation for y-axis
self.autolabel(plt.gca().patches)
plt.tight_layout()
plt.show()

except Exception as e:
QMessageBox.critical(self, 'Lỗi', f'Đã xảy ra lỗi: {str(e)}',
QMessageBox.Ok)

def autolabel(self, rects):


"""Attach a text label above each bar in *rects*, displaying its
height."""
for rect in rects:
height = rect.get_height()
plt.annotate('{:.2f}'.format(height),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom')

def import_excel_history(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Import Excel", "",
"Excel Files (*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
df = pd.read_excel(fileName)
data = df.values.tolist()
self.update_table_history(data)
print(f"Excel file imported from {fileName}") # Print the
file path
except Exception as e:
QMessageBox.critical(self, 'Error', f'Error importing file:
{str(e)}', QMessageBox.Ok)

if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

Save code Wweb:


# coding=utf-8

from time import time, sleep


from datetime import datetime
import json
import hmac
import hashlib
import urllib.request
import urllib.parse
import random
from flask import Flask, request, jsonify, render_template
from collections import OrderedDict
import logging
import threading, socket
import qrcode
from io import BytesIO
import base64

app = Flask(__name__)
config = {
"app_id": 2554,
"key1": "sdngKKJmqEMzvh5QQcdD2A9XBSKUNaYn",
"key2": "trMrHtvjo6myautxDUiAcYsVtaeQ8nhf",
"endpoint": "https://sb-openapi.zalopay.vn/v2/create"
}

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

# Global variable to store the latest app_trans_id


latest_app_trans_id = None

# Khai báo client socket global


gui_socket = None

# Hàm để gửi thông điệp đến GUI thông qua socket


def send_message_to_gui(message):
global gui_socket
if gui_socket:
try:
gui_socket.sendall(message.encode())
except Exception as e:
logging.error(f"Lỗi khi gửi thông điệp đến GUI: {str(e)}")

# Function to continuously check order status


def check_order_status(app_trans_id):
url = f"http://127.0.0.1:5000/order-status/{app_trans_id}"
print(url)
send_message_to_gui(f"http://127.0.0.1:5000/order-status/{app_trans_id}")
while True:
try:
response = urllib.request.urlopen(url)
result = json.loads(response.read().decode('utf-8'))
logging.info(f"Trạng thái hiện tại của đơn hàng {app_trans_id}:
{result}")
send_message_to_gui(f"Trạng thái hiện tại của đơn hàng
{app_trans_id}: {result}")

if "return_message" in result and result["return_message"] ==


"Giao dịch thành công":
print(f"Đơn hàng {app_trans_id} đã hoàn thành thành công.")
logging.info(f"Đơn hàng {app_trans_id} đã hoàn thành thành
công.")
send_message_to_gui(f"Đơn hàng {app_trans_id} đã hoàn thành
thành công.")
break
sleep(3) # Wait for 3 seconds before sending the next request
except Exception as e:
logging.error(f"Lỗi khi gửi yêu cầu đến {url}: {str(e)}")
send_message_to_gui(f"Lỗi khi gửi yêu cầu đến {url}: {str(e)}")
sleep(3) # Retry after 3 seconds

# Define the payment route


@app.route("/", methods=["GET"])
def index():
return render_template('index.html')

@app.route('/get_latest_app_trans_id')
def get_latest_app_trans_id():
global latest_app_trans_id
if latest_app_trans_id:
return latest_app_trans_id
else:
return "No app_trans_id available"

@app.route("/payment", methods=["POST"])
def payment():
global latest_app_trans_id

transID = random.randrange(1000000)
amount = int(request.form['amount'])
description = request.form['description']

order = {
"app_id": config["app_id"],
"app_trans_id": "{:%y%m%d}_{}".format(datetime.today(), transID),
"app_user": "user123",
"app_time": int(round(time() * 1000)),
"embed_data": json.dumps({}),
"item": json.dumps([{}]),
"amount": amount,
"description": description,
"bank_code": "zalopayapp"
}

data = "{}|{}|{}|{}|{}|{}|{}".format(order["app_id"],
order["app_trans_id"], order["app_user"],
order["amount"], order["app_time"],
order["embed_data"], order["item"])

order["mac"] = hmac.new(config['key1'].encode(), data.encode(),


hashlib.sha256).hexdigest()

print(f"{order['app_trans_id']}")
send_message_to_gui(f"{order['app_trans_id']}")
latest_app_trans_id = order["app_trans_id"] # Update the
latest_app_trans_id value

try:
response = urllib.request.urlopen(url=config["endpoint"],
data=urllib.parse.urlencode(order).encode())
result = json.loads(response.read().decode('utf-8'))

# Add app_trans_id to the ordered_result


ordered_result = OrderedDict([
("return_code", result.get("return_code")),
("return_message", result.get("return_message")),
("sub_return_code", result.get("sub_return_code")),
("sub_return_message", result.get("sub_return_message")),
("zp_trans_token", result.get("zp_trans_token")),
("order_url", result.get("order_url")),
("order_token", result.get("order_token")),
("app_trans_id", order["app_trans_id"]) # Include app_trans_id
here
])

# Generate QR code
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(result.get("order_url"))
qr.make(fit=True)

img = qr.make_image(fill='black', back_color='white')


buffered = BytesIO()
img.save(buffered, format="PNG")
img_str = base64.b64encode(buffered.getvalue()).decode()

ordered_result["qr_code_base64"] = img_str

print(result.get("order_url"))
send_message_to_gui(result.get("order_url"))

print(f"MÃ QR là: {img_str}")


send_message_to_gui(f"MÃ QR là: {img_str}")

# Start a new thread to check order status


threading.Thread(target=check_order_status,
args=(latest_app_trans_id,), daemon=True).start()

return jsonify(ordered_result)
except Exception as e:
return jsonify({"error": str(e)}), 500

# CALLBACK
@app.route('/callback', methods=['POST', 'GET'])
def callback():
result = {}

try:
if request.method == 'POST' or request.method == 'GET':
if request.method == 'POST':
cbdata = request.json
elif request.method == 'GET':
cbdata = request.args.to_dict()

mac = hmac.new(config['key2'].encode(), cbdata['data'].encode(),


hashlib.sha256).hexdigest()

if mac != cbdata['mac']:
result['return_code'] = -1
result['return_message'] = 'mac not equal'
else:
dataJson = json.loads(cbdata['data'])
app_trans_id = dataJson['app_trans_id']
logging.info(f"Cập nhật trạng thái đơn hàng thành công cho
app_trans_id = {app_trans_id}")

result['return_code'] = 1
result['return_message'] = 'success'
else:
result['return_code'] = -1
result['return_message'] = 'Method Not Allowed'
except Exception as e:
result['return_code'] = 0
result['return_message'] = str(e)

return jsonify(result)

# Order status route


@app.route('/order-status/<app_trans_id>', methods=['POST', 'GET'])
def order_status(app_trans_id):
params = {
"app_id": config["app_id"],
"app_trans_id": app_trans_id
}

data = "{}|{}|{}".format(config["app_id"], params["app_trans_id"],


config["key1"])
params["mac"] = hmac.new(config['key1'].encode(), data.encode(),
hashlib.sha256).hexdigest()

try:
status_endpoint = "https://sb-openapi.zalopay.vn/v2/query"
response = urllib.request.urlopen(url=status_endpoint,
data=urllib.parse.urlencode(params).encode())
result = json.loads(response.read().decode('utf-8'))

# Log the result to terminal


# logging.info(f"Trạng thái đơn hàng cho app_trans_id {app_trans_id}:
{result}")

if result.get("return_message") == "Giao dịch thành công":


result["status"] = "Đang kiểm tra trạng thái đơn hàng trên web" #
web
logging.info("Đang kiểm tra trạng thái đơn hàng trên web") #
terminal
return jsonify(result)
except Exception as e:
error_message = {"error": str(e)}
logging.error(f"Lỗi khi truy vấn trạng thái đơn hàng cho app_trans_id
{app_trans_id}: {error_message}")
return jsonify(error_message), 500

if __name__ == "__main__":
# Khởi tạo server socket để kết nối với GUI
HOST = 'localhost' # Địa chỉ IP của máy chạy GUI.py
PORT = 12345 # Cổng mà GUI.py sẽ lắng nghe

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:


s.bind((HOST, PORT))
s.listen()
logging.info(f"Server socket đang lắng nghe trên cổng {PORT}...")

# Chấp nhận kết nối từ GUI.py


gui_socket, addr = s.accept()
logging.info(f"Đã kết nối với {addr}")
app.run(debug=False)

ZALO FORM PAYMENT:


{% extends "layout.html" %}

{% block title %}Thanh toán ZaloPay{% endblock %}

{% block content %}
<div class="row">
<div class="col-md-6 offset-md-3">
<h2 class="mb-4">ZaloPay Payment Form</h2>
<form action="/payment" method="POST">
<div class="form-group">
<label for="amount">Amount:</label>
<input type="number" class="form-control" id="amount"
name="amount" required>
</div>
<div class="form-group">
<label for="description">Description:</label>
<textarea class="form-control" id="description"
name="description" rows="3" required></textarea>
</div>
<button type="submit" class="btn">Pay with ZaloPay</button>
</form>
<br>
<h2>Other Actions</h2>
<!-- <button class="btn" onclick="window.location.href='/callback'">Go
to Callback</button>-->
<button class="btn" onclick="window.location.href='/login'">Go to
Login</button>
<br><br>
<form onsubmit="checkOrderStatus(event)">
<div class="form-group">
<label for="app_trans_id">App Trans ID:</label>
<input type="text" class="form-control" id="app_trans_id"
name="app_trans_id" value="{{ app_trans_id }}" required>
</div>
<button type="submit" class="btn">Check Order Status</button>
<button type="button" class="btn"
onclick="updateAppTransId()">Update from Terminal</button>
</form>
</div>
</div>

<script>
function checkOrderStatus(event) {
event.preventDefault();
const appTransId = document.getElementById('app_trans_id').value;
window.location.href = `/order-status/${appTransId}`;
}

function updateAppTransId() {
fetch('/get_latest_app_trans_id')
.then(response => response.text())
.then(data => {
document.getElementById('app_trans_id').value = data;
})
.catch(error => {
console.error('Error fetching latest app_trans_id:', error);
});
}
</script>
{% endblock %}

Ready Lamp
# pip install Pillow
# pip uninstall -y setuptools
# pip install setuptools==39.1.0
# pip install setuptools==69.5.1
# pip install hydra
# shell = True
# pip install hydra-core>=1.2.0

import os

os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
from paddleocr import PaddleOCR, draw_ocr
from PIL import Image
from PyQt5 import uic
from PyQt5.QtMultimedia import QCameraInfo
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel,
QTableWidgetItem, QPushButton, QLineEdit, QMessageBox, \
QFileDialog, QInputDialog
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QTimer, QDateTime, Qt,
QDate, QTime
from PyQt5.QtGui import QImage, QPixmap
import cv2, time, sysinfo
import numpy as np
import random as rnd
import random
import sys
from ultralytics import YOLO
import pandas as pd
import openpyxl
import matplotlib

matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from collections import defaultdict

class ThreadClass(QThread):
ImageUpdate = pyqtSignal(np.ndarray) # Tạo tín hiệu để cập nhật hình ảnh
FPS = pyqtSignal(int) # Tạo tín hiệu để cập nhật số khung hình trên giây
(FPS)
global camIndex # Đặt biến camIndex là biến toàn cục

def __init__(self, fileName=None, camIndex=None, parent=None):


super(ThreadClass, self).__init__(parent)
self.fileName = fileName

def run(self):
if self.fileName:
Capture = cv2.VideoCapture(self.fileName)
elif camIndex == 0:
Capture = cv2.VideoCapture(camIndex) # Mở camera với chỉ số
camIndex
elif camIndex == 1:
Capture = cv2.VideoCapture(camIndex, cv2.CAP_DSHOW) # Mở camera
với chỉ số camIndex sử dụng DirectShow

Capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # Đặt chiều cao khung


hình
Capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # Đặt chiều rộng khung
hình
self.ThreadActive = True # Đặt cờ hiệu hoạt động của luồng
prev_frame_time = 0 # Thời gian khung hình trước đó
new_frame_time = 0 # Thời gian khung hình mới
while self.ThreadActive:
ret, frame_cap = Capture.read() # Đọc một khung hình từ camera
# flip_frame = cv2.flip(src=frame_cap, flipCode=1) # Lật khung
hình 1
new_frame_time = time.time() # Lấy thời gian hiện tại
fps = 1 / (new_frame_time - prev_frame_time) # Tính toán FPS
prev_frame_time = new_frame_time # Cập nhật thời gian khung hình
trước đó
if ret:
self.ImageUpdate.emit(frame_cap) # Phát tín hiệu cập nhật
hình ảnh
self.FPS.emit(fps) # Phát tín hiệu cập nhật FPS
time.sleep(1 / 30) # Sleep to mimic video frame rate
else:
self.ThreadActive = False
Capture.release()

def stop(self):
self.ThreadActive = False # Dừng luồng
self.quit() # Thoát luồng

class boardInfoClass(QThread):
cpu = pyqtSignal(float) # Tạo tín hiệu để cập nhật sử dụng CPU
ram = pyqtSignal(tuple) # Tạo tín hiệu để cập nhật sử dụng RAM

def run(self):
self.ThreadActive = True # Đặt cờ hiệu hoạt động của luồng
while self.ThreadActive:
cpu = sysinfo.getCPU() # Lấy thông tin sử dụng CPU
ram = sysinfo.getRAM() # Lấy thông tin sử dụng RAM
# temp = sysinfo.getTemp() # Lấy thông tin nhiệt độ (bị comment
do không dùng)
self.cpu.emit(cpu) # Phát tín hiệu cập nhật CPU
self.ram.emit(ram) # Phát tín hiệu cập nhật RAM

def stop(self):
self.ThreadActive = False # Dừng luồng
self.quit() # Thoát luồng

class randomColorClass(QThread):
color = pyqtSignal(tuple) # Tạo tín hiệu để cập nhật màu ngẫu nhiên

def run(self):
self.ThreadActive = True # Đặt cờ hiệu hoạt động của luồng
while self.ThreadActive:
color = ([rnd.randint(0, 256), rnd.randint(0, 256), rnd.randint(0,
256)],
[rnd.randint(0, 256), rnd.randint(0, 256), rnd.randint(0,
256)],
[rnd.randint(0, 256), rnd.randint(0, 256), rnd.randint(0,
256)]
) # Tạo ba màu ngẫu nhiên
self.color.emit(color) # Phát tín hiệu cập nhật màu
time.sleep(2) # Chờ 2 giây trước khi tạo màu mới

def stop(self):
self.ThreadActive = False # Dừng luồng
self.quit() # Thoát luồng

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = uic.loadUi("Opencv_PiDash.ui", self) # Tải giao diện từ tệp
Opencv_PiDash.ui

self.online_cam = QCameraInfo.availableCameras() # Lấy danh sách các


camera có sẵn
self.camlist.addItems([c.description() for c in self.online_cam]) #
Thêm mô tả của các camera vào danh sách
self.btn_start.clicked.connect(self.StartWebCam) # Kết nối nút bắt
đầu với hàm StartWebCam
self.btn_stop.clicked.connect(self.StopWebcam) # Kết nối nút dừng với
hàm StopWebcam

self.resource_usage = boardInfoClass() # Tạo đối tượng boardInfoClass


để lấy thông tin hệ thống
self.resource_usage.start() # Bắt đầu luồng lấy thông tin hệ thống
self.resource_usage.cpu.connect(self.getCPU_usage) # Kết nối tín hiệu
cập nhật CPU với hàm getCPU_usage
self.resource_usage.ram.connect(self.getRAM_usage) # Kết nối tín hiệu
cập nhật RAM với hàm getRAM_usage

# self.Ready_Lamp = randomColorClass() # Tạo đối tượng


randomColorClass để tạo màu ngẫu nhiên
# self.Ready_Lamp.start() # Bắt đầu luồng tạo màu ngẫu nhiên
# self.Ready_Lamp.color.connect(
# self.get_randomColors) # Kết nối tín hiệu cập nhật màu với hàm
get_randomColors

# Khu vực QTimer


self.ready_lamp_on = QTimer(self, interval=1000) # Tạo bộ đếm thời
gian cho đèn sẵn sàng, mỗi giây một lần
self.ready_lamp_on.timeout.connect(self.Ready_Light)
self.ready_lamp_on.start() # Bắt đầu bộ đếm thời gian

self.run_lamp_on = QTimer(self, interval=1000) # Tạo bộ đếm thời gian


cho đèn sẵn sàng, mỗi giây một lần
self.run_lamp_on.timeout.connect(self.Run_Light)
# self.run_lamp_on.start() # Bắt đầu bộ đếm thời gian

self.motor_on = QTimer(self, interval=1000) # Tạo bộ đếm thời gian


cho động cơ, mỗi giây một lần
self.motor_on.timeout.connect(
self.Motor_state) # Kết nối tín hiệu timeout của bộ đếm thời gian
với hàm Motor_state

self.lcd_timer = QTimer() # Tạo bộ đếm thời gian cho đồng hồ LCD


self.lcd_timer.timeout.connect(self.clock) # Kết nối tín hiệu timeout
của bộ đếm thời gian với hàm clock
self.lcd_timer.start() # Bắt đầu bộ đếm thời gian

self.flag_motor = True # Cờ hiệu cho trạng thái động cơ


self.Status_lamp = [True, True, True] # Trạng thái của các đèn
# Kết thúc khu vực QTimer

self.Motor_status.setPixmap(QPixmap('images/setting.png')) # Đặt hình


ảnh của trạng thái động cơ là "MotorOFF"
self.btn_motor.setText('Motor ON') # Đặt text của nút động cơ là
"Motor ON"

self.btn_motor.setCheckable(True) # Cho phép nút động cơ có thể


bật/tắt
self.btn_motor.clicked.connect(self.Motor_on) # Kết nối tín hiệu
clicked của nút động cơ với hàm Motor_on

self.btn_setObject1.setCheckable(True) # Cho phép nút đối tượng 1 có


thể bật/tắt
self.btn_setObject1.clicked.connect(
self.set_object1) # Kết nối tín hiệu clicked của nút đối tượng 1
với hàm set_object1

self.btn_close.clicked.connect(
self.Close_software) # Kết nối tín hiệu clicked của nút đóng với
hàm Close_software

self.btn_stop.setEnabled(False) # Vô hiệu hóa nút dừng

self.comboBox.currentIndexChanged.connect(self.update_graph)

# Connect the button click to the snapshot function


self.btn_snap_shot.clicked.connect(self.take_snapshot)

self.initUI()
# self.generate_random_data()

def get_randomColors(self, color):


self.RanColor1 = color[0] # Lấy màu ngẫu nhiên đầu tiên
self.RanColor2 = color[1] # Lấy màu ngẫu nhiên thứ hai
self.RanColor3 = color[2] # Lấy màu ngẫu nhiên thứ ba

def getCPU_usage(self, cpu):


self.Qlabel_cpu.setText(str(cpu) + " %") # Hiển thị sử dụng CPU dưới
dạng phần trăm
if cpu > 15: self.Qlabel_cpu.setStyleSheet("color: rgb(23, 63, 95);")
# Thay đổi màu sắc dựa trên giá trị CPU
if cpu > 25: self.Qlabel_cpu.setStyleSheet("color: rgb(32, 99, 155);")
if cpu > 45: self.Qlabel_cpu.setStyleSheet("color: rgb(60, 174,
163);")
if cpu > 65: self.Qlabel_cpu.setStyleSheet("color: rgb(246, 213,
92);")
if cpu > 85: self.Qlabel_cpu.setStyleSheet("color: rgb(237, 85, 59);")

def getRAM_usage(self, ram):


self.Qlabel_ram.setText(str(ram[2]) + " %") # Hiển thị sử dụng RAM
dưới dạng phần trăm
if ram[2] > 15: self.Qlabel_ram.setStyleSheet(
"color: rgb(23, 63, 95);") # Thay đổi màu sắc dựa trên giá trị
RAM
if ram[2] > 25: self.Qlabel_ram.setStyleSheet("color: rgb(32, 99,
155);")
if ram[2] > 45: self.Qlabel_ram.setStyleSheet("color: rgb(60, 174,
163);")
if ram[2] > 65: self.Qlabel_ram.setStyleSheet("color: rgb(246, 213,
92);")
if ram[2] > 85: self.Qlabel_ram.setStyleSheet("color: rgb(237, 85,
59);")

def get_FPS(self, fps):


self.Qlabel_fps.setText(str(fps)) # Hiển thị FPS hiện tại
if fps > 5: self.Qlabel_fps.setStyleSheet(
"color: rgb(237, 85, 59);") # Thay đổi màu sắc của nhãn dựa trên
giá trị FPS
if fps > 15: self.Qlabel_fps.setStyleSheet("color: rgb(60, 174,
155);")
if fps > 25: self.Qlabel_fps.setStyleSheet("color: rgb(85, 170,
255);")
if fps > 35: self.Qlabel_fps.setStyleSheet("color: rgb(23, 63, 95);")

def clock(self):
self.DateTime = QDateTime.currentDateTime() # Lấy thời gian hiện tại
self.lcd_clock.display(
self.DateTime.toString('hh:mm:ss dd-MM-yyyy')) # Hiển thị thời
gian hiện tại trên đồng hồ LCD

# Đóng cửa sổ thông báo lỗi


def Close_Error(self):
self.Win_error.close() # Đóng cửa sổ báo lỗi

def initUI(self):
self.TableList()

# Kết nối nút Set với phương thức cập nhật thời gian trong bảng
self.setButton = self.findChild(QPushButton, 'setButton')
self.setButton.clicked.connect(self.update_time_in_table)

# Tìm các ô đầu vào trên giao diện


self.name_input = self.findChild(QLineEdit, 'nameInput')
self.phone_input = self.findChild(QLineEdit, 'phoneInput')
self.license_plate_input = self.findChild(QLineEdit,
'licensePlateInput')
self.license_plate_YOLO = self.findChild(QLineEdit, 'License_plate')
# Result YOLOv8

# Tìm các nút Clear


self.clear_all_button = self.findChild(QPushButton, 'clearAllButton')
self.clear_all_button.clicked.connect(self.clear_all_rows)

self.clear_selected_button = self.findChild(QPushButton,
'clearSelectedButton')
self.clear_selected_button.clicked.connect(self.clear_selected_row)

# Tìm nút Open File


self.open_file_button = self.findChild(QPushButton, 'openFileButton')
self.open_file_button.clicked.connect(self.open_file_dialog)

# Tìm QLabel để hiển thị ảnh hoặc video frame


self.disp_main = self.findChild(QLabel, 'disp_main')

self.export_button = self.findChild(QPushButton, 'Export_button')


self.export_button.clicked.connect(self.export_excel)

self.export_button_history = self.findChild(QPushButton,
'Export_history')
self.export_button_history.clicked.connect(self.export_excel_history)

self.import_button = self.findChild(QPushButton, 'Import_button')


self.import_button.clicked.connect(self.import_excel)

self.import_button_history = self.findChild(QPushButton,
'Import_history')
self.import_button_history.clicked.connect(self.import_excel_history)

self.btn_snap_shot = self.findChild(QPushButton, 'btn_snap_shot')

# Biến lưu trữ ThreadClass object


self.thread = None

def TableList(self):
# Đặt tên cho các cột
headers = ["Name", "Phone", "License Plate", "Time"]
self.tableWidget.setColumnCount(len(headers))
self.tableWidget.setHorizontalHeaderLabels(headers)

headers_history = ["Name", "Phone", "License Plate", "Time", "Money"]


self.tableWidget_History.setColumnCount(len(headers_history))
self.tableWidget_History.setHorizontalHeaderLabels(headers_history)

# Dữ liệu ban đầu


data = [
["John Doe", "123456789", "H982 FKL", "10:00:00 21-06-2024"],
["Jane Smith", "987654321", "51A-138.83", "11:00:00 21-06-2024"],
["Mike Johnson", "555666777", "60A-999.99", "12:00:00 21-06-
2024"],
["Emily Davis", "111222333", "51F-886.86", "13:00:00 21-06-2024"]
]

self.update_table(data)

def update_table(self, data):


for rowData in data:
# Thêm một hàng mới vào bảng
rowPosition = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowPosition)

for column, value in enumerate(rowData):


if column >= self.tableWidget.columnCount():
self.tableWidget.insertColumn(column)

item = QTableWidgetItem(value)
self.tableWidget.setItem(rowPosition, column, item)

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()

def update_table_history(self, data):


rowCount = self.tableWidget_History.rowCount()
columnCount = self.tableWidget_History.columnCount()
for rowData in data:
# Thêm một hàng mới vào bảng
rowPosition = self.tableWidget_History.rowCount()
self.tableWidget_History.insertRow(rowPosition)

# Kiểm tra và thêm cột mới nếu cần thiết


if len(rowData) > columnCount:
for i in range(len(rowData) - columnCount):
self.tableWidget_History.insertColumn(columnCount + i)

# Cập nhật dữ liệu vào bảng


for column, value in enumerate(rowData):
item = QTableWidgetItem(str(value))
self.tableWidget_History.setItem(rowPosition, column, item)

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget_History.resizeColumnsToContents()
self.tableWidget_History.resizeRowsToContents()

def update_time_in_table(self, plate_num):


currentTime = QDateTime.currentDateTime().toString('hh:mm:ss dd-MM-
yyyy')

# Lấy giá trị từ các ô đầu vào


name = self.name_input.text()
phone = self.phone_input.text()
license_plate = self.license_plate_input.text()
license_plate_YOLO = self.license_plate_YOLO.text()

found = False
money = "0 VND"

# Kiểm tra xem license_plate đã tồn tại trong cột "License Plate" hay
chưa
for row in range(self.tableWidget.rowCount()):
item = self.tableWidget.item(row, 2) # Cột "License Plate" là cột
thứ 3 (chỉ mục 2)
if item.text() == plate_num:
QMessageBox.warning(self, 'Thông báo',
'Biển số xe đã tồn tại trong danh sách
đăng ký. Bạn không cần phải trả tiền.',
QMessageBox.Ok)
# Lưu lại lịch sử giao dịch
name = self.tableWidget.item(row, 0).text()
phone = self.tableWidget.item(row, 1).text()
license_plate = self.tableWidget.item(row, 2).text()
self.update_transaction_history(name, phone, license_plate,
currentTime, money)
found = True
break # Dừng lại nếu tìm thấy biển số xe trùng

if not found:
reply = QMessageBox.question(self, 'Thông báo',
'Biển số xe chưa được đăng ký.\nBạn
có muốn đăng ký vé tháng chứ?',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
# Hiển thị các hộp nhập liệu để người dùng điền thông tin
name, ok1 = QInputDialog.getText(self, 'Đăng ký vé tháng',
'Nhập tên:')
phone, ok2 = QInputDialog.getText(self, 'Đăng ký vé tháng',
'Nhập số điện thoại:')
license_plate, ok3 = QInputDialog.getText(self, 'Đăng ký vé
tháng', 'Nhập biển số xe:', text=plate_num)

if ok1 and ok2 and ok3:


money = "500000 VND"

# Thêm một hàng mới vào bảng


rowPosition = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowPosition)

# Cập nhật các giá trị vào hàng mới


self.tableWidget.setItem(rowPosition, 0,
QTableWidgetItem(name))
self.tableWidget.setItem(rowPosition, 1,
QTableWidgetItem(phone))
self.tableWidget.setItem(rowPosition, 2,
QTableWidgetItem(license_plate))
self.tableWidget.setItem(rowPosition, 3,
QTableWidgetItem(currentTime))

# Tự động điều chỉnh kích thước của các cột và hàng dựa
trên nội dung
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()

# Lưu lại lịch sử giao dịch


self.update_transaction_history(name, phone,
license_plate, currentTime, money)
else:
QMessageBox.warning(self, 'Thông báo', 'Bạn phải điền đầy
đủ thông tin để đăng ký vé tháng.',
QMessageBox.Ok)
else:
print("Tôi không thanh toán")

def update_transaction_history(self, name, phone, license_plate,


currentTime, money):
# Thêm một hàng mới vào bảng lịch sử giao dịch
rowPosition = self.tableWidget_History.rowCount()
self.tableWidget_History.insertRow(rowPosition)

# Cập nhật các giá trị vào hàng mới


self.tableWidget_History.setItem(rowPosition, 0,
QTableWidgetItem(name))
self.tableWidget_History.setItem(rowPosition, 1,
QTableWidgetItem(phone))
self.tableWidget_History.setItem(rowPosition, 2,
QTableWidgetItem(license_plate))
self.tableWidget_History.setItem(rowPosition, 3,
QTableWidgetItem(currentTime))
self.tableWidget_History.setItem(rowPosition, 4,
QTableWidgetItem(money))
# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget_History.resizeColumnsToContents()
self.tableWidget_History.resizeRowsToContents()

def autolabel(self, rects):


"""Attach a text label above each bar in *rects*, displaying its
height."""
for rect in rects:
height = rect.get_height()
plt.annotate('{:.2f}'.format(height),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom')

def clear_table(self):
self.tableWidget_History.setRowCount(0)

def filter_data(self):
start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()
filtered_data = []

for row in range(self.tableWidget_History.rowCount()):


name = self.tableWidget_History.item(row, 0).text()
phone = self.tableWidget_History.item(row, 1).text()
license_plate = self.tableWidget_History.item(row, 2).text()
time_str = self.tableWidget_History.item(row, 3).text()
money_str = self.tableWidget_History.item(row, 4).text().replace("
VND", "")
money = int(money_str)

date = QDateTime.fromString(time_str, 'hh:mm:ss dd-MM-


yyyy').date()
if start_date <= date <= end_date:
filtered_data.append([name, phone, license_plate, time_str,
money])

self.update_table_history(filtered_data)
return filtered_data

def update_table_only(self):
self.filter_data()

def update_graph(self):
self.show_revenue_graph()

def show_revenue_graph(self):
try:
# Get selected time period
selected_option = self.comboBox.currentText()
date_revenue = defaultdict(int)

start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()
# Calculate revenue based on selected time period
for row in range(self.tableWidget_History.rowCount()):
money_str = self.tableWidget_History.item(row,
4).text().replace(" VND", "")
money = int(money_str)
date_str = self.tableWidget_History.item(row, 3).text()
date = QDateTime.fromString(date_str, 'hh:mm:ss dd-MM-
yyyy').date()

if start_date <= date <= end_date:


if selected_option == "Daily":
date_revenue[date] += money
elif selected_option == "Monthly":
month_year = QDate(date.year(), date.month(), 1)
date_revenue[month_year] += money
elif selected_option == "Yearly":
year = QDate(date.year(), 1, 1)
date_revenue[year] += money

dates = sorted(date_revenue.keys())
revenues = [date_revenue[date] / 1_000_000 for date in dates] #
Convert to million VND

if not dates:
QMessageBox.warning(self, 'Thông báo', 'Không có dữ liệu để vẽ
đồ thị.', QMessageBox.Ok)
return

# Clear previous plot if needed


plt.figure(figsize=(10, 6))

if selected_option == "Monthly":
x_labels = [d.toString('MM-yyyy') for d in dates] # Change x-
axis label format for "Monthly"
elif selected_option == "Yearly":
x_labels = [d.toString('yyyy') for d in dates] # Change x-
axis label format for "Yearly"
else:
x_labels = [d.toString('dd-MM-yyyy') for d in dates]

plt.bar(x_labels, revenues)
plt.xlabel('Date')
plt.ylabel('Revenue (Million VND)') # Update y-axis label
plt.title(f'Revenue Over Time ({selected_option})')
plt.xticks(rotation=45)
plt.gca().get_yaxis().get_major_formatter().set_scientific(False)
# Disable scientific notation for y-axis
self.autolabel(plt.gca().patches)
plt.tight_layout()
plt.show()

except Exception as e:
QMessageBox.critical(self, 'Lỗi', f'Đã xảy ra lỗi: {str(e)}',
QMessageBox.Ok)

def generate_random_data(self):
data_history = []
start_date = QDateTime(QDate(2023, 1, 1), QTime(0, 0))
end_date = QDateTime(QDate(2024, 12, 31), QTime(23, 59))

current_date = start_date
while current_date <= end_date:
name = f"Person-{random.randint(1, 100)}"
phone = f"{random.randint(100000000, 999999999)}"
license_plate = f"{random.choice(['H', '51A', '60A'])}-
{random.uniform(100.00, 999.99):.2f}"
time_str = current_date.toString('hh:mm:ss dd-MM-yyyy')
money = random.randint(10000, 1000000)

data_history.append([name, phone, license_plate, time_str,


f"{money} VND"])

current_date = current_date.addDays(1)

self.update_table_history(data_history)
print("Random data generated successfully.")

def export_excel(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(self, "Export Excel", "",
"Excel Files (*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
data = []
for row in range(self.tableWidget.rowCount()):
rowData = []
for column in range(self.tableWidget.columnCount()):
item = self.tableWidget.item(row, column)
if item is not None:
rowData.append(item.text())
else:
rowData.append('')
data.append(rowData)

# Sử dụng pandas để tạo DataFrame và lưu vào file Excel


df = pd.DataFrame(data, columns=["Name", "Phone", "License
Plate", "Time"])
df.to_excel(fileName, index=False)

# Mở file Excel bằng openpyxl để điều chỉnh kích thước cột


wb = openpyxl.load_workbook(fileName)
ws = wb.active

# Đổi tên sheet


ws.title = "Report"

# Điều chỉnh kích thước cột


for col in ws.columns:
max_length = 0
column = col[0].column_letter # Get the column name
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(cell.value)
except:
pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column].width = adjusted_width

# Lưu lại file Excel


wb.save(fileName)

print(f"Excel file saved at {fileName}") # Print the file


path
except Exception as e:
print(f"Error saving file: {e}")

def export_excel_history(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(self, "Export Excel", "",
"Excel Files (*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
data = []
for row in range(self.tableWidget_History.rowCount()):
rowData = []
for column in
range(self.tableWidget_History.columnCount()):
item = self.tableWidget_History.item(row, column)
if item is not None:
rowData.append(item.text())
else:
rowData.append('')
data.append(rowData)

# Sử dụng pandas để tạo DataFrame và lưu vào file Excel


df = pd.DataFrame(data, columns=["Name", "Phone", "License
Plate", "Time", "Money"])
df.to_excel(fileName, index=False)

# Mở file Excel bằng openpyxl để điều chỉnh kích thước cột


wb = openpyxl.load_workbook(fileName)
ws = wb.active

# Đổi tên sheet


ws.title = "Report"

# Điều chỉnh kích thước cột


for col in ws.columns:
max_length = 0
column = col[0].column_letter # Get the column name
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(cell.value)
except:
pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column].width = adjusted_width
# Lưu lại file Excel
wb.save(fileName)

print(f"Excel file saved at {fileName}") # Print the file


path
except Exception as e:
print(f"Error saving file: {e}")

def import_excel(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Import Excel", "",
"Excel Files (*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
df = pd.read_excel(fileName)
data = df.values.tolist()
self.update_table(data)
print(f"Excel file imported from {fileName}") # Print the
file path
except Exception as e:
print(f"Error importing file: {e}")

def import_excel_history(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Import Excel", "",
"Excel Files (*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
df = pd.read_excel(fileName)
data = df.values.tolist()
self.update_table_history(data)
print(f"Excel file imported from {fileName}") # Print the
file path
except Exception as e:
QMessageBox.critical(self, 'Error', f'Error importing file:
{str(e)}', QMessageBox.Ok)

def clear_all_rows(self):
self.tableWidget.setRowCount(0)

def clear_selected_row(self):
current_row = self.tableWidget.currentRow()
if current_row != -1:
self.tableWidget.removeRow(current_row)
else:
QMessageBox.warning(self, 'Warning', 'No row selected to delete.',
QMessageBox.Ok)

def open_file_dialog(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Open File", "",
"Images and Videos (*.png
*.jpg *.bmp *.mp4 *.avi);;All Files (*)",
options=options)
if fileName:
if fileName.lower().endswith(('.png', '.jpg', '.bmp')):
self.display_image(fileName)
print(f"Selected file: {fileName}") # In ra địa chỉ file trên
terminal
if self.btn_setObject1.isChecked():
self.GetObject_one(fileName) # Gọi phương thức
GetObject_one với fileName được chọn
elif fileName.lower().endswith(('.mp4', '.avi')):
self.display_video(fileName)

def display_image(self, fileName):


if self.thread:
self.thread.stop()
pixmap = QPixmap(fileName)
self.disp_main.setPixmap(pixmap.scaled(self.disp_main.size(),
Qt.KeepAspectRatio))

def display_video(self, fileName):


if self.thread:
self.thread.stop()
self.thread = ThreadClass(fileName=fileName)
self.thread.ImageUpdate.connect(self.update_image)
self.thread.start()

def update_image(self, frame):


frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
height, width, _ = frame.shape
qImg = QImage(frame.data, width, height, width * 3,
QImage.Format_RGB888)

self.disp_main.setPixmap(QPixmap.fromImage(qImg).scaled(self.disp_main.size(),
Qt.KeepAspectRatio))

def take_snapshot(self):
# Grab the content of disp_main
pixmap = self.disp_main.pixmap()
if pixmap:
# Convert QPixmap to QImage
qimage = pixmap.toImage()

# Display the QImage on disp_obj1 and disp_obj2


self.disp_obj1.setPixmap(QPixmap.fromImage(qimage))
self.disp_obj1.setScaledContents(True)

# Convert QImage to numpy array for OpenCV


width = qimage.width()
height = qimage.height()
ptr = qimage.bits()
ptr.setsize(qimage.byteCount())
arr = np.array(ptr).reshape(height, width, 4) # 4 for RGBA

# Convert RGBA to BGR for OpenCV


arr = cv2.cvtColor(arr, cv2.COLOR_RGBA2BGR)

# Save the image using OpenCV


cv2.imwrite('Snapshot_image.jpg', arr)
print("Snapshot saved as 'Snapshot_image.jpg'")
self.GetObject_one('./Snapshot_image.jpg')

@pyqtSlot(np.ndarray)
def opencv_emit(self, Image):

# Định dạng QPixmap


original = self.cvt_cv_qt(Image) # Chuyển đổi hình ảnh từ định dạng
numpy array sang QPixmap
# Định dạng Numpy Array

"""
Trong quá trình chuyển đổi hình ảnh để hiển thị trên Qlabel, cần phải
chuyển đổi từ np.array sang QPixmap.
Hình ảnh nhận được là Numpy array, khi dùng hàm original =
cvt_cv_qt(Image), biến original sẽ có kiểu QtGui.QPixmap.
"""

self.disp_main.setPixmap(original) # Hiển thị hình ảnh ban đầu trên


nhãn chính
self.disp_main.setScaledContents(True) # Cho phép thay đổi kích thước
nội dung của nhãn

def cvt_cv_qt(self, Image):


offset = 5 # Độ lệch cho việc vẽ hình chữ nhật
rgb_img = cv2.cvtColor(src=Image, code=cv2.COLOR_BGR2RGB) # Chuyển
đổi hình ảnh từ BGR sang RGB

h, w, ch = rgb_img.shape # Lấy chiều cao, chiều rộng và số kênh của


hình ảnh
bytes_per_line = ch * w # Tính số byte trên mỗi dòng
cvt2QtFormat = QImage(rgb_img.data, w, h, bytes_per_line,
QImage.Format_RGB888) # Chuyển đổi hình ảnh
sang định dạng QImage
pixmap = QPixmap.fromImage(cvt2QtFormat) # Chuyển đổi QImage sang
QPixmap

return pixmap # Trả về QPixmap

#
------------------------------------------------------------------------------
----------------------

def StartWebCam(self, pin):


try:
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Start Webcam
({self.camlist.currentText()})") # Thêm văn bản vào textEdit khi bắt đầu
webcam
self.btn_stop.setEnabled(True) # Kích hoạt nút dừng
self.btn_start.setEnabled(False) # Vô hiệu hóa nút bắt đầu

global camIndex
camIndex = self.camlist.currentIndex() # Lấy chỉ số của camera
hiện tại

# Opencv QThread
self.Worker1_Opencv = ThreadClass() # Tạo đối tượng ThreadClass
để xử lý video
self.Worker1_Opencv.ImageUpdate.connect(
self.opencv_emit) # Kết nối tín hiệu ImageUpdate với phương
thức opencv_emit
self.Worker1_Opencv.FPS.connect(self.get_FPS) # Kết nối tín hiệu
FPS với phương thức get_FPS
self.Worker1_Opencv.start() # Bắt đầu luồng

except Exception as error:


pass # Bỏ qua mọi lỗi xảy ra

def StopWebcam(self, pin):


self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Stop Webcam
({self.camlist.currentText()})") # Thêm văn bản vào textEdit khi dừng webcam
self.btn_start.setEnabled(True) # Kích hoạt nút bắt đầu
self.btn_stop.setEnabled(False) # Vô hiệu hóa nút dừng

# Đặt lại biểu tượng về trạng thái dừng


self.Motor_status.setPixmap(QPixmap('Icons/MotorOFF.png')) # Đặt biểu
tượng motor về trạng thái tắt
self.Worker1_Opencv.stop() # Dừng luồng xử lý video
self.motor_on.stop() # Dừng motor

def set_object1(self):
if self.btn_setObject1.isChecked():
print("Object Detect is checked.")
self.run_lamp_on.start()
else:
print("Object Detect is unchecked.")
self.run_lamp_on.stop()

def GetObject_one(self, image_path):


try:
# Tải mô hình YOLO
model_path = "best.pt"
model = YOLO(model_path)

# Use the model


results_image = model(image_path, save=False) # predict on an
image

print("Object detection results:", results_image)

# Read original image


image = cv2.imread(image_path)

# Extract coordinates of the detected object (assuming it's a


license plate)
x1, y1, x2, y2 = map(int, results_image[0][0][:4])

# Crop detected region from the original image


cropped_image = image[y1:y2, x1:x2]
# Chuyển sang ảnh xám và phát hiện cạnh
gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
# Lưu hình ảnh đã chỉnh sửa
cv2.imwrite('cropped_image.jpg', cropped_image)
print("Đã lưu hình ảnh đã chỉnh sửa với tên 'cropped_image.jpg'")
cv2.imwrite('corrected_image.jpg', edges)
print("Đã lưu hình ảnh đã chỉnh sửa với tên
'corrected_image.jpg'")

# create copy of image


im2 = gray.copy()

# Paddleocr supports Chinese, English, French, German, Korean and


Japanese.
# You can set the parameter `lang` as `ch`, `en`, `fr`, `german`,
`korean`, `japan`
# to switch the language model in order.
ocr = PaddleOCR(use_angle_cls=True,
lang='en') # need to run only once to download
and load model into memory
img_path = './corrected_image.jpg'
result = ocr.ocr(img_path, cls=True)
for idx in range(len(result)):
res = result[idx]
for line in res:
print(line)

result = result[0]
image = Image.open(img_path).convert('RGB')
boxes = [line[0] for line in result]
txts = [line[1][0] for line in result]
scores = [line[1][1] for line in result]
im_show = draw_ocr(image, boxes, txts, scores,
font_path='./latin.ttf')
im_show = Image.fromarray(im_show)
im_show.save('result.jpg')

# Join the elements of 'txts' into a single string


plate_num = ' '.join(txts)

# Print the recognized plate number


print("Recognized plate number:", plate_num)

Image_Obj_1 = './cropped_image.jpg'
original = QImage(Image_Obj_1) # Chuyển đổi hình ảnh từ định dạng
numpy array sang QPixmap
self.disp_obj1.setPixmap(QPixmap(original)) # Hiển thị hình ảnh
ban đầu trên nhãn chính
self.disp_obj1.setScaledContents(True) # Cho phép thay đổi kích
thước nội dung của nhãn

Image_Obj_2 = './corrected_image.jpg'
self.disp_obj2.setPixmap(QPixmap(QImage(Image_Obj_2))) # Hiển thị
hình ảnh ban đầu trên nhãn chính
self.disp_obj2.setScaledContents(True) # Cho phép thay đổi kích
thước nội dung của nhãn
self.License_plate.setText(plate_num)
# Kiểm tra và cập nhật bảng
self.update_time_in_table(plate_num)
except Exception as error:
pass # Bỏ qua mọi lỗi xảy ra

def Close_software(self):
self.Worker1_Opencv.stop() # Dừng luồng xử lý video
self.resource_usage.stop() # Dừng đo lường tài nguyên
sys.exit(app.exec_()) # Thoát ứng dụng

def Motor_on(self):
if self.btn_motor.isChecked():
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor ON")
# Thêm văn bản vào textEdit khi motor bật
self.btn_motor.setText('Motor OFF') # Thay đổi nhãn của nút motor
thành 'Motor OFF'
# self.motor_on = QTimer(self, interval=1000)
# self.motor_on.timeout.connect(self.Motor_state)
self.motor_on.start() # Bắt đầu motor
else:
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor OFF")
# Thêm văn bản vào textEdit khi motor tắt
self.motor_on.stop() # Dừng motor
self.btn_motor.setText('Motor ON') # Thay đổi nhãn của nút motor
thành 'Motor ON'

# Trạng thái của motor và đèn báo


def Motor_state(self):
if self.flag_motor:
self.Motor_status.setPixmap(QPixmap('images/home.png')) # Đặt
biểu tượng motor về trạng thái bật
else:
self.Motor_status.setPixmap(QPixmap('images/help.png')) # Đặt
biểu tượng motor về trạng thái tắt
self.flag_motor = not self.flag_motor # Đảo ngược trạng thái motor

def Ready_Light(self):
if self.Status_lamp[0]:
self.QLabel_Ready_Light.setStyleSheet(
"background-color: rgb(85, 255, 0); border-radius:35px") #
Đặt màu nền của đèn xanh là màu xanh lá
else:
self.QLabel_Ready_Light.setStyleSheet(
"background-color: rgb(184, 230, 191); border-radius:35px") #
Đặt màu nền của đèn xanh là màu xanh nhạt

self.Status_lamp[0] = not self.Status_lamp[0] # Đảo ngược trạng thái


của đèn xanh
# IO.output(IO_OUTPUT[0], self.Status_lamp[0])

def Run_Light(self):
if self.Status_lamp[1]:
self.Qlabel_yellowlight.setStyleSheet(
"background-color: rgb(255, 195, 0); border-radius:35px") #
Đặt màu nền của đèn vàng là màu vàng đậm
else:
self.Qlabel_yellowlight.setStyleSheet(
"background-color: rgb(242, 214, 117); border-radius:35px") #
Đặt màu nền của đèn vàng là màu vàng nhạt
self.Status_lamp[1] = not self.Status_lamp[1] # Đảo ngược trạng thái
của đèn vàng
# IO.output(IO_OUTPUT[1], self.Status_lamp[1])

if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()

# pip install Pillow


# pip uninstall -y setuptools
# pip install setuptools==39.1.0
# pip install setuptools==69.5.1
# pip install hydra
# shell = True
# pip install hydra-core>=1.2.0

import os

os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
from paddleocr import PaddleOCR, draw_ocr
from PIL import Image
from PyQt5 import uic
from PyQt5.QtMultimedia import QCameraInfo
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel,
QTableWidgetItem, QPushButton, QLineEdit, QMessageBox, \
QFileDialog, QInputDialog, QDialog, QWidget, QVBoxLayout, QTextEdit
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QTimer, QDateTime, Qt,
QDate, QTime
from PyQt5.QtGui import QImage, QPixmap
import cv2, time, sysinfo
import numpy as np
import random as rnd
import random
import sys
from ultralytics import YOLO
import pandas as pd
import openpyxl
import matplotlib

matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from collections import defaultdict
import socket

class ThreadClass(QThread):
ImageUpdate = pyqtSignal(np.ndarray) # Tạo tín hiệu để cập nhật hình ảnh
FPS = pyqtSignal(int) # Tạo tín hiệu để cập nhật số khung hình trên giây (FPS)
global camIndex # Đặt biến camIndex là biến toàn cục

def __init__(self, fileName=None, camIndex=None, parent=None):


super(ThreadClass, self).__init__(parent)
self.fileName = fileName

def run(self):
if self.fileName:
Capture = cv2.VideoCapture(self.fileName)
elif camIndex == 0:
Capture = cv2.VideoCapture(camIndex) # Mở camera với chỉ số camIndex
elif camIndex == 1:
Capture = cv2.VideoCapture(camIndex, cv2.CAP_DSHOW) # Mở camera với
chỉ số camIndex sử dụng DirectShow

Capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # Đặt chiều cao khung hình


Capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # Đặt chiều rộng khung hình
self.ThreadActive = True # Đặt cờ hiệu hoạt động của luồng
prev_frame_time = 0 # Thời gian khung hình trước đó
new_frame_time = 0 # Thời gian khung hình mới
while self.ThreadActive:
ret, frame_cap = Capture.read() # Đọc một khung hình từ camera
# flip_frame = cv2.flip(src=frame_cap, flipCode=1) # Lật khung hình 1
new_frame_time = time.time() # Lấy thời gian hiện tại
fps = 1 / (new_frame_time - prev_frame_time) # Tính toán FPS
prev_frame_time = new_frame_time # Cập nhật thời gian khung hình trước
đó
if ret:
self.ImageUpdate.emit(frame_cap) # Phát tín hiệu cập nhật hình ảnh
self.FPS.emit(fps) # Phát tín hiệu cập nhật FPS
time.sleep(1 / 30) # Sleep to mimic video frame rate
else:
self.ThreadActive = False
Capture.release()

def stop(self):
self.ThreadActive = False # Dừng luồng
self.quit() # Thoát luồng

class boardInfoClass(QThread):
cpu = pyqtSignal(float) # Tạo tín hiệu để cập nhật sử dụng CPU
ram = pyqtSignal(tuple) # Tạo tín hiệu để cập nhật sử dụng RAM

def run(self):
self.ThreadActive = True # Đặt cờ hiệu hoạt động của luồng
while self.ThreadActive:
cpu = sysinfo.getCPU() # Lấy thông tin sử dụng CPU
ram = sysinfo.getRAM() # Lấy thông tin sử dụng RAM
# temp = sysinfo.getTemp() # Lấy thông tin nhiệt độ (bị comment do không
dùng)
self.cpu.emit(cpu) # Phát tín hiệu cập nhật CPU
self.ram.emit(ram) # Phát tín hiệu cập nhật RAM

def stop(self):
self.ThreadActive = False # Dừng luồng
self.quit() # Thoát luồng

class SocketThread(QThread):
data_signal = pyqtSignal(str)

def __init__(self):
super().__init__()
self.host = '0.0.0.0'
self.port = 9000
self._run_flag = True

def stop(self):
self._run_flag = False

def run(self):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((self.host, self.port))
s.listen()
s.settimeout(1) # Set a timeout for accept()
print(f"Listening on {self.host}:{self.port}")
while self._run_flag:
try:
conn, addr = s.accept()
with conn:
print(f"Connected by {addr}")
while self._run_flag:
data = conn.recv(1024)
if not data:
break
messages = data.decode().strip().split('\n')
for message in messages:
self.data_signal.emit(message)
except socket.timeout:
continue
except Exception as e:
print(f"Error in SocketThread: {e}")

class RFIDReader(QDialog):
def __init__(self):
super().__init__()
self.initUI()
self.initSocket()
self.DateTime = QDateTime.currentDateTime()
self.motor_on = QTimer(self, interval=1000) # Timer for motor state, example
usage

def initUI(self):
self.setWindowTitle('RFID Reader and Motor Control')
self.setGeometry(100, 100, 400, 300)

self.uidLabel = QLabel('UID: ', self)


self.statusLabel = QLabel('Status: ', self)
self.textEdit = QTextEdit(self)
self.textEdit.setReadOnly(True)

self.btn_motor = QPushButton('Motor ON', self)


self.btn_motor.setCheckable(True)
self.btn_motor.clicked.connect(self.toggle_motor)

layout = QVBoxLayout()
layout.addWidget(self.uidLabel)
layout.addWidget(self.statusLabel)
layout.addWidget(self.textEdit)
layout.addWidget(self.btn_motor)

self.setLayout(layout)

def initSocket(self):
self.thread = SocketThread()
self.thread.data_signal.connect(self.updateUI)
self.thread.start()

def updateUI(self, message):


print(f"Received message: {message}") # Debug print
if message.startswith("UID:"):
self.uidLabel.setText(message)
elif message.startswith("STATUS:"):
self.statusLabel.setText(message)
else:
self.textEdit.append(f"Received: {message}")

def toggle_motor(self):
if self.btn_motor.isChecked():
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor ON")
self.btn_motor.setText('Motor OFF')
self.sendCommand("SERVO:ON")
else:
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor OFF")
self.btn_motor.setText('Motor ON')
self.sendCommand("SERVO:OFF")

def sendCommand(self, command):


try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(5)
print(f"Attempting to connect to ESP8266 at 192.168.137.152:9000")
s.connect(('192.168.137.152', 8000))
print("Connected successfully")
s.sendall((command + '\n').encode())
print(f"Sent command: {command}")
response = s.recv(1024).decode().strip()
print(f"Received response: {response}")
except Exception as e:
print(f"Error sending command: {e}")

def closeEvent(self, event):


self.thread.stop()
self.thread.quit()
self.thread.wait(5000) # Wait for a maximum of 5 seconds
if self.thread.isRunning():
self.thread.terminate()
event.accept()

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = uic.loadUi("Opencv_PiDash.ui", self) # Tải giao diện từ tệp
Opencv_PiDash.ui

self.online_cam = QCameraInfo.availableCameras() # Lấy danh sách các


camera có sẵn
self.camlist.addItems([c.description() for c in self.online_cam]) # Thêm mô tả
của các camera vào danh sách
self.btn_start.clicked.connect(self.StartWebCam) # Kết nối nút bắt đầu với
hàm StartWebCam
self.btn_stop.clicked.connect(self.StopWebcam) # Kết nối nút dừng với hàm
StopWebcam

self.resource_usage = boardInfoClass() # Tạo đối tượng boardInfoClass để lấy


thông tin hệ thống
self.resource_usage.start() # Bắt đầu luồng lấy thông tin hệ thống

self.run_lamp_on = QTimer(self, interval=1000) # Tạo bộ đếm thời gian cho


đèn sẵn sàng, mỗi giây một lần
self.run_lamp_on.timeout.connect(self.Run_Light)
# self.run_lamp_on.start() # Bắt đầu bộ đếm thời gian

self.motor_on = QTimer(self, interval=1000) # Tạo bộ đếm thời gian cho động


cơ, mỗi giây một lần
self.motor_on.timeout.connect(
self.Motor_state) # Kết nối tín hiệu timeout của bộ đếm thời gian với hàm
Motor_state

self.lcd_timer = QTimer() # Tạo bộ đếm thời gian cho đồng hồ LCD


self.lcd_timer.timeout.connect(self.clock) # Kết nối tín hiệu timeout của bộ
đếm thời gian với hàm clock
self.lcd_timer.start() # Bắt đầu bộ đếm thời gian

self.flag_motor = True # Cờ hiệu cho trạng thái động cơ


self.Status_lamp = [True, True, True] # Trạng thái của các đèn
# Kết thúc khu vực QTimer

self.Motor_status.setPixmap(QPixmap('images/setting.png')) # Đặt hình ảnh


của trạng thái động cơ là "MotorOFF"

self.btn_setObject1.setCheckable(True) # Cho phép nút đối tượng 1 có thể


bật/tắt
self.btn_setObject1.clicked.connect(
self.set_object1) # Kết nối tín hiệu clicked của nút đối tượng 1 với hàm
set_object1

self.btn_close.clicked.connect(
self.Close_software) # Kết nối tín hiệu clicked của nút đóng với hàm
Close_software

self.btn_stop.setEnabled(False) # Vô hiệu hóa nút dừng

self.comboBox.currentIndexChanged.connect(self.update_graph)

# Connect the button click to the snapshot function


self.btn_snap_shot.clicked.connect(self.take_snapshot)

self.btn_call.clicked.connect(self.showRFIDReader)

self.initUI()

def showRFIDReader(self):
self.rfid_reader = RFIDReader()
self.rfid_reader.exec_()

def clock(self):
self.DateTime = QDateTime.currentDateTime() # Lấy thời gian hiện tại
self.lcd_clock.display(
self.DateTime.toString('hh:mm:ss dd-MM-yyyy')) # Hiển thị thời gian hiện tại
trên đồng hồ LCD

# Đóng cửa sổ thông báo lỗi


def Close_Error(self):
self.Win_error.close() # Đóng cửa sổ báo lỗi

def initUI(self):
self.TableList()

# Kết nối nút Set với phương thức cập nhật thời gian trong bảng
self.setButton = self.findChild(QPushButton, 'setButton')
self.setButton.clicked.connect(self.update_time_in_table)

# Tìm các ô đầu vào trên giao diện


self.name_input = self.findChild(QLineEdit, 'nameInput')
self.phone_input = self.findChild(QLineEdit, 'phoneInput')
self.license_plate_input = self.findChild(QLineEdit, 'licensePlateInput')
self.license_plate_YOLO = self.findChild(QLineEdit, 'License_plate') # Result
YOLOv8

# Tìm các nút Clear


self.clear_all_button = self.findChild(QPushButton, 'clearAllButton')
self.clear_all_button.clicked.connect(self.clear_all_rows)

self.clear_selected_button = self.findChild(QPushButton, 'clearSelectedButton')


self.clear_selected_button.clicked.connect(self.clear_selected_row)

# Tìm nút Open File


self.open_file_button = self.findChild(QPushButton, 'openFileButton')
self.open_file_button.clicked.connect(self.open_file_dialog)

# Tìm QLabel để hiển thị ảnh hoặc video frame


self.disp_main = self.findChild(QLabel, 'disp_main')

self.export_button = self.findChild(QPushButton, 'Export_button')


self.export_button.clicked.connect(self.export_excel)

self.export_button_history = self.findChild(QPushButton, 'Export_history')


self.export_button_history.clicked.connect(self.export_excel_history)

self.import_button = self.findChild(QPushButton, 'Import_button')


self.import_button.clicked.connect(self.import_excel)

self.import_button_history = self.findChild(QPushButton, 'Import_history')


self.import_button_history.clicked.connect(self.import_excel_history)

self.btn_snap_shot = self.findChild(QPushButton, 'btn_snap_shot')

# Biến lưu trữ ThreadClass object


self.thread = None

def TableList(self):
# Đặt tên cho các cột
headers = ["Name", "Phone", "License Plate", "Time"]
self.tableWidget.setColumnCount(len(headers))
self.tableWidget.setHorizontalHeaderLabels(headers)

headers_history = ["Name", "Phone", "License Plate", "Time", "Money"]


self.tableWidget_History.setColumnCount(len(headers_history))
self.tableWidget_History.setHorizontalHeaderLabels(headers_history)

# Dữ liệu ban đầu


data = [
["John Doe", "123456789", "H982 FKL", "10:00:00 21-06-2024"],
["Jane Smith", "987654321", "51A-138.83", "11:00:00 21-06-2024"],
["Mike Johnson", "555666777", "60A-999.99", "12:00:00 21-06-2024"],
["Emily Davis", "111222333", "51F-886.86", "13:00:00 21-06-2024"]
]

self.update_table(data)

def update_table(self, data):


for rowData in data:
# Thêm một hàng mới vào bảng
rowPosition = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowPosition)

for column, value in enumerate(rowData):


if column >= self.tableWidget.columnCount():
self.tableWidget.insertColumn(column)

item = QTableWidgetItem(value)
self.tableWidget.setItem(rowPosition, column, item)

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()

def update_table_history(self, data):


rowCount = self.tableWidget_History.rowCount()
columnCount = self.tableWidget_History.columnCount()

for rowData in data:


# Thêm một hàng mới vào bảng
rowPosition = self.tableWidget_History.rowCount()
self.tableWidget_History.insertRow(rowPosition)

# Kiểm tra và thêm cột mới nếu cần thiết


if len(rowData) > columnCount:
for i in range(len(rowData) - columnCount):
self.tableWidget_History.insertColumn(columnCount + i)
# Cập nhật dữ liệu vào bảng
for column, value in enumerate(rowData):
item = QTableWidgetItem(str(value))
self.tableWidget_History.setItem(rowPosition, column, item)

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget_History.resizeColumnsToContents()
self.tableWidget_History.resizeRowsToContents()

def update_time_in_table(self, plate_num):


currentTime = QDateTime.currentDateTime().toString('hh:mm:ss dd-MM-yyyy')

# Lấy giá trị từ các ô đầu vào


name = self.name_input.text()
phone = self.phone_input.text()
license_plate = self.license_plate_input.text()
license_plate_YOLO = self.license_plate_YOLO.text()

found = False
money = "0 VND"

# Kiểm tra xem license_plate đã tồn tại trong cột "License Plate" hay chưa
for row in range(self.tableWidget.rowCount()):
item = self.tableWidget.item(row, 2) # Cột "License Plate" là cột thứ 3 (chỉ
mục 2)
if item.text() == plate_num:
QMessageBox.warning(self, 'Thông báo',
'Biển số xe đã tồn tại trong danh sách đăng ký. Bạn không
cần phải trả tiền.',
QMessageBox.Ok)
# Lưu lại lịch sử giao dịch
name = self.tableWidget.item(row, 0).text()
phone = self.tableWidget.item(row, 1).text()
license_plate = self.tableWidget.item(row, 2).text()
self.update_transaction_history(name, phone, license_plate, currentTime,
money)
found = True
break # Dừng lại nếu tìm thấy biển số xe trùng

if not found:
reply = QMessageBox.question(self, 'Thông báo',
'Biển số xe chưa được đăng ký.\nBạn có muốn đăng ký vé
tháng chứ?',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
# Hiển thị các hộp nhập liệu để người dùng điền thông tin
name, ok1 = QInputDialog.getText(self, 'Đăng ký vé tháng', 'Nhập tên:')
phone, ok2 = QInputDialog.getText(self, 'Đăng ký vé tháng', 'Nhập số điện
thoại:')
license_plate, ok3 = QInputDialog.getText(self, 'Đăng ký vé tháng', 'Nhập
biển số xe:', text=plate_num)

if ok1 and ok2 and ok3:


money = "500000 VND"

# Thêm một hàng mới vào bảng


rowPosition = self.tableWidget.rowCount()
self.tableWidget.insertRow(rowPosition)

# Cập nhật các giá trị vào hàng mới


self.tableWidget.setItem(rowPosition, 0, QTableWidgetItem(name))
self.tableWidget.setItem(rowPosition, 1, QTableWidgetItem(phone))
self.tableWidget.setItem(rowPosition, 2,
QTableWidgetItem(license_plate))
self.tableWidget.setItem(rowPosition, 3,
QTableWidgetItem(currentTime))

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget.resizeColumnsToContents()
self.tableWidget.resizeRowsToContents()

# Lưu lại lịch sử giao dịch


self.update_transaction_history(name, phone, license_plate,
currentTime, money)
else:
QMessageBox.warning(self, 'Thông báo', 'Bạn phải điền đầy đủ thông
tin để đăng ký vé tháng.',
QMessageBox.Ok)
else:
print("Tôi không thanh toán")

def update_transaction_history(self, name, phone, license_plate, currentTime,


money):
# Thêm một hàng mới vào bảng lịch sử giao dịch
rowPosition = self.tableWidget_History.rowCount()
self.tableWidget_History.insertRow(rowPosition)

# Cập nhật các giá trị vào hàng mới


self.tableWidget_History.setItem(rowPosition, 0, QTableWidgetItem(name))
self.tableWidget_History.setItem(rowPosition, 1, QTableWidgetItem(phone))
self.tableWidget_History.setItem(rowPosition, 2,
QTableWidgetItem(license_plate))
self.tableWidget_History.setItem(rowPosition, 3,
QTableWidgetItem(currentTime))
self.tableWidget_History.setItem(rowPosition, 4, QTableWidgetItem(money))

# Tự động điều chỉnh kích thước của các cột và hàng dựa trên nội dung
self.tableWidget_History.resizeColumnsToContents()
self.tableWidget_History.resizeRowsToContents()

def autolabel(self, rects):


"""Attach a text label above each bar in *rects*, displaying its height."""
for rect in rects:
height = rect.get_height()
plt.annotate('{:.2f}'.format(height),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom')

def clear_table(self):
self.tableWidget_History.setRowCount(0)

def filter_data(self):
start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()
filtered_data = []

for row in range(self.tableWidget_History.rowCount()):


name = self.tableWidget_History.item(row, 0).text()
phone = self.tableWidget_History.item(row, 1).text()
license_plate = self.tableWidget_History.item(row, 2).text()
time_str = self.tableWidget_History.item(row, 3).text()
money_str = self.tableWidget_History.item(row, 4).text().replace(" VND", "")
money = int(money_str)

date = QDateTime.fromString(time_str, 'hh:mm:ss dd-MM-yyyy').date()


if start_date <= date <= end_date:
filtered_data.append([name, phone, license_plate, time_str, money])

self.update_table_history(filtered_data)
return filtered_data

def update_table_only(self):
self.filter_data()

def update_graph(self):
self.show_revenue_graph()

def show_revenue_graph(self):
try:
# Get selected time period
selected_option = self.comboBox.currentText()
date_revenue = defaultdict(int)

start_date = self.dateEditStart.date()
end_date = self.dateEditEnd.date()

# Calculate revenue based on selected time period


for row in range(self.tableWidget_History.rowCount()):
money_str = self.tableWidget_History.item(row, 4).text().replace(" VND",
"")
money = int(money_str)
date_str = self.tableWidget_History.item(row, 3).text()
date = QDateTime.fromString(date_str, 'hh:mm:ss dd-MM-yyyy').date()

if start_date <= date <= end_date:


if selected_option == "Daily":
date_revenue[date] += money
elif selected_option == "Monthly":
month_year = QDate(date.year(), date.month(), 1)
date_revenue[month_year] += money
elif selected_option == "Yearly":
year = QDate(date.year(), 1, 1)
date_revenue[year] += money

dates = sorted(date_revenue.keys())
revenues = [date_revenue[date] / 1_000_000 for date in dates] # Convert
to million VND

if not dates:
QMessageBox.warning(self, 'Thông báo', 'Không có dữ liệu để vẽ đồ thị.',
QMessageBox.Ok)
return

# Clear previous plot if needed


plt.figure(figsize=(10, 6))

if selected_option == "Monthly":
x_labels = [d.toString('MM-yyyy') for d in dates] # Change x-axis label
format for "Monthly"
elif selected_option == "Yearly":
x_labels = [d.toString('yyyy') for d in dates] # Change x-axis label format
for "Yearly"
else:
x_labels = [d.toString('dd-MM-yyyy') for d in dates]
plt.bar(x_labels, revenues)
plt.xlabel('Date')
plt.ylabel('Revenue (Million VND)') # Update y-axis label
plt.title(f'Revenue Over Time ({selected_option})')
plt.xticks(rotation=45)
plt.gca().get_yaxis().get_major_formatter().set_scientific(False) # Disable
scientific notation for y-axis
self.autolabel(plt.gca().patches)
plt.tight_layout()
plt.show()

except Exception as e:
QMessageBox.critical(self, 'Lỗi', f'Đã xảy ra lỗi: {str(e)}', QMessageBox.Ok)

def export_excel(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(self, "Export Excel", "", "Excel Files
(*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
data = []
for row in range(self.tableWidget.rowCount()):
rowData = []
for column in range(self.tableWidget.columnCount()):
item = self.tableWidget.item(row, column)
if item is not None:
rowData.append(item.text())
else:
rowData.append('')
data.append(rowData)

# Sử dụng pandas để tạo DataFrame và lưu vào file Excel


df = pd.DataFrame(data, columns=["Name", "Phone", "License Plate",
"Time"])
df.to_excel(fileName, index=False)

# Mở file Excel bằng openpyxl để điều chỉnh kích thước cột


wb = openpyxl.load_workbook(fileName)
ws = wb.active

# Đổi tên sheet


ws.title = "Report"

# Điều chỉnh kích thước cột


for col in ws.columns:
max_length = 0
column = col[0].column_letter # Get the column name
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(cell.value)
except:
pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column].width = adjusted_width

# Lưu lại file Excel


wb.save(fileName)

print(f"Excel file saved at {fileName}") # Print the file path


except Exception as e:
print(f"Error saving file: {e}")

def export_excel_history(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getSaveFileName(self, "Export Excel", "", "Excel Files
(*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
data = []
for row in range(self.tableWidget_History.rowCount()):
rowData = []
for column in range(self.tableWidget_History.columnCount()):
item = self.tableWidget_History.item(row, column)
if item is not None:
rowData.append(item.text())
else:
rowData.append('')
data.append(rowData)

# Sử dụng pandas để tạo DataFrame và lưu vào file Excel


df = pd.DataFrame(data, columns=["Name", "Phone", "License Plate",
"Time", "Money"])
df.to_excel(fileName, index=False)

# Mở file Excel bằng openpyxl để điều chỉnh kích thước cột


wb = openpyxl.load_workbook(fileName)
ws = wb.active

# Đổi tên sheet


ws.title = "Report"
# Điều chỉnh kích thước cột
for col in ws.columns:
max_length = 0
column = col[0].column_letter # Get the column name
for cell in col:
try:
if len(str(cell.value)) > max_length:
max_length = len(cell.value)
except:
pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column].width = adjusted_width

# Lưu lại file Excel


wb.save(fileName)

print(f"Excel file saved at {fileName}") # Print the file path


except Exception as e:
print(f"Error saving file: {e}")

def import_excel(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Import Excel", "", "Excel Files
(*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
df = pd.read_excel(fileName)
data = df.values.tolist()
self.update_table(data)
print(f"Excel file imported from {fileName}") # Print the file path
except Exception as e:
print(f"Error importing file: {e}")

def import_excel_history(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Import Excel", "", "Excel Files
(*.xlsx);;All Files (*)",
options=options)
if fileName:
try:
df = pd.read_excel(fileName)
data = df.values.tolist()
self.update_table_history(data)
print(f"Excel file imported from {fileName}") # Print the file path
except Exception as e:
QMessageBox.critical(self, 'Error', f'Error importing file: {str(e)}',
QMessageBox.Ok)

def clear_all_rows(self):
self.tableWidget.setRowCount(0)

def clear_selected_row(self):
current_row = self.tableWidget.currentRow()
if current_row != -1:
self.tableWidget.removeRow(current_row)
else:
QMessageBox.warning(self, 'Warning', 'No row selected to delete.',
QMessageBox.Ok)

def open_file_dialog(self):
options = QFileDialog.Options()
fileName, _ = QFileDialog.getOpenFileName(self, "Open File", "",
"Images and Videos (*.png *.jpg *.bmp *.mp4
*.avi);;All Files (*)",
options=options)
if fileName:
if fileName.lower().endswith(('.png', '.jpg', '.bmp')):
self.display_image(fileName)
print(f"Selected file: {fileName}") # In ra địa chỉ file trên terminal
if self.btn_setObject1.isChecked():
self.GetObject_one(fileName) # Gọi phương thức GetObject_one với
fileName được chọn
elif fileName.lower().endswith(('.mp4', '.avi')):
self.display_video(fileName)

def display_image(self, fileName):


if self.thread:
self.thread.stop()
pixmap = QPixmap(fileName)
self.disp_main.setPixmap(pixmap.scaled(self.disp_main.size(),
Qt.KeepAspectRatio))

def display_video(self, fileName):


if self.thread:
self.thread.stop()
self.thread = ThreadClass(fileName=fileName)
self.thread.ImageUpdate.connect(self.update_image)
self.thread.start()

def update_image(self, frame):


frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
height, width, _ = frame.shape
qImg = QImage(frame.data, width, height, width * 3, QImage.Format_RGB888)
self.disp_main.setPixmap(QPixmap.fromImage(qImg).scaled(self.disp_main.size(),
Qt.KeepAspectRatio))

def take_snapshot(self):
# Grab the content of disp_main
pixmap = self.disp_main.pixmap()
if pixmap:
# Convert QPixmap to QImage
qimage = pixmap.toImage()

# Display the QImage on disp_obj1 and disp_obj2


self.disp_obj1.setPixmap(QPixmap.fromImage(qimage))
self.disp_obj1.setScaledContents(True)

# Convert QImage to numpy array for OpenCV


width = qimage.width()
height = qimage.height()
ptr = qimage.bits()
ptr.setsize(qimage.byteCount())
arr = np.array(ptr).reshape(height, width, 4) # 4 for RGBA

# Convert RGBA to BGR for OpenCV


arr = cv2.cvtColor(arr, cv2.COLOR_RGBA2BGR)

# Save the image using OpenCV


cv2.imwrite('Snapshot_image.jpg', arr)
print("Snapshot saved as 'Snapshot_image.jpg'")
self.GetObject_one('./Snapshot_image.jpg')

@pyqtSlot(np.ndarray)
def opencv_emit(self, Image):

# Định dạng QPixmap


original = self.cvt_cv_qt(Image) # Chuyển đổi hình ảnh từ định dạng numpy
array sang QPixmap
# Định dạng Numpy Array

"""
Trong quá trình chuyển đổi hình ảnh để hiển thị trên Qlabel, cần phải chuyển
đổi từ np.array sang QPixmap.
Hình ảnh nhận được là Numpy array, khi dùng hàm original =
cvt_cv_qt(Image), biến original sẽ có kiểu QtGui.QPixmap.
"""

self.disp_main.setPixmap(original) # Hiển thị hình ảnh ban đầu trên nhãn


chính
self.disp_main.setScaledContents(True) # Cho phép thay đổi kích thước nội
dung của nhãn

def cvt_cv_qt(self, Image):


offset = 5 # Độ lệch cho việc vẽ hình chữ nhật
rgb_img = cv2.cvtColor(src=Image, code=cv2.COLOR_BGR2RGB) # Chuyển
đổi hình ảnh từ BGR sang RGB

h, w, ch = rgb_img.shape # Lấy chiều cao, chiều rộng và số kênh của hình ảnh
bytes_per_line = ch * w # Tính số byte trên mỗi dòng
cvt2QtFormat = QImage(rgb_img.data, w, h, bytes_per_line,
QImage.Format_RGB888) # Chuyển đổi hình ảnh sang định dạng
QImage
pixmap = QPixmap.fromImage(cvt2QtFormat) # Chuyển đổi QImage sang
QPixmap

return pixmap # Trả về QPixmap

# ----------------------------------------------------------------------------------------------------

def StartWebCam(self, pin):


try:
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Start Webcam
({self.camlist.currentText()})") # Thêm văn bản vào textEdit khi bắt đầu webcam
self.btn_stop.setEnabled(True) # Kích hoạt nút dừng
self.btn_start.setEnabled(False) # Vô hiệu hóa nút bắt đầu

global camIndex
camIndex = self.camlist.currentIndex() # Lấy chỉ số của camera hiện tại

# Opencv QThread
self.Worker1_Opencv = ThreadClass() # Tạo đối tượng ThreadClass để xử lý
video
self.Worker1_Opencv.ImageUpdate.connect(
self.opencv_emit) # Kết nối tín hiệu ImageUpdate với phương thức
opencv_emit
self.Worker1_Opencv.start() # Bắt đầu luồng

except Exception as error:


pass # Bỏ qua mọi lỗi xảy ra

def StopWebcam(self, pin):


self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Stop Webcam
({self.camlist.currentText()})") # Thêm văn bản vào textEdit khi dừng webcam
self.btn_start.setEnabled(True) # Kích hoạt nút bắt đầu
self.btn_stop.setEnabled(False) # Vô hiệu hóa nút dừng

# Đặt lại biểu tượng về trạng thái dừng


self.Motor_status.setPixmap(QPixmap('Icons/MotorOFF.png')) # Đặt biểu tượng
motor về trạng thái tắt
self.Worker1_Opencv.stop() # Dừng luồng xử lý video
self.motor_on.stop() # Dừng motor

def set_object1(self):
if self.btn_setObject1.isChecked():
print("Object Detect is checked.")
self.run_lamp_on.start()
else:
print("Object Detect is unchecked.")
self.run_lamp_on.stop()

def GetObject_one(self, image_path):


try:
# Tải mô hình YOLO
model_path = "best.pt"
model = YOLO(model_path)

# Use the model


results_image = model(image_path, save=False) # predict on an image

print("Object detection results:", results_image)

# Read original image


image = cv2.imread(image_path)

# Extract coordinates of the detected object (assuming it's a license plate)


x1, y1, x2, y2 = map(int, results_image[0][0][:4])

# Crop detected region from the original image


cropped_image = image[y1:y2, x1:x2]
# Chuyển sang ảnh xám và phát hiện cạnh
gray = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)

# Lưu hình ảnh đã chỉnh sửa


cv2.imwrite('cropped_image.jpg', cropped_image)
print("Đã lưu hình ảnh đã chỉnh sửa với tên 'cropped_image.jpg'")
cv2.imwrite('corrected_image.jpg', edges)
print("Đã lưu hình ảnh đã chỉnh sửa với tên 'corrected_image.jpg'")

# create copy of image


im2 = gray.copy()
# Paddleocr supports Chinese, English, French, German, Korean and
Japanese.
# You can set the parameter `lang` as `ch`, `en`, `fr`, `german`, `korean`,
`japan`
# to switch the language model in order.
ocr = PaddleOCR(use_angle_cls=True,
lang='en') # need to run only once to download and load model
into memory
img_path = './corrected_image.jpg'
result = ocr.ocr(img_path, cls=True)
for idx in range(len(result)):
res = result[idx]
for line in res:
print(line)

result = result[0]
image = Image.open(img_path).convert('RGB')
boxes = [line[0] for line in result]
txts = [line[1][0] for line in result]
scores = [line[1][1] for line in result]
im_show = draw_ocr(image, boxes, txts, scores, font_path='./latin.ttf')
im_show = Image.fromarray(im_show)
im_show.save('result.jpg')

# Join the elements of 'txts' into a single string


plate_num = ' '.join(txts)

# Print the recognized plate number


print("Recognized plate number:", plate_num)

Image_Obj_1 = './cropped_image.jpg'
original = QImage(Image_Obj_1) # Chuyển đổi hình ảnh từ định dạng
numpy array sang QPixmap
self.disp_obj1.setPixmap(QPixmap(original)) # Hiển thị hình ảnh ban đầu
trên nhãn chính
self.disp_obj1.setScaledContents(True) # Cho phép thay đổi kích thước nội
dung của nhãn

Image_Obj_2 = './corrected_image.jpg'
self.disp_obj2.setPixmap(QPixmap(QImage(Image_Obj_2))) # Hiển thị hình
ảnh ban đầu trên nhãn chính
self.disp_obj2.setScaledContents(True) # Cho phép thay đổi kích thước nội
dung của nhãn
self.License_plate.setText(plate_num)

# Kiểm tra và cập nhật bảng


self.update_time_in_table(plate_num)
except Exception as error:
pass # Bỏ qua mọi lỗi xảy ra

def Close_software(self):
self.Worker1_Opencv.stop() # Dừng luồng xử lý video
self.resource_usage.stop() # Dừng đo lường tài nguyên
sys.exit(app.exec_()) # Thoát ứng dụng

# Trạng thái của motor và đèn báo


def Motor_state(self):
if self.flag_motor:
self.Motor_status.setPixmap(QPixmap('images/home.png')) # Đặt biểu
tượng motor về trạng thái bật
else:
self.Motor_status.setPixmap(QPixmap('images/help.png')) # Đặt biểu tượng
motor về trạng thái tắt
self.flag_motor = not self.flag_motor # Đảo ngược trạng thái motor

def Run_Light(self):
if self.Status_lamp[1]:
self.Qlabel_yellowlight.setStyleSheet(
"background-color: rgb(255, 195, 0); border-radius:35px") # Đặt màu nền
của đèn vàng là màu vàng đậm
else:
self.Qlabel_yellowlight.setStyleSheet(
"background-color: rgb(242, 214, 117); border-radius:35px") # Đặt màu
nền của đèn vàng là màu vàng nhạt
self.Status_lamp[1] = not self.Status_lamp[1] # Đảo ngược trạng thái của đèn
vàng
# IO.output(IO_OUTPUT[1], self.Status_lamp[1])

if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec_()

import sys
import socket
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel,
QTextEdit, QVBoxLayout, QWidget, QDialog
from PyQt5.QtCore import QThread, pyqtSignal, QDateTime, QTimer

class SocketThread(QThread):
data_signal = pyqtSignal(str)

def __init__(self):
super().__init__()
self.host = '0.0.0.0'
self.port = 9000
self._run_flag = True

def stop(self):
self._run_flag = False

def run(self):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((self.host, self.port))
s.listen()
s.settimeout(1) # Set a timeout for accept()
print(f"Listening on {self.host}:{self.port}")
while self._run_flag:
try:
conn, addr = s.accept()
with conn:
print(f"Connected by {addr}")
while self._run_flag:
data = conn.recv(1024)
if not data:
break
messages = data.decode().strip().split('\n')
for message in messages:
self.data_signal.emit(message)
except socket.timeout:
continue
except Exception as e:
print(f"Error in SocketThread: {e}")

class RFIDReader(QDialog):
def __init__(self):
super().__init__()
self.initUI()
self.initSocket()
self.DateTime = QDateTime.currentDateTime()
self.motor_on = QTimer(self, interval=1000) # Timer for motor state, example
usage

def initUI(self):
self.setWindowTitle('RFID Reader and Motor Control')
self.setGeometry(100, 100, 400, 300)

self.uidLabel = QLabel('UID: ', self)


self.statusLabel = QLabel('Status: ', self)
self.textEdit = QTextEdit(self)
self.textEdit.setReadOnly(True)

self.btn_motor = QPushButton('Motor ON', self)


self.btn_motor.setCheckable(True)
self.btn_motor.clicked.connect(self.toggle_motor)

layout = QVBoxLayout()
layout.addWidget(self.uidLabel)
layout.addWidget(self.statusLabel)
layout.addWidget(self.textEdit)
layout.addWidget(self.btn_motor)

self.setLayout(layout)

def initSocket(self):
self.thread = SocketThread()
self.thread.data_signal.connect(self.updateUI)
self.thread.start()

def updateUI(self, message):


print(f"Received message: {message}") # Debug print
if message.startswith("UID:"):
self.uidLabel.setText(message)
elif message.startswith("STATUS:"):
self.statusLabel.setText(message)
else:
self.textEdit.append(f"Received: {message}")

def toggle_motor(self):
if self.btn_motor.isChecked():
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor ON")
self.btn_motor.setText('Motor OFF')
self.sendCommand("SERVO:ON")
else:
self.textEdit.append(
f"{self.DateTime.toString('d MMMM yy hh:mm:ss')}: Motor OFF")
self.btn_motor.setText('Motor ON')
self.sendCommand("SERVO:OFF")

def sendCommand(self, command):


try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(5)
print(f"Attempting to connect to ESP8266 at 192.168.137.152:9000")
s.connect(('192.168.137.152', 8000))
print("Connected successfully")
s.sendall((command + '\n').encode())
print(f"Sent command: {command}")
response = s.recv(1024).decode().strip()
print(f"Received response: {response}")
except Exception as e:
print(f"Error sending command: {e}")

def closeEvent(self, event):


self.thread.stop()
self.thread.quit()
self.thread.wait(5000) # Wait for a maximum of 5 seconds
if self.thread.isRunning():
self.thread.terminate()
event.accept()

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()

def initUI(self):
self.setWindowTitle('Main Window')
self.setGeometry(100, 100, 300, 200)

self.btn_call = QPushButton('Open RFID Reader', self)


self.btn_call.clicked.connect(self.showRFIDReader)

layout = QVBoxLayout()
layout.addWidget(self.btn_call)

container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)

def showRFIDReader(self):
self.rfid_reader = RFIDReader()
self.rfid_reader.exec_()
if __name__ == '__main__':
app = QApplication(sys.argv)
main_win = MainWindow()
main_win.show()
sys.exit(app.exec_())

You might also like