🎆

005 생선가게 POS기 만들기 - 3

1. 기획

이제 본격적으로 POS기를 만들어 보도록 하겠습니다. 위에서 학습한 내용을 바탕으로 코드 작성을 하도록 하겠습니다.

2. 코드

구현 안된 기능
  • 이전에 생성된 spinbox 값 변경 시 변경 내용 무시됨 → 아래 코드2-2확인
import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import QIcon, QPixmap, QFont from PyQt5.QtCore import QCoreApplication, Qt, QDate import re class 생선가게POS기(QWidget): def __init__(self): super().__init__() self.UI초기화() def UI초기화(self): # 나중에 메서드에서 사용 될 변수들 self.i = 0 self.중복확인 = [] self.마리수 = 0 self.금액 = 0 self.몇마리_temp = 0 self.금액_temp = 0 self.오늘총금액 = 0 # UI 초기화 가로정렬 = QHBoxLayout() 가로정렬.addWidget(self.메뉴()) 가로정렬.addWidget(self.주문내역()) 세로정렬 = QVBoxLayout() 세로정렬.addWidget(self.오늘()) 세로정렬.addLayout(가로정렬) self.setLayout(세로정렬) self.setWindowTitle('(주)캣네생선 POS기') self.setWindowIcon(QIcon('img/캣네생선.png')) self.setGeometry(700, 400, 650, 480) self.show() # 화면 상단부에 나오는 날짜와 오늘매출을 표시하는 메서드 def 오늘(self): self.날짜 = QDate.currentDate() self.오늘날짜 = QLabel(QDate.currentDate().toString('yyyy년 MM월 dd일')) self.오늘매출 = QLabel('오늘 총 매출 : 0원') self.그룹박스_오늘 = QGroupBox() self.가로정렬_오늘 = QHBoxLayout() self.가로정렬_오늘.addWidget(self.오늘날짜) self.가로정렬_오늘.addWidget(self.오늘매출, alignment=Qt.AlignRight) self.그룹박스_오늘.setLayout(self.가로정렬_오늘) self.그룹박스_오늘.setFixedHeight(50) return self.그룹박스_오늘 # 화면 왼쪽의 메뉴를 표시하는 메서드 def 메뉴(self): self.삼치 = QPushButton('\n삼치\n3500원\n',self) self.고등어 = QPushButton('\n고등어\n4500원\n',self) self.꽁치 = QPushButton('\n꽁치\n5000원\n',self) self.연어 = QPushButton('\n연어\n5500원\n',self) self.광어 = QPushButton('\n광어\n6000원\n',self) self.굴비 = QPushButton('\n굴비\n7000원\n',self) self.넙치 = QPushButton('\n넙치\n8000원\n',self) self.갈치 = QPushButton('\n갈치\n12000원\n',self) self.참돔 = QPushButton('\n참돔\n20000원\n',self) self.그룹박스_생선 = QGroupBox('메뉴') self.생선들 = QGridLayout() self.생선들.addWidget(self.삼치, 1,0) self.생선들.addWidget(self.고등어, 1,1) self.생선들.addWidget(self.꽁치, 1,2) self.생선들.addWidget(self.연어, 2,0) self.생선들.addWidget(self.광어, 2,1) self.생선들.addWidget(self.굴비, 2,2) self.생선들.addWidget(self.넙치, 3,0) self.생선들.addWidget(self.갈치, 3,1) self.생선들.addWidget(self.참돔, 3,2) self.삼치.clicked.connect(lambda:self.생선선택하기(self.삼치_주문)) self.고등어.clicked.connect(lambda:self.생선선택하기(self.고등어_주문)) self.꽁치.clicked.connect(lambda:self.생선선택하기(self.꽁치_주문)) self.연어.clicked.connect(lambda:self.생선선택하기(self.연어_주문)) self.광어.clicked.connect(lambda:self.생선선택하기(self.광어_주문)) self.굴비.clicked.connect(lambda:self.생선선택하기(self.굴비_주문)) self.넙치.clicked.connect(lambda:self.생선선택하기(self.넙치_주문)) self.갈치.clicked.connect(lambda:self.생선선택하기(self.갈치_주문)) self.참돔.clicked.connect(lambda:self.생선선택하기(self.참돔_주문)) self.그룹박스_생선.setLayout(self.생선들) return self.그룹박스_생선 # 메뉴를 클릭하면 나타나는 주문내역과 결제창을 보여주는 메서드 def 주문내역(self): self.그룹박스_주문내역 = QGroupBox('주문 상품 목록') self.세로정렬_주문내역 = QVBoxLayout() self.세로정렬_주문내역.addWidget(self.주문내역_숨김()) self.세로정렬_주문내역.addWidget(self.결제박스()) self.그룹박스_주문내역.setLayout(self.세로정렬_주문내역) self.그룹박스_주문내역.setFixedWidth(200) return self.그룹박스_주문내역 # '주문내역' grid박스 내 아래쪽에 보여지는 결제박스 def 결제박스(self): self.총수량_라벨 = QLabel('총 수량 :') self.총수량 = QLabel('- 마리') self.총금액_라벨 = QLabel('총 금액 :') self.총금액 = QLabel(' - 원') self.결제버튼 = QPushButton('결제하기',self) # 인코딩문제로 인해서 lambda함수 사용함 # self.결제버튼.clicked.connect(self.결제하기_버튼클릭) self.결제버튼.clicked.connect(lambda:self.결제하기_버튼클릭()) self.그룹박스_결제 = QGroupBox() self.그룹박스_결제.setFlat(True) self.결제값 = QGridLayout() self.결제값.addWidget(self.총수량_라벨,0,0,1,3) self.결제값.addWidget(self.총수량,0,5) self.결제값.addWidget(self.총금액_라벨,1,0,1,3) self.결제값.addWidget(self.총금액,1,5) self.결제값.addWidget(self.결제버튼,2,0,1,6) self.그룹박스_결제.setLayout(self.결제값) self.그룹박스_결제.setFixedHeight(100) return self.그룹박스_결제 # 메뉴를 클릭하면 오른쪽 주문내역에서 나타나는 라벨 def 주문내역_숨김(self): self.삼치_주문 = QLabel('삼치 3500원') self.고등어_주문 = QLabel('고등어 4500원') self.꽁치_주문 = QLabel('꽁치 5000원') self.연어_주문 = QLabel('연어 5500원') self.광어_주문 = QLabel('광어 6000원') self.굴비_주문 = QLabel('굴비 7000원') self.넙치_주문 = QLabel('넙치 8000원') self.갈치_주문 = QLabel('갈치 12000원') self.참돔_주문 = QLabel('참돔 20000원') self.그룹박스_선택 = QGroupBox() self.선택하기 = QGridLayout() self.그룹박스_선택.setLayout(self.선택하기) return self.그룹박스_선택 def 스핀박스_만들기(self): self.스핀박스 = QSpinBox() self.스핀박스.setFixedWidth(45) return self.스핀박스 # 결제하기 버튼 클릭했을 경우 실행되는 메서드 def 결제하기_버튼클릭(self): # 오늘 총 매출 나타내기 if self.총금액.text() == ' - 원': self.총금액.setText('0') self.오늘총금액 += int(self.총금액.text()) self.오늘매출.setText(f'오늘 총 매출 : {self.오늘총금액}원') # 마리수, 금액, 중복확인리스트 초기화 self.마리수 = 0 self.금액 = 0 self.몇마리_temp = 0 self.금액_temp = 0 self.중복확인 = [] # ui초기화 self.총수량.setText('- 마리') self.총금액.setText(' - 원') # ui초기화 - 선택하기 초기화 for i in reversed(range(self.선택하기.count())): 삭제할위젯 = self.선택하기.itemAt(i).widget() self.선택하기.removeWidget(삭제할위젯) 삭제할위젯.setParent(None) #생선을 선택했을 때 실행되는 메서드 def 생선선택하기(self, 생선주문): 생선이름 = 생선주문 생선가격 = int(re.sub('[^0-9]','',생선주문.text())) # 같은 생선을 중복생성할 경우를 막기위한 if문 if 생선이름 in self.중복확인: pass else: self.몇마리_temp += self.마리수 self.금액_temp += self.금액 self.i += 1 self.중복확인.append(생선이름) self.선택하기.addWidget(생선주문, self.i, 0) self.선택하기.addWidget(self.스핀박스_만들기(), self.i, 3) self.선택하기.setAlignment(Qt.AlignTop) self.스핀박스.valueChanged.connect(lambda:self.계산하기(생선가격)) # 총수량과 총금액을 계산하기 위한 메서드 def 계산하기(self, 생선가격): self.마리수 = self.스핀박스.value() self.금액 = self.마리수*생선가격 self.총수량.setText(str(self.마리수+self.몇마리_temp)) self.총금액.setText(str(self.금액+self.금액_temp)) 프로그램무한반복 = QApplication(sys.argv) 실행인스턴스 = 생선가게POS기() 프로그램무한반복.exec_()
 

2-2 코드

  • '이전에 생성된 spinbox 값 변경 시 변경 내용 무시됨' → spinbox에 있는 값 받아와서 다른 메뉴를 클릭했을 경우 spinbox 위젯 삭제 후 label로 표기(이전값을 변경하지 못하도록 우회) → 정정버튼 추가
import sys from PyQt5.QtWidgets import * from PyQt5.QtGui import QIcon, QPixmap, QFont from PyQt5.QtCore import QCoreApplication, Qt, QDate import re class 생선가게POS기(QWidget): def __init__(self): super().__init__() self.UI초기화() def UI초기화(self): # 나중에 메서드에서 사용 될 변수들 self.j = 1 self.i = 0 self.중복확인 = [] self.마리수 = 0 self.금액 = 0 self.마리수_temp = 0 self.금액_temp = 0 self.오늘총금액 = 0 self.스핀박스_리스트 = [] # UI 초기화 가로정렬 = QHBoxLayout() 가로정렬.addWidget(self.메뉴()) 가로정렬.addWidget(self.주문내역()) 세로정렬 = QVBoxLayout() 세로정렬.addWidget(self.오늘()) 세로정렬.addLayout(가로정렬) self.setLayout(세로정렬) self.setWindowTitle('(주)캣네생선 POS기') self.setWindowIcon(QIcon('img/캣네생선.png')) self.setGeometry(700, 400, 650, 430) self.show() # 화면 상단부에 나오는 날짜와 오늘매출을 표시하는 메서드 def 오늘(self): self.날짜 = QDate.currentDate() self.오늘날짜 = QLabel(QDate.currentDate().toString('yyyy년 MM월 dd일')) self.오늘매출 = QLabel('오늘 총 매출 : 0원') 그룹박스_오늘 = QGroupBox() 가로정렬_오늘 = QHBoxLayout() 가로정렬_오늘.addWidget(self.오늘날짜) 가로정렬_오늘.addWidget(self.오늘매출, alignment=Qt.AlignRight) 그룹박스_오늘.setLayout(가로정렬_오늘) 그룹박스_오늘.setFixedHeight(50) return 그룹박스_오늘 # 화면 왼쪽의 메뉴를 표시하는 메서드 def 메뉴(self): 삼치 = QPushButton('\n삼치\n3500원\n',self) 고등어 = QPushButton('\n고등어\n4500원\n',self) 꽁치 = QPushButton('\n꽁치\n5000원\n',self) 연어 = QPushButton('\n연어\n5500원\n',self) 광어 = QPushButton('\n광어\n6000원\n',self) 굴비 = QPushButton('\n굴비\n7000원\n',self) 넙치 = QPushButton('\n넙치\n8000원\n',self) 갈치 = QPushButton('\n갈치\n12000원\n',self) 참돔 = QPushButton('\n참돔\n20000원\n',self) 그룹박스_생선 = QGroupBox('메뉴') 생선들 = QGridLayout() 생선들.addWidget(삼치, 1,0) 생선들.addWidget(고등어, 1,1) 생선들.addWidget(꽁치, 1,2) 생선들.addWidget(연어, 2,0) 생선들.addWidget(광어, 2,1) 생선들.addWidget(굴비, 2,2) 생선들.addWidget(넙치, 3,0) 생선들.addWidget(갈치, 3,1) 생선들.addWidget(참돔, 3,2) 삼치.clicked.connect(lambda:self.생선선택하기(self.삼치_주문)) 고등어.clicked.connect(lambda:self.생선선택하기(self.고등어_주문)) 꽁치.clicked.connect(lambda:self.생선선택하기(self.꽁치_주문)) 연어.clicked.connect(lambda:self.생선선택하기(self.연어_주문)) 광어.clicked.connect(lambda:self.생선선택하기(self.광어_주문)) 굴비.clicked.connect(lambda:self.생선선택하기(self.굴비_주문)) 넙치.clicked.connect(lambda:self.생선선택하기(self.넙치_주문)) 갈치.clicked.connect(lambda:self.생선선택하기(self.갈치_주문)) 참돔.clicked.connect(lambda:self.생선선택하기(self.참돔_주문)) 그룹박스_생선.setLayout(생선들) return 그룹박스_생선 # 메뉴를 클릭하면 나타나는 주문내역과 결제창을 보여주는 메서드 def 주문내역(self): 그룹박스_주문내역 = QGroupBox('주문 상품 목록') 세로정렬_주문내역 = QVBoxLayout() 세로정렬_주문내역.addWidget(self.주문내역_숨김()) 세로정렬_주문내역.addWidget(self.결제박스()) 그룹박스_주문내역.setLayout(세로정렬_주문내역) 그룹박스_주문내역.setFixedWidth(200) return 그룹박스_주문내역 # '주문내역' grid박스 내 아래쪽에 보여지는 결제박스 def 결제박스(self): 총수량_라벨 = QLabel('총 수량 :') self.총수량 = QLabel('- 마리') 총금액_라벨 = QLabel('총 금액 :') self.총금액 = QLabel(' - 원') 정정버튼 = QPushButton('정정하기',self) 결제버튼 = QPushButton('결제하기',self) # 인코딩문제로 인해서 lambda함수 사용함 # self.결제버튼.clicked.connect(self.결제하기_버튼클릭) 정정버튼.clicked.connect(lambda:self.정정하기_버튼클릭()) 결제버튼.clicked.connect(lambda:self.결제하기_버튼클릭()) 그룹박스_결제 = QGroupBox() 그룹박스_결제.setFlat(True) 결제값 = QGridLayout() 결제값.addWidget(총수량_라벨,0,0,1,3) 결제값.addWidget(self.총수량,0,5) 결제값.addWidget(총금액_라벨,1,0,1,3) 결제값.addWidget(self.총금액,1,5) 결제값.addWidget(정정버튼,2,0,1,6) 결제값.addWidget(결제버튼,3,0,1,6) 그룹박스_결제.setLayout(결제값) 그룹박스_결제.setFixedHeight(130) return 그룹박스_결제 # 메뉴를 클릭하면 오른쪽 주문내역에서 나타나는 라벨 def 주문내역_숨김(self): self.삼치_주문 = QLabel('삼치 3500원') self.고등어_주문 = QLabel('고등어 4500원') self.꽁치_주문 = QLabel('꽁치 5000원') self.연어_주문 = QLabel('연어 5500원') self.광어_주문 = QLabel('광어 6000원') self.굴비_주문 = QLabel('굴비 7000원') self.넙치_주문 = QLabel('넙치 8000원') self.갈치_주문 = QLabel('갈치 12000원') self.참돔_주문 = QLabel('참돔 20000원') 그룹박스_숨김 = QGroupBox() self.주문상품_목록 = QGridLayout() 그룹박스_숨김.setLayout(self.주문상품_목록) return 그룹박스_숨김 def 스핀박스_만들기(self): self.스핀박스 = QSpinBox() self.스핀박스.setFixedWidth(45) self.스핀박스_리스트.append(self.스핀박스) return self.스핀박스 # 정정하기 버튼 클릭했을 경우 실행되는 메서드 def 정정하기_버튼클릭(self): # 마리수, 금액, 중복확인리스트 초기화 self.마리수 = 0 self.금액 = 0 self.마리수_temp = 0 self.금액_temp = 0 self.중복확인 = [] self.스핀박스_리스트 = [] self.i = 0 # ui초기화 self.총수량.setText('- 마리') self.총금액.setText(' - 원') # ui초기화 - 주문상품_목록 초기화 self.위젯삭제하기() # 결제하기 버튼 클릭했을 경우 실행되는 메서드 def 결제하기_버튼클릭(self): if self.총금액.text() == ' - 원': self.총금액.setText('0') self.오늘총금액 += int(self.총금액.text()) self.오늘매출.setText(f'오늘 총 매출 : {self.오늘총금액}원') self.정정하기_버튼클릭() def 위젯삭제하기(self): for i in reversed(range(self.주문상품_목록.count())): 삭제할위젯 = self.주문상품_목록.itemAt(i).widget() self.주문상품_목록.removeWidget(삭제할위젯) 삭제할위젯.setParent(None) def 스핀박스_삭제하기(self): 마지막위젯 = self.스핀박스_리스트.pop(-1) self.주문상품_목록.removeWidget(마지막위젯) 마지막위젯.deleteLater() # 생선(메뉴)을 선택했을 때 실행되는 메서드 def 생선선택하기(self, 생선_주문): 생선이름 = str(re.sub('[^ㄱ-힗]', '', 생선_주문.text()))[:-1] 생선가격 = int(re.sub('[^0-9]','',생선_주문.text())) # 같은 생선을 중복생성할 경우를 막기위한 if문 if 생선이름 in self.중복확인: pass else: # 이전에 생성된 스핀박스가 있다면 그 값을 가져와서 변경이 불가능한 라벨로 만들기 if self.스핀박스_리스트: temp = self.스핀박스.value() self.스핀박스_라벨 = QLabel(str(temp)) self.스핀박스_삭제하기() self.주문상품_목록.addWidget(self.스핀박스_라벨, self.i,3) self.마리수_temp += self.마리수 self.금액_temp += self.금액 self.마리수,self.금액 = 0,0 self.i += 1 self.중복확인.append(생선이름) self.주문상품_목록.addWidget(생선_주문, self.i, 0) self.주문상품_목록.addWidget(self.스핀박스_만들기(), self.i, 3) self.주문상품_목록.setAlignment(Qt.AlignTop) self.스핀박스.valueChanged.connect(lambda:self.계산하기(생선가격)) # 총수량과 총금액을 계산하기 위한 메서드 def 계산하기(self, 생선가격): self.마리수 = self.스핀박스.value() self.금액 = self.마리수 * 생선가격 self.총수량.setText(str(self.마리수 + self.마리수_temp)) self.총금액.setText(str(self.금액 + self.금액_temp)) 프로그램무한반복 = QApplication(sys.argv) 실행인스턴스 = 생선가게POS기() 프로그램무한반복.exec_()
 

3. 상세 내용

  • 전체 UI는 - 화면 상단에서 오늘의 날짜와 매출금액을 확인할 수 있는 '오늘' - 화면 왼쪽에서 주문할 메뉴를 선택할 수 있는 '메뉴' - 화면 오른쪽에서 선택된 메뉴들의 가격과 수량을 선택할 수 있는 '주문내역' 이렇게 세부분으로 구성되어 있습니다.
 
# 생선(메뉴)을 선택했을 때 실행되는 메서드 def 생선선택하기(self, 생선_주문): 생선이름 = str(re.sub('[^ㄱ-힗]', '', 생선_주문.text()))[:-1] 생선가격 = int(re.sub('[^0-9]','',생선_주문.text()))
  • 화면 왼쪽, 생선 이름으로 생성된 버튼을 클릭했을 때 실행되는 함수입니다.
  • 화면 오른쪽에 생기는 주문내역을 표시해주기 위해 '생선_주문'인자값을 받아옵니다. 이 인자값에서 정규식을 이용하여 한글만 뽑아내면 '생선이름+원'을 추출할 수 있고, 숫자만 뽑아내면 생선가격 값을 추출할 수 있습니다. (문자열 인덱싱을 통해 생선이름 마지막에 붙어있는 '원'을 제거해줍니다.)
 
# 같은 생선을 중복생성할 경우를 막기위한 if문 if 생선이름 in self.중복확인: pass else: # 이전에 생성된 스핀박스가 있다면 그 값을 가져와서 변경이 불가능한 라벨로 만들기 if self.스핀박스_리스트: temp = self.스핀박스.value() self.스핀박스_라벨 = QLabel(str(temp)) self.스핀박스_삭제하기() self.주문상품_목록.addWidget(self.스핀박스_라벨, self.i,3) self.마리수_temp += self.마리수 self.금액_temp += self.금액 self.마리수, self.금액 = 0, 0 self.i += 1 self.중복확인.append(생선이름) self.주문상품_목록.addWidget(생선_주문, self.i, 0) self.주문상품_목록.addWidget(self.스핀박스_만들기(), self.i, 3) self.주문상품_목록.setAlignment(Qt.AlignTop) self.스핀박스.valueChanged.connect(lambda:self.계산하기(생선가격)) # 총수량과 총금액을 계산하기 위한 메서드 def 계산하기(self, 생선가격): self.마리수 = self.스핀박스.value() self.금액 = self.마리수 * 생선가격 self.총수량.setText(str(self.마리수 + self.마리수_temp)) self.총금액.setText(str(self.금액 + self.금액_temp))
  • 같은 버튼이 여러번 클릭되었을 경우를 방지하기 위해 '중복확인'이라는 리스트를 초기화 시킬때 선언해줍니다.
  • 중복확인 리스트 안에 생선이름이 없을 경우에만 QGridLayout으로 선언된 '주문 상품 목록'에 현재 선택된 생선 이름의 라벨과 SpinBox를 생성해줍니다.
  • 중첩된 if문은 이전 spinbox를 제거하고 label로 생성해주는 코드입니다.
  • 마리수_temp, 금액_temp은 새로운 메뉴 버튼이 눌렸을 경우 이전값을 가져오기 위한 임시 저장소라고 생각하시면 됩니다.
  • 스핀박스의 값이 변화되었을 경우, 계산하기 함수를 호출하여 총수량과 총금액을 실시간으로 출력해줍니다.
 
정정버튼 = QPushButton('정정하기',self) 결제버튼 = QPushButton('결제하기',self) 정정버튼.clicked.connect(lambda:self.정정하기_버튼클릭()) 결제버튼.clicked.connect(lambda:self.결제하기_버튼클릭())
# 정정하기 버튼 클릭했을 경우 실행되는 메서드 def 정정하기_버튼클릭(self): # 마리수, 금액, 중복확인리스트 초기화 self.마리수 = 0 self.금액 = 0 self.마리수_temp = 0 self.금액_temp = 0 self.중복확인 = [] self.스핀박스_리스트 = [] self.i = 0 # ui초기화 self.총수량.setText('- 마리') self.총금액.setText(' - 원') # ui초기화 - 주문상품_목록 초기화 self.위젯삭제하기() # 결제하기 버튼 클릭했을 경우 실행되는 메서드 def 결제하기_버튼클릭(self): if self.총금액.text() == ' - 원': self.총금액.setText('0') self.오늘총금액 += int(self.총금액.text()) self.오늘매출.setText(f'오늘 총 매출 : {self.오늘총금액}원') self.정정하기_버튼클릭()
  • '정정하기' 버튼을 클릭했을 경우에는 '정정하기_버튼클릭()' 메서드를 연결하고, '결제하기' 버튼을 클릭했을 경우에는 '결제하기_버튼클릭()' 메서드를 연결시켜줍니다.
  • 정정하기 버튼을 클릭했을 때와 결제하기 버튼을 클릭했을 경우 동작원리는 비슷하여 결제하기 버튼에서 호출하도록 했습니다. 다만 결제하기 버튼에서 오늘 총금액을 계산해줘야 하므로 해당 코드를 추가했습니다.
 
def 위젯삭제하기(self): for i in reversed(range(self.주문상품_목록.count())): 삭제할위젯 = self.주문상품_목록.itemAt(i).widget() self.주문상품_목록.removeWidget(삭제할위젯) 삭제할위젯.setParent(None)
  • 위젯을 삭제해주는 메서드입니다.
  • 우선 주문상품_목록의 갯수만큼 for문 동작시켜 주문상품_목록에서 item을 찾아 삭제할 위젯에 할당해주고, removeWidget을 이용하여 레이아웃 리스트에서 제거한 뒤 setParent를 이용해서 gui까지 삭제해주면 위젯이 삭제됩니다.
 
def 스핀박스_삭제하기(self): 마지막위젯 = self.스핀박스_리스트.pop(-1) self.주문상품_목록.removeWidget(마지막위젯) 마지막위젯.deleteLater()
  • 스핀박스를 삭제해주는 메서드입니다.
  • '스핀박스_만들기()' 메서드에서 생성된 '스핀박스'를 '스핀박스_리스트'에 저장해두고, 그 저장해둔 스핀박스를 꺼내서 지워줍니다.
  • deleteLater()는 개체를 삭제하도록 예약합니다.

4. 실행 화면

notion imagenotion image