This commit is contained in:
seo-amugae committed 2026-02-24 18:30:51 +09:00
1 parent e429161a89
commit 0c55b8f1aa
134 files changed
+6071 -5801

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
# Qt Creator
*.pro.user*
.qtcreator/
build*/
*.o
*.obj
moc_*
ui_*
qrc_*
Makefile*
.qmake.stash
QTCart
*.exe
*.out
*.app
*.qtds
# Python
.venv/
venv/
__pycache__/
*.py[cod]
*$py.class
.env
.pytest_cache/
# IDE & System
.vscode/
.idea/
.DS_Store
[Tt]humbs.db
QT-Main/Makefile
QT-Main/build/
QT-Server/build/
+21 -5
View File
@@ -1,30 +1,46 @@
QT += core gui
QT += core gui network serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
barcodescanner.cpp \
main.cpp \
mainwidget.cpp \
pagecart.cpp \
pageguide.cpp \
pagepay.cpp \
pagepay_card.cpp \
pagetotalpay.cpp \
pagewelcome.cpp
HEADERS += \
barcodescanner.h \
mainwidget.h \
pagecart.h \
pageguide.h \
pagepay.h \
pagepay_card.h \
pagetotalpay.h \
pagewelcome.h
FORMS += \
mainwidget.ui \
pagecart.ui \
pageguide.ui \
pagepay.ui \
pagepay_card.ui \
pagetotalpay.ui \
pagewelcome.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES +=
RESOURCES += \
etc_image/etc_image.qrc \
item_image/item_image.qrc
+108
View File
@@ -0,0 +1,108 @@
#include "barcodescanner.h"
#include <QNetworkRequest>
#include <QUrl>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariant>
BarcodeScanner::BarcodeScanner(QObject *parent)
: QObject(parent),
manager(new QNetworkAccessManager(this))
{
connect(manager, &QNetworkAccessManager::finished,
this, &BarcodeScanner::onNetworkReply);
}
void BarcodeScanner::setApiBaseUrl(const QString &ip, int port)
{
m_apiBaseUrl = QString("http://%1:%2").arg(ip).arg(port);
qDebug() << "[Scanner] API Base URL set to:" << m_apiBaseUrl;
}
// 상품 추가
void BarcodeScanner::fetchItemDetails(const QString& itemId)
{
QString path = QString("%1/cart/add/%2").arg(m_apiBaseUrl).arg(itemId);
QUrl url(path);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
manager->post(request, QByteArray());
qDebug() << "==================================================";
qDebug() << "[Scanner] Requesting ADD -> ID:" << itemId;
qDebug() << "[Scanner] URL:" << url.toString();
}
// 상품 제거
void BarcodeScanner::removeItem(const QString& itemId)
{
QString path = QString("%1/cart/remove/%2").arg(m_apiBaseUrl).arg(itemId);
QUrl url(path);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
manager->post(request, QByteArray());
qDebug() << "==================================================";
qDebug() << "[Scanner] Requesting REMOVE -> ID:" << itemId;
qDebug() << "[Scanner] URL:" << url.toString();
}
void BarcodeScanner::onNetworkReply(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "[Scanner] Network Error:" << reply->errorString();
emit fetchFailed(reply->errorString());
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
qDebug() << "[Scanner] Raw Response Data:" << data;
QJsonDocument doc = QJsonDocument::fromJson(data);
if (!doc.isObject()) {
qDebug() << "[Scanner] Invalid JSON response";
reply->deleteLater();
return;
}
QJsonObject obj = doc.object();
QString action = obj["action"].toString();
qDebug() << "[Scanner] Parsed Action:" << action;
// 데이터 유효성 검사
if (action == "add") {
Item item;
if (obj.contains("item")) item.name = obj["item"].toString();
else if (obj.contains("name")) item.name = obj["name"].toString();
else item.name = "알수없음";
if (obj.contains("id")) {
item.id = obj["id"].toVariant().toString();
}
if (obj.contains("price")) item.price = obj["price"].toDouble();
double expectedWeight = 0.0;
if (obj.contains("expected_weight")) {
expectedWeight = obj["expected_weight"].toDouble();
item.weight = expectedWeight;
}
qDebug() << "[Scanner] Item Parsed: " << item.name << ", " << item.price;
emit itemFetched(item, expectedWeight);
}
else if (action == "remove") {
qDebug() << "[Scanner] Remove confirmed.";
}
reply->deleteLater();
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef BARCODESCANNER_H
#define BARCODESCANNER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QString>
class Item {
public:
QString id = "";
QString name = "";
double price = 0.0;
double weight = 0.0;
int stock = 0;
};
class BarcodeScanner : public QObject
{
Q_OBJECT
public:
explicit BarcodeScanner(QObject *parent = nullptr);
void setApiBaseUrl(const QString &ip, int port);
void fetchItemDetails(const QString& itemId);
void removeItem(const QString& itemId);
signals:
void itemFetched(const Item &item, double cartWeight);
void fetchFailed(const QString &error);
private slots:
void onNetworkReply(QNetworkReply *reply);
private:
QNetworkAccessManager *manager;
QString m_apiBaseUrl;
};
#endif // BARCODESCANNER_H
+1
View File
@@ -0,0 +1 @@
<RCC/>
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

+37
View File
@@ -0,0 +1,37 @@
<RCC>
<qresource prefix="/etc">
<file>card.png</file>
<file>cart.png</file>
<file>counter.png</file>
<file>food.png</file>
<file>home.png</file>
<file>kitty.jpg</file>
<file>search.png</file>
<file>wind.png</file>
<file>map.pgm</file>
<file>guide_map.png</file>
<file>map_handcream.png</file>
<file>map_phone.png</file>
<file>mappin.png</file>
<file>map_snack.png</file>
<file>map_toy.png</file>
<file>map_pay.png</file>
<file>map_cream.png</file>
<file>map_toy_2.png</file>
<file>map_phone_1.png</file>
<file>map_door.png</file>
</qresource>
<qresource prefix="/">
<file>cart_puzzle.png</file>
<file>cart_snack(1).jpg</file>
<file>cart_snack(2).png</file>
<file>cart_snack(3).jpg</file>
<file>cart_snack(4).jpg</file>
<file>cart_snack(5).jpg</file>
<file>cart_snack(6).jpg</file>
<file>cart_snack(7).jpg</file>
<file>cart_snack(8).jpg</file>
<file>cart_handcream.jpg</file>
<file>cart_iphone.jpg</file>
</qresource>
</RCC>
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

+10
View File
@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/item">
<file>apple.jpg</file>
<file>banana.jpg</file>
<file>cart_handcream.jpg</file>
<file>cart_iphone.jpg</file>
<file>cart_snack.jpg</file>
<file>fire.jpg</file>
</qresource>
</RCC>
+28
View File
@@ -0,0 +1,28 @@
#include "mainwidget.h"
#include <QApplication>
#include <QScreen>
int main(int argc, char *argv[])
{
// (선택) DPI 흔들림 줄이기 — QApplication 생성 전에
QCoreApplication::setAttribute(Qt::AA_Use96Dpi);
QApplication a(argc, argv);
// (선택) 스타일/폰트 통일
QApplication::setStyle("Fusion");
// QApplication::setFont(QFont("Noto Sans CJK KR", 10));
MainWidget w;
// ✅ 800x480 고정
w.setFixedSize(800, 480);
// ✅ 타이틀바까지 꽉 채우고 싶으면 FullScreen
w.showFullScreen();
// (대안) 창모드로 크게만: w.show();
// (대안) 최대화: w.showMaximized();
return a.exec();
}
+281
View File
@@ -0,0 +1,281 @@
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QDebug>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QNetworkDatagram>
#include <QApplication>
#include <QEvent>
#include <QKeyEvent>
#include <QMessageBox>
#include <QUrl>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
, m_isCheckingWeight(false)
{
ui->setupUi(this);
qApp->installEventFilter(this);
m_udpSocket = new QUdpSocket(this);
m_udpSocket->bind(QHostAddress::Any, QT_LISTEN_PORT, QUdpSocket::ShareAddress);
connect(m_udpSocket, &QUdpSocket::readyRead, this, &MainWidget::onUdpReadyRead);
m_networkManager = new QNetworkAccessManager(this);
connect(m_networkManager, &QNetworkAccessManager::finished, this, &MainWidget::onCheckWeightResponse);
pPageWelcome = new PageWelcome(this);
pPageCart = new PageCart(this);
pPageCart->setApiConfig(API_SERVER_IP, API_SERVER_PORT);
pPageGuide = new PageGuide(this);
pPagePay = new PagePay(this);
pPageCard = new pagepay_card(this);
pPageTotalPay= new PageTotalPay(this);
ui->pstackedWidget->addWidget(pPageWelcome);
ui->pstackedWidget->addWidget(pPageCart);
ui->pstackedWidget->addWidget(pPageGuide);
ui->pstackedWidget->addWidget(pPagePay);
ui->pstackedWidget->addWidget(pPageCard);
ui->pstackedWidget->addWidget(pPageTotalPay);
ui->pstackedWidget->setCurrentWidget(pPageWelcome);
connect(pPageWelcome, &PageWelcome::startRequested, this, &MainWidget::slotShowCartPage);
connect(pPageGuide, &PageGuide::requestGoal, this, &MainWidget::onGoalRequestReceived);
connect(pPageGuide, &PageGuide::requestStop, this, &MainWidget::onStopRequestReceived);
connect(this, SIGNAL(robotArrivedSignal()), pPageGuide, SLOT(onRobotArrived()));
connect(pPageGuide, SIGNAL(backToCartClicked()), this, SLOT(slotShowCartPage()));
connect(pPageCart, SIGNAL(guideModeClicked()), this, SLOT(slotShowGuidePage()));
connect(pPageCart, &PageCart::requestCheckout, this, &MainWidget::onCheckoutRequested);
connect(pPageCart, SIGNAL(goWelcome()), this, SLOT(slotShowWelcomePage()));
connect(pPagePay, SIGNAL(creditCardClicked()), this, SLOT(slotShowPayCardPage()));
connect(pPagePay, SIGNAL(backCartClicked()), this, SLOT(slotShowCartPage()));
connect(pPageCard, SIGNAL(goTotalPayClicked()), this, SLOT(slotShowTotalPayPage_2()));
connect(pPageTotalPay, &PageTotalPay::backToStartClicked, this, &MainWidget::slotShowWelcomePage);
connect(ui->pstackedWidget, &QStackedWidget::currentChanged, this, &MainWidget::onPageChanged);
onPageChanged(ui->pstackedWidget->currentIndex());
}
MainWidget::~MainWidget()
{
delete ui;
}
void MainWidget::sendUdpData(const QString &cmd)
{
QByteArray data = cmd.toUtf8();
m_udpSocket->writeDatagram(data, QHostAddress(ROS_SERVER_IP), ROS_SERVER_PORT);
qDebug() << "[UDP Send]" << cmd << "to" << ROS_SERVER_IP;
}
void MainWidget::onCheckoutRequested(double expectedWeight)
{
if (m_isCheckingWeight) {
qDebug() << "Check skipped: Already verifying weight...";
return;
}
m_isCheckingWeight = true; // Lock 설정
QString urlStr = QString("http://%1:%2/cart/check_weight")
.arg(API_SERVER_IP)
.arg(API_SERVER_PORT);
QUrl url(urlStr);
QNetworkRequest request(url);
// GET 요청 전송 (응답은 onCheckWeightResponse 에서 처리)
m_networkManager->get(request);
qDebug() << "[API Request] Check Weight ->" << urlStr;
}
// ROS에서 온 UDP 수신
void MainWidget::onUdpReadyRead()
{
while (m_udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = m_udpSocket->receiveDatagram();
QString msg = QString::fromUtf8(datagram.data()).trimmed();
qDebug() << "[UDP Recv]" << msg;
if (msg == "STATUS:ARRIVED") {
emit robotArrivedSignal();
}
else if (msg == "STATUS:WEIGHT_OK") {
// [성공] 무게가 일치함 -> 결제 페이지로 이동
proceedToPayPage();
}
else if (msg == "ERROR:WEIGHT_MISMATCH") { // 데이터 파싱 (콜론 : 으로 나눔)
QStringList parts = msg.split(":");
double expected = 0.0;
double real = 0.0;
double diff = 0.0;
// 데이터가 같이 왔는지 확인 (안 왔으면 0.0으로 표시됨)
if (parts.size() >= 5) {
expected = parts[2].toDouble();
real = parts[3].toDouble();
diff = parts[4].toDouble();
}
// 요청하신 팝업 코드 적용
QString alertMsg = QString("상품 무게가 일치하지 않습니다.\n\n"
"예상 무게: %1 g\n"
"실제 무게: %2 g\n"
"차이: %3 g\n\n"
"카트의 물건을 확인해주세요.")
.arg(expected, 0, 'f', 1)
.arg(real, 0, 'f', 1)
.arg(diff, 0, 'f', 1);
QMessageBox::warning(this, "출발 불가", alertMsg);
}
}
}
void MainWidget::proceedToPayPage()
{
auto cartLines = pPageCart->getCartLines();
QVector<PagePay::PayLine> payLines;
payLines.reserve(cartLines.size());
for (const auto &c : cartLines) {
PagePay::PayLine p;
p.name = c.name;
p.qty = c.qty;
p.unitPrice = c.unitPrice;
payLines.push_back(p);
}
pPagePay->setPayItems(payLines);
ui->pstackedWidget->setCurrentWidget(pPagePay);
}
void MainWidget::onGoalRequestReceived(double x, double y)
{
QString cmd = QString("GOAL:%1,%2").arg(x).arg(y);
sendUdpData(cmd);
}
void MainWidget::onStopRequestReceived()
{
sendUdpData("STOP");
}
// 페이지 변경 시 모드 변경
void MainWidget::onPageChanged(int index)
{
QWidget *currentWidget = ui->pstackedWidget->widget(index);
int mode = 0;
if (currentWidget == pPageWelcome) mode = 0; // 정지
else if (currentWidget == pPageCart) mode = 1; // 따라가기
else if (currentWidget == pPageGuide) mode = 2; // 안내모드
else if (currentWidget == pPagePay) mode = 0; // 결제 중 정지
else mode = 0;
sendRobotMode(mode);
}
void MainWidget::sendRobotMode(int mode)
{
sendUdpData(QString("MODE:%1").arg(mode));
}
void MainWidget::on_pPBStartClicked() { ui->pstackedWidget->setCurrentWidget(pPageCart); }
void MainWidget::slotShowCartPage() { ui->pstackedWidget->setCurrentWidget(pPageCart); }
void MainWidget::slotShowGuidePage() { ui->pstackedWidget->setCurrentWidget(pPageGuide); }
void MainWidget::slotShowPayCardPage(){ ui->pstackedWidget->setCurrentWidget(pPageCard); }
void MainWidget::slotShowTotalPayPage_2() { ui->pstackedWidget->setCurrentWidget(pPageTotalPay); }
void MainWidget::slotShowWelcomePage()
{
ui->pstackedWidget->setCurrentWidget(pPageWelcome);
pPageCart->resetCart();
}
void MainWidget::slotShowPayPage()
{
auto cartLines = pPageCart->getCartLines();
QVector<PagePay::PayLine> payLines;
payLines.reserve(cartLines.size());
for (const auto &c : cartLines) {
PagePay::PayLine p;
p.name = c.name;
p.qty = c.qty;
p.unitPrice = c.unitPrice;
payLines.push_back(p);
}
pPagePay->setPayItems(payLines);
ui->pstackedWidget->setCurrentWidget(pPagePay);
}
bool MainWidget::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
auto *ke = static_cast<QKeyEvent*>(event);
if (ke->key() == Qt::Key_F12) {
qApp->quit();
return true;
}
}
return QWidget::eventFilter(obj, event);
}
void MainWidget::onCheckWeightResponse(QNetworkReply *reply)
{
// 요청한 URL이 check_weight 인지 확인 (다른 API 요청과 섞이지 않게)
if (!reply->request().url().toString().contains("/cart/check_weight")) {
return;
}
if (reply->error()) {
qDebug() << "API Error:" << reply->errorString();
QMessageBox::critical(this, "통신 오류", "서버와 통신할 수 없습니다.\n" + reply->errorString());
reply->deleteLater();
m_isCheckingWeight = false;
return;
}
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
QJsonObject obj = doc.object();
// Python 서버 응답 필드: expected_weight, real_weight, diff, movable
bool movable = obj["movable"].toBool();
double expected = obj["expected_weight"].toDouble();
double real = obj["real_weight"].toDouble();
double diff = obj["diff"].toDouble();
qDebug() << "[API Response] Movable:" << movable << "Diff:" << diff;
if (movable) {
// [성공] 오차 범위 내 -> 결제 페이지로 이동
proceedToPayPage();
} else {
// [실패] 오차 범위 초과 -> 팝업 띄우기
QString msg = QString("상품 무게가 일치하지 않습니다.\n\n"
"예상 무게: %1 g\n"
"실제 무게: %2 g\n"
"차이: %3 g\n\n"
"카트의 물건을 확인해주세요.")
.arg(expected, 0, 'f', 1)
.arg(real, 0, 'f', 1)
.arg(diff, 0, 'f', 1);
QMessageBox::warning(this, "출발 불가", msg);
}
reply->deleteLater();
m_isCheckingWeight = false;
}
+87
View File
@@ -0,0 +1,87 @@
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QtNetwork/QUdpSocket>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QJsonDocument>
#include <QJsonObject>
#define ROS_SERVER_IP "192.168.123.42"
#define ROS_SERVER_PORT 55555
#define API_SERVER_IP "192.168.123.43"
#define API_SERVER_PORT 8000
#define QT_LISTEN_PORT 55555
#include "pagewelcome.h"
#include "pagecart.h"
#include "pageguide.h"
#include "pagepay.h"
#include "pagepay_card.h"
#include "pagetotalpay.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = nullptr);
~MainWidget();
signals:
void robotArrivedSignal();
private:
Ui::MainWidget *ui;
QUdpSocket *m_udpSocket;
QNetworkAccessManager *m_networkManager;
PageWelcome *pPageWelcome;
PageCart *pPageCart;
PageGuide *pPageGuide;
PagePay *pPagePay;
pagepay_card *pPageCard;
PageTotalPay *pPageTotalPay;
bool m_isCheckingWeight;
void sendUdpData(const QString &cmd);
void sendRobotMode(int mode); // 모드 전송 (0:정지, 1:따라가기, 2:안내)
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void onUdpReadyRead(); // UDP 수신부 (무게 검증 결과 처리)
void onCheckWeightResponse(QNetworkReply *reply);
void on_pPBStartClicked();
void slotShowPayPage();
void slotShowCartPage();
void slotShowGuidePage();
void slotShowPayCardPage();
void slotShowTotalPayPage_2();
void slotShowWelcomePage();
void onCheckoutRequested(double expectedWeight);
void proceedToPayPage();
void onGoalRequestReceived(double x, double y);
void onStopRequestReceived();
void onPageChanged(int index);
};
#endif // MAINWIDGET_H
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>685</width>
<height>463</height>
<width>565</width>
<height>348</height>
</rect>
</property>
<property name="windowTitle">
+408
View File
@@ -0,0 +1,408 @@
#include "pagecart.h"
#include "ui_pagecart.h"
#include <QGraphicsDropShadowEffect>
#include <QPushButton>
#include <QTableWidgetItem>
#include <QDebug>
#include <QMessageBox>
#include <QKeyEvent>
#include <QApplication>
#include <QHeaderView>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <cmath>
static QString moneyKR(qint64 v)
{
const QLocale loc(QLocale::Korean, QLocale::SouthKorea);
return loc.toString(v) + "원";
}
static QString imageForName(const QString& name)
{
if(name=="홈런볼 초코맛") return ":/cart_snack(1).jpg";
if(name=="허니버터칩") return ":/cart_snack(2).png";
if(name=="오레오 초콜릿크림") return ":/cart_snack(3).jpg";
if(name=="빼빼로 아몬드") return ":/cart_snack(4).jpg";
if(name=="빈츠") return ":/cart_snack(5).jpg";
if(name=="예감 치즈그르탕") return ":/cart_snack(6).jpg";
if(name=="오!감자 감자그라탕") return ":/cart_snack(7).jpg";
if(name=="포카칩 오리지널") return ":/cart_snack(8).jpg";
if(name=="아이폰 15 프로") return ":/cart_iphone.jpg";
if(name=="핸드크림") return ":/cart_handcream.jpg";
if(name=="크리스마스 퍼즐") return ":/cart_puzzle.png";
return "";
}
PageCart::PageCart(QWidget *parent)
: QWidget(parent),
ui(new Ui::PageCart)
{
ui->setupUi(this);
// 그림자 효과
auto makeShadow = [](QObject *parent){
auto *s = new QGraphicsDropShadowEffect(parent);
s->setBlurRadius(18);
s->setOffset(0, 4);
s->setColor(QColor(0, 0, 0, 60));
return s;
};
ui->tableCart->setStyleSheet(
"QTableWidget::item { padding: 0px; margin: 0px; }"
"QTableWidget { border: none; }"
);
if (ui->widget_2) ui->widget_2->setGraphicsEffect(makeShadow(ui->widget_2));
if (ui->widget) ui->widget->setGraphicsEffect(makeShadow(ui->widget));
ui->tableCart->setSelectionMode(QAbstractItemView::NoSelection);
ui->tableCart->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableCart->setShowGrid(false);
// 바코드 입력용 숨김 QLineEdit
m_editBarcode = new QLineEdit(this);
m_editBarcode->setVisible(false);
m_editBarcode->setFocusPolicy(Qt::StrongFocus);
m_editBarcode->setFocus();
connect(m_editBarcode, SIGNAL(returnPressed()), this, SLOT(onBarcodeEntered()));
qApp->installEventFilter(this);
m_scanner = new BarcodeScanner(this);
connect(m_scanner, &BarcodeScanner::itemFetched, this, &PageCart::handleItemFetched);
connect(m_scanner, &BarcodeScanner::fetchFailed, this, &PageCart::handleFetchFailed);
// 테이블 컬럼 설정
ui->tableCart->setColumnCount(7);
ui->tableCart->horizontalHeader()->setVisible(false);
ui->tableCart->verticalHeader()->setVisible(false);
ui->tableCart->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui->tableCart->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
ui->tableCart->setColumnWidth(0, 54);
ui->tableCart->setColumnWidth(2, 52);
ui->tableCart->setColumnWidth(3, 42);
ui->tableCart->setColumnWidth(5, 76);
ui->tableCart->setColumnWidth(6, 46);
updateTotal();
if (ui->btnHome) {
connect(ui->btnHome, &QPushButton::clicked, this, [this](){
emit goWelcome();
});
}
}
PageCart::~PageCart()
{
delete ui;
}
// MainWidget으로부터 API 설정 주입
void PageCart::setApiConfig(const QString &ip, int port)
{
if (m_scanner) {
m_scanner->setApiBaseUrl(ip, port);
}
resetCart();
}
// 카트 행 추가
void PageCart::addRowForItem(const QString& id, const QString& name, int unitPrice, int qty, double weight)
{
int row = ui->tableCart->rowCount();
ui->tableCart->insertRow(row);
ui->tableCart->setRowHeight(row, 60);
m_unitPrice.append(unitPrice);
ItemInfo info;
info.id = id;
info.name = name;
info.price = unitPrice;
info.weight = weight;
m_items.append(info);
auto makeCenterCell = [&](QWidget *child) -> QWidget* {
QWidget *w = new QWidget(ui->tableCart);
auto *lay = new QHBoxLayout(w);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->setAlignment(Qt::AlignCenter);
lay->addWidget(child);
return w;
};
auto makeRightCell = [&](QWidget *child) -> QWidget* {
QWidget *w = new QWidget(ui->tableCart);
auto *lay = new QHBoxLayout(w);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
lay->addStretch();
lay->addWidget(child);
return w;
};
// 이미지
QLabel *img = new QLabel(ui->tableCart);
img->setFixedSize(44, 44);
img->setAlignment(Qt::AlignCenter);
QPixmap px(imageForName(name));
if (!px.isNull()) {
img->setPixmap(px.scaled(44, 44, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
ui->tableCart->setCellWidget(row, 0, makeCenterCell(img));
ui->tableCart->setItem(row, 1, new QTableWidgetItem(name));
QPushButton *btnPlus = new QPushButton("+", ui->tableCart);
btnPlus->setFixedSize(26, 30);
btnPlus->setStyleSheet("QPushButton { background-color: rgb(37,99,235); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 2, makeCenterCell(btnPlus));
connect(btnPlus, &QPushButton::clicked, this, &PageCart::onPlusClicked);
QTableWidgetItem *qtyItem = new QTableWidgetItem(QString::number(qty));
qtyItem->setTextAlignment(Qt::AlignCenter);
ui->tableCart->setItem(row, 3, qtyItem);
QPushButton *btnMinus = new QPushButton("-", ui->tableCart);
btnMinus->setFixedSize(30, 30);
btnMinus->setStyleSheet("QPushButton { background-color: rgb(154,153,150); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 4, makeCenterCell(btnMinus));
connect(btnMinus, &QPushButton::clicked, this, &PageCart::onMinusClicked);
QTableWidgetItem *priceItem = new QTableWidgetItem(moneyKR(0));
priceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tableCart->setItem(row, 5, priceItem);
// 삭제 버튼
QPushButton *btnDelete = new QPushButton("X", ui->tableCart);
btnDelete->setFixedSize(30, 30);
btnDelete->setStyleSheet("QPushButton { background-color: rgb(224,27,36); color: white; border-radius: 10px; font-weight: bold; }");
ui->tableCart->setCellWidget(row, 6, makeRightCell(btnDelete));
connect(btnDelete, &QPushButton::clicked, this, &PageCart::onDeleteClicked);
updateRowAmount(row);
}
void PageCart::onPlusClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
m_scanner->fetchItemDetails(itemId);
}
void PageCart::onMinusClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
m_scanner->removeItem(itemId);
int currentQty = ui->tableCart->item(row, 3)->text().toInt();
int newQty = currentQty - 1;
if (newQty <= 0) {
m_unitPrice.removeAt(row);
m_items.removeAt(row);
ui->tableCart->removeRow(row);
} else {
ui->tableCart->item(row, 3)->setText(QString::number(newQty));
updateRowAmount(row);
}
updateTotal();
}
void PageCart::onDeleteClicked()
{
QPushButton *btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = getRowFromButton(btn);
if (row < 0 || row >= m_items.size()) return;
QString itemId = m_items[row].id;
int qty = ui->tableCart->item(row, 3)->text().toInt();
for (int i = 0; i < qty; ++i) {
m_scanner->removeItem(itemId);
}
m_unitPrice.removeAt(row);
m_items.removeAt(row);
ui->tableCart->removeRow(row);
updateTotal();
}
void PageCart::updateRowAmount(int row)
{
if (row < 0 || row >= m_unitPrice.size()) return;
int qty = ui->tableCart->item(row, 3)->text().toInt();
int amount = m_unitPrice[row] * qty;
ui->tableCart->item(row, 5)->setText(moneyKR(amount));
}
void PageCart::updateTotal()
{
int totalCount = 0;
int totalPrice = 0;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
auto *qtyItem = ui->tableCart->item(r, 3);
if (!qtyItem) continue;
int qty = qtyItem->text().toInt();
totalCount += qty;
int unit = (r < m_unitPrice.size()) ? m_unitPrice[r] : 0;
totalPrice += unit * qty;
}
if (ui->lblTotalPrice_2)
ui->lblTotalPrice_2->setText(moneyKR(totalPrice));
if (ui->lblCartTitle)
ui->lblCartTitle->setText(QString("Cart (%1 products)").arg(totalCount));
}
void PageCart::onBarcodeEntered()
{
QString code = m_editBarcode->text().trimmed();
m_editBarcode->clear();
if (code.isEmpty()) return;
m_scanner->fetchItemDetails(code);
}
bool PageCart::eventFilter(QObject *obj, QEvent *event)
{
Q_UNUSED(obj);
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
if (!m_barcodeData.isEmpty()) {
m_scanner->fetchItemDetails(m_barcodeData);
m_barcodeData.clear();
}
return true;
}
else if (!keyEvent->text().isEmpty() &&
!(keyEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier))) {
m_barcodeData.append(keyEvent->text());
return true;
}
}
return QWidget::eventFilter(obj, event);
}
void PageCart::handleItemFetched(const Item &item)
{
QString strId = item.id;
int rowFound = -1;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
if (m_items[r].id == strId || m_items[r].name == item.name) {
rowFound = r;
break;
}
}
if (rowFound == -1) {
int unit = static_cast<int>(std::lround(item.price));
addRowForItem(strId, item.name, unit, 1, item.weight);
} else {
int qty = ui->tableCart->item(rowFound, 3)->text().toInt();
ui->tableCart->item(rowFound, 3)->setText(QString::number(qty + 1));
m_items[rowFound].id = strId;
m_items[rowFound].weight = item.weight;
updateRowAmount(rowFound);
}
updateTotal();
}
void PageCart::handleFetchFailed(const QString &err)
{
qDebug() << "Barcode fetch failed:" << err;
QMessageBox::warning(this, "스캔 실패", "상품 정보를 불러오지 못했습니다.\n" + err);
}
void PageCart::on_btnGuide_clicked() { emit guideModeClicked(); }
void PageCart::on_pushButton_clicked() { resetCart(); }
QVector<PageCart::CartLine> PageCart::getCartLines() const
{
QVector<CartLine> out;
for (int r = 0; r < ui->tableCart->rowCount(); ++r) {
auto *nameItem = ui->tableCart->item(r, 1);
auto *qtyItem = ui->tableCart->item(r, 3);
if (!nameItem || !qtyItem) continue;
int qty = qtyItem->text().toInt();
if (qty <= 0) continue;
int unitPrice = (r < m_unitPrice.size()) ? m_unitPrice[r] : 0;
out.push_back({ nameItem->text(), qty, unitPrice });
}
return out;
}
void PageCart::resetCart()
{
ui->tableCart->setRowCount(0);
m_unitPrice.clear();
m_items.clear();
updateTotal();
}
int PageCart::getRowFromButton(QWidget *btn)
{
if (!btn) return -1;
int rowCount = ui->tableCart->rowCount();
for (int r = 0; r < rowCount; ++r) {
int targetCols[] = {2, 4, 6};
for (int c : targetCols) {
QWidget *cellWidget = ui->tableCart->cellWidget(r, c);
if (cellWidget && (cellWidget == btn || cellWidget->isAncestorOf(btn))) {
return r;
}
}
}
return -1;
}
double PageCart::getTotalWeight() const
{
double totalWeight = 0.0;
// m_items와 테이블의 행은 동기화되어 있다고 가정
for(int i = 0; i < m_items.size(); ++i) {
if (i >= ui->tableCart->rowCount()) break;
QTableWidgetItem *itemQty = ui->tableCart->item(i, 3);
if (itemQty) {
int qty = itemQty->text().toInt();
totalWeight += (m_items[i].weight * qty);
}
}
return totalWeight;
}
void PageCart::on_btnCheckout_clicked()
{
if (ui->tableCart->rowCount() == 0) {
QMessageBox::information(this, "알림", "장바구니가 비어있습니다.");
return;
}
// [수정] 예상 무게를 계산하여 MainWidget으로 전달
double expected = getTotalWeight();
qDebug() << "Request Checkout - Expected Weight:" << expected;
emit requestCheckout(expected);
}
+84
View File
@@ -0,0 +1,84 @@
#ifndef PAGECART_H
#define PAGECART_H
#include <QWidget>
#include <QVector>
#include <QLineEdit>
#include <QString>
#include "barcodescanner.h"
namespace Ui { class PageCart; }
struct ItemInfo {
QString id;
QString name;
int price;
double weight;
};
class PageCart : public QWidget
{
Q_OBJECT
public:
explicit PageCart(QWidget *parent = nullptr);
~PageCart();
struct CartLine {
QString name;
int qty;
int unitPrice;
};
QVector<CartLine> getCartLines() const;
// MainWidget에서 API 설정 받기
void setApiConfig(const QString &ip, int port);
double getTotalWeight() const;
public slots:
void resetCart();
signals:
void guideModeClicked();
void goWelcome();
void requestCheckout(double totalWeight);
private slots:
void onPlusClicked();
void onMinusClicked();
void onDeleteClicked();
void onBarcodeEntered();
void handleItemFetched(const Item &item);
void handleFetchFailed(const QString &err);
void on_pushButton_clicked(); // 카트 비우기
void on_btnCheckout_clicked(); // 결제 버튼
void on_btnGuide_clicked(); // 안내 버튼
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private:
Ui::PageCart *ui;
QVector<ItemInfo> m_items;
QVector<int> m_unitPrice;
QLineEdit *m_editBarcode = nullptr;
QString m_barcodeData;
BarcodeScanner *m_scanner = nullptr;
void addRowForItem(const QString& id, const QString& name, int unitPrice, int qty, double weight);
void updateRowAmount(int row);
void updateTotal();
int getRowFromButton(QWidget *btn);
};
#endif // PAGECART_H
+478
View File
@@ -0,0 +1,478 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageCart</class>
<widget class="QWidget" name="PageCart">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(242, 244, 245);
</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QWidget" name="widget_2" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>60</y>
<width>531</width>
<height>371</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;
</string>
</property>
<widget class="QTableWidget" name="tableCart">
<property name="geometry">
<rect>
<x>10</x>
<y>120</y>
<width>511</width>
<height>241</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>50</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Product</string>
</property>
</widget>
<widget class="QLabel" name="label_11">
<property name="geometry">
<rect>
<x>230</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Count</string>
</property>
</widget>
<widget class="QLabel" name="label_12">
<property name="geometry">
<rect>
<x>415</x>
<y>70</y>
<width>71</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 14pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Price</string>
</property>
</widget>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>531</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(200, 200, 200);
border-radius: 0px;
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 23pt &quot;Umpush&quot;;
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="text">
<string>Cart</string>
</property>
</widget>
<widget class="QLabel" name="lblCartTitle">
<property name="geometry">
<rect>
<x>90</x>
<y>20</y>
<width>151</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Umpush&quot;;
border-style: solid;
border-top: 1px solid rgb(255, 255, 255);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>410</x>
<y>10</y>
<width>111</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: rgb(224, 27, 36);
font: 300 12pt &quot;Umpush&quot;;
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
}
QPushButton:hover {
background-color: rgb(192, 28, 40);
}
QPushButton:pressed {
background-color: rgb(120, 0, 10);
}
QPushButton:disabled {
background-color: rgb(120, 120, 120);
color: rgb(230, 230, 230);
}
</string>
</property>
<property name="text">
<string>clear cart</string>
</property>
</widget>
</widget>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>550</x>
<y>60</y>
<width>191</width>
<height>261</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<widget class="QFrame" name="frame">
<property name="geometry">
<rect>
<x>0</x>
<y>180</y>
<width>351</width>
<height>81</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(200, 200, 200);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnCheckout">
<property name="geometry">
<rect>
<x>3</x>
<y>21</y>
<width>181</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
</string>
</property>
<property name="text">
<string>결제하기</string>
</property>
</widget>
</widget>
<widget class="QLabel" name="label_13">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>321</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/new/prefix1/Gemini_Generated_Image_u34sf6u34sf6u34s.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>111</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 18pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
border-radius: 16px;
</string>
</property>
<property name="text">
<string>총 금액</string>
</property>
</widget>
<widget class="QLabel" name="lblTotalPrice_2">
<property name="geometry">
<rect>
<x>20</x>
<y>100</y>
<width>161</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 21pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
border-radius: 16px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QPushButton" name="btnGuide">
<property name="geometry">
<rect>
<x>640</x>
<y>10</y>
<width>41</width>
<height>41</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font: 23pt &quot;Ria Sans&quot;;
image: url(:/etc/search.png);
border-radius: 0px;
background-color: rgb(242, 244, 245);
</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>80</height>
</size>
</property>
</widget>
<widget class="QPushButton" name="btnHome">
<property name="geometry">
<rect>
<x>700</x>
<y>10</y>
<width>41</width>
<height>41</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">font: 23pt &quot;Ria Sans&quot;;
image: url(:/etc/home.png);
border-radius: 0px;
padding: 0px 0px;
background-color: rgb(242, 244, 245);
</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>80</height>
</size>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>330</x>
<y>20</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0);
color: rgb(154, 153, 150);</string>
</property>
<property name="text">
<string>O Checkout </string>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>470</x>
<y>19</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Payment </string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>220</x>
<y>19</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
background-color: #F2F4F7;
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Cart </string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>550</x>
<y>330</y>
<width>191</width>
<height>101</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/etc/counter.png);
border-radius: 16px;
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+180
View File
@@ -0,0 +1,180 @@
#include "pageguide.h"
#include "ui_pageguide.h"
#include <QPixmap>
#include <QDebug>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QResizeEvent>
#include <QDialog>
#include <QLabel>
#include <QGraphicsDropShadowEffect>
PageGuide::PageGuide(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageGuide)
{
ui->setupUi(this);
QPixmap pm(":/etc/guide_map.png");
if(!pm.isNull()) {
m_treePixmap = pm;
ui->lblmap->setPixmap(pm);
ui->lblmap->setScaledContents(true);
}
ui->lblmap->setStyleSheet("border-radius:14px; background:#ffffff; border: none;");
// 버튼 연결
connect(ui->btnBackToCart, &QPushButton::clicked, this, &PageGuide::onBackToCartClicked);
connect(ui->btnpuzzle, &QPushButton::clicked, this, &PageGuide::onPuzzleClicked);
connect(ui->btncream, &QPushButton::clicked, this, &PageGuide::onCreamClicked);
connect(ui->btnsnack, &QPushButton::clicked, this, &PageGuide::onSnackClicked);
connect(ui->btnpay, &QPushButton::clicked, this, &PageGuide::onPayClicked);
connect(ui->btnphone, &QPushButton::clicked, this, &PageGuide::onbtnphoneClicked);
}
PageGuide::~PageGuide()
{
delete ui;
}
void PageGuide::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
applyTreePixmap();
}
void PageGuide::applyTreePixmap()
{
if (m_treePixmap.isNull() || !ui->lblmap) return;
QSize target = ui->lblmap->size();
QPixmap scaled = m_treePixmap.scaled(target, Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->lblmap->setPixmap(scaled);
}
void PageGuide::onBackToCartClicked()
{
emit backToCartClicked();
}
void PageGuide::onPuzzleClicked()
{
emit requestGoal(-0.1, -2.4);
showMovePopup("01코너 식품");
}
void PageGuide::onSnackClicked()
{
emit requestGoal(3.0, 0.55);
showMovePopup("02코너 장난감");
}
void PageGuide::onCreamClicked()
{
emit requestGoal(2.6, -6.5);
showMovePopup("03코너 생필품");
}
void PageGuide::onbtnphoneClicked()
{
emit requestGoal(-0.1, -5.0);
showMovePopup("04코너 전자제품");
}
void PageGuide::onPayClicked()
{
emit requestGoal(-0.07, -6.7);
showMovePopup("결제 구역");
}
void PageGuide::showMovePopup(const QString &zoneText)
{
// 이미 떠있는 팝업이 있다면 닫기
if (m_currentPopup) {
m_currentPopup->close();
delete m_currentPopup;
m_currentPopup = nullptr;
}
QWidget *parentW = this->window();
m_currentPopup = new QDialog(parentW);
QDialog &dlg = *m_currentPopup;
dlg.setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
dlg.setModal(true);
dlg.setAttribute(Qt::WA_DeleteOnClose);
dlg.setStyleSheet("QDialog { background: transparent; }");
dlg.setFixedSize(520, 260);
// 팝업이 닫힐 때 포인터 초기화
connect(&dlg, &QDialog::destroyed, this, [this](){
m_currentPopup = nullptr;
});
// 중앙 정렬
const QPoint parentTopLeft = parentW->mapToGlobal(QPoint(0, 0));
const int x = parentTopLeft.x() + (parentW->width() - dlg.width()) / 2;
const int y = parentTopLeft.y() + (parentW->height() - dlg.height()) / 2;
dlg.move(x, y);
// 카드 스타일 UI 구성
QFrame *card = new QFrame(&dlg);
card->setObjectName("popupCard");
card->setStyleSheet("#popupCard { background: rgb(246, 245, 244); border-radius: 16px; }");
auto *shadow = new QGraphicsDropShadowEffect(card);
shadow->setBlurRadius(30);
shadow->setOffset(0, 8);
shadow->setColor(QColor(0, 0, 0, 80));
card->setGraphicsEffect(shadow);
QVBoxLayout *root = new QVBoxLayout(&dlg);
root->setContentsMargins(0, 0, 0, 0);
root->addWidget(card);
QVBoxLayout *lay = new QVBoxLayout(card);
lay->setContentsMargins(28, 24, 28, 24);
lay->setSpacing(14);
QLabel *title = new QLabel(QString("%1로 이동 중입니다").arg(zoneText), card);
title->setAlignment(Qt::AlignCenter);
title->setStyleSheet("font-size:24px; font-weight:900; color:#111827;");
QLabel *desc = new QLabel("확인을 누르면 이동을 중단합니다.", card);
desc->setAlignment(Qt::AlignCenter);
desc->setStyleSheet("font-size:16px; font-weight:700; color:#6B7280;");
QPushButton *stopBtn = new QPushButton("확인(이동 중단)", card);
stopBtn->setFixedHeight(48);
stopBtn->setMinimumWidth(220);
stopBtn->setStyleSheet(
"QPushButton{ background:#EF4444; color:white; border:none; border-radius:12px; "
"font-size:18px; font-weight:900; padding:8px 18px; }"
"QPushButton:hover{ background:#DC2626; }"
"QPushButton:pressed{ background:#B91C1C; }"
);
lay->addStretch();
lay->addWidget(title);
lay->addWidget(desc);
lay->addSpacing(8);
lay->addWidget(stopBtn, 0, Qt::AlignHCenter);
lay->addStretch();
// 안내 중지 버튼 -> MainWidget에 신호
connect(stopBtn, &QPushButton::clicked, this, [this, &dlg](){
emit requestStop();
dlg.accept();
});
dlg.show();
}
// 도착 알림 -> 팝업 끄기
void PageGuide::onRobotArrived()
{
if (m_currentPopup) {
qDebug() << "PageGuide: Robot Arrived! Closing popup.";
m_currentPopup->accept();
}
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef PAGEGUIDE_H
#define PAGEGUIDE_H
#include <QWidget>
#include <QPixmap>
#include <QDialog>
namespace Ui { class PageGuide; }
class PageGuide : public QWidget
{
Q_OBJECT
public:
explicit PageGuide(QWidget *parent = nullptr);
~PageGuide();
public slots:
void onRobotArrived();
signals:
void backToCartClicked();
void requestGoal(double x, double y);
void requestStop();
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
void onBackToCartClicked();
void onPuzzleClicked();
void onCreamClicked();
void onSnackClicked();
void onPayClicked();
void onbtnphoneClicked();
private:
Ui::PageGuide *ui;
QPixmap m_treePixmap;
QDialog *m_currentPopup = nullptr;
void applyTreePixmap();
void showMovePopup(const QString &zoneText);
};
#endif // PAGEGUIDE_H
+438
View File
@@ -0,0 +1,438 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageGuide</class>
<widget class="QWidget" name="PageGuide">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QFrame" name="frame">
<property name="styleSheet">
<string notr="true">/* ===== 카드(프레임) ===== */
QFrame#frameCornerList {
background-color: rgb(255, 255, 255);
border-radius: 16px;
border: 1px solid rgb(229, 231, 235); /* 연회색 */
}
/* 제목 라벨 */
QLabel#lblCornerTitle {
background: transparent;
color: rgb(17, 24, 39); /* 진한 글씨 */
font-size: 20px;
font-weight: 800;
}
/* ===== 리스트 위젯 ===== */
QListWidget#listCorners {
background: transparent;
border: none;
outline: 0;
padding: 6px;
}
/* 스크롤바 깔끔하게 (선택) */
QListWidget#listCorners QScrollBar:vertical {
width: 10px;
background: transparent;
}
QListWidget#listCorners QScrollBar::handle:vertical {
background: rgb(209, 213, 219);
border-radius: 5px;
min-height: 20px;
}
QListWidget#listCorners QScrollBar::add-line:vertical,
QListWidget#listCorners QScrollBar::sub-line:vertical {
height: 0px;
}
/* ===== 아이템(행) 스타일 ===== */
QListWidget#listCorners::item {
background-color: rgb(249, 250, 251); /* 살짝 회색 */
border: 1px solid rgb(229, 231, 235);
border-radius: 12px;
padding: 14px 16px; /* 안쪽 여백 */
margin: 6px 4px; /* 아이템 간 간격 */
color: rgb(17, 24, 39);
font-size: 16px;
font-weight: 700;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnpay">
<property name="geometry">
<rect>
<x>90</x>
<y>350</y>
<width>61</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_pay.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnpuzzle">
<property name="geometry">
<rect>
<x>390</x>
<y>150</y>
<width>61</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_toy_2.png);
QPushButton#Region1:hover{
background-color: rgb(222, 221, 218);
};
QPushButton#Region1:pressed{
background-color: rgb(222, 221, 218);
}</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnsnack">
<property name="geometry">
<rect>
<x>190</x>
<y>170</y>
<width>61</width>
<height>71</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_snack.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btnBackToCart">
<property name="geometry">
<rect>
<x>550</x>
<y>10</y>
<width>201</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
font: 14pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>장바구니로 돌아가기</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>121</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 22pt &quot;Ubuntu&quot;;
color: rgb(46, 194, 126);</string>
</property>
<property name="text">
<string>안내 지도</string>
</property>
</widget>
<widget class="QLabel" name="lblmap">
<property name="geometry">
<rect>
<x>20</x>
<y>70</y>
<width>521</width>
<height>341</height>
</rect>
</property>
<property name="baseSize">
<size>
<width>780</width>
<height>800</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/guide_map.png);
border-style: solid;
border-top: 3px solid rgb(200, 200, 200);
border-left: 3px solid rgb(200, 200, 200);
border-right: 3px solid rgb(200, 200, 200);
border-bottom: 3px solid rgb(200, 200, 200);
</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>140</x>
<y>20</y>
<width>391</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 300 italic 11pt &quot;Ubuntu&quot;;
color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>**지도의 아이콘을 터치하면 해당 구역으로 안내가 시작됩니다</string>
</property>
</widget>
<widget class="QPushButton" name="btnphone">
<property name="geometry">
<rect>
<x>100</x>
<y>290</y>
<width>51</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_phone_1.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QPushButton" name="btncream">
<property name="geometry">
<rect>
<x>390</x>
<y>350</y>
<width>51</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image: url(:/etc/map_cream.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>180</x>
<y>180</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>01</string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>380</x>
<y>140</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>02</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>370</x>
<y>350</y>
<width>21</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>03</string>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>80</x>
<y>290</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>04</string>
</property>
</widget>
<widget class="QFrame" name="frameCornerList">
<property name="geometry">
<rect>
<x>560</x>
<y>60</y>
<width>191</width>
<height>361</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblCornerTitle">
<property name="geometry">
<rect>
<x>60</x>
<y>0</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">
font: 700 16pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>물품위치</string>
</property>
</widget>
<widget class="QListWidget" name="listCorners">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>191</width>
<height>341</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 11pt &quot;Ubuntu&quot;;</string>
</property>
<item>
<property name="text">
<string>01코너 식품</string>
</property>
</item>
<item>
<property name="text">
<string>02코너 장난감</string>
</property>
</item>
<item>
<property name="text">
<string>03코너 생필품</string>
</property>
</item>
<item>
<property name="text">
<string>04코너 전자제품</string>
</property>
</item>
<item>
<property name="text">
<string>05코너 계산대</string>
</property>
</item>
</widget>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>70</x>
<y>360</y>
<width>21</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 14pt &quot;Ubuntu&quot;;
color: rgb(255, 255, 255);
background-color: rgb(224, 27, 36);</string>
</property>
<property name="text">
<string>05</string>
</property>
</widget>
<zorder>lblmap</zorder>
<zorder>btnpay</zorder>
<zorder>btnpuzzle</zorder>
<zorder>btnsnack</zorder>
<zorder>btnBackToCart</zorder>
<zorder>label</zorder>
<zorder>label_3</zorder>
<zorder>btnphone</zorder>
<zorder>btncream</zorder>
<zorder>label_4</zorder>
<zorder>label_5</zorder>
<zorder>label_6</zorder>
<zorder>label_7</zorder>
<zorder>frameCornerList</zorder>
<zorder>label_8</zorder>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+179
View File
@@ -0,0 +1,179 @@
#include "pagepay.h"
#include "ui_pagepay.h"
#include <QGraphicsDropShadowEffect>
#include <QHeaderView>
#include <QTableWidgetItem>
#include <QPushButton>
#include <QPixmap>
static QString moneyKR(qint64 v)
{
// ✅ 무조건 "1,234" 형식으로 나오게(환경 영향 최소화)
const QLocale loc(QLocale::Korean, QLocale::SouthKorea);
return loc.toString(v) + "원";
}
PagePay::PagePay(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PagePay)
{
ui->setupUi(this);
auto makeShadow = [](QObject *parent){
auto *s = new QGraphicsDropShadowEffect(parent);
s->setBlurRadius(18);
s->setOffset(0, 4);
s->setColor(QColor(0, 0, 0, 60));
return s;
};
// 그림자
if (ui->frame_2) ui->frame_2->setGraphicsEffect(makeShadow(ui->frame_2));
if (ui->widget) ui->widget->setGraphicsEffect(makeShadow(ui->widget));
// ✅ 버튼 연결 (새 UI 기준)
if (ui->btnBack) connect(ui->btnBack, &QPushButton::clicked, this, &PagePay::onBackPressed);
if (ui->btnNext) connect(ui->btnNext, &QPushButton::clicked, this, &PagePay::onNextPressed);
// 테이블 세팅
setupUiTable();
// 카드 아이콘(임시 이미지)
if (ui->lblCardIcon) {
QPixmap px(":/etc/card.png");
if (!px.isNull()) {
ui->lblCardIcon->setPixmap(px.scaled(180, 180, Qt::KeepAspectRatio, Qt::SmoothTransformation));
ui->lblCardIcon->setAlignment(Qt::AlignCenter);
} else {
ui->lblCardIcon->setText("CARD");
ui->lblCardIcon->setAlignment(Qt::AlignCenter);
}
}
// frame 스타일
if (ui->frame_2) {
ui->frame_2->setStyleSheet("QFrame { background: white; border-radius: 16px; }");
}
// 안내 문구 (너 UI가 lblCarGuide면 그대로, lblCardGuide면 그걸로)
if (ui->lblCarGuide) {
ui->lblCarGuide->setText("카드를 투입구에 넣어주세요");
}
// 만약 실제로 lblCardGuide라면 위 대신 아래를 쓰면 됨:
// if (ui->lblCardGuide) ui->lblCardGuide->setText("카드를 투입구에 넣어주세요");
// 처음엔 비어있으니 기본 표시
refreshTableAndTotal();
}
PagePay::~PagePay()
{
delete ui;
}
void PagePay::setupUiTable()
{
ui->tablePayItems->horizontalHeader()->setVisible(false);
ui->tablePayItems->verticalHeader()->setVisible(false);
ui->tablePayItems->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tablePayItems->setSelectionMode(QAbstractItemView::NoSelection);
ui->tablePayItems->setShowGrid(false);
// ✅ 5컬럼: [물품][간격][갯수][간격][가격]
ui->tablePayItems->setColumnCount(5);
auto *hh = ui->tablePayItems->horizontalHeader();
hh->setSectionResizeMode(0, QHeaderView::Stretch); // 물품은 남는 폭 다 먹기
hh->setSectionResizeMode(1, QHeaderView::Fixed); // 간격
hh->setSectionResizeMode(2, QHeaderView::Fixed); // 갯수
hh->setSectionResizeMode(3, QHeaderView::Fixed); // 간격
hh->setSectionResizeMode(4, QHeaderView::Fixed); // 가격
// ✅ 800x480 최적값(기본)
int gapW = 12; // 물품-갯수 / 갯수-가격 사이 간격
int qtyW = 60; // "3개" 정도 충분
int priceW = 110; // "13000원" 정도 충분
// ✅ 테이블 폭에 따라 살짝 자동 보정 (작아지면 더 줄임)
int tableW = ui->tablePayItems->viewport()->width();
if (tableW > 0) {
// 물품 칼럼 최소폭을 남기기 위해, 너무 좁으면 qty/price/gap를 줄여줌
int fixed = gapW*2 + qtyW + priceW;
int nameMin = 160; // 물품명 최소로 확보(원하면 140~200 조절)
if (tableW - fixed < nameMin) {
// 줄일 여지가 있는 것들부터 줄임
gapW = 8;
qtyW = 52;
priceW = 78;
}
}
ui->tablePayItems->setColumnWidth(1, gapW);
ui->tablePayItems->setColumnWidth(2, qtyW);
ui->tablePayItems->setColumnWidth(3, gapW);
ui->tablePayItems->setColumnWidth(4, priceW);
// (옵션) 글자와 셀 padding 조금 줄여서 7인치에 더 꽉 차게
ui->tablePayItems->setStyleSheet(
"QTableWidget::item { padding-left: 4px; padding-right: 4px; }"
);
}
void PagePay::setPayItems(const QVector<PayLine>& lines)
{
m_lines = lines;
refreshTableAndTotal();
}
void PagePay::refreshTableAndTotal()
{
ui->tablePayItems->setRowCount(0);
int totalPrice = 0;
for (int i = 0; i < m_lines.size(); ++i) {
const auto &it = m_lines[i];
int row = ui->tablePayItems->rowCount();
ui->tablePayItems->insertRow(row);
ui->tablePayItems->setRowHeight(row, 40); // ✅ 7인치면 40~44 추천
// (0) 물품
auto *nameItem = new QTableWidgetItem(it.name);
nameItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignLeft);
ui->tablePayItems->setItem(row, 0, nameItem);
// (2) 갯수
auto *qtyItem = new QTableWidgetItem(QString("%1개").arg(it.qty));
qtyItem->setTextAlignment(Qt::AlignCenter);
ui->tablePayItems->setItem(row, 2, qtyItem);
// (4) 가격
int linePrice = it.unitPrice * it.qty;
totalPrice += linePrice;
auto *priceItem = new QTableWidgetItem(moneyKR(linePrice));
priceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tablePayItems->setItem(row, 4, priceItem);
}
if (ui->lblTotalPriceValue)
ui->lblTotalPriceValue->setText(moneyKR(totalPrice));
ui->btnNext->setEnabled(!m_lines.isEmpty());
}
void PagePay::onBackPressed()
{
emit backCartClicked(); // ✅ PageCart로
}
void PagePay::onNextPressed()
{
emit creditCardClicked(); // ✅ pagepay_card로
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef PAGEPAY_H
#define PAGEPAY_H
#include <QWidget>
#include <QVector>
#include <QString>
namespace Ui { class PagePay; }
class PagePay : public QWidget
{
Q_OBJECT
public:
explicit PagePay(QWidget *parent = nullptr);
~PagePay();
struct PayLine {
QString name;
int qty = 0;
int unitPrice = 0;
};
void setPayItems(const QVector<PayLine>& lines);
signals:
void backCartClicked(); // <- 이전 (PageCart로)
void creditCardClicked(); // 다음 (pagepay_card로)
private slots:
void onBackPressed();
void onNextPressed();
private:
void setupUiTable();
void refreshTableAndTotal();
private:
Ui::PagePay *ui;
QVector<PayLine> m_lines;
};
#endif // PAGEPAY_H
+358
View File
@@ -0,0 +1,358 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PagePay</class>
<widget class="QWidget" name="PagePay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: #F2F4F7;</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="btnNext">
<property name="geometry">
<rect>
<x>460</x>
<y>370</y>
<width>281</width>
<height>61</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(53, 132, 228);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;
font: 14pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>다음으로 넘어가기</string>
</property>
</widget>
<widget class="QPushButton" name="btnBack">
<property name="geometry">
<rect>
<x>610</x>
<y>10</y>
<width>95</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(154, 153, 150);
color: rgb(255, 255, 255);
border: none;
border-radius: 14px;
padding: 10px 18px;</string>
</property>
<property name="text">
<string>이전 화면</string>
</property>
</widget>
<widget class="QFrame" name="frame_2">
<property name="geometry">
<rect>
<x>460</x>
<y>90</y>
<width>281</width>
<height>271</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblCardIcon">
<property name="geometry">
<rect>
<x>30</x>
<y>-10</y>
<width>201</width>
<height>211</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/etc/card.png);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblCarGuide">
<property name="geometry">
<rect>
<x>50</x>
<y>200</y>
<width>241</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 500 12pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>카드를 투입구에 넣어주세요</string>
</property>
</widget>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>330</x>
<y>19</y>
<width>121</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0);
background-color: #F2F4F7;
</string>
</property>
<property name="text">
<string>O Checkout </string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>220</x>
<y>20</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Cart </string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>301</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(0, 0, 0)</string>
</property>
<property name="text">
<string>아래 구매 내역을 확인해주세요</string>
</property>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>441</width>
<height>341</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 16px;</string>
</property>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>141</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 500 20pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string>구매 내역</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Prdoucts</string>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>230</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Count</string>
</property>
</widget>
<widget class="QLabel" name="label_12">
<property name="geometry">
<rect>
<x>385</x>
<y>50</y>
<width>91</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 15pt &quot;Ubuntu Mono&quot;;</string>
</property>
<property name="text">
<string>Price</string>
</property>
</widget>
<widget class="QTableWidget" name="tablePayItems">
<property name="geometry">
<rect>
<x>0</x>
<y>90</y>
<width>441</width>
<height>191</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-radius: 0px;
font: 300 13pt &quot;Umpush&quot;;
border-top: 1px solid #cfcfcf;
border-bottom: 0px;
border-left: 0px;
border-right: 0px;</string>
</property>
</widget>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>290</y>
<width>451</width>
<height>51</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
border-style: solid;
border-top: 1px solid rgb(200, 200, 200);
border-left: 1px solid rgb(255, 255, 255);
border-right: 1px solid rgb(255, 255, 255);
border-bottom: 1px solid rgb(255, 255, 255);
border-radius: 0px;</string>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="label_10">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>121</width>
<height>31</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 16pt &quot;Umpush&quot;;
background-color: rgb(255, 255, 255);
border: none;
</string>
</property>
<property name="text">
<string>총 결제금액</string>
</property>
</widget>
<widget class="QLabel" name="lblTotalPriceValue">
<property name="geometry">
<rect>
<x>240</x>
<y>10</y>
<width>101</width>
<height>41</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border: none;
font: 15pt &quot;Umpush&quot;;
color: rgb(28, 113, 216);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>460</x>
<y>20</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 13pt &quot;Ubuntu Condensed&quot;;
color: rgb(154, 153, 150);
background-color: #F2F4F7;</string>
</property>
<property name="text">
<string>O Payment </string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+213
View File
@@ -0,0 +1,213 @@
#include "pagepay_card.h"
#include "ui_pagepay_card.h"
#include <QDialog>
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QStackedWidget>
#include <QTimer>
#include <QShowEvent>
#include <QPainter>
#include <QFont>
#include <QSharedPointer>
// ⏳ 텍스트를 Pixmap으로 렌더링
static QPixmap textToPixmap(const QString &text, int fontPx)
{
QFont f;
f.setPixelSize(fontPx);
const int size = fontPx * 2;
QPixmap pm(size, size);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::TextAntialiasing, true);
p.setFont(f);
p.setPen(Qt::black);
p.drawText(pm.rect(), Qt::AlignCenter, text);
return pm;
}
// Pixmap을 고정 캔버스 안에서 회전
static QPixmap rotatedPixmap(const QPixmap &src, qreal angleDeg)
{
QPixmap out(src.size());
out.fill(Qt::transparent);
QPainter p(&out);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
const QPointF c(out.width() / 2.0, out.height() / 2.0);
p.translate(c);
p.rotate(angleDeg);
p.translate(-c);
p.drawPixmap(0, 0, src);
return out;
}
pagepay_card::pagepay_card(QWidget *parent)
: QWidget(parent)
, ui(new Ui::pagepay_card)
{
ui->setupUi(this);
}
pagepay_card::~pagepay_card()
{
delete ui;
}
void pagepay_card::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
// 페이지가 뜬 뒤 모달 띄우기
QTimer::singleShot(0, this, [this]() {
showPaymentModal();
});
}
void pagepay_card::showPaymentModal()
{
QWidget *parentW = this->window();
if (!parentW) parentW = this;
// ✅ 팝업 자체를 카드 크기로만 만들기 (전체 덮지 않음)
QDialog *dlg = new QDialog(parentW);
dlg->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
dlg->setModal(true);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setFixedSize(520, 260);
// ✅ 부모(800x480) 기준 "정중앙" 위치
const int x = (parentW->width() - dlg->width()) / 2;
const int y = (parentW->height() - dlg->height()) / 2;
dlg->move(x, y);
// ===== 다이얼로그 루트 레이아웃 =====
auto *dlgLay = new QVBoxLayout(dlg);
dlgLay->setContentsMargins(0,0,0,0);
dlgLay->setSpacing(0);
// ===== 카드 프레임 (둥근 모서리) =====
QFrame *card = new QFrame(dlg);
card->setObjectName("card");
card->setStyleSheet(
"#card { background: white; border-radius: 16px; }"
);
dlgLay->addWidget(card);
// ===== 카드 내부 스택 =====
QStackedWidget *stack = new QStackedWidget(card);
QVBoxLayout *cardLay = new QVBoxLayout(card);
cardLay->setContentsMargins(24,24,24,24);
cardLay->addWidget(stack);
// --- processing ---
QWidget *wProc = new QWidget(card);
QVBoxLayout *pLay = new QVBoxLayout(wProc);
pLay->setContentsMargins(0,0,0,0);
pLay->setSpacing(12);
QLabel *spinner = new QLabel(wProc);
spinner->setAlignment(Qt::AlignCenter);
spinner->setStyleSheet("background: transparent;");
const QPixmap baseHourglass = textToPixmap("⏳", 52);
spinner->setPixmap(baseHourglass);
QLabel *title = new QLabel("카드를 뽑지말아주세요", wProc);
title->setAlignment(Qt::AlignCenter);
title->setStyleSheet("font-size: 22px; font-weight: 800; color: #111827;");
QLabel *desc = new QLabel("결제가 진행중입니다", wProc);
desc->setAlignment(Qt::AlignCenter);
desc->setStyleSheet("font-size: 14px; color: #6B7280;");
QLabel *countLbl = new QLabel("5", wProc);
countLbl->setAlignment(Qt::AlignCenter);
countLbl->setStyleSheet("font-size: 64px; font-weight: 900; color: #2563EB; margin-top: 6px;");
pLay->addStretch();
pLay->addWidget(spinner);
pLay->addWidget(title);
pLay->addWidget(desc);
pLay->addWidget(countLbl);
pLay->addStretch();
// --- complete ---
QWidget *wDone = new QWidget(card);
QVBoxLayout *dLay = new QVBoxLayout(wDone);
dLay->setContentsMargins(0,0,0,0);
dLay->setSpacing(16);
QLabel *check = new QLabel("✓", wDone);
check->setAlignment(Qt::AlignCenter);
check->setStyleSheet("font-size: 72px; font-weight: 900; color: #22C55E;");
QLabel *doneTxt = new QLabel("결제가 완료되었습니다", wDone);
doneTxt->setAlignment(Qt::AlignCenter);
doneTxt->setStyleSheet("font-size: 22px; font-weight: 800; color: #111827;");
QPushButton *btnOk = new QPushButton("확인", wDone);
btnOk->setFixedHeight(44);
btnOk->setFixedWidth(180);
btnOk->setStyleSheet(
"QPushButton{background:#2563EB;color:white;border:none;border-radius:12px;font-size:16px;font-weight:800; padding: 0 18px;}"
"QPushButton:hover{background:#1D4ED8;}"
"QPushButton:pressed{background:#1E40AF;}"
);
dLay->addStretch();
dLay->addWidget(check);
dLay->addWidget(doneTxt);
dLay->addWidget(btnOk, 0, Qt::AlignHCenter);
dLay->addStretch();
stack->addWidget(wProc);
stack->addWidget(wDone);
stack->setCurrentWidget(wProc);
// ===== (A) ⏳ 회전 타이머 (느리게) =====
auto angle = QSharedPointer<qreal>::create(0.0);
QTimer *spinTimer = new QTimer(dlg);
spinTimer->setInterval(30); // ✅ 16ms(빠름) -> 30ms(느리게)
QObject::connect(spinTimer, &QTimer::timeout, dlg, [=]() mutable {
*angle += 2.0; // ✅ 4.0(빠름) -> 2.0(느리게)
if (*angle >= 360.0) *angle -= 360.0;
spinner->setPixmap(rotatedPixmap(baseHourglass, *angle));
});
spinTimer->start();
// ===== (B) 5초 카운트다운 =====
auto count = QSharedPointer<int>::create(5);
QTimer *t = new QTimer(dlg);
t->setInterval(1000);
QObject::connect(t, &QTimer::timeout, dlg, [=]() mutable {
(*count)--;
if (*count >= 0) countLbl->setText(QString::number(*count));
if (*count <= 0) {
t->stop();
spinTimer->stop();
stack->setCurrentWidget(wDone);
}
});
QObject::connect(btnOk, &QPushButton::clicked, dlg, [this, dlg]() {
dlg->accept();
emit goTotalPayClicked();
});
t->start();
dlg->exec();
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef PAGEPAY_CARD_H
#define PAGEPAY_CARD_H
#include <QWidget>
namespace Ui {
class pagepay_card;
}
class QShowEvent;
class pagepay_card : public QWidget
{
Q_OBJECT
public:
explicit pagepay_card(QWidget *parent = nullptr);
~pagepay_card();
signals:
void goTotalPayClicked();
protected:
void showEvent(QShowEvent *e) override;
private:
void showPaymentModal();
private:
Ui::pagepay_card *ui;
};
#endif // PAGEPAY_CARD_H
@@ -1,18 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>pPageWelcome</class>
<widget class="QWidget" name="pPageWelcome">
<class>pagepay_card</class>
<widget class="QWidget" name="pagepay_card">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(36, 31, 49);</string>
</property>
</widget>
<resources/>
<connections/>
+14
View File
@@ -0,0 +1,14 @@
#include "pagepayment.h"
#include "ui_pagepayment.h"
pagepayment::pagepayment(QWidget *parent)
: QWidget(parent)
, ui(new Ui::pagepayment)
{
ui->setupUi(this);
}
pagepayment::~pagepayment()
{
delete ui;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef PAGEPAYMENT_H
#define PAGEPAYMENT_H
#include <QWidget>
namespace Ui {
class pagepayment;
}
class pagepayment : public QWidget
{
Q_OBJECT
public:
explicit pagepayment(QWidget *parent = nullptr);
~pagepayment();
private:
Ui::pagepayment *ui;
};
#endif // PAGEPAYMENT_H
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PagePayment</class>
<widget class="QWidget" name="PagePayment">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>230</x>
<y>140</y>
<width>160</width>
<height>80</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</widget>
<resources/>
<connections/>
</ui>
+211
View File
@@ -0,0 +1,211 @@
#include "pagetotalpay.h"
#include "ui_pagetotalpay.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QVariantAnimation>
#include <QVBoxLayout>
#include <QLabel>
#include <QEvent>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QTimer>
#include <QtMath>
#include <QPainter>
#include <QPen>
#include <QEasingCurve>
PageTotalPay::PageTotalPay(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageTotalPay)
{
ui->setupUi(this);
if (objectName().isEmpty())
setObjectName("PageTotalPay");
// ✅ 여기 추가
setStyleSheet("QWidget#PageTotalPay { background-color: rgb(34,197,94); }");
// ✅ Scene/View
m_scene = new QGraphicsScene(this);
m_view = new QGraphicsView(this);
m_view->setScene(m_scene);
m_view->setFrameShape(QFrame::NoFrame);
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_view->setStyleSheet("background: transparent;");
m_view->setAlignment(Qt::AlignCenter);
// 클릭 이벤트를 view/viewport에서도 잡기
m_view->viewport()->installEventFilter(this);
m_view->installEventFilter(this);
// ✅ content 위젯(아이콘+텍스트)
m_content = new QWidget();
m_content->setAttribute(Qt::WA_TranslucentBackground);
m_content->installEventFilter(this);
auto *lay = new QVBoxLayout(m_content);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(28);
lay->setAlignment(Qt::AlignCenter);
QLabel *lblIcon = new QLabel(m_content);
lblIcon->setFixedSize(140, 140);
lblIcon->setAlignment(Qt::AlignCenter);
lblIcon->setPixmap(makeCheckIcon(128));
lblIcon->setStyleSheet("background: transparent;");
QLabel *lblTitle = new QLabel("구매해주셔서 감사합니다", m_content);
lblTitle->setAlignment(Qt::AlignCenter);
lblTitle->setStyleSheet(
"background: transparent;"
"color: rgb(0,0,0);"
"font-weight: 900;"
"font-size: 44px;"
);
QLabel *lblSub = new QLabel("화면을 다시 눌러 시작하기", m_content);
lblSub->setAlignment(Qt::AlignCenter);
lblSub->setStyleSheet(
"background: transparent;"
"color: rgb(0,0,0);"
"font-weight: 700;"
"font-size: 22px;"
);
lay->addWidget(lblIcon, 0, Qt::AlignCenter);
lay->addWidget(lblTitle, 0, Qt::AlignCenter);
lay->addWidget(lblSub, 0, Qt::AlignCenter);
// ✅ proxy로 올리기(스케일 애니메이션 대상)
m_proxy = m_scene->addWidget(m_content);
m_proxy->setTransformOriginPoint(m_proxy->boundingRect().center());
// ✅ 레이아웃 붙이기 (중요: 기존 레이아웃 있으면 거기에 붙임)
if (this->layout()) {
this->layout()->setContentsMargins(0,0,0,0);
this->layout()->addWidget(m_view);
} else {
auto *root = new QVBoxLayout(this);
root->setContentsMargins(0,0,0,0);
root->addWidget(m_view);
}
// ✅❗️여기가 너 코드에서 크래시 난 부분: m_pulseAnim을 반드시 생성해야 함
m_pulseAnim = new QVariantAnimation(this);
// ✅ 코사인 루프 (자연스럽게 1.0 -> 1.15 -> 1.0, 점프 없음)
m_pulseAnim->setStartValue(0.0);
m_pulseAnim->setEndValue(2.0 * M_PI);
m_pulseAnim->setDuration(2000);
m_pulseAnim->setLoopCount(-1);
m_pulseAnim->setEasingCurve(QEasingCurve::Linear);
connect(m_pulseAnim, &QVariantAnimation::valueChanged, this, [this](const QVariant &v){
const qreal t = v.toReal();
const qreal amp = 0.15;
const qreal scale = 1.0 + amp * 0.5 * (1.0 - qCos(t));
if (m_proxy) m_proxy->setScale(scale);
});
m_pulseAnim->start();
// ✅ viewport size가 0일 수 있으니 show 이후에 중앙 정렬
QTimer::singleShot(0, this, [this](){ centerProxy(); });
}
PageTotalPay::~PageTotalPay()
{
delete ui;
}
QPixmap PageTotalPay::makeCheckIcon(int sizePx) const
{
const int pad = 6;
QPixmap pm(sizePx + pad*2, sizePx + pad*2);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing, true);
// ✅ 초록색 (Tailwind green-500 비슷)
QColor green(34, 197, 94);
// circle stroke
QPen pen(green); // ✅ 여기!
pen.setWidthF(sizePx * 0.07);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
QRectF rc(pad, pad, sizePx, sizePx);
p.drawEllipse(rc);
// check
p.setPen(Qt::NoPen);
p.setBrush(green);
p.drawEllipse(rc);
// 체크: 흰색
QPen pen2(Qt::white);
pen2.setWidthF(sizePx * 0.09);
pen2.setCapStyle(Qt::RoundCap);
pen2.setJoinStyle(Qt::RoundJoin);
p.setPen(pen2);
p.setBrush(Qt::NoBrush);
QPointF a(rc.left() + sizePx*0.28, rc.top() + sizePx*0.55);
QPointF b(rc.left() + sizePx*0.46, rc.top() + sizePx*0.70);
QPointF c(rc.left() + sizePx*0.74, rc.top() + sizePx*0.36);
p.drawLine(a, b);
p.drawLine(b, c);
return pm;
}
void PageTotalPay::centerProxy()
{
if (!m_view || !m_scene || !m_proxy) return;
const QSize vsz = m_view->viewport()->size();
if (vsz.width() <= 0 || vsz.height() <= 0) return; // ✅ 0이면 아직 표시 전
m_scene->setSceneRect(0, 0, vsz.width(), vsz.height());
const QPointF origin = m_proxy->boundingRect().center();
m_proxy->setTransformOriginPoint(origin);
const QPointF sceneCenter = m_scene->sceneRect().center();
m_proxy->setPos(sceneCenter - origin);
}
void PageTotalPay::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
centerProxy();
}
bool PageTotalPay::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonPress) {
emit backToStartClicked();
return true;
}
return QWidget::eventFilter(obj, event);
}
void PageTotalPay::mousePressEvent(QMouseEvent *e)
{
emit backToStartClicked();
QWidget::mousePressEvent(e);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef PAGETOTALPAY_H
#define PAGETOTALPAY_H
#include <QWidget>
#include <QPixmap>
class QGraphicsView;
class QGraphicsScene;
class QGraphicsProxyWidget;
class QVariantAnimation;
namespace Ui {
class PageTotalPay;
}
class PageTotalPay : public QWidget
{
Q_OBJECT
public:
explicit PageTotalPay(QWidget *parent = nullptr);
~PageTotalPay();
signals:
void backToStartClicked(); // ✅ 어디든 클릭하면 Welcome으로 돌아가기
protected:
void resizeEvent(QResizeEvent *e) override;
bool eventFilter(QObject *obj, QEvent *event) override;
void mousePressEvent(QMouseEvent *e) override;
private:
QPixmap makeCheckIcon(int sizePx) const;
void centerProxy();
private:
Ui::PageTotalPay *ui = nullptr;
QGraphicsView *m_view = nullptr;
QGraphicsScene *m_scene = nullptr;
QGraphicsProxyWidget *m_proxy = nullptr;
QWidget *m_content = nullptr;
QVariantAnimation *m_pulseAnim = nullptr;
};
#endif // PAGETOTALPAY_H
+73
View File
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageTotalPay</class>
<widget class="QWidget" name="PageTotalPay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(51, 209, 122);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="lblTitle">
<property name="geometry">
<rect>
<x>260</x>
<y>200</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblSubTitle">
<property name="geometry">
<rect>
<x>270</x>
<y>230</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="lblIcon">
<property name="geometry">
<rect>
<x>250</x>
<y>140</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+223
View File
@@ -0,0 +1,223 @@
#include "pagewelcome.h"
#include "ui_pagewelcome.h"
#include <QPushButton>
#include <QPropertyAnimation>
#include <QEasingCurve>
#include <QTimer>
#include <QPixmap>
#include <QDebug>
#include <QShowEvent>
#include <QHideEvent>
#include <QResizeEvent>
PageWelcome::PageWelcome(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PageWelcome)
{
ui->setupUi(this);
// ✅ 버튼 연결
connect(ui->start_button, &QPushButton::clicked,
this, &PageWelcome::onStartClicked);
// ✅ 이미지 로드/세팅(한 번만)
loadPixmapsOnce();
// ✅ UI 레이아웃 적용된 다음 기본 Y 저장 + 중앙 정렬
QTimer::singleShot(0, this, [this]() {
if (ui->start_image && m_baseYStartImage < 0)
m_baseYStartImage = ui->start_image->y();
if (ui->qtcart_text && m_baseYTitle < 0)
m_baseYTitle = ui->qtcart_text->y();
if (ui->start_text && m_baseYStartText < 0)
m_baseYStartText = ui->start_text->y();
if (ui->start_button && m_baseYStartBtn < 0)
m_baseYStartBtn = ui->start_button->y();
resetWelcome();
});
}
PageWelcome::~PageWelcome()
{
if (m_anim) {
m_anim->stop();
delete m_anim;
m_anim = nullptr;
}
delete ui;
}
void PageWelcome::loadPixmapsOnce()
{
if (m_pixLoaded) return;
m_pixLoaded = true;
QPixmap cartPx(":/etc/cart.png");
QPixmap windPx(":/etc/wind.png");
if (ui->cart_image) {
ui->cart_image->setPixmap(cartPx);
ui->cart_image->setScaledContents(true);
ui->cart_image->setAttribute(Qt::WA_TranslucentBackground);
ui->cart_image->show();
}
if (ui->wind_image) {
ui->wind_image->setPixmap(windPx);
ui->wind_image->setScaledContents(true);
ui->wind_image->setAttribute(Qt::WA_TranslucentBackground);
ui->wind_image->show();
}
if (ui->start_image) ui->start_image->show();
}
void PageWelcome::showEvent(QShowEvent *e)
{
QWidget::showEvent(e);
QTimer::singleShot(0, this, [this]() {
resetWelcome();
});
}
void PageWelcome::hideEvent(QHideEvent *e)
{
QWidget::hideEvent(e);
// ✅ 페이지가 숨겨질 때 애니메이션 남아있으면 정리(안전)
if (m_anim) {
m_anim->stop();
m_anim->deleteLater();
m_anim = nullptr;
}
}
void PageWelcome::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
// ✅ 떠나지 않은 상태면 리사이즈 때도 가운데 유지
if (!m_leaving) {
centerStartImage(); // ✅ 기존 유지
centerWelcomeWidgets(); // ✅ 추가
}
}
void PageWelcome::resetWelcome()
{
// ✅ 상태 초기화
m_leaving = false;
// ✅ 버튼 다시 누르게
if (ui->start_button) {
ui->start_button->setEnabled(true);
}
// ✅ 애니메이션 남아있으면 정리
if (m_anim) {
m_anim->stop();
m_anim->deleteLater();
m_anim = nullptr;
}
// ✅ 위젯 다시 보이기
if (ui->start_image) ui->start_image->show();
if (ui->cart_image) ui->cart_image->show();
if (ui->wind_image) ui->wind_image->show();
// 레이아웃 반영 후 이동
QTimer::singleShot(0, this, [this]() {
if (ui->start_image && m_baseYStartImage < 0) m_baseYStartImage = ui->start_image->y();
if (ui->qtcart_text && m_baseYTitle < 0) m_baseYTitle = ui->qtcart_text->y();
if (ui->start_text && m_baseYStartText < 0) m_baseYStartText = ui->start_text->y();
if (ui->start_button&& m_baseYStartBtn < 0) m_baseYStartBtn = ui->start_button->y();
centerStartImage(); // ✅ 기존 유지
centerWelcomeWidgets(); // ✅ 추가
});
}
// ✅ 기존 함수는 그대로: start_image만 가운데
void PageWelcome::centerStartImage()
{
if (!ui->start_image) return;
QWidget *parentW = ui->start_image->parentWidget();
if (!parentW) parentW = this;
const int pw = parentW->width();
const int w = ui->start_image->width();
const int x = (pw - w) / 2;
int y = (m_baseYStartImage >= 0) ? m_baseYStartImage : ui->start_image->y();
ui->start_image->move(x, y);
}
// ✅ 추가: qcart_text, start_text, start_button도 “X만” 중앙정렬
void PageWelcome::centerWelcomeWidgets()
{
QWidget *parentW = this; // 보통 PageWelcome 전체 기준이 가장 깔끔함
const int pw = parentW->width();
auto centerXKeepY = [&](QWidget *w, int baseY){
if (!w) return;
const int x = (pw - w->width()) / 2;
const int y = (baseY >= 0) ? baseY : w->y();
w->move(x, y);
};
centerXKeepY(ui->qtcart_text, m_baseYTitle);
centerXKeepY(ui->start_text, m_baseYStartText);
centerXKeepY(ui->start_button, m_baseYStartBtn);
}
void PageWelcome::onStartClicked()
{
if (m_leaving) return;
m_leaving = true;
if (ui->start_button)
ui->start_button->setEnabled(false);
if (!ui->start_image) {
emit startRequested();
emit startClicked();
return;
}
QWidget *parentW = ui->start_image->parentWidget();
if (!parentW) parentW = this;
const QPoint startPos = ui->start_image->pos();
const int y = startPos.y();
// ✅ 오른쪽 화면 밖으로 슈웅~
const int endX = parentW->width() + ui->start_image->width() + 20;
const QPoint endPos(endX, y);
// ✅ 멤버로 애니메이션 보관(돌아올 때 stop/정리 가능)
m_anim = new QPropertyAnimation(ui->start_image, "pos", this);
m_anim->setDuration(700);
m_anim->setEasingCurve(QEasingCurve::InOutCubic);
m_anim->setStartValue(startPos);
m_anim->setEndValue(endPos);
connect(m_anim, &QPropertyAnimation::finished, this, [this]() {
if (m_anim) {
m_anim->deleteLater();
m_anim = nullptr;
}
emit startRequested();
emit startClicked();
});
m_anim->start();
}
+56
View File
@@ -0,0 +1,56 @@
#ifndef PAGEWELCOME_H
#define PAGEWELCOME_H
#include <QWidget>
class QPropertyAnimation;
class QShowEvent;
class QHideEvent;
class QResizeEvent;
namespace Ui {
class PageWelcome;
}
class PageWelcome : public QWidget
{
Q_OBJECT
public:
explicit PageWelcome(QWidget *parent = nullptr);
~PageWelcome();
signals:
void startClicked();
void startRequested();
protected:
void showEvent(QShowEvent *e) override;
void hideEvent(QHideEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private slots:
void onStartClicked();
private:
void loadPixmapsOnce();
void centerStartImage(); // ✅ 기존: start_image만 가운데
void centerWelcomeWidgets(); // ✅ 추가: 텍스트/버튼도 가운데
void resetWelcome();
private:
Ui::PageWelcome *ui;
bool m_leaving = false;
bool m_pixLoaded = false;
// ✅ 각 위젯의 "기본 Y"를 기억해두고 X만 중앙정렬
int m_baseYStartImage = -1;
int m_baseYTitle = -1; // qcart_text
int m_baseYStartText = -1; // start_text
int m_baseYStartBtn = -1; // start_button
QPropertyAnimation *m_anim = nullptr;
};
#endif // PAGEWELCOME_H
+163
View File
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PageWelcome</class>
<widget class="QWidget" name="PageWelcome">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>11</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<bold>false</bold>
</font>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QLabel" name="start_text">
<property name="geometry">
<rect>
<x>0</x>
<y>340</y>
<width>781</width>
<height>22</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);
</string>
</property>
<property name="text">
<string>시작하기 버튼을 눌러 쇼핑을 시작하세요</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QWidget" name="start_image" native="true">
<property name="geometry">
<rect>
<x>300</x>
<y>80</y>
<width>211</width>
<height>171</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<widget class="QLabel" name="cart_image">
<property name="geometry">
<rect>
<x>90</x>
<y>30</y>
<width>121</width>
<height>101</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="wind_image">
<property name="geometry">
<rect>
<x>0</x>
<y>70</y>
<width>61</width>
<height>71</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QLabel" name="qtcart_text">
<property name="geometry">
<rect>
<x>5</x>
<y>250</y>
<width>771</width>
<height>77</height>
</rect>
</property>
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true">font: 700 40pt &quot;Ria Sans&quot;;
color: rgb(87, 227, 137);
background-color: rgb(255, 255, 255);
</string>
</property>
<property name="text">
<string>QT CART</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="start_button">
<property name="geometry">
<rect>
<x>260</x>
<y>390</y>
<width>261</width>
<height>30</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">font: 700 11pt &quot;Ria Sans&quot;;
QPushButton {
background-color: rgb(224, 27, 36); /* ✅ 기본 상태: 더 진한 빨강 */
font: 300 14pt &quot;Umpush&quot;;
color: rgb(255, 255, 255);
border: none;
border-radius: 14px; /* ✅ 동글동글 */
padding: 10px 18px; /* 버튼 크기/여백 */
}
</string>
</property>
<property name="text">
<string>시작하기</string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+62
View File
@@ -0,0 +1,62 @@
from fastapi import APIRouter
from pydantic import BaseModel
from datetime import datetime
router = APIRouter(
prefix="/dashboard",
tags=["Dashboard"]
)
bot_state = {
"status": "OFFLINE",
"last_updated": datetime.now(),
"battery": 0.0,
"speed": 0.0
}
BOT_TIMEOUT_SEC = 5 # 5초간 보고 없으면 오프라인
class BotReport(BaseModel):
status: str
battery: float = 0.0
speed: float = 0.0
class BotConnectionInfo(BaseModel):
bot_status: str
battery: float
speed: float
last_update: str
# 로봇 -> 서버
@router.post("/bot/report")
def report_bot_status(report: BotReport):
global bot_state
bot_state["status"] = report.status
bot_state["last_updated"] = datetime.now()
bot_state["battery"] = report.battery
bot_state["speed"] = report.speed
return {"msg": "status report received"}
# 서버 -> 프론트엔드
@router.get("/bot/check", response_model=BotConnectionInfo)
def check_bot_connection():
global bot_state
elapsed = (datetime.now() - bot_state["last_updated"]).total_seconds()
current_status = bot_state["status"]
if elapsed > BOT_TIMEOUT_SEC:
current_status = "OFFLINE"
bot_state["speed"] = 0.0
return {
"bot_status": current_status,
"battery": bot_state["battery"],
"speed": bot_state["speed"],
"last_update": bot_state["last_updated"].strftime("%H:%M:%S")
}
# 서버 연결 확인
@router.get("/health")
def health_check():
return {"status": "ok", "time": datetime.now().isoformat()}
+20
View File
@@ -0,0 +1,20 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./shopping_cart.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
+74
View File
@@ -0,0 +1,74 @@
import lgpio
import time
class HX711:
def __init__(self, dout, pd_sck, gain=128):
self.DOUT = dout
self.PD_SCK = pd_sck
self.chip = lgpio.gpiochip_open(0)
lgpio.gpio_claim_input(self.chip, self.DOUT)
lgpio.gpio_claim_output(self.chip, self.PD_SCK)
self.GAIN = 1 if gain == 128 else 3 if gain == 64 else 2
self.OFFSET = 0
self.REFERENCE_UNIT = 1
self.set_gain(gain)
def is_ready(self):
return lgpio.gpio_read(self.chip, self.DOUT) == 0
def set_gain(self, gain):
if gain == 128:
self.GAIN = 1
elif gain == 64:
self.GAIN = 3
elif gain == 32:
self.GAIN = 2
self.read_raw()
def read_raw(self):
while not self.is_ready():
pass
data = 0
for _ in range(24):
lgpio.gpio_write(self.chip, self.PD_SCK, 1)
data = (data << 1) | lgpio.gpio_read(self.chip, self.DOUT)
lgpio.gpio_write(self.chip, self.PD_SCK, 0)
for _ in range(self.GAIN):
lgpio.gpio_write(self.chip, self.PD_SCK, 1)
lgpio.gpio_write(self.chip, self.PD_SCK, 0)
if data & 0x800000:
data -= 0x1000000
return data
def set_offset(self, offset):
self.OFFSET = offset
def set_reference_unit(self, value):
self.REFERENCE_UNIT = value
def get_value(self, times=5):
return sum(self.read_raw() for _ in range(times)) / times
def get_weight(self, times=5):
value = self.get_value(times) - self.OFFSET
return value / self.REFERENCE_UNIT
def power_down(self):
lgpio.gpio_write(self.chip, self.PD_SCK, 0)
lgpio.gpio_write(self.chip, self.PD_SCK, 1)
time.sleep(0.0001)
def power_up(self):
lgpio.gpio_write(self.chip, self.PD_SCK, 0)
time.sleep(0.0001)
+4
View File
@@ -0,0 +1,4 @@
{
"offset": 449326.96,
"scale": 311.0220000000001
}
+38
View File
@@ -0,0 +1,38 @@
from hx711 import HX711
import time, json
DT_PIN = 5
SCK_PIN = 6
hx = HX711(DT_PIN, SCK_PIN)
print("HX711 보정 시작")
time.sleep(2)
# 1) 빈 상태
print("1️⃣ 아무것도 안 올리고 기다리세요...")
N = 30
empty = sum(hx.get_value(5) for _ in range(N)) / N
print("OFFSET =", empty)
known = float(input("2️⃣ 기준 무게(g): "))
print("3️⃣ 기준 무게 올리고 기다리세요...")
raw = sum(hx.get_value(5) for _ in range(N)) / N
scale = (raw - empty) / known
print("\n보정 완료")
print("offset =", empty)
print("scale =", scale)
calib = {
"offset": empty,
"scale": scale
}
with open("hx711_calib.json", "w") as f:
json.dump(calib, f, indent=4)
print("✔ hx711_calib.json 저장 완료")
+23
View File
@@ -0,0 +1,23 @@
from hx711 import HX711
import json, time
DT_PIN = 5
SCK_PIN = 6
with open("hx711_calib.json") as f:
calib = json.load(f)
OFFSET = calib["offset"]
SCALE = calib["scale"]
hx = HX711(DT_PIN, SCK_PIN)
hx.set_offset(OFFSET)
hx.set_reference_unit(SCALE)
print(" 무게 측정 시작")
while True:
weight = hx.get_weight(10)
print(f"무게: {weight:.1f} g")
time.sleep(0.5)
+200
View File
@@ -0,0 +1,200 @@
import uvicorn
from fastapi import FastAPI, Depends, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
from typing import List
from datetime import datetime
import models, database
import dashboard
import logging
from read_weight import read_cart_weight
CART_ITEMS = []
EXPECTED_WEIGHT = 0.0
app = FastAPI()
models.Base.metadata.create_all(bind=database.engine)
# 대시보드 라우터 등록
app.include_router(dashboard.router)
app.mount("/static", StaticFiles(directory="static"), name="static")
def get_db():
db = database.SessionLocal()
try: yield db
finally: db.close()
# 무게 검증 로직
def is_movable(expected, real):
if expected <= 0: return True
diff_ratio = abs(real - expected) / expected
return diff_ratio <= 0.2
# 페이지 라우팅
@app.get("/dashboard")
async def dashboard_view(): return FileResponse("templates/dashboard.html")
@app.get("/inventory")
async def inventory_view(): return FileResponse("templates/inventory.html")
# 카트 무게 기준값 설정
@app.post("/cart/tare")
def tare_cart():
global EXPECTED_WEIGHT
EXPECTED_WEIGHT = read_cart_weight()
return {"expected_weight": EXPECTED_WEIGHT}
@app.get("/items", response_model=List[models.ItemSchema])
def read_all_items(db: Session = Depends(get_db)):
return db.query(models.Item).all()
# 상품 추가
@app.post("/cart/add/{item_id}")
def add_item(item_id: int, db: Session = Depends(get_db)):
global EXPECTED_WEIGHT, CART_ITEMS
item = db.query(models.Item).filter(models.Item.id == item_id).first()
if not item:
raise HTTPException(404, "Item not found")
CART_ITEMS.append({
"id": item.id,
"name": item.name,
"price": item.price,
"weight": item.weight
})
EXPECTED_WEIGHT += item.weight
real = read_cart_weight()
movable = is_movable(EXPECTED_WEIGHT, real)
return {
"action": "add",
"id": item.id,
"item": item.name,
"price": item.price,
"expected_weight": EXPECTED_WEIGHT,
"real_weight": real,
"diff": abs(real - EXPECTED_WEIGHT),
"movable": movable
}
# 상품 제거
@app.post("/cart/remove/{item_id}")
def remove_item(item_id: int):
global EXPECTED_WEIGHT, CART_ITEMS
for i, item in enumerate(CART_ITEMS):
if item["id"] == item_id:
removed = CART_ITEMS.pop(i)
EXPECTED_WEIGHT -= removed["weight"]
break
else:
raise HTTPException(404, "Item not in cart")
EXPECTED_WEIGHT = max(EXPECTED_WEIGHT, 0)
real = read_cart_weight()
movable = is_movable(EXPECTED_WEIGHT, real)
return {
"action": "remove",
"id": removed["id"],
"item": removed["name"],
"price": removed["price"],
"expected_weight": EXPECTED_WEIGHT,
"real_weight": real,
"movable": movable
}
# 장바구니 조회
@app.get("/cart")
def get_cart():
total_price = sum(item["price"] for item in CART_ITEMS)
return {
"items": CART_ITEMS,
"total_count": len(CART_ITEMS),
"total_price": total_price,
"expected_weight": EXPECTED_WEIGHT
}
# 무게 체크
@app.get("/cart/check_weight")
def check_cart():
real = read_cart_weight()
movable = is_movable(EXPECTED_WEIGHT, real)
return {
"expected_weight": EXPECTED_WEIGHT,
"real_weight": real,
"diff": abs(real - EXPECTED_WEIGHT),
"movable": movable
}
# 대시보드 상태 조회
@app.get("/cart/status")
def get_cart_status_for_dashboard():
global EXPECTED_WEIGHT, CART_ITEMS
real = read_cart_weight()
movable = is_movable(EXPECTED_WEIGHT, real)
system_status = "NORMAL" if movable else "WARNING_WEIGHT_MISMATCH"
return {
"real_weight": real,
"expected_weight": EXPECTED_WEIGHT,
"cart_items": CART_ITEMS,
"total_price": sum(item["price"] for item in CART_ITEMS),
"total_count": len(CART_ITEMS),
"system_status": system_status,
"movable": movable
}
# 결제
@app.post("/cart/pay")
def pay_cart(db: Session = Depends(get_db)):
global CART_ITEMS, EXPECTED_WEIGHT
if not CART_ITEMS:
raise HTTPException(status_code=400, detail="장바구니가 비어있습니다.")
total_price = sum(item["price"] for item in CART_ITEMS)
new_pay = models.Pay(
total_price=total_price,
created_at=datetime.now()
)
db.add(new_pay)
db.commit()
reset_cart()
return {"msg": "결제 완료", "amount": total_price}
# 결제 내역 조회
@app.get("/payments", response_model=List[models.PaySchema])
def get_payment_history(db: Session = Depends(get_db)):
return db.query(models.Pay).order_by(models.Pay.created_at.desc()).all()
# 카트 초기화
@app.post("/cart/reset")
def reset_cart():
global EXPECTED_WEIGHT, CART_ITEMS
EXPECTED_WEIGHT = 0.0
CART_ITEMS.clear()
return {"ok": True}
# 로그 필터
class EndpointFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
msg = record.getMessage()
return all(x not in msg for x in ["/bot/check", "/bot/report", "/cart/status"])
logging.getLogger("uvicorn.access").addFilter(EndpointFilter())
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
+74
View File
@@ -0,0 +1,74 @@
from sqlalchemy import Column, Integer, String, Float, DateTime
from database import Base
from pydantic import BaseModel
from datetime import datetime
# 상품 테이블
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
price = Column(Float)
stock = Column(Integer)
weight = Column(Float) # 무게 로직용
expiry_date = Column(String) # 대시보드 유통기한 관리용
# 결제 내역 테이블
class Pay(Base):
__tablename__ = "payments"
id = Column(Integer, primary_key=True, index=True)
total_price = Column(Float)
created_at = Column(DateTime, default=datetime.now)
# 상품 생성 요청
class ItemCreate(BaseModel):
name: str
price: float
stock: int
weight: float
expiry_date: str
# 상품 조회 응답
class ItemSchema(BaseModel):
id: int
name: str
price: float
stock: int
weight: float
expiry_date: str
class Config:
from_attributes = True
# 결제 내역 응답 (대시보드 차트용)
class PaySchema(BaseModel):
id: int
total_price: float
created_at: datetime
class Config:
from_attributes = True
class CartScanResponse(BaseModel):
item: ItemSchema
cart_weight: float
expected_weight: float
weight_ok: bool
diff: float
allowed: float
# 무게 검증 응답
class CartScanResponse(BaseModel):
item: ItemSchema # 스캔된 상품 정보
cart_weight: float # 현재 실제 무게
expected_weight: float # 예상 무게
weight_ok: bool # 무게 일치 여부
diff: float # 오차
allowed: float # 허용 오차 범위
# 무게 포함 아이템 정보
class ItemWithWeight(ItemSchema):
cart_weight: float
+33
View File
@@ -0,0 +1,33 @@
from hx711 import HX711
import json, time, threading
# --- 보정값 로드 ---
with open("hx711_calib.json", "r") as f:
calib = json.load(f)
OFFSET = calib["offset"]
SCALE = calib["scale"]
hx = HX711(5, 6)
hx_lock = threading.Lock()
def read_cart_weight():
"""현재 카트 무게(g)를 반환"""
with hx_lock:
# 여러 번 평균 내서 노이즈 줄이기
samples = 10
total = 0
for _ in range(samples):
total += hx.read_raw()
time.sleep(0.02)
raw = total / samples
weight = (raw - OFFSET) / SCALE
# 음수/미세값 보호
if weight < 0:
weight = 0
return round(weight, 2)
+4
View File
@@ -0,0 +1,4 @@
fastapi
uvicorn[standard]
SQLAlchemy
pydantic
+310
View File
@@ -0,0 +1,310 @@
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from geometry_msgs.msg import TwistStamped, PoseWithCovarianceStamped
from nav2_msgs.action import NavigateToPose
from sensor_msgs.msg import BatteryState
from nav_msgs.msg import Odometry
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy, HistoryPolicy
import socket
import time
import math
import requests
import threading
from collections import deque
UDP_PORT_UWB = 44444
UDP_PORT_QT = 55555
BUFFER_SIZE = 1024
SERVER_IP = "192.168.123.43"
API_REPORT_URL = f"http://{SERVER_IP}:8000/dashboard/bot/report"
API_WEIGHT_URL = f"http://{SERVER_IP}:8000/cart/check_weight"
TAG_HEIGHT = 1.85
ANCHOR_Z = 2.37
H_DIFF = abs(ANCHOR_Z - TAG_HEIGHT)
# 고정 오프셋 보정값
FIXED_OFFSETS = {0: -7.00, 1: -7.00, 2: -7.50}
# 앵커 절대 좌표
ANCHORS_POS = {
0: {'x': 0.00, 'y': 0.00},
1: {'x': 6.00, 'y': 0.00},
2: {'x': 3.00, 'y': 15.00}
}
MIN_UPDATE_DIST = 1.2
NAV_UPDATE_INTERVAL = 2.0
class IntegratedRosController(Node):
def __init__(self):
super().__init__('integrated_ros_controller_node')
amcl_qos = QoSProfile(
depth=1,
reliability=ReliabilityPolicy.RELIABLE,
durability=DurabilityPolicy.TRANSIENT_LOCAL,
history=HistoryPolicy.KEEP_LAST
)
self.cmd_vel_pub = self.create_publisher(TwistStamped, '/cmd_vel', 10)
self.battery_sub = self.create_subscription(BatteryState, '/battery_state', self.battery_callback, 10)
self.odom_sub = self.create_subscription(Odometry, '/odom', self.odom_callback, 10)
self.amcl_sub = self.create_subscription(
PoseWithCovarianceStamped,
'/amcl_pose',
self.amcl_callback,
amcl_qos
)
self.nav_to_pose_client = ActionClient(self, NavigateToPose, 'navigate_to_pose')
self.sock_uwb = self.create_udp_socket(UDP_PORT_UWB)
self.sock_qt = self.create_udp_socket(UDP_PORT_QT)
self.current_mode = 0
self.battery_level = 0.0
self.current_speed = 0.0
self.qt_addr = None
self.robot_x = 0.0
self.robot_y = 0.0
self.human_x = 0.0
self.human_y = 0.0
self.x_his = deque(maxlen=15)
self.y_his = deque(maxlen=5)
self.uwb_dists = [0.0, 0.0, 0.0]
self.uwb_last_update = [0.0, 0.0, 0.0]
self.nav_goal_handle = None
self.last_goal_time = 0.0
self.last_target_pos = [0.0, 0.0]
self.is_weight_ok = True
self.last_weight_check_time = 0.0
self.weight_check_interval = 3.0
self.get_logger().info("Waiting for Nav2 Server...")
if not self.nav_to_pose_client.wait_for_server(timeout_sec=5.0):
self.get_logger().warn("Nav2 Server not ready yet.")
self.create_timer(0.05, self.control_loop)
self.create_timer(1.0, self.send_bot_status)
self.get_logger().info("ROS Controller Started")
def create_udp_socket(self, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', port))
sock.setblocking(False)
return sock
# 무게 체크
def trigger_async_weight_check(self):
def _check():
try:
response = requests.get(API_WEIGHT_URL, timeout=0.5)
if response.status_code == 200:
data = response.json()
movable = data.get("movable", True)
self.is_weight_ok = movable
if not movable:
self.send_udp_to_qt("ERROR:WEIGHT_MISMATCH")
except: pass
threading.Thread(target=_check, daemon=True).start()
def send_udp_to_qt(self, message):
if self.qt_addr:
try: self.sock_qt.sendto(message.encode(), self.qt_addr)
except: pass
def battery_callback(self, msg):
self.battery_level = int(msg.percentage) if msg.percentage >= 0 else 0.0
def odom_callback(self, msg):
self.current_speed = msg.twist.twist.linear.x
def amcl_callback(self, msg):
self.robot_x = msg.pose.pose.position.x
self.robot_y = msg.pose.pose.position.y
# 로봇 상태 전송
def send_bot_status(self):
def _post():
try:
payload = {
"status": "ONLINE",
"mode": self.current_mode,
"battery": round(self.battery_level, 1),
"speed": round(self.current_speed, 2),
"x": self.robot_x,
"y": self.robot_y,
"human_x": self.human_x,
"human_y": self.human_y
}
requests.post(API_REPORT_URL, json=payload, timeout=2.0)
except: pass
# 스레드 비동기 전송 후 종료
threading.Thread(target=_post, daemon=True).start()
# 삼변측량 함수
def calculate_trilateration(self, distances):
try:
x1, y1, r1 = ANCHORS_POS[0]['x'], ANCHORS_POS[0]['y'], distances[0]
x2, y2, r2 = ANCHORS_POS[1]['x'], ANCHORS_POS[1]['y'], distances[1]
x3, y3, r3 = ANCHORS_POS[2]['x'], ANCHORS_POS[2]['y'], distances[2]
A, B = 2*(x2-x1), 2*(y2-y1)
C = r1**2 - r2**2 - x1**2 + x2**2 - y1**2 + y2**2
D, E = 2*(x3-x2), 2*(y3-y2)
F = r2**2 - r3**2 - x2**2 + x3**2 - y2**2 + y3**2
den = (A * E) - (B * D)
if abs(den) < 0.0001: return None
return ((C * E) - (B * F)) / den, ((A * F) - (C * D)) / den
except: return None
def transform_to_map(self, ux, uy):
mx = 3.25 + (ux * -0.9725)
my = -7.35 + (uy * 0.5966)
return mx, my
# UWB 데이터 수신
def receive_uwb_data(self):
try:
for _ in range(10):
data, _ = self.sock_uwb.recvfrom(BUFFER_SIZE)
msg = data.decode('utf-8').strip()
idx = -1
if msg.startswith("A1:"): idx = 0
elif msg.startswith("A2:"): idx = 1
elif msg.startswith("A3:"): idx = 2
if idx != -1:
raw_val = float(msg.split(":")[1])
corrected_dist = raw_val - FIXED_OFFSETS[idx]
h_dist = math.sqrt(corrected_dist**2 - H_DIFF**2) if corrected_dist > H_DIFF else 0.1
self.uwb_dists[idx] = h_dist
self.uwb_last_update[idx] = time.time()
except BlockingIOError: pass
except: pass
def receive_qt_command(self):
try:
while True:
data, addr = self.sock_qt.recvfrom(BUFFER_SIZE)
self.qt_addr = addr
text = data.decode('utf-8').strip()
if text.startswith("MODE:"): self.change_mode(int(text.split(":")[1]))
elif text.startswith("GOAL:"):
if self.current_mode != 2: self.change_mode(2)
p = text.split(":")[1].split(",")
self.start_navigation(float(p[0]), float(p[1]))
elif text.startswith("STOP"): self.change_mode(0)
except BlockingIOError: pass
except: pass
def control_loop(self):
self.receive_uwb_data()
self.receive_qt_command()
now = time.time()
if now - self.last_weight_check_time > self.weight_check_interval:
self.trigger_async_weight_check()
self.last_weight_check_time = now
if all(now - self.uwb_last_update[i] < 1.0 for i in range(3)):
pos = self.calculate_trilateration(self.uwb_dists)
if pos:
raw_x = max(0, min(pos[0], 6.0))
raw_y = max(0, min(pos[1], 15.0))
self.x_his.append(raw_x)
self.y_his.append(raw_y)
avg_x = sum(self.x_his) / len(self.x_his)
avg_y = sum(self.y_his) / len(self.y_his)
final_uwb_x = (avg_x / 6.0) * (3.80 - 0.10) + 0.10
final_uwb_y = avg_y
mx, my = self.transform_to_map(final_uwb_x, final_uwb_y)
self.human_x = mx
self.human_y = my
if self.current_mode == 1:
dist_diff = math.sqrt((mx - self.last_target_pos[0])**2 + (my - self.last_target_pos[1])**2)
if self.is_weight_ok:
if (now - self.last_goal_time > NAV_UPDATE_INTERVAL) and (dist_diff > MIN_UPDATE_DIST):
self.get_logger().info(f"Target Moved -> Goal: ({mx:.2f}, {my:.2f})")
self.start_navigation(mx, my)
self.last_goal_time = now
self.last_target_pos = [mx, my]
if self.current_mode == 0:
if self.nav_goal_handle: self.cancel_navigation()
self.stop_robot()
def change_mode(self, new_mode):
if self.current_mode == new_mode: return
self.get_logger().info(f"Mode Switching: {self.current_mode} -> {new_mode}")
self.cancel_navigation()
self.stop_robot()
self.current_mode = new_mode
self.last_target_pos = [0.0, 0.0]
self.trigger_async_weight_check()
# 네비게이션 실행
def start_navigation(self, x, y):
if not self.is_weight_ok: return
if not self.nav_to_pose_client.server_is_ready():
self.get_logger().error("Nav2 Server not ready!")
return
goal_msg = NavigateToPose.Goal()
goal_msg.pose.header.frame_id = 'map'
goal_msg.pose.header.stamp = self.get_clock().now().to_msg()
goal_msg.pose.pose.position.x = x
goal_msg.pose.pose.position.y = y
goal_msg.pose.pose.orientation.w = 1.0
self._send_goal_future = self.nav_to_pose_client.send_goal_async(goal_msg)
self._send_goal_future.add_done_callback(self.goal_response_callback)
def goal_response_callback(self, future):
handle = future.result()
if handle.accepted:
self.nav_goal_handle = handle
self._get_result_future = handle.get_result_async()
self._get_result_future.add_done_callback(self.get_result_callback)
def get_result_callback(self, future):
result = future.result()
if result.status == 4:
self.send_udp_to_qt("STATUS:ARRIVED")
self.nav_goal_handle = None
def cancel_navigation(self):
if self.nav_goal_handle:
self.nav_goal_handle.cancel_goal_async()
self.nav_goal_handle = None
def stop_robot(self):
msg = TwistStamped()
msg.header.stamp = self.get_clock().now().to_msg()
msg.header.frame_id = 'base_link'
self.cmd_vel_pub.publish(msg)
def main():
rclpy.init()
node = IntegratedRosController()
try: rclpy.spin(node)
except KeyboardInterrupt: pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Binary file not shown.
+253
View File
@@ -0,0 +1,253 @@
body {
background-color: #0f172a;
color: #f1f5f9;
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 헤더 스타일 */
header {
background-color: #1e293b;
border-bottom: 1px solid #334155;
padding: 0 30px;
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.logo { font-weight: 800; font-size: 1.3rem; margin-right: 40px; color: #fff; }
nav { display: flex; gap: 10px; flex: 1; }
.nav-btn {
text-decoration: none;
color: #94a3b8;
font-weight: 600;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.2s;
font-size: 0.9rem;
}
.nav-btn:hover { background-color: #334155; color: #fff; }
.nav-btn.active { background-color: #3b82f6; color: #fff; box-shadow: 0 0 10px rgba(59, 130, 246, 0.3); }
/* 상단 상태 배지 */
.status-group { display: flex; gap: 10px; }
.status-badge {
font-size: 0.8rem;
padding: 4px 12px;
border-radius: 99px;
background-color: #334155;
color: #94a3b8;
font-weight: 700;
display: flex; align-items: center; gap: 6px; border: 1px solid #475569;
}
.dot { width: 6px; height: 6px; border-radius: 50%; background-color: #94a3b8; }
.status-badge.on { background-color: #064e3b; color: #34d399; border-color: #059669; }
.status-badge.on .dot { background-color: #34d399; box-shadow: 0 0 4px #34d399; }
.status-badge.off { background-color: #450a0a; color: #f87171; border-color: #b91c1c; }
.status-badge.off .dot { background-color: #f87171; box-shadow: 0 0 4px #f87171; }
/* --- 메인 레이아웃 --- */
main {
padding: 20px;
max-width: 100%;
margin: 0 auto;
width: 100%;
box-sizing: border-box;
flex: 1;
overflow-y: hidden;
display: flex;
flex-direction: column;
gap: 20px;
}
/* 상단 카트 요약 그리드 */
.cart-select-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 15px;
flex-shrink: 0;
}
.cart-summary-card {
background-color: #1e293b;
border: 2px solid #334155;
border-radius: 12px;
padding: 15px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
flex-direction: column;
gap: 10px;
position: relative;
}
.cart-summary-card:hover { transform: translateY(-2px); border-color: #64748b; }
.cart-summary-card.selected {
border-color: #3b82f6;
background-color: #1e293b;
box-shadow: 0 0 15px rgba(59, 130, 246, 0.2);
}
.summary-header { display: flex; justify-content: space-between; align-items: center; }
.summary-name { font-weight: 700; color: #fff; font-size: 1rem; }
.summary-status-dot { width: 8px; height: 8px; border-radius: 50%; background-color: #475569; }
.summary-status-dot.online { background-color: #10b981; box-shadow: 0 0 5px #10b981; }
.summary-status-dot.warning { background-color: #e45d0f; box-shadow: 0 0 5px #e45d0f; }
.summary-status-dot.offline { background-color: #fa0303; box-shadow: 0 0 5px #e45d0f;}
.summary-row { display: flex; justify-content: space-between; font-size: 0.85rem; color: #94a3b8; }
.summary-val { color: #f1f5f9; font-weight: 600; }
/* 하단 상세 패널 */
.cart-panel {
flex: 1;
background-color: #1e293b;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
border: 2px solid #334155;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}
.panel-header {
background-color: #0f172a;
padding: 15px 25px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #334155;
flex-shrink: 0;
}
.cart-info { display: flex; align-items: center; gap: 10px; }
.cart-icon { font-size: 1.5rem; }
.cart-name { font-size: 1.2rem; font-weight: 800; color: #fff; }
.panel-status { font-weight: 700; font-size: 0.95rem; color: #64748b; }
/* --- 패널 내부 비율 (4:6) --- */
.panel-body {
display: flex;
flex: 1;
overflow: hidden;
flex-direction: row;
}
/* 왼쪽 영역 (정보) */
.panel-left {
flex: 4;
display: flex;
flex-direction: column;
border-right: 1px solid #334155;
min-width: 320px;
overflow-y: auto;
}
/* 오른쪽 영역 (지도) */
.panel-right {
flex: 6;
background-color: #0f172a;
display: flex;
flex-direction: column;
padding: 20px;
min-width: 0;
}
/* 센서 그리드 */
.sensor-grid {
padding: 25px;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 15px;
border-bottom: 1px solid #334155;
flex-shrink: 0;
}
.data-box {
background-color: #28364d;
padding: 20px;
border-radius: 12px;
text-align: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
display: flex; flex-direction: column; justify-content: center;
}
.data-label { font-size: 0.9rem; color: #94a3b8; margin-bottom: 5px; font-weight: 600; }
.data-value { font-size: 1.8rem; font-weight: 800; color: #fff; }
.unit { font-size: 0.9rem; color: #64748b; font-weight: 500; margin-left: 3px; }
.val-normal { color: #38bdf8; }
.val-warning { color: #f87171; }
.val-success { color: #34d399; }
.val-offline { color: #64748b; }
/* 아이템 리스트 */
.items-section {
padding: 25px;
background-color: #1e293b;
flex: 1;
display: flex;
flex-direction: column;
}
.items-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
.items-title { font-size: 1.1rem; font-weight: 700; color: #e2e8f0; }
.items-summary { font-size: 0.95rem; color: #94a3b8; font-weight: 600; }
.total-price { color: #34d399; font-weight: 800; font-size: 1.1rem; margin-left: 8px; }
.table-scroll-box {
flex: 1; overflow-y: auto;
border: 1px solid #334155; border-radius: 8px;
background-color: #1e293b;
min-height: 150px;
}
.table-scroll-box::-webkit-scrollbar { width: 8px; }
.table-scroll-box::-webkit-scrollbar-track { background: #1e293b; }
.table-scroll-box::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
.items-table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.items-table th {
text-align: left; color: #94a3b8; padding: 12px 20px; background-color: #0f172a;
position: sticky; top: 0; z-index: 1; border-bottom: 1px solid #334155; font-size: 0.85rem; font-weight: 700;
}
.items-table td { padding: 12px 20px; border-bottom: 1px solid #334155; color: #f1f5f9; }
.items-table tr:hover td { background-color: #334155; }
/* 지도 헤더 */
.map-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-shrink: 0;
}
.map-title { font-size: 1.1rem; font-weight: 700; color: #e2e8f0; }
.map-coords { font-family: monospace; color: #34d399; font-size: 0.9rem; }
/* 캔버스 컨테이너 */
.canvas-container {
flex: 1;
background-color: #020617;
border: 1px solid #334155;
border-radius: 8px;
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
canvas {
background-color: transparent;
width: 100%;
height: 100%;
object-fit: contain;
}
+130
View File
@@ -0,0 +1,130 @@
/* --- 다크 모드 기본 스타일 --- */
body {
background-color: #0f172a;
color: #f1f5f9;
font-family: 'Pretendard', sans-serif;
margin: 0;
padding: 0;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* --- 헤더 --- */
header {
background-color: #1e293b;
color: #fff;
border-bottom: 1px solid #334155;
padding: 0 30px;
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.logo { font-weight: 800; font-size: 1.3rem; margin-right: 40px; }
nav { display: flex; gap: 10px; flex: 1; }
.nav-btn { text-decoration: none; color: #94a3b8; font-weight: 600; padding: 8px 16px; border-radius: 8px; transition: all 0.2s; font-size: 0.9rem; }
.nav-btn:hover { background-color: #334155; color: #fff; }
.nav-btn.active { background-color: #3b82f6; color: #fff; box-shadow: 0 0 10px rgba(59, 130, 246, 0.3); }
.container { display: flex; flex-direction: column; flex: 1; min-height: 0; padding: 20px; gap: 20px; box-sizing: border-box; }
/* 다크 모드 섹션 스타일 */
.inventory-section {
flex: 1;
background: #1e293b;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid #334155;
}
.section-header {
padding: 15px 20px;
border-bottom: 1px solid #334155;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 700;
font-size: 1.1rem;
color: #e2e8f0;
background-color: #0f172a;
}
.table-wrapper { flex: 1; overflow-y: auto; }
/* 다크 모드 테이블 */
table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
th {
position: sticky; top: 0;
background-color: #0f172a;
color: #94a3b8;
padding: 12px 20px;
text-align: left;
border-bottom: 2px solid #334155;
font-size: 0.85rem;
font-weight: 700;
z-index: 10;
}
td {
padding: 12px 20px;
border-bottom: 1px solid #334155;
color: #f1f5f9;
vertical-align: middle;
}
tr:hover td { background-color: #334155; }
.sortable { cursor: pointer; user-select: none; transition: color 0.2s; }
.sortable:hover { color: #3b82f6; background-color: #1e293b; }
/* 다크 모드 배지 스타일 */
.badge { padding: 4px 10px; border-radius: 6px; font-size: 0.8rem; font-weight: 700; display: inline-block; min-width: 80px; text-align: center; }
.expiry-expired { background-color: #450a0a; color: #f87171; border: 1px solid #b91c1c; } /* 만료 */
.expiry-warning { background-color: #422006; color: #fbbf24; border: 1px solid #b45309; } /* 임박 */
.expiry-good { background-color: #064e3b; color: #34d399; border: 1px solid #059669; } /* 안전 */
.stock-badge { padding: 4px 10px; border-radius: 6px; font-size: 0.8rem; font-weight: 700; display: inline-block; text-align: center; }
.stock-low { background-color: #450a0a; color: #f87171; border: 1px solid #b91c1c; }
.stock-normal { background-color: #334155; color: #94a3b8; border: 1px solid #475569; }
/* 매출 섹션 */
.sales-section { flex: 1; display: flex; gap: 20px; min-height: 0; }
.kpi-container { display: flex; flex-direction: column; gap: 15px; width: 250px; flex-shrink: 0; }
.kpi-card {
background: #1e293b;
border-radius: 12px;
padding: 20px;
border: 1px solid #334155;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.kpi-title { font-size: 0.9rem; color: #94a3b8; margin-bottom: 5px; font-weight: 600; }
.kpi-value { font-size: 1.8rem; font-weight: 800; color: #fff; }
.kpi-icon { font-size: 2rem; margin-bottom: 10px; }
.chart-container {
flex: 1;
background: #1e293b;
border-radius: 12px;
border: 1px solid #334155;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
padding: 20px;
position: relative;
display: flex;
flex-direction: column;
}
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #0f172a; }
::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #64748b; }
+341
View File
@@ -0,0 +1,341 @@
let currentCartId = 1;
const mapImg = new Image();
mapImg.src = "/static/map601.png";
const MAP_BOUNDS = {
LEFT_X: -0.67,
RIGHT_X: 3.34,
TOP_Y: 1.66,
BOTTOM_Y: -7.4
};
const allCartsData = {
1: { id: 1, name: "QTCART 1호차", isOnline: false, battery: 0, speed: 0, weight: 0, expected: 0, items: [], status: "OFFLINE", x: 0.0, y: 0.0, human_x: 0.0, human_y: 0.0 },
2: { id: 2, name: "QTCART 2호차", isOnline: true, battery: 92, speed: 50, weight: 710, expected: 700, items: generateMockItems(3), status: "ONLINE", x: 1.5, y: 1.0 },
3: { id: 3, name: "QTCART 3호차", isOnline: true, battery: 34, speed: 0, weight: 0, expected: 0, items: [], status: "ONLINE", x: -0.67, y: 1.66 },
4: { id: 4, name: "QTCART 4호차", isOnline: true, battery: 65, speed: 0, weight: 0, expected: 0, items: [], status: "ONLINE", x: 3.34, y: -7.4 }
};
function generateMockItems(count) {
const mockDb = [
{name: "코카콜라 500ml", price: 2000, weight: 500},
{name: "포카칩 오리지널", price: 1500, weight: 100},
{name: "제주 삼다수 2L", price: 1000, weight: 2000},
{name: "신라면 멀티팩", price: 4500, weight: 600}
];
let items = [];
for(let i=0; i<count; i++) {
const rand = mockDb[Math.floor(Math.random() * mockDb.length)];
items.push({...rand, id: 100+i});
}
return items;
}
// 주기적으로 데이터 갱신
async function updateDashboard() {
const apiBadge = document.getElementById('apiBadge');
try {
// 로봇 상태 조회
const botRes = await fetch('/dashboard/bot/check');
const botData = await botRes.json();
// 카트 상태 조회
let cartData = null;
try {
const cRes = await fetch('/cart/status');
if(cRes.ok) cartData = await cRes.json();
} catch(e) {}
apiBadge.className = 'status-badge on';
apiBadge.innerHTML = '<span class="dot"></span> 서버 연결됨';
const c1 = allCartsData[1];
if(botData.bot_status === 'ONLINE') {
c1.isOnline = true;
c1.status = "ONLINE";
const batVal = parseFloat(botData.battery);
c1.battery = isNaN(batVal) ? 0 : Math.round(batVal);
c1.speed = Math.abs(Math.round(botData.speed * 100));
// 좌표 업데이트
c1.x = botData.x !== undefined ? botData.x : 0.0;
c1.y = botData.y !== undefined ? botData.y : 0.0;
c1.human_x = botData.human_x !== undefined ? botData.human_x : 0.0;
c1.human_y = botData.human_y !== undefined ? botData.human_y : 0.0;
// 카트 데이터 업데이트
if(cartData) {
c1.weight = cartData.real_weight;
c1.expected = cartData.expected_weight;
c1.items = cartData.cart_items || [];
if(cartData.system_status === "WARNING_WEIGHT_MISMATCH") {
c1.status = "WARNING";
} else {
c1.status = "ONLINE";
}
}
} else {
c1.isOnline = false;
c1.status = "OFFLINE";
c1.speed = 0;
}
} catch (err) {
apiBadge.className = 'status-badge off';
apiBadge.innerHTML = '<span class="dot"></span> 서버 끊김';
allCartsData[1].isOnline = false;
allCartsData[1].status = "OFFLINE";
}
updateMockData(2);
updateMockData(3);
updateMockData(4);
renderGrid();
renderDetailPanel();
drawMap();
}
function updateMockData(id) {
const c = allCartsData[id];
if(!c.isOnline) return;
if(Math.random() > 0.98) c.battery = Math.max(0, c.battery - 1);
if(Math.random() > 0.7) c.speed = Math.floor(Math.random() * 80);
c.x += (Math.random() - 0.5) * 0.1;
c.y += (Math.random() - 0.5) * 0.1;
}
function renderGrid() {
const grid = document.getElementById('cartGrid');
grid.innerHTML = '';
for(let i=1; i<=4; i++) {
const data = allCartsData[i];
const isSelected = (i === currentCartId) ? 'selected' : '';
let dotClass = 'offline';
let statusColor = '#64748b';
if(data.isOnline) {
if(data.status === 'WARNING') {
dotClass = 'warning';
statusColor = '#fbbf24';
} else {
dotClass = 'online';
statusColor = '#34d399';
}
}
const speedDisplay = data.isOnline ? `${data.speed} cm/s` : '--';
const html = `
<div class="cart-summary-card ${isSelected}" onclick="selectCart(${i})">
<div class="summary-header">
<div class="summary-name">${data.name}</div>
<div class="summary-status-dot ${dotClass}" style="${dotClass === 'warning' ? 'background-color:#f97316; box-shadow:0 0 5px #f97316;' : ''}"></div>
</div>
<div class="summary-row" style="margin-top:5px;">
<span>배터리</span>
<span class="summary-val" style="color:${statusColor}">${data.isOnline ? data.battery+'%' : '-'}</span>
</div>
<div class="summary-row">
<span>현재 속도</span> <span class="summary-val">${speedDisplay}</span> </div>
</div>`;
grid.innerHTML += html;
}
}
function renderDetailPanel() {
const data = allCartsData[currentCartId];
const statusText = document.getElementById('detailStatusText');
const panel = document.getElementById('detailPanel');
document.getElementById('detailName').innerText = data.name;
if(data.isOnline) {
if(data.status === 'WARNING') {
statusText.innerText = '● 무게 불일치 경고';
statusText.style.color = '#fbbf24';
panel.style.borderColor = '#fbbf24';
} else {
statusText.innerText = '● 온라인';
statusText.style.color = '#34d399';
panel.style.borderColor = '#334155';
}
} else {
statusText.innerText = '● 오프라인';
statusText.style.color = '#f87171';
panel.style.borderColor = '#ef4444';
}
const elReal = document.getElementById('detailReal');
const elExp = document.getElementById('detailExpected');
const elBat = document.getElementById('detailBattery');
const elSpd = document.getElementById('detailSpeed');
if(data.isOnline) {
elReal.innerText = typeof data.weight === 'number' ? data.weight.toFixed(1) : data.weight;
elExp.innerText = typeof data.expected === 'number' ? data.expected.toFixed(1) : data.expected;
elBat.innerText = data.battery;
elSpd.innerText = data.speed;
elBat.parentElement.className = data.battery < 20 ? "data-value val-warning" : "data-value val-success";
if(data.status === 'WARNING') elReal.parentElement.className = "data-value val-warning";
else elReal.parentElement.className = "data-value val-normal";
} else {
elReal.innerText = '--';
elExp.innerText = '--';
elBat.innerText = '--';
elSpd.innerText = '--';
elReal.parentElement.className = "data-value val-offline";
elBat.parentElement.className = "data-value val-offline";
}
const tbody = document.getElementById('detailListBody');
const countEl = document.getElementById('detailCount');
const priceEl = document.getElementById('detailPrice');
if(!data.isOnline || data.items.length === 0) {
tbody.innerHTML = '<tr><td colspan="4" style="text-align: center; color: #64748b; padding: 30px;">데이터 없음</td></tr>';
countEl.innerText = '0';
priceEl.innerText = '0원';
} else {
const agg = {};
let totalP = 0;
let totalC = 0;
data.items.forEach(item => {
if(agg[item.name]) agg[item.name].qty++;
else agg[item.name] = {...item, qty: 1};
totalP += item.price;
totalC++;
});
let html = '';
Object.values(agg).forEach(item => {
html += `<tr>
<td style="font-weight:600;">${item.name}</td>
<td style="text-align: right;">${item.price.toLocaleString()}원</td>
<td style="text-align: right;">${item.weight}g</td>
<td style="text-align: right; font-weight: bold; color: #38bdf8;">${item.qty}</td>
</tr>`;
});
tbody.innerHTML = html;
countEl.innerText = totalC;
priceEl.innerText = totalP.toLocaleString() + '원';
}
}
function drawMap() {
const canvas = document.getElementById('robotMap');
if(!canvas) return;
const ctx = canvas.getContext('2d');
const data = allCartsData[currentCartId];
const container = canvas.parentElement;
if(canvas.width !== container.clientWidth || canvas.height !== container.clientHeight) {
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
}
const cvsW = canvas.width;
const cvsH = canvas.height;
ctx.clearRect(0, 0, cvsW, cvsH);
// 이미지 로딩 체크
if (!mapImg.complete || mapImg.naturalWidth === 0) {
ctx.fillStyle = '#94a3b8';
ctx.font = '14px sans-serif';
ctx.fillText("Loading Map...", 20, 30);
return;
}
const imgW = mapImg.naturalWidth;
const imgH = mapImg.naturalHeight;
const imgRatio = imgW / imgH;
const cvsRatio = cvsW / cvsH;
let drawW, drawH, drawX, drawY;
if (cvsRatio > imgRatio) {
drawH = cvsH;
drawW = cvsH * imgRatio;
drawX = (cvsW - drawW) / 2;
drawY = 0;
} else {
drawW = cvsW;
drawH = cvsW / imgRatio;
drawX = 0;
drawY = (cvsH - drawH) / 2;
}
ctx.drawImage(mapImg, drawX, drawY, drawW, drawH);
const toPixel = (rosX, rosY) => {
const xRange = MAP_BOUNDS.RIGHT_X - MAP_BOUNDS.LEFT_X;
let ratioX = (rosX - MAP_BOUNDS.LEFT_X) / xRange;
const yRange = MAP_BOUNDS.BOTTOM_Y - MAP_BOUNDS.TOP_Y;
let ratioY = (rosY - MAP_BOUNDS.TOP_Y) / yRange;
let finalX = drawX + (ratioX * drawW);
let finalY = drawY + (ratioY * drawH);
return { x: finalX, y: finalY };
};
if (data.isOnline) {
console.log(`Robot(${data.x}, ${data.y})`);
const botPos = toPixel(data.x, data.y);
ctx.beginPath();
ctx.arc(botPos.x, botPos.y, 6, 0, Math.PI * 2);
let color = (currentCartId === 1) ? '#ef4444' : (data.status === 'WARNING' ? '#fbbf24' : '#34d399');
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 12px sans-serif';
ctx.fillText("BOT", botPos.x + 10, botPos.y + 4);
if (currentCartId === 1) {
const humanPos = toPixel(data.human_x, data.human_y);
ctx.beginPath();
ctx.arc(humanPos.x, humanPos.y, 6, 0, Math.PI * 2);
ctx.fillStyle = '#3b82f6';
ctx.fill();
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.fillStyle = '#3b82f6';
ctx.fillText("ME", humanPos.x + 10, humanPos.y + 4);
}
const coordEl = document.getElementById('mapCoords');
if(coordEl) coordEl.innerText = `Robot: (${data.x.toFixed(2)}, ${data.y.toFixed(2)})`;
} else {
const coordEl = document.getElementById('mapCoords');
if(coordEl) coordEl.innerText = "OFFLINE";
}
}
function selectCart(id) {
currentCartId = id;
renderGrid();
renderDetailPanel();
drawMap();
}
// 1초마다 갱신
setInterval(updateDashboard, 1000);
updateDashboard();
+151
View File
@@ -0,0 +1,151 @@
let currentItems = [];
function refreshAll() {
fetchInventory();
fetchSalesStats();
}
async function fetchInventory() {
try {
const res = await fetch('/items');
if (res.ok) {
currentItems = await res.json();
renderTable('expiry');
}
} catch (e) { console.error(e); }
}
function renderTable(sortKey) {
const tbody = document.getElementById('inventoryBody');
if (sortKey === 'expiry') {
currentItems.sort((a, b) => new Date(a.expiry_date) - new Date(b.expiry_date));
} else if (sortKey === 'stock') {
currentItems.sort((a, b) => a.stock - b.stock);
}
const today = new Date();
today.setHours(0,0,0,0);
let html = currentItems.map(item => {
const expiryDate = new Date(item.expiry_date);
const diffTime = expiryDate - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
let dateClass = 'expiry-good';
let dateText = `D-${diffDays}`;
if (diffDays < 0) {
dateClass = 'expiry-expired';
dateText = '만료됨';
} else if (diffDays === 0) {
dateClass = 'expiry-warning';
dateText = '오늘 만료';
} else if (diffDays <= 3) {
dateClass = 'expiry-warning';
dateText = `${diffDays}일 남음`;
}
let stockHtml = '';
if (item.stock === 0) {
stockHtml = `<span class="stock-badge stock-low">부족 (${item.stock})</span>`;
} else {
stockHtml = `<span class="stock-badge stock-normal">${item.stock}개</span>`;
}
return `<tr>
<td style="color:#94a3b8;">#${item.id}</td>
<td style="font-weight: bold;">${item.name}</td>
<td>${item.price.toLocaleString()}원</td>
<td>
<span class="badge ${dateClass}">${item.expiry_date} (${dateText})</span>
</td>
<td>${stockHtml}</td>
</tr>`;
}).join('');
tbody.innerHTML = html || '<tr><td colspan="5" style="text-align:center; padding:30px; color:#64748b;">등록된 상품이 없습니다.</td></tr>';
}
let myChart = null;
// ----- 결제 정보 -----
async function fetchSalesStats() {
try {
const res = await fetch('/payments');
if (!res.ok) return;
const payments = await res.json();
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
let monthTotal = 0;
let monthCount = 0;
const dailySales = {};
for (let i = 0; i < 30; i++) {
const d = new Date();
d.setDate(today.getDate() - i);
dailySales[d.toISOString().split('T')[0]] = 0;
}
payments.forEach(pay => {
const payDate = new Date(pay.created_at);
if (payDate >= thirtyDaysAgo) {
monthTotal += pay.total_price;
monthCount += 1;
const dateStr = pay.created_at.split('T')[0];
if (dailySales[dateStr] !== undefined) dailySales[dateStr] += pay.total_price;
}
});
document.getElementById('monthTotal').innerText = monthTotal.toLocaleString() + '원';
document.getElementById('monthCount').innerText = monthCount + '건';
updateChart(dailySales);
} catch (e) { console.error(e); }
}
function updateChart(dailyData) {
const sortedDates = Object.keys(dailyData).sort();
const dataValues = sortedDates.map(date => dailyData[date]);
const labels = sortedDates.map(date => date.slice(5));
const ctx = document.getElementById('salesChart').getContext('2d');
if (myChart) myChart.destroy();
myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '매출 (원)',
data: dataValues,
borderColor: '#8b5cf6',
backgroundColor: 'rgba(139, 92, 246, 0.2)',
borderWidth: 2,
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: {
beginAtZero: true,
grid: { borderDash: [5, 5], color: '#334155' },
ticks: { color: '#94a3b8' }
},
x: {
grid: { display: false },
ticks: { color: '#94a3b8' }
}
}
}
});
}
refreshAll();
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+98
View File
@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QTCART 관제 시스템</title>
<link rel="stylesheet" href="/static/css/dashboard.css">
</head>
<body>
<header>
<div class="logo">🛒 QT-CART</div>
<nav>
<a href="/dashboard" class="nav-btn active">관제 모니터링</a>
<a href="/inventory" class="nav-btn">통합 관리</a>
</nav>
<div class="status-group">
<div id="apiBadge" class="status-badge off"><span class="dot"></span> 서버 연결</div>
</div>
</header>
<main>
<div class="cart-select-grid" id="cartGrid">
</div>
<div id="detailPanel" class="cart-panel">
<div class="panel-header">
<div class="cart-info">
<div class="cart-icon">🛒</div>
<div class="cart-name" id="detailName">QTCART 1호차</div>
</div>
<div id="detailStatusText" class="panel-status">● 데이터 로딩중</div>
</div>
<div class="panel-body">
<div class="panel-left">
<div class="sensor-grid">
<div class="data-box">
<div class="data-label">배터리 잔량</div>
<div class="data-value"><span id="detailBattery">--</span><span class="unit">%</span></div>
</div>
<div class="data-box">
<div class="data-label">현재 속도</div>
<div class="data-value"><span id="detailSpeed">--</span><span class="unit">cm/s</span></div>
</div>
<div class="data-box">
<div class="data-label">기대 무게</div>
<div class="data-value" style="color:#fbbf24;"><span id="detailExpected">0</span><span class="unit">g</span></div>
</div>
<div class="data-box">
<div class="data-label">실제 무게</div>
<div class="data-value"><span id="detailReal">--</span><span class="unit">g</span></div>
</div>
</div>
<div class="items-section">
<div class="items-header">
<div class="items-title">📦 담긴 상품 목록</div>
<div class="items-summary">
총 <span id="detailCount">0</span>개
<span class="total-price" id="detailPrice">0원</span>
</div>
</div>
<div class="table-scroll-box">
<table class="items-table">
<thead>
<tr>
<th>상품명</th>
<th style="text-align: right;">단가</th>
<th style="text-align: right;">무게</th>
<th style="text-align: right;">수량</th>
</tr>
</thead>
<tbody id="detailListBody">
<tr><td colspan="4" style="text-align: center; color: #64748b; padding: 30px;">데이터 없음</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-right">
<div class="map-header">
<div class="map-title">🗺️ 실시간 위치</div>
<div class="map-coords" id="mapCoords">X: 0.0, Y: 0.0</div>
</div>
<div class="canvas-container">
<canvas id="robotMap" width="500" height="400"></canvas>
</div>
</div>
</div>
</div>
</main>
<script src="/static/js/dashboard.js"></script>
</body>
</html>
+66
View File
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QTCART 통합 관리</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="/static/css/inventory.css">
</head>
<body>
<header>
<div class="logo">🛒 QT-CART</div>
<nav>
<a href="/dashboard" class="nav-btn">관제 모니터링</a>
<a href="/inventory" class="nav-btn active">통합 관리</a>
</nav>
</header>
<div class="container">
<div class="inventory-section">
<div class="section-header">
📦 재고 및 유통기한 관리
<button onclick="refreshAll()" style="background:none; border:none; color:#3b82f6; cursor:pointer; font-weight:600;">🔄 새로고침</button>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th width="10%">ID</th>
<th width="30%">상품명</th>
<th width="20%">가격</th>
<th width="25%" class="sortable" onclick="renderTable('expiry')">유통기한 ▼</th>
<th width="15%" class="sortable" onclick="renderTable('stock')">재고량 ▼</th>
</tr>
</thead>
<tbody id="inventoryBody"></tbody>
</table>
</div>
</div>
<div class="sales-section">
<div class="kpi-container">
<div class="kpi-card">
<div class="kpi-icon">💰</div>
<div class="kpi-title">이번 달 총 매출</div>
<div class="kpi-value" id="monthTotal">0원</div>
</div>
<div class="kpi-card">
<div class="kpi-icon">🧾</div>
<div class="kpi-title">이번 달 거래 수</div>
<div class="kpi-value" id="monthCount">0건</div>
</div>
</div>
<div class="chart-container">
<div style="font-weight:700; color:#e2e8f0; margin-bottom:10px;">📉 최근 30일 매출 추이</div>
<div style="flex:1; position: relative;">
<canvas id="salesChart"></canvas>
</div>
</div>
</div>
</div>
<script src="/static/js/inventory.js"></script>
</body>
</html>
-23
View File
@@ -1,23 +0,0 @@
QMAKE_CXX.QT_COMPILER_STDCXX = 201703L
QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 11
QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 4
QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0
QMAKE_CXX.COMPILER_MACROS = \
QT_COMPILER_STDCXX \
QMAKE_GCC_MAJOR_VERSION \
QMAKE_GCC_MINOR_VERSION \
QMAKE_GCC_PATCH_VERSION
QMAKE_CXX.INCDIRS = \
/usr/include/c++/11 \
/usr/include/x86_64-linux-gnu/c++/11 \
/usr/include/c++/11/backward \
/usr/lib/gcc/x86_64-linux-gnu/11/include \
/usr/local/include \
/usr/include/x86_64-linux-gnu \
/usr/include
QMAKE_CXX.LIBDIRS = \
/usr/lib/gcc/x86_64-linux-gnu/11 \
/usr/lib/x86_64-linux-gnu \
/usr/lib \
/lib/x86_64-linux-gnu \
/lib
-346
View File
@@ -1,346 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 18.0.0, 2025-12-08T19:19:38. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{63b9673a-654d-4546-b58a-b51c28d2b22b}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">2</value>
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
<value type="int" key="RcSync">0</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{69eb4bf4-1a59-4f97-a489-e2827346197c}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ubuntu/QT/src/QTCart/build/Desktop-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/ubuntu/QT/src/QTCart/build/Desktop-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/ubuntu/QT/src/QTCart/QTCart.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">%{RunConfig:Executable:Path}</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/ubuntu/QT/src/QTCart/QTCart.pro</value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">%{RunConfig:Executable:Path}</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.1</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="bool" key="HasPerBcDcs">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Qt 6.8.3</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Qt 6.8.3</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{3ff0c7d5-10d9-474f-80e6-cba74e9bcfa2}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="int" key="EnableQmlDebugging">0</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ubuntu/QT/src/QTCart/build/Qt_6_8_3-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/ubuntu/QT/src/QTCart/build/Qt_6_8_3-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">%{RunConfig:Executable:Path}</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">%{RunConfig:Executable:Path}</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">2</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>
-2270
View File
File diff suppressed because it is too large. Load diff
BIN
View File
Binary file not shown.
-23
View File
@@ -1,23 +0,0 @@
QMAKE_CXX.QT_COMPILER_STDCXX = 201703L
QMAKE_CXX.QMAKE_GCC_MAJOR_VERSION = 11
QMAKE_CXX.QMAKE_GCC_MINOR_VERSION = 4
QMAKE_CXX.QMAKE_GCC_PATCH_VERSION = 0
QMAKE_CXX.COMPILER_MACROS = \
QT_COMPILER_STDCXX \
QMAKE_GCC_MAJOR_VERSION \
QMAKE_GCC_MINOR_VERSION \
QMAKE_GCC_PATCH_VERSION
QMAKE_CXX.INCDIRS = \
/usr/include/c++/11 \
/usr/include/x86_64-linux-gnu/c++/11 \
/usr/include/c++/11/backward \
/usr/lib/gcc/x86_64-linux-gnu/11/include \
/usr/local/include \
/usr/include/x86_64-linux-gnu \
/usr/include
QMAKE_CXX.LIBDIRS = \
/usr/lib/gcc/x86_64-linux-gnu/11 \
/usr/lib/x86_64-linux-gnu \
/usr/lib \
/lib/x86_64-linux-gnu \
/lib
-434
View File
@@ -1,434 +0,0 @@
#############################################################################
# Makefile for building: QTCart
# Generated by qmake (3.1) (Qt 6.2.4)
# Project: ../../QTCart.pro
# Template: app
# Command: /usr/lib/qt6/bin/qmake -o Makefile ../../QTCart.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug
#############################################################################
MAKEFILE = Makefile
EQ = =
####### Compiler, tools and options
CC = gcc
CXX = g++
DEFINES = -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB
CFLAGS = -pipe -g -Wall -Wextra -D_REENTRANT -fPIC $(DEFINES)
CXXFLAGS = -pipe -g -std=gnu++1z -Wall -Wextra -D_REENTRANT -fPIC $(DEFINES)
INCPATH = -I../../../QTCart -I. -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtWidgets -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I. -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++
QMAKE = /usr/lib/qt6/bin/qmake
DEL_FILE = rm -f
CHK_DIR_EXISTS= test -d
MKDIR = mkdir -p
COPY = cp -f
COPY_FILE = cp -f
COPY_DIR = cp -f -R
INSTALL_FILE = install -m 644 -p
INSTALL_PROGRAM = install -m 755 -p
INSTALL_DIR = cp -f -R
QINSTALL = /usr/lib/qt6/bin/qmake -install qinstall
QINSTALL_PROGRAM = /usr/lib/qt6/bin/qmake -install qinstall -exe
DEL_FILE = rm -f
SYMLINK = ln -f -s
DEL_DIR = rmdir
MOVE = mv -f
TAR = tar -cf
COMPRESS = gzip -9f
DISTNAME = QTCart1.0.0
DISTDIR = /home/ubuntu/QT/src/QTCart/build/Desktop-Debug/.tmp/QTCart1.0.0
LINK = g++
LFLAGS = -Wl,-rpath-link,/usr/lib/x86_64-linux-gnu
LIBS = $(SUBLIBS) /usr/lib/x86_64-linux-gnu/libQt6Widgets.so /usr/lib/x86_64-linux-gnu/libQt6Gui.so /usr/lib/x86_64-linux-gnu/libGLX.so /usr/lib/x86_64-linux-gnu/libOpenGL.so /usr/lib/x86_64-linux-gnu/libQt6Core.so -lpthread -lGLX -lOpenGL
AR = ar cqs
RANLIB =
SED = sed
STRIP = strip
####### Output directory
OBJECTS_DIR = ./
####### Files
SOURCES = ../../main.cpp \
../../mainwidget.cpp \
../../pagecart.cpp \
../../pagewelcome.cpp moc_mainwidget.cpp \
moc_pagecart.cpp \
moc_pagewelcome.cpp
OBJECTS = main.o \
mainwidget.o \
pagecart.o \
pagewelcome.o \
moc_mainwidget.o \
moc_pagecart.o \
moc_pagewelcome.o
DIST = /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf \
../../.qmake.stash \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qml_debug.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/uic.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf \
../../../../QTCart.pro ../../mainwidget.h \
../../pagecart.h \
../../pagewelcome.h ../../main.cpp \
../../mainwidget.cpp \
../../pagecart.cpp \
../../pagewelcome.cpp
QMAKE_TARGET = QTCart
DESTDIR =
TARGET = QTCart
first: all
####### Build rules
QTCart: ui_mainwidget.h ui_pagecart.h ui_pagewelcome.h $(OBJECTS)
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)
Makefile: ../../QTCart.pro /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf \
.qmake.stash \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qml_debug.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/uic.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf \
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf \
../../QTCart.pro \
/usr/lib/x86_64-linux-gnu/libQt6Widgets.prl \
/usr/lib/x86_64-linux-gnu/libQt6Gui.prl \
/usr/lib/x86_64-linux-gnu/libQt6Core.prl
$(QMAKE) -o Makefile ../../QTCart.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/sanitize.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/gcc-base-unix.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-base.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/g++-unix.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/qconfig.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_concurrent_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_core_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_dbus_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_devicediscovery_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_gbm_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfs_kms_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_eglfsdeviceintegration_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_fb_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_gui_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_input_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_kms_support_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_network_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_opengl_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_openglwidgets_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_printsupport_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_sql_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_testlib_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_widgets_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xcb_qpa_lib_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/modules/qt_lib_xml_private.pri:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_functions.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt_config.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++/qmake.conf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_post.prf:
.qmake.stash:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exclusive_builds.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/toolchain.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_pre.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resolve_config.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/default_post.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qml_debug.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/warn_on.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qt.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources_functions.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/resources.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/moc.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/opengl.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/uic.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/unix/thread.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/qmake_use.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/file_copies.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/testcase_targets.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/exceptions.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/yacc.prf:
/usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/lex.prf:
../../QTCart.pro:
/usr/lib/x86_64-linux-gnu/libQt6Widgets.prl:
/usr/lib/x86_64-linux-gnu/libQt6Gui.prl:
/usr/lib/x86_64-linux-gnu/libQt6Core.prl:
qmake: FORCE
@$(QMAKE) -o Makefile ../../QTCart.pro -spec linux-g++ CONFIG+=debug CONFIG+=qml_debug
qmake_all: FORCE
all: Makefile QTCart
dist: distdir FORCE
(cd `dirname $(DISTDIR)` && $(TAR) $(DISTNAME).tar $(DISTNAME) && $(COMPRESS) $(DISTNAME).tar) && $(MOVE) `dirname $(DISTDIR)`/$(DISTNAME).tar.gz . && $(DEL_FILE) -r $(DISTDIR)
distdir: FORCE
@test -d $(DISTDIR) || mkdir -p $(DISTDIR)
$(COPY_FILE) --parents $(DIST) $(DISTDIR)/
$(COPY_FILE) --parents /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp $(DISTDIR)/
$(COPY_FILE) --parents ../../mainwidget.h ../../pagecart.h ../../pagewelcome.h $(DISTDIR)/
$(COPY_FILE) --parents ../../main.cpp ../../mainwidget.cpp ../../pagecart.cpp ../../pagewelcome.cpp $(DISTDIR)/
$(COPY_FILE) --parents ../../mainwidget.ui ../../pagecart.ui ../../pagewelcome.ui $(DISTDIR)/
clean: compiler_clean
-$(DEL_FILE) $(OBJECTS)
-$(DEL_FILE) *~ core *.core
distclean: clean
-$(DEL_FILE) $(TARGET)
-$(DEL_FILE) .qmake.stash
-$(DEL_FILE) Makefile
####### Sub-libraries
mocclean: compiler_moc_header_clean compiler_moc_objc_header_clean compiler_moc_source_clean
mocables: compiler_moc_header_make_all compiler_moc_objc_header_make_all compiler_moc_source_make_all
check: first
benchmark: first
compiler_rcc_make_all:
compiler_rcc_clean:
compiler_moc_predefs_make_all: moc_predefs.h
compiler_moc_predefs_clean:
-$(DEL_FILE) moc_predefs.h
moc_predefs.h: /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp
g++ -pipe -g -std=gnu++1z -Wall -Wextra -dM -E -o moc_predefs.h /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp
compiler_moc_header_make_all: moc_mainwidget.cpp moc_pagecart.cpp moc_pagewelcome.cpp
compiler_moc_header_clean:
-$(DEL_FILE) moc_mainwidget.cpp moc_pagecart.cpp moc_pagewelcome.cpp
moc_mainwidget.cpp: ../../mainwidget.h \
../../pagewelcome.h \
../../pagecart.h \
moc_predefs.h \
/usr/lib/qt6/libexec/moc
/usr/lib/qt6/libexec/moc $(DEFINES) --include /home/ubuntu/QT/src/QTCart/build/Desktop-Debug/moc_predefs.h -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -I/home/ubuntu/QT/src/QTCart -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtWidgets -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/include/c++/11 -I/usr/include/x86_64-linux-gnu/c++/11 -I/usr/include/c++/11/backward -I/usr/lib/gcc/x86_64-linux-gnu/11/include -I/usr/local/include -I/usr/include/x86_64-linux-gnu -I/usr/include ../../mainwidget.h -o moc_mainwidget.cpp
moc_pagecart.cpp: ../../pagecart.h \
moc_predefs.h \
/usr/lib/qt6/libexec/moc
/usr/lib/qt6/libexec/moc $(DEFINES) --include /home/ubuntu/QT/src/QTCart/build/Desktop-Debug/moc_predefs.h -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -I/home/ubuntu/QT/src/QTCart -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtWidgets -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/include/c++/11 -I/usr/include/x86_64-linux-gnu/c++/11 -I/usr/include/c++/11/backward -I/usr/lib/gcc/x86_64-linux-gnu/11/include -I/usr/local/include -I/usr/include/x86_64-linux-gnu -I/usr/include ../../pagecart.h -o moc_pagecart.cpp
moc_pagewelcome.cpp: ../../pagewelcome.h \
moc_predefs.h \
/usr/lib/qt6/libexec/moc
/usr/lib/qt6/libexec/moc $(DEFINES) --include /home/ubuntu/QT/src/QTCart/build/Desktop-Debug/moc_predefs.h -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -I/home/ubuntu/QT/src/QTCart -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtWidgets -I/usr/include/x86_64-linux-gnu/qt6/QtGui -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/include/c++/11 -I/usr/include/x86_64-linux-gnu/c++/11 -I/usr/include/c++/11/backward -I/usr/lib/gcc/x86_64-linux-gnu/11/include -I/usr/local/include -I/usr/include/x86_64-linux-gnu -I/usr/include ../../pagewelcome.h -o moc_pagewelcome.cpp
compiler_moc_objc_header_make_all:
compiler_moc_objc_header_clean:
compiler_moc_source_make_all:
compiler_moc_source_clean:
compiler_uic_make_all: ui_mainwidget.h ui_pagecart.h ui_pagewelcome.h
compiler_uic_clean:
-$(DEL_FILE) ui_mainwidget.h ui_pagecart.h ui_pagewelcome.h
ui_mainwidget.h: ../../mainwidget.ui \
/usr/lib/qt6/libexec/uic
/usr/lib/qt6/libexec/uic ../../mainwidget.ui -o ui_mainwidget.h
ui_pagecart.h: ../../pagecart.ui \
/usr/lib/qt6/libexec/uic
/usr/lib/qt6/libexec/uic ../../pagecart.ui -o ui_pagecart.h
ui_pagewelcome.h: ../../pagewelcome.ui \
/usr/lib/qt6/libexec/uic
/usr/lib/qt6/libexec/uic ../../pagewelcome.ui -o ui_pagewelcome.h
compiler_yacc_decl_make_all:
compiler_yacc_decl_clean:
compiler_yacc_impl_make_all:
compiler_yacc_impl_clean:
compiler_lex_make_all:
compiler_lex_clean:
compiler_clean: compiler_moc_predefs_clean compiler_moc_header_clean compiler_uic_clean
####### Compile
main.o: ../../main.cpp ../../mainwidget.h \
../../pagewelcome.h \
../../pagecart.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o ../../main.cpp
mainwidget.o: ../../mainwidget.cpp ../../mainwidget.h \
../../pagewelcome.h \
../../pagecart.h \
../../ui_mainwidget.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o mainwidget.o ../../mainwidget.cpp
pagecart.o: ../../pagecart.cpp ../../pagecart.h \
ui_pagecart.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o pagecart.o ../../pagecart.cpp
pagewelcome.o: ../../pagewelcome.cpp ../../pagewelcome.h \
../../ui_pagewelcome.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o pagewelcome.o ../../pagewelcome.cpp
moc_mainwidget.o: moc_mainwidget.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_mainwidget.o moc_mainwidget.cpp
moc_pagecart.o: moc_pagecart.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_pagecart.o moc_pagecart.cpp
moc_pagewelcome.o: moc_pagewelcome.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_pagewelcome.o moc_pagewelcome.cpp
####### Install
install_target: first FORCE
@test -d $(INSTALL_ROOT)/opt/QTCart/bin || mkdir -p $(INSTALL_ROOT)/opt/QTCart/bin
$(QINSTALL_PROGRAM) $(QMAKE_TARGET) $(INSTALL_ROOT)/opt/QTCart/bin/$(QMAKE_TARGET)
uninstall_target: FORCE
-$(DEL_FILE) $(INSTALL_ROOT)/opt/QTCart/bin/$(QMAKE_TARGET)
-$(DEL_DIR) $(INSTALL_ROOT)/opt/QTCart/bin/
install: install_target FORCE
uninstall: uninstall_target FORCE
FORCE:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,122 +0,0 @@
/****************************************************************************
** Meta object code from reading C++ file 'mainwidget.h'
**
** Created by: The Qt Meta Object Compiler version 68 (Qt 6.2.4)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include <memory>
#include "../../mainwidget.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'mainwidget.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 68
#error "This file was generated using the moc from 6.2.4. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_MainWidget_t {
const uint offsetsAndSize[6];
char stringdata0[31];
};
#define QT_MOC_LITERAL(ofs, len) \
uint(offsetof(qt_meta_stringdata_MainWidget_t, stringdata0) + ofs), len
static const qt_meta_stringdata_MainWidget_t qt_meta_stringdata_MainWidget = {
{
QT_MOC_LITERAL(0, 10), // "MainWidget"
QT_MOC_LITERAL(11, 18), // "on_pPBStartClicked"
QT_MOC_LITERAL(30, 0) // ""
},
"MainWidget\0on_pPBStartClicked\0"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_MainWidget[] = {
// content:
10, // revision
0, // classname
0, 0, // classinfo
1, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
// slots: name, argc, parameters, tag, flags, initial metatype offsets
1, 0, 20, 2, 0x08, 1 /* Private */,
// slots: parameters
QMetaType::Void,
0 // eod
};
void MainWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<MainWidget *>(_o);
(void)_t;
switch (_id) {
case 0: _t->on_pPBStartClicked(); break;
default: ;
}
}
(void)_a;
}
const QMetaObject MainWidget::staticMetaObject = { {
QMetaObject::SuperData::link<QWidget::staticMetaObject>(),
qt_meta_stringdata_MainWidget.offsetsAndSize,
qt_meta_data_MainWidget,
qt_static_metacall,
nullptr,
qt_incomplete_metaTypeArray<qt_meta_stringdata_MainWidget_t
, QtPrivate::TypeAndForceComplete<MainWidget, std::true_type>
, QtPrivate::TypeAndForceComplete<void, std::false_type>
>,
nullptr
} };
const QMetaObject *MainWidget::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *MainWidget::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_MainWidget.stringdata0))
return static_cast<void*>(this);
return QWidget::qt_metacast(_clname);
}
int MainWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 1)
qt_static_metacall(this, _c, _id, _a);
_id -= 1;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 1)
*reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();
_id -= 1;
}
return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE
Binary file not shown.
Loaded 100 of 134 files, more files were not shown because too many files have changed in this diff. Show more