GithubHelp home page GithubHelp logo

laserlithography's Introduction

Laser Lithography

Information

2023년 2학기 물리학 캡스톤디자인1에 진행한 Maskless Laser Lithography이다.

Figure 1

Setup

Figure 2

장치를 모두 연결한 후 프로그램을 실행하여, ...

Code

main.py가 실행 파일이고, controller.py가 메인 실행 파일에 들어가는 모듈이다.

config.yml에서 초기값 설정을 할 수 있다.

resiurcesinput_image.jpg를 넣어 스캐닝하면 filtered_values.csv 데이터 파일이 만들어진다.

log는 명령어 실행 시 생성되는 로그이며, src는 개발 중 소스 파일이다.

config.yml

config code [접기/펼치기]
image:
  setup:
    x_size: 100
    y_size: 100
    intensity: 0.35

이미지의 크기 및 명암에 따른 수치 (1, 0)을 설정할 수 있다.

motor:
  setup:
    port: COM3
    baudrate: 9600
    # Check (-2000, -2000) to (2000, 2000). 
    # Boolean: True or False.
    check_range: False
  x_axis:
    l_speed: 400
    f_speed: 400
    rate: 20
    s_rate: 20
  y_axis:
    l_speed: 400
    f_speed: 400
    rate: 20
    s_rate: 20
  initial_position: 
    x: 0
    y: 0

모터 컨트롤러의 초기값 설정할 수 있다.

laser:
  setup:
    port: COM7
    baudrate: 9600
    # Enter delay duration in seconds.
    duration_time: 0.10

레이저의 ON/OFF 사이의 시간을 설정할 수 있다.

controller.py

import code [접기/펼치기]
import yaml
import csv
import serial
import time
import os
import tkinter as tk
import threading
import torch
import torchvision.transforms as tr
from PIL import Image
from loguru import logger
from datetime import datetime

import하는 모듈이다.

Config code [접기/펼치기]
class Config():
    def __init__(self):
        try:
            with open(file="config.yml", mode="r") as config_file:
                self.configuration = yaml.safe_load(config_file)
        except FileNotFoundError:
            print("File not found or path incorrect.")
            self.configuration = {}

    def readConfig(self):
        try:
            with open(file="config.yml", mode="r") as config_file:
                self.configuration = yaml.safe_load(config_file)
        except FileNotFoundError:
            print("File not found or path incorrect.")
            self.configuration = {}

config 설정을 불러오는 클래스이다.

ImageConverter code [접기/펼치기]
class ImageConverter:
    def __init__(self, app, image_path="resources\\input_image.jpg"):
        self.image_path = image_path
        self.csv_filtered = "resources\\filtered_values.csv"

        self.count = 0
        self.app = app
        self.stop_event = threading.Event()
        self.is_running = False  # 작업 실행 여부를 나타내는 플래그
        self.csv_size = 0
        # self.app = app

        self.configInstance = Config()
        self.config = self.configInstance.configuration

        self.x_size = int(self.config['image']['setup']['x_size'])
        self.y_size = int(self.config['image']['setup']['y_size'])
        self.intensity = float(self.config['image']['setup']['intensity'])

    def readConfig(self):
        self.configInstance.readConfig()
        self.config = self.configInstance.configuration

        self.x_size = int(self.config['image']['setup']['x_size'])
        self.y_size = int(self.config['image']['setup']['y_size'])
        self.intensity = float(self.config['image']['setup']['intensity'])

    def processAndFilterImage(self):
        self.readConfig()

        img = Image.open(self.image_path)
        img.thumbnail((self.x_size, self.y_size))
        transform = tr.Compose([tr.ToTensor()])
        image_tensor = transform(img)
        _, height, width = image_tensor.shape

        with open(self.csv_filtered, mode='w', newline='') as filtered_csv_file:
            writer = csv.writer(filtered_csv_file)
            writer.writerow(["X", "Y"])

            self.csv_size = height * width
            self.count = 0

            for row in range(height):
                for col in range(width):
                    red_value = image_tensor[0, row, col].item()
                    green_value = image_tensor[1, row, col].item()
                    blue_value = image_tensor[2, row, col].item()
                    result = 1 if red_value <= self.intensity or green_value <= self.intensity or blue_value <= self.intensity else 0
                    if result == 1:
                        writer.writerow([col, row])
                    self.count += 1
                    self.updateScanningCountLabel()

        self.stop_event.set()
        self.is_running = False

    def updateScanningCountLabel(self):
        self.app.root.after(0, self.app.updateScanningCountLabel)  # GUI 업데이트 요청

    def stopScan(self):
        self.stop_event.set()
        self.is_running = False  # 작업이 중지됨을 표시

    def startScan(self):
        self.stop_event.clear()
        threading.Thread(target=self.processAndFilterImage).start()  # 새 스레드에서 goRecipe 실행
        self.is_running = True  # 작업이 시작됨을 표시

    def isRunning(self):
        return self.is_running

jpeg 파일을 csv로 변환하는 클래스이다.

CSVDataReader code [접기/펼치기]
class CSVDataReader:
    def __init__(self, path):
        self.file_path = path
        self.X = []
        self.Y = []

    def read_csv(self):
        with open(self.file_path, mode="r") as csv_file:
            csv_reader = csv.DictReader(csv_file)
            
            for row in csv_reader:
                x_value = int(row['X'])
                y_value = int(row['Y'])
                self.X.append(x_value)
                self.Y.append(y_value)

    def close(self):
        # 파일을 열었지만 닫지 않는 것이 좋습니다.
        pass

csv 파일을 리스트로 불러오는 클래스이다.

Logger code [접기/펼치기]
class Logger:
    def __init__(self, log_dir="log"):
        self.log_dir = log_dir
        self.log_file = self.generateLogFile()
        self.configureLogger()
    
    def generateLogFile(self):
        now = datetime.now()
        formatted_datetime = now.strftime("%Y-%m-%d_%H-%M-%S")
        log_file = os.path.join(self.log_dir, f"Report_{formatted_datetime}.log")
        return log_file
    
    def configureLogger(self):
        logger.remove()
        logger.add(self.log_file, level="TRACE", format="<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <8}</level> | <level>{message}</level>", rotation="1 week", retention="2 weeks")
    
    def getLogger(self):
        return logger

로그를 저장하는 클래스이다.

Motor code [접기/펼치기]
class Motor:
    def __init__(self, port='COM3', baudrate=9600, checkRange=True):
        
        self.configInstance = Config()
        self.config = self.configInstance.configuration

        # Set port and baudrate value with config
        port = str(self.config['motor']['setup']['port'])
        baudrate = int(self.config['motor']['setup']['baudrate'])
        check_range = self.config['motor']['setup']['check_range']
            
        try:
            self.ser = serial.Serial(port=port, baudrate=baudrate, stopbits=1)
        except Exception as e:
            raise Exception(f"Usb not connected or port doesn't have permission for serial. try 'sudo chmod 666 {port}'")
        
        # Check for CW and CCW soft limit status
        cw_soft_limit_status = self.writeAndGetResponse(":CWLSET?")
        ccw_soft_limit_status = self.writeAndGetResponse(":CCWLSET?")
        
        if cw_soft_limit_status == '1':
            raise Exception("CW Soft Limit is enabled.")
        if ccw_soft_limit_status == '1':
            raise Exception("CCW Soft Limit is enabled.")
                        
        # Set speed value with config
        x_l_speed = int(self.config['motor']['x_axis']['l_speed'])
        x_f_speed = int(self.config['motor']['x_axis']['f_speed'])
        y_l_speed = int(self.config['motor']['y_axis']['l_speed'])
        y_f_speed = int(self.config['motor']['y_axis']['f_speed'])
        
        # Set rate value with config
        x_rate = int(self.config['motor']['x_axis']['rate'])
        x_s_rate = int(self.config['motor']['x_axis']['s_rate'])
        y_rate = int(self.config['motor']['y_axis']['rate'])
        y_s_rate = int(self.config['motor']['y_axis']['s_rate'])
        
        # Set initial position value with config
        self.init_x_pos = int(self.config['motor']['initial_position']['x'])
        self.init_y_pos = int(self.config['motor']['initial_position']['y'])
        
        # axis setting
        self.x = "1"
        self.y = "2"
        
        self.setSpeed(self.x, x_l_speed, x_f_speed)
        self.setSpeed(self.y, y_l_speed, y_f_speed)
        self.setRate(self.x, x_rate, x_s_rate)
        self.setRate(self.y, y_rate, y_s_rate)
        self.goAbs(self.init_x_pos, self.init_y_pos)
        
        if checkRange == check_range:
            self.goAbs(0, 0)
            self.goAbs(2000, 2000)
            self.goAbs(-2000, -2000)
            self.goAbs(self.init_x_pos, self.init_y_pos)

    def readConfig(self):
        self.configInstance.readConfig()
        self.config = self.configInstance.configuration

        self.init_x_pos = int(self.config['motor']['initial_position']['x'])
        self.init_y_pos = int(self.config['motor']['initial_position']['y'])

    def writeCommand(self, command: str):
        command += '\r'
        self.ser.write(command.encode())
    
    def getResponse(self):
        output = self.ser.read_until(b'\r')
        return output.decode()[:-1]
    
    def writeAndGetResponse(self, command: str):
        self.writeCommand(command)
        return self.getResponse()
    
    def goAbsWithOutStop(self, xPos, yPos):
        self.writeCommand(f"axi1:goabs {xPos}")
        self.writeCommand(f"axi2:goabs {yPos}")
    
    def goAbs(self, xPos, yPos):
        self.writeCommand(f"axi1:goabs {xPos}")
        self.writeCommand(f"axi2:goabs {yPos}")
        self.waitForStop()
        
    def setSpeed(self, axis, lSpeed, fSpeed):
        # set L_Speed
        self.writeCommand(f"axi{axis}:L{axis} {lSpeed}")
        # set F_Speed
        self.writeCommand(f"axi{axis}:F{axis} {fSpeed}")
        
    def setRate(self, axis, Rate, sRate):
        # set Rate
        self.writeCommand(f"axi{axis}:R{axis} {Rate}")
        # set S_Rate
        self.writeCommand(f"axi{axis}:S{axis} {sRate}")

    def requestPos(self, axis):
        self.writeCommand(f"axi{axis}:PULSA")
        return f"axi{axis}:PULSA"
    
    def waitForStop(self):
        while True:
            if self.writeAndGetResponse("MOTIONAll?") == '0':
                break
            time.sleep(0.01)
        time.sleep(0.1)
        
    def close(self):
        self.ser.close()

모터 컨트롤러를 제어하는 클래스이다.

Laser code [접기/펼치기]
class Laser:
    def __init__(self, port='COM4', baudrate=9600):
        
        self.configInstance = Config()
        self.config = self.configInstance.configuration

        port = str(self.config['laser']['setup']['port'])
        baudrate = int(self.config['laser']['setup']['baudrate'])

        try:
            self.arduino = serial.Serial(port=port, baudrate=baudrate)
        except Exception as e:
            raise Exception(f"Usb not connected or port doesn't have permission for serial. try 'sudo chmod 666 {port}'")
        
        time.sleep(2)

    def onLaser(self):
        self.arduino.write(b'1')
        print("Laser ON")

    def offLaser(self):
        self.arduino.write(b'0')
        print("Laser OFF")

    def close(self):
        self.arduino.close()

레이저를 제어하는 클래스이다.

Recipe code [접기/펼치기]
class Recipe():
    def __init__(self, app):
        self.count = 0
        self.stop_event = threading.Event()
        self.is_running = False  # 작업 실행 여부를 나타내는 플래그
        self.app = app
        self.csv_size = 0

        self.motor = Motor()
        self.laser = Laser()
        self.logInstance = Logger()
        self.logger = self.logInstance.getLogger()
        self.configInstance = Config()
        self.config = self.configInstance.configuration
        self.delayDuration = float(self.config['laser']['setup']['duration_time'])
        
        self.csv_reader = CSVDataReader(path="resources\\filtered_values.csv")
        self.csv_reader.read_csv()
        self.csv_size = len(self.csv_reader.X)

    def readConfig(self):
        self.configInstance = Config()
        self.config = self.configInstance.configuration
        self.delayDuration = float(self.config['laser']['setup']['duration_time'])
        
        self.csv_reader.read_csv()
        self.csv_size = len(self.csv_reader.X)

    def stopRecipe(self):
        self.stop_event.set()
        self.is_running = False  # 작업이 중지됨을 표시

    def startRecipe(self):
        self.stop_event.clear()
        threading.Thread(target=self.goRecipe).start()  # 새 스레드에서 goRecipe 실행
        self.is_running = True  # 작업이 시작됨을 표시

    def isRunning(self):
        return self.is_running
        
    def goRecipe(self):
        self.readConfig()

        self.csv_size = len(self.csv_reader.X)
        try:
            for self.count in range(self.csv_size):
                if self.stop_event.is_set():
                    break
                self.target_x = self.motor.init_x_pos + (self.csv_reader.X[self.count] * 20)
                self.target_y = self.motor.init_y_pos + (self.csv_reader.Y[self.count] * 20)
                self.motor.goAbs(self.target_x, self.target_y)  # scale factor = 20
                self.laser.onLaser()
                time.sleep(self.delayDuration)
                self.laser.offLaser()
                self.updateCountLabel()
                self.logger.trace(f"Perform lithography at absolute position ({self.target_x}, {self.target_y}).")
                
        except Exception as e:
            self.logger.error(f"An error occurred in absolute position ({self.target_x}, {self.target_y}).")
            raise Exception(f"An error occurred in absolute position ({self.target_x}, {self.target_y}).")
        else:
            self.logger.success("Success Lithography.")

    def updateCountLabel(self):
        self.app.root.after(0, self.app.updateCountLabel)  # GUI 업데이트 요청

    def startAbs(self, x, y):
        if not self.isRunning():  # 작업이 실행 중이지 않은 경우에만 실행
            self.motor.goAbs(x * 20, y * 20)  # scale factor = 20
            self.is_running = True  # 작업이 시작됨을 표시
            print("Starting Abs")

    def stopAbs(self):
        if self.isRunning():  # 작업이 실행 중인 경우에만 실행
            pass  # 예시로 motor를 중지하는 코드를 추가할 수 있습니다.
            self.is_running = False  # 작업이 중지됨을 표시
            print("Stopping Abs")

    def enableButtons(self):
        # 작업이 끝난 후에 버튼을 활성화하는 코드 추가
        if self.app:
            self.app.enableButtons()

레시피 알고리즘 및 모터, 레이저를 작동하는 클래스이다.

Window code [접기/펼치기]
class Window:
    def __init__(self, root):
        self.root = root
        self.recipe = Recipe(self)
        self.converter = ImageConverter(self)
        # self.app = self

        self.LargeFrame = tk.Frame(root)
        self.LargeFrame.pack(anchor="center")
        self.root.title("Laser Lithography")
        
        # Frame1: Title Frame
        self.title_frame = self.setFrame(self.LargeFrame, 0, 0)
        
        self.title1 = self.setTitle(self.title_frame, 0, 0, text="Laser Lithography Control Window")
        
        self.title_empty_10 = self.setEmptyBox(self.title_frame, 1, 0, height=2)
        
        # Frame2: Moving Commands Frame
        self.moving_frame = self.setFrame(self.LargeFrame, 1, 0)
        
        self.moving_subtitle_00 = self.setSubTitle(self.moving_frame, 0, 0, text="< Test moving: Move to Position >", columnspan=5)

        self.moving_empty_10 = tk.Label(self.moving_frame, height=1)
        self.moving_empty_10.grid(row=1, column=0)
        
        self.posi_x = tk.Label(self.moving_frame, text="0", font=('Arial', 11))
        self.posi_x.grid(row=2, column=0, columnspan=2)
        self.posi_y = tk.Label(self.moving_frame, text="0", font=('Arial', 11))
        self.posi_y.grid(row=2, column=3, columnspan=2)
        
        self.x_label = tk.Label(self.moving_frame, text="X:", width=2, font=('Arial', 11))
        self.x_label.grid(row=3, column=0)
        self.x_entry = tk.Entry(self.moving_frame, width=10, font=('Arial', 11))
        self.x_entry.grid(row=3, column=1)
        
        self.moving_empty_02 = tk.Label(self.moving_frame, width=2)
        self.moving_empty_02.grid(row=3, column=2)
        
        self.y_label = tk.Label(self.moving_frame, text="Y:", width=2, font=('Arial', 11))
        self.y_label.grid(row=3, column=3)
        self.y_entry = tk.Entry(self.moving_frame, width=10, font=('Arial', 11))
        self.y_entry.grid(row=3, column=4)
        
        self.moving_empty_20 = tk.Label(self.moving_frame, height=1)
        self.moving_empty_20.grid(row=4, column=0)
        
        self.go_abs_button = tk.Button(self.moving_frame, text="Go Absolute", width=26, font=('Arial', 11), command=self.startAbsButton)
        self.go_abs_button.grid(row=5, column=0, columnspan=5)

        self.moving_empty_40 = self.setEmptyBox(self.moving_frame, 5, 0, height=3)
        
        # Frame3: Recipe operating Frame
        self.recipe_frame = self.setFrame(self.LargeFrame, 3, 0)

        self.recipe_subtitle_00 = self.setSubTitle(self.recipe_frame, 0, 0, text="< Recipe operating >", columnspan=5)

        self.recipe_empty_10 = tk.Label(self.recipe_frame, height=1)
        self.recipe_empty_10.grid(row=1, column=0)

        self.scan_count_label = tk.Label(self.recipe_frame, text="- / -", font=('Arial', 12))
        self.scan_count_label.grid(row=2, column=0, columnspan=4)

        self.scan_count_per_label = tk.Label(self.recipe_frame, text="0%", font=('Arial', 12))
        self.scan_count_per_label.grid(row=2, column=4, columnspan=1)

        self.run_scan_button = tk.Button(self.recipe_frame, text="Run Scanning", width=12, font=('Arial', 11), command=self.startScanningButton)
        self.run_scan_button.grid(row=3, column=0, columnspan=2)

        self.recipe_empty_32 = tk.Label(self.recipe_frame, width=2)
        self.recipe_empty_32.grid(row=3, column=2)

        self.stop_scan_button = tk.Button(self.recipe_frame, text="Stop Scanning", width=12, font=('Arial', 11), command=self.stopScanningButton)
        self.stop_scan_button.grid(row=3, column=3, columnspan=2)

        self.recipe_empty_40 = tk.Label(self.recipe_frame, height=1)
        self.recipe_empty_40.grid(row=4, column=0)

        self.recipe_count_label = tk.Label(self.recipe_frame, text="- / -", font=('Arial', 12))
        self.recipe_count_label.grid(row=5, column=0, columnspan=4)

        self.recipe_count_per_label = tk.Label(self.recipe_frame, text="0%", font=('Arial', 12))
        self.recipe_count_per_label.grid(row=5, column=4, columnspan=1)

        self.run_recipe_button = tk.Button(self.recipe_frame, text="Run Recipe", width=12, font=('Arial', 11), command=self.startRecipeButton)
        self.run_recipe_button.grid(row=6, column=0, columnspan=2)

        self.recipe_empty_62 = tk.Label(self.recipe_frame, width=2)
        self.recipe_empty_62.grid(row=6, column=2)

        self.stop_recipe_button = tk.Button(self.recipe_frame, text="Stop Recipe", width=12, font=('Arial', 11), command=self.stopRecipeButton)
        self.stop_recipe_button.grid(row=6, column=3, columnspan=2)
        
        # Frame4: Exit Frame
        self.exit_frame = self.setFrame(self.LargeFrame, 4, 0)
        
        self.exit_empty_00 = tk.Label(self.exit_frame, height=3)
        self.exit_empty_00.grid(row=0)
        
        self.exit_button = tk.Button(self.exit_frame, text="Exit", width=12, font=('Arial', 11), command=self.closeWindowButton)
        self.exit_button.grid(row=1)
        
        self.exit_empty_20 = tk.Label(self.exit_frame, height=2)
        self.exit_empty_20.grid(row=2)
        
        
    def setFrame(self, root, row, column, rowspan=1, columnspan=1):
        self.frame = tk.Frame(root)
        self.frame.grid(row=row, column=column, rowspan=rowspan, columnspan=columnspan)
        return self.frame
        
    def setTitle(self, frame, row, column, text, rowspan=1, columnspan=1, width=30, font=('Arial', 20)):
        self.title_label = tk.Label(frame, text=text, width=width, font=font)
        self.title_label.grid(row=row, column=column, rowspan=rowspan, columnspan=columnspan)
        
    def setSubTitle(self, frame, row, column, text, rowspan=1, columnspan=1, bg="#E0E0E0", font=('Arial', 12)):
        self.subtitle_label = tk.Label(frame, text=text, bg=bg, font=font, justify=tk.LEFT, relief="groove")
        self.subtitle_label.grid(row=row, column=column, rowspan=rowspan, columnspan=columnspan, sticky='w')
        
    def setEmptyBox(self, frame, row, column, width=1, height=1):
        self.emptybox = tk.Label(frame, width=width, height=height)
        self.emptybox.grid(row=row, column=column)
    
    # startAbs
    def startAbsButton(self):
        # Disable command (버튼 커멘드가 실행되는 동안 다른 버튼을 비활성화 상태로 설정)
        self.go_abs_button.config(state=tk.DISABLED)
        self.run_scan_button.config(state=tk.DISABLED)
        self.stop_scan_button.config(state=tk.DISABLED)
        self.run_recipe_button.config(state=tk.DISABLED)
        self.stop_recipe_button.config(state=tk.DISABLED)
        self.exit_button.config(state=tk.DISABLED)

        thread = threading.Thread(target=self.runAbsWithButtonControl)
        thread.start()

    def runAbsWithButtonControl(self):
        try:
            # 좌표 입력 필드에서 값을 가져옴
            x_input = self.x_entry.get()
            y_input = self.y_entry.get()
            
            if not x_input or not y_input:
                self.enableButtons()
                return  # 입력이 없는 경우 아무 작업도 수행하지 않음
            
            x_pos = int(x_input)
            y_pos = int(y_input)

            self.recipe.startAbs(x_pos, y_pos)
            
        except Exception as e:
            print(f"An error occurred: {str(e)}")
        
        time.sleep(2)
        self.recipe.stopRecipe()
        self.enableButtons()

    # startRecipe
    def startRecipeButton(self):
        if not self.recipe.isRunning():  # 작업이 실행 중이지 않은 경우에만 실행
            self.go_abs_button.config(state=tk.DISABLED)
            self.run_scan_button.config(state=tk.DISABLED)
            self.stop_scan_button.config(state=tk.DISABLED)
            self.run_recipe_button.config(state=tk.DISABLED)
            self.exit_button.config(state=tk.DISABLED)

            start_thread = threading.Thread(target=self.runRecipeWithButtonControl)
            start_thread.start()

    def runRecipeWithButtonControl(self):
        self.recipe.startRecipe()
        print("On startRecipeButton")

        # Recipe 작업이 완료될 때까지 대기
        while self.recipe.isRunning():
            time.sleep(0.1)  # 일시적으로 대기, 이 과정을 수정하여 적절한 대기 방법으로 변경할 수 있습니다.

        # Recipe 작업이 끝나면 버튼 활성화
        self.recipe.enableButtons() # self.root.after(0, self.enableButtons)

        if self.recipe.count+1 == self.recipe.csv_size:
            self.stopRecipeButton()
            self.recipe.enableButtons()
            time.sleep(0.1)
        else:
            while self.recipe.isRunning():
                time.sleep(0.1)

    # stopRecipe
    def stopRecipeButton(self):
        if self.recipe.isRunning():  # 작업이 실행 중인 경우에만 실행
            self.go_abs_button.config(state=tk.DISABLED)
            self.run_scan_button.config(state=tk.DISABLED)
            self.stop_scan_button.config(state=tk.DISABLED)
            self.run_recipe_button.config(state=tk.DISABLED)
            self.stop_recipe_button.config(state=tk.DISABLED)
            self.exit_button.config(state=tk.DISABLED)

            self.recipe.stopRecipe()
            print("On stopRecipeButton")

            # Recipe 작업이 완료될 때까지 대기
            while self.recipe.isRunning():
                time.sleep(0.1)  # 일시적으로 대기, 적절한 대기 방법으로 변경 가능

            self.enableButtons()

    def startScanningButton(self):
        if not self.converter.isRunning():  # 작업이 실행 중이지 않은 경우에만 실행
            self.go_abs_button.config(state=tk.DISABLED)
            self.run_scan_button.config(state=tk.DISABLED)
            self.run_recipe_button.config(state=tk.DISABLED)
            self.stop_recipe_button.config(state=tk.DISABLED)
            self.exit_button.config(state=tk.DISABLED)

            start_thread = threading.Thread(target=self.runScanWithButtonControl)
            start_thread.start()

    def runScanWithButtonControl(self):
        self.converter.startScan()
        print("On startScanningButton")

        # Recipe 작업이 완료될 때까지 대기
        while self.converter.isRunning():
            time.sleep(0.1)  # 일시적으로 대기, 이 과정을 수정하여 적절한 대기 방법으로 변경할 수 있습니다.

        # Recipe 작업이 끝나면 버튼 활성화
        self.converter.enableButtons() # self.root.after(0, self.enableButtons)

    def stopScanningButton(self):
        if self.converter.isRunning():  # 작업이 실행 중인 경우에만 실행
            self.go_abs_button.config(state=tk.DISABLED)
            self.run_scan_button.config(state=tk.DISABLED)
            self.stop_scan_button.config(state=tk.DISABLED)
            self.run_recipe_button.config(state=tk.DISABLED)
            self.stop_recipe_button.config(state=tk.DISABLED)
            self.exit_button.config(state=tk.DISABLED)

            self.converter.startScan()
            print("On stopScanningButton")

            # Recipe 작업이 완료될 때까지 대기
            while self.converter.isRunning():
                time.sleep(0.1)  # 일시적으로 대기, 적절한 대기 방법으로 변경 가능

            self.converter.enableButtons()

    def enableButtons(self):
        self.go_abs_button.config(state=tk.NORMAL)
        self.run_scan_button.config(state=tk.NORMAL)
        self.stop_scan_button.config(state=tk.NORMAL)
        self.run_recipe_button.config(state=tk.NORMAL)
        self.stop_recipe_button.config(state=tk.NORMAL)
        self.exit_button.config(state=tk.NORMAL)

    def closeWindowButton(self):
        self.root.destroy()

    def updateCountLabel(self):
        count_str = str(self.recipe.count+1).zfill(len(str(self.recipe.csv_size)))
        csv_size_str = str(self.recipe.csv_size)
        formatted_text = f"{count_str} / {csv_size_str}"
        count_per_str = str(int(((self.recipe.count+1)/self.recipe.csv_size)*100))
        formatted_per_text = f"{count_per_str}%"
        
        self.recipe_count_label.config(text=formatted_text, justify='center')
        self.recipe_count_per_label.config(text=formatted_per_text, justify='center')

    def updateScanningCountLabel(self):
        count_str = str(self.converter.count+1).zfill(len(str(self.converter.csv_size)))
        csv_size_str = str(self.converter.csv_size)
        formatted_text = f"{count_str} / {csv_size_str}"
        count_per_str = str(int(((self.converter.count+1)/self.converter.csv_size)*100))
        formatted_per_text = f"{count_per_str}%"
        
        self.scan_count_label.config(text=formatted_text, justify='center')
        self.scan_count_per_label.config(text=formatted_per_text, justify='center')

윈도우 앱 패널을 만드는 클래스이다.

laserlithography's People

Contributors

shihyeon avatar kkkkiie avatar quantum-love avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.