import json
import sys
import time
import math
import traceback
import pandas as pd
import PyQt6.QtGui as QtGui
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QApplication, QCheckBox, QHBoxLayout, QLabel, QMainWindow, QSpinBox, QPushButton, QScrollArea, QVBoxLayout, QWidget, QLineEdit, QTableWidget, QTableWidgetItem, QProgressBar, QHeaderView, QFileDialog, QDialog, QFormLayout, QComboBox, QMessageBox
from PyQt6.QtCore import QThread, pyqtSignal, QSize

class ThreadDataProcess(QThread):
    def __init__(self, view):
        super().__init__()
        self.view = view

    def run(self):
        self.view.page_filter()

class ThreadDataInit(QThread):
    signal_complete = pyqtSignal(int)

    def __init__(self, view):
        super().__init__()
        self.view = view

        self.signal_complete.connect(self.view.slot_data_init_complete)

    def run(self):
        self.signal_complete.emit(self.view.data_init())

class FunctionDialog(QDialog):
    signal_configuring = pyqtSignal()

    def __init__(self, parent, width = 600, height = 400):
        super().__init__(parent)
        self.resize(QSize(width, height))

    def mouseMoveEvent(self, event: QtGui.QMouseEvent):
        self.signal_configuring.emit()

class View(QMainWindow):
    ########## 信号 ##########
    # 进度更新信号
    signal_progress_update = pyqtSignal(int)
    signal_error = pyqtSignal(str)

    def __init__(self):
        super().__init__()

        # 线程
        self.is_abort = False
        self.thread_data_process = ThreadDataProcess(self)
        self.thread_data_init = ThreadDataInit(self)

        # 当日日期
        self.date = time.strftime("%Y%m%d", time.localtime())

        # 状态
        self.is_initialized = False

        # 数据文件名
        self.data_struct_path = ""
        self.data_path = ""
        self.current_data_path = "./"
        self.current_data_struct_path = "./"

        # 数据
        self.page_num = -1
        ## 保存当前条件及数据下当前页面所读取的区域起始索引
        ### 索引为 page_num - 1
        ### 值为区域起始索引，每个区域值单位大小为1000
        self.page_sections = []
        ### 保存当前条件及数据下当前页面行数
        # self.page_line_amounts = []
        ### 单页数据容量
        self.page_capacity = 1000
        ## 起始区域索引
        self.begin_section = 1
        ## 终止区域索引
        self.end_section = 1
        ## 当前区域索引
        self.current_section = 0

        ## 当前页面行数
        self.current_line_num = 0

        ## 文件是否存在表头
        self.has_header = False
        ## 文件总行数
        self.max_line_num = 0
        ## 文件最大索引
        self.max_section = 0

        self.data = {}
        self.data_view = {}
        self.data_struct = {}
        self.table_labels = []

        # 筛选条件
        self.dict_str_condition = {
            "=": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] == target],
            "≠": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] != target],
            "≤": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] <= target],
            "≥": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] >= target],
            "<": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] < target],
            ">": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] > target],
            "between": self.between_expression_parse,
            "contain": lambda df, target, index: df[df[list(self.data_struct.keys())[index]].str.contains(target) != 0]
        }
        self.dict_decimal_condition = {
            "=": lambda df, target, index: df[abs(df[list(self.data_struct.keys())[index]] - target) <= 0.00001],
            "≠": lambda df, target, index: df[abs(df[list(self.data_struct.keys())[index]] - target) > 0.00001],
            "≤": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] <= target],
            "≥": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] >= target],
            "<": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] < target],
            ">": lambda df, target, index: df[df[list(self.data_struct.keys())[index]] > target],
            "between": self.between_expression_parse,
        }
        # 筛选条件缓存
        self.checkbox_select_all = QCheckBox()
        self.checkbox_select_all.setChecked(True)

        self.combobox_condition_cache = {}
        self.input_condition_cache = {}
        self.checkbox_condition_cache = {}
        self.label_condition_cache = {}

        self.combobox_condition_recover_cache = {}
        # 保存 index
        self.input_condition_recover_cache = {}
        self.checkbox_condition_recover_cache = {}
        self.label_condition_recover_cache = {}

        self.basic_data_type = ["int32", "int64", "float64", "str"]
        self.combobox_edit_cache = {}
        self.combobox_edit_recover_cache = {}

        ## 对话框
        ### 文件对话框
        self.dialog_data_file = QFileDialog()
        self.dialog_data_struct_file = QFileDialog()

        ## 表格
        ### 隐藏列名
        self.table_hidden_column = []
        ### 数据表格
        self.table_widget = QTableWidget()

        # 界面设计
        ## 主界面
        self.widget_center = QWidget(self)
        ## 按钮
        ### 打开文件
        #### 数据结构文件
        self.button_open_data_struct_file = QPushButton("Open...")
        self.button_open_data_struct_file.setToolTip("打开一个JSON格式的数据结构文件")
        #### 数据文件
        self.button_open_file = QPushButton("Open...")
        self.button_open_file.setToolTip("打开一个csv格式的数据文件，需与数据结构匹配")
        ### 生成数据结构
        self.button_generate_data_struct = QPushButton("Generate")
        self.button_generate_data_struct.setToolTip("根据csv数据文件表头生成数据结构")
        #### 根据是否存在表头设置
        self.button_generate_data_struct.setEnabled(self.has_header)
        ### 编辑数据结构
        self.button_edit_data_struct = QPushButton("Edit")
        self.button_edit_data_struct.setToolTip("编辑数据结构")
        ### 刷新
        self.button_refresh = QPushButton("Refresh")
        self.button_refresh.setToolTip("刷新数据")
        ### 筛选数据
        self.button_filter = QPushButton("Filter")
        self.button_filter.setToolTip("设定字段筛选条件")
        ### 终止筛选与数据读取
        self.button_abort = QPushButton("Abort")
        self.button_abort.setToolTip("仅解析数据时可用，终止当前操作")
        ### 翻页
        self.button_previous = QPushButton("Previous")
        self.button_previous.setToolTip("上一区域")
        self.label_page_num = QLabel("1")
        self.button_next = QPushButton("Next")
        self.button_next.setToolTip("下一区域")

        ### 筛选对话框 重置所有筛选条件
        self.button_reset_filter = QPushButton("Reset Filter")
        self.button_reset_filter.setToolTip("重置筛选条件")
        ### 筛选对话框 恢复最近一次条件筛选输入
        self.button_recover_filter = QPushButton("Recover Filter")
        self.button_recover_filter.setToolTip("还原上一次执行的筛选条件")
        ### 筛选对话框 确认执行
        self.button_execute = QPushButton("Execute")
        self.button_execute.setToolTip("执行筛选")

        ### 数据结构编辑对话框
        self.button_save = QPushButton("Save Structure")
        self.button_save.setToolTip("保存数据结构")
        self.button_recover_struct = QPushButton("Recover Structure")
        self.button_recover_struct.setToolTip("还原上一次保存的数据结构")

        ### 参数配置 设置参数
        self.button_apply_param = QPushButton("Apply Parameter")
        self.button_apply_param.setToolTip("应用参数配置（配置未修改时不会重新解析数据）")
        ### 参数配置 重置参数
        self.button_reset_param = QPushButton("Reset Parameter")
        self.button_reset_param.setToolTip("重置参数配置并应用")

        ## 单选框
        ### 数据文件是否存在表头
        self.checkbox_has_header = QCheckBox("Header")
        self.checkbox_has_header.setToolTip("数据文件是否包含表头")
        self.checkbox_has_header.setChecked(False)

        ## 标签
        self.label_status = QLabel("Ok.")

        ## 输入框
        ### 数据结构路径 输入框
        self.label_data_struct_path = QLabel("Data Struct File Path")
        self.input_data_struct_path = QLineEdit()
        ### 文件路径 输入框
        self.label_data_path = QLabel("Data File Path")
        self.input_data_path = QLineEdit()

        # 参数配置
        self.spinbox_page_capacity = QSpinBox()
        self.spinbox_page_capacity.setToolTip("每页数据参考容量（单位文件块长度）")
        self.spinbox_begin_section = QSpinBox()
        self.spinbox_begin_section.setToolTip("文件解析起始块（包含该文件块）")
        self.spinbox_end_section = QSpinBox()
        self.spinbox_end_section.setToolTip("文件解析终止块（包含该文件块）")

        self.spinbox_page_capacity.setMaximum(10000)
        self.spinbox_page_capacity.setValue(self.page_capacity)

        self.spinbox_begin_section.setMinimum(1)
        self.spinbox_begin_section.setMaximum(self.begin_section)
        self.spinbox_begin_section.setValue(self.begin_section)

        self.spinbox_end_section.setMinimum(1)
        self.spinbox_end_section.setMaximum(self.end_section)
        self.spinbox_end_section.setValue(self.end_section)

        # 数据展示
        ## 数据文件块读取进度标识
        self.label_section_progress = QLabel("0/0")
        self.label_section_progress.setToolTip("指示当前已解析的文件块索引")
        ## 文件总行数
        self.label_max_line_num = QLabel("0")
        self.label_max_line_num.setToolTip("文件行数")
        ## 当前 section 行数
        self.label_current_line_num = QLabel("0")
        self.label_current_line_num.setToolTip("当前文件块起始行数")

        # 进度条
        ## 数据处理进度
        self.progress_bar_filter = QProgressBar()

        # 布局
        ## 顶层布局
        self.layout_v_main = QVBoxLayout(self.widget_center)
        ## 基础配置布局
        self.layout_h_config = QHBoxLayout()
        ## 功能布局
        self.layout_v_function = QVBoxLayout()
        self.layout_h_function_buttons = QHBoxLayout()
        self.layout_h_function_input = QHBoxLayout()
        self.layout_h_function_data_struct_input = QHBoxLayout()
        ## 参数配置布局
        self.layout_f_param_config = QFormLayout()
        self.layout_v_param_set = QVBoxLayout()
        ## 数据展示布局
        self.layout_f_properties_show = QFormLayout()
        ## 翻页布局
        self.layout_h_page = QHBoxLayout()
        ## 进度布局
        self.layout_h_progress = QHBoxLayout()

        # 绘制
        self.draw()

        # 读取缓存
        self.cache_recover()

    # 页面属性初始化
    def page_init(self):
        # 重置分页数据
        self.page_num = 1
        self.page_sections = []
        # self.page_line_amounts = []
        self.current_section = self.begin_section - 1
        self.current_line_num = 0

        self.label_page_num.setText("1")
        # self.max_line_num = 0

    def data_struct_reset(self):
        self.combobox_condition_cache = {}
        self.input_condition_cache = {}
        self.checkbox_condition_cache = {}
        self.label_condition_cache = {}

        self.combobox_condition_recover_cache = {}
        self.input_condition_recover_cache = {}
        self.checkbox_condition_recover_cache = {}
        self.label_condition_recover_cache = {}

        self.combobox_edit_cache = {}
        self.combobox_edit_recover_cache = {}

    # 数据初始化 默认值（配置文件）
    def data_struct_init(self):
        self.data_struct = {}

        self.table_labels = []
        self.table_hidden_column = []

        # 初始化表头选项
        if self.data_struct_path == "generate_data_struct.json":
            self.has_header = True
            self.checkbox_has_header.setChecked(True)
        else:
            self.has_header = False
            self.checkbox_has_header.setChecked(False)
        # 配置文件
        ## 设置数据类型及表格表头
        with open(file=self.data_struct_path, mode='r+') as data_struct_json:
            self.data_struct = json.load(data_struct_json)
            for item in self.data_struct:
                self.table_labels.append(item)

        self.input_data_struct_path.setText(self.data_struct_path)
        self.redraw_data_related()

    def cache_recover(self):
        try:
            with open(file="./cache.json", mode='r+') as cache_json:
                cache = json.load(cache_json)
                self.data_struct_path = cache['data_struct_path']
                self.current_data_struct_path = self.data_struct_path
                self.current_data_path = cache['data_path']

                self.data_struct_reset()
                self.data_struct_init()
        except FileNotFoundError:
            self.label_status.setText("Cache recover failed.")
        except json.decoder.JSONDecodeError:
            self.label_status.setText("Cache recover failed.")

    def data_read(self, skiprows, nrows):
        if self.has_header:
            skiprows += 1
        return pd.read_csv(self.data_path, header=None, names=self.table_labels, dtype=self.data_struct, skiprows=skiprows, nrows=nrows)

    def data_init(self):
        # 数据文件
        self.data = {}
        self.data_view = {}

        try:
            self.begin_section = 1
            self.page_init()
            # 计算总行数
            self.compute_max_line_num()
            # # 读取首段数据
            # self.data = self.data_read(0, self.page_capacity)
            # self.data_view = self.data
            # 设置基础属性

            self.end_section = self.max_section

        except ValueError:
            if self.max_line_num <= 0:
                self.signal_error.emit("The data file content dose not match the data structure.")
                return -1
            else:
                self.end_section = self.max_section
                return 1
        except FileNotFoundError:
            self.signal_error.emit("Please import data file.")
            return -2

        return 0

    def table_init(self):
        if not self.is_initialized:
            return

        r = 0
        for row in self.data_view.values:
            self.table_widget.insertRow(self.table_widget.rowCount())
            c = 0
            offset = 0
            for value in row:
                if self.table_hidden_column.count(self.table_labels[c]) != 0:
                    offset += 1
                else:
                    self.table_widget.setItem(r, c - offset, QTableWidgetItem(str(value)))
                c += 1
            r += 1

    def draw(self):
        self.setWindowIcon(QIcon("./resource/csv_viewer.ico"))

        # 绘制
        # 添加主窗体
        self.setCentralWidget(self.widget_center)

        # 顶层布局
        self.widget_center.setLayout(self.layout_v_main)
        ## 基础配置布局
        self.layout_v_main.addLayout(self.layout_h_config)
        ## 表格组件
        self.layout_v_main.addWidget(self.table_widget, stretch=200)
        ## 翻页
        self.layout_v_main.addLayout(self.layout_h_page)
        ## 进度条
        self.layout_v_main.addLayout(self.layout_h_progress)
        ## 状态栏
        self.layout_v_main.addWidget(self.label_status)

        # 基础配置布局
        ## 功能布局
        self.layout_h_config.addLayout(self.layout_v_function, stretch=4)
        self.layout_h_config.addStretch(1)
        self.layout_h_config.addLayout(self.layout_f_param_config, stretch=1)
        self.layout_h_config.addLayout(self.layout_v_param_set, stretch=1)
        self.layout_h_config.addStretch(1)
        self.layout_h_config.addLayout(self.layout_f_properties_show, stretch=1)

        # 功能布局
        ## 数据结构文件路径输入框
        self.layout_v_function.addLayout(self.layout_h_function_data_struct_input)
        self.layout_h_function_data_struct_input.addWidget(self.label_data_struct_path)
        self.layout_h_function_data_struct_input.addWidget(self.input_data_struct_path)
        self.input_data_struct_path.setReadOnly(True)
        ## 打开文件按钮
        self.layout_h_function_data_struct_input.addWidget(self.button_open_data_struct_file)
        ## 编辑按钮
        self.layout_h_function_data_struct_input.addWidget(self.button_edit_data_struct)
        ## 生成数据结构按钮
        self.layout_h_function_data_struct_input.addWidget(self.button_generate_data_struct)
        ## 数据文件路径输入框
        self.layout_v_function.addLayout(self.layout_h_function_input)
        self.layout_h_function_input.addWidget(self.label_data_path)
        self.layout_h_function_input.addWidget(self.input_data_path)
        self.input_data_path.setReadOnly(True)
        ## 解析按钮
        self.layout_h_function_input.addWidget(self.button_open_file)
        ## 表头选项
        self.layout_h_function_input.addWidget(self.checkbox_has_header)

        # 功能按钮布局
        self.layout_v_function.addLayout(self.layout_h_function_buttons)
        ## 刷新按钮
        self.layout_h_function_buttons.addWidget(self.button_refresh)
        ## 筛选按钮
        self.layout_h_function_buttons.addWidget(self.button_filter)
        self.button_filter.setEnabled(False)
        ## 终止按钮
        self.layout_h_function_buttons.addWidget(self.button_abort)
        self.button_abort.setEnabled(False)

        # 参数配置布局
        self.layout_f_param_config.addRow("Page Capacity", self.spinbox_page_capacity)
        self.layout_f_param_config.addRow("Begin Section", self.spinbox_begin_section)
        self.layout_f_param_config.addRow("End Section", self.spinbox_end_section)
        self.layout_v_param_set.addWidget(self.button_apply_param)
        self.layout_v_param_set.addWidget(self.button_reset_param)
        self.layout_v_param_set.addStretch(1)
        # 数据展示布局
        self.layout_f_properties_show.addRow("Section", self.label_section_progress)
        self.layout_f_properties_show.addRow("Line", self.label_max_line_num)
        self.layout_f_properties_show.addRow("Current Line", self.label_current_line_num)

        # 翻页布局
        self.layout_h_page.addWidget(self.button_previous)
        self.layout_h_page.addStretch(2)
        self.layout_h_page.addWidget(self.label_page_num)
        self.layout_h_page.addStretch(2)
        self.layout_h_page.addWidget(self.button_next)
        self.button_previous.setEnabled(False)
        self.button_next.setEnabled(False)

        # 进度布局
        # self.layout_h_progress.addWidget(self.label_section_progress)
        self.layout_h_progress.addWidget(self.progress_bar_filter)
        self.progress_bar_filter.hide()

        # 信号槽连接
        self.communicate()

    # 更新data_struct时清空缓存
    def draw_filter_dialog(self):
        # 筛选条件对话框
        dialog_filter = FunctionDialog(self)
        dialog_filter.setModal(True)
        dialog_filter.setWindowTitle("Filter")

        widget_form = QWidget()

        # 滚动区域
        scroll_area_filter = QScrollArea()
        # 筛选对话框布局
        ## 总体布局
        layout_h_dialog = QHBoxLayout()
        ### 按钮布局
        layout_v_button = QVBoxLayout()
        ### 条件表单布局
        layout_f_filter = QFormLayout()

        # 布局配置
        dialog_filter.setLayout(layout_h_dialog)
        widget_form.setLayout(layout_f_filter)

        layout_h_dialog.addWidget(scroll_area_filter)
        layout_h_dialog.addLayout(layout_v_button)

        # 按钮
        layout_v_button.addWidget(self.button_execute)
        layout_v_button.addWidget(self.button_reset_filter)
        layout_v_button.addWidget(self.button_recover_filter)

        layout_f_filter.addRow("Show All", self.checkbox_select_all)
        layout_f_filter.addRow('', QLabel())

        # 表单
        if len(self.combobox_condition_cache) == 0:
            for (key, value) in self.data_struct.items():
                layout_h_condition = QHBoxLayout()
                checkbox_condition = QCheckBox()
                combobox_condition = QComboBox()
                input_condition = QLineEdit()
                label_condition = QLabel()

                checkbox_condition.setChecked(True)
                input_condition.setPlaceholderText(value)

                # 信号槽连接
                combobox_condition.currentTextChanged.connect(lambda text, input=input_condition, data_type=value: self.slot_between_selected(text, input, data_type))
                checkbox_condition.stateChanged.connect(lambda state, combobox=combobox_condition, input=input_condition, field=key: self.slot_field_display(state, combobox, input, field))

                layout_f_filter.addRow(key, layout_h_condition)
                layout_h_condition.addWidget(checkbox_condition)
                layout_h_condition.addWidget(combobox_condition)
                layout_h_condition.addWidget(input_condition)
                layout_f_filter.addRow('', label_condition)

                if value == "str":
                    combobox_condition.addItems(list(self.dict_str_condition.keys()))
                else:
                    combobox_condition.addItems(list(self.dict_decimal_condition.keys()))

                # 记录缓存
                self.checkbox_condition_cache[key] = checkbox_condition
                self.combobox_condition_cache[key] = combobox_condition
                self.input_condition_cache[key] = input_condition
                self.label_condition_cache[key] = label_condition
        else:
            for (key, value) in self.data_struct.items():
                layout_h_condition = QHBoxLayout()
                layout_f_filter.addRow(key, layout_h_condition)

                layout_h_condition.addWidget(self.checkbox_condition_cache[key])
                layout_h_condition.addWidget(self.combobox_condition_cache[key])
                layout_h_condition.addWidget(self.input_condition_cache[key])

                layout_f_filter.addRow('', self.label_condition_cache[key])

        ## 表单滚动
        scroll_area_filter.setWidget(widget_form)
        scroll_area_filter.setWidgetResizable(True)

        dialog_filter.open()
        ## 关闭信号绑定
        dialog_filter.rejected.connect(self.slot_exit_filter)
        ## 操作信号绑定
        dialog_filter.setMouseTracking(True)
        self.slot_configure_filter()

    def draw_data_struct_edit_dialog(self):
        # 筛选条件对话框
        dialog_edit = FunctionDialog(self, 400, 400)
        dialog_edit.setModal(True)
        dialog_edit.setWindowTitle("Data Struct Editor")

        widget_form = QWidget()

        # 滚动区域
        scroll_area_filter = QScrollArea()
        # 筛选对话框布局
        ## 总体布局
        layout_h_dialog = QHBoxLayout()
        ### 按钮布局
        layout_v_button = QVBoxLayout()
        ### 条件表单布局
        layout_f_edit = QFormLayout()

        # 布局配置
        dialog_edit.setLayout(layout_h_dialog)
        widget_form.setLayout(layout_f_edit)

        layout_h_dialog.addWidget(scroll_area_filter)
        layout_h_dialog.addLayout(layout_v_button)

        # 按钮
        layout_v_button.addWidget(self.button_save)
        layout_v_button.addWidget(self.button_recover_struct)

        # 表单
        if len(self.combobox_edit_cache) == 0:
            for (key, value) in self.data_struct.items():
                combobox_edit = QComboBox()

                combobox_edit.addItems(self.basic_data_type)
                combobox_edit.setCurrentText(value)

                layout_f_edit.addRow(key, combobox_edit)

                # 记录缓存
                self.combobox_edit_cache[key] = combobox_edit
        else:
            for (key, value) in self.data_struct.items():
                layout_f_edit.addRow(key, self.combobox_edit_cache[key])

        ## 表单滚动
        scroll_area_filter.setWidget(widget_form)
        scroll_area_filter.setWidgetResizable(True)

        dialog_edit.open()
        ## 关闭信号绑定
        dialog_edit.rejected.connect(self.slot_exit_editor)
        ## 操作信号绑定
        dialog_edit.setMouseTracking(True)
        self.slot_configure_editor()

    def redraw_table_widget(self):
        table_widget = self.table_widget

        # if self.page_num < 0:
        #     self.label_status.setText("Please import data first.")
        #     return

        ## 表格
        ### 数据表格
        self.table_widget = QTableWidget()
        self.table_widget.setRowCount(0)
        table_labels = self.table_labels.copy()

        for column in self.table_hidden_column:
            if table_labels.count(column) != 0:
                table_labels.remove(column)

        self.table_widget.setColumnCount(len(table_labels))
        self.table_widget.setHorizontalHeaderLabels(table_labels)
        self.table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)

        self.layout_v_main.replaceWidget(table_widget, self.table_widget)

        # 连接信号槽
        self.table_widget.cellClicked.connect(self.slot_current_line_update)

        self.table_init()

    # 初始化时调用
    def redraw_data_related(self):
        if len(self.input_data_struct_path.text()) == 0:
            self.label_status.setText("Please import data struct.")
            return
        # if len(self.input_data_path.text()) == 0:
        #     self.redraw_table_widget()
        #     self.label_status.setText("Please import data file.")
        #     return

        if self.progress_bar_filter.isHidden():
            self.progress_bar_filter.show()
        self.progress_bar_filter.setMaximum(0)

        self.data_path = self.input_data_path.text()

        self.enable_buttons(False)
        self.thread_data_init.start()
        # self.run_page_filter()
        self.label_status.setText("Importing data...")

    def enable_buttons(self, state):
        self.button_next.setEnabled(state)
        self.button_previous.setEnabled(state)

        # self.button_filter.setEnabled(state)
        self.button_refresh.setEnabled(state)
        self.button_abort.setEnabled(not state)
        self.button_open_data_struct_file.setEnabled(state)
        self.button_generate_data_struct.setEnabled(state and self.has_header)
        self.button_edit_data_struct.setEnabled(state)
        self.button_open_file.setEnabled(state)

        self.button_execute.setEnabled(state)
        self.button_recover_filter.setEnabled(state)
        self.button_reset_filter.setEnabled(state)

        self.button_apply_param.setEnabled(state)
        self.button_reset_param.setEnabled(state)

        self.checkbox_has_header.setEnabled(state)

    def communicate(self):
        # 文件对话框
        ## 数据结构
        self.button_open_data_struct_file.clicked.connect(self.slot_data_struct_file_select)
        ## 生成数据结构
        self.button_generate_data_struct.clicked.connect(self.slot_generate_data_struct)
        ## 编辑数据结构
        self.button_edit_data_struct.clicked.connect(self.draw_data_struct_edit_dialog)
        ## 选择数据结构后按钮可用
        self.input_data_struct_path.textChanged.connect(lambda text: self.button_generate_data_struct.setEnabled(True))
        self.input_data_struct_path.textChanged.connect(lambda text: self.button_edit_data_struct.setEnabled(True))
        ## 数据
        self.button_open_file.clicked.connect(self.slot_file_select)
        ## 存在表头
        self.checkbox_has_header.stateChanged.connect(self.slot_has_header)

        # 编辑对话框
        ## 保存
        self.button_save.clicked.connect(self.slot_save_struct)
        ## 恢复
        self.button_recover_struct.clicked.connect(self.slot_recover_struct)

        # 打开筛选条件对话框
        self.button_filter.clicked.connect(self.draw_filter_dialog)
        ## 选择数据结构后按钮可用
        self.input_data_struct_path.textChanged.connect(lambda text: self.button_filter.setEnabled(True))

        # 刷新数据
        self.button_refresh.clicked.connect(self.slot_execute_filter)

        # 筛选对话框
        ## 全选按钮
        self.checkbox_select_all.stateChanged.connect(self.slot_select_all)
        ## 执行条件筛选
        self.button_execute.clicked.connect(self.slot_execute_filter)
        ## 重置
        self.button_reset_filter.clicked.connect(self.slot_reset_filter)
        ## 恢复
        self.button_recover_filter.clicked.connect(self.slot_recover_filter)

        # 终止数据读取
        self.button_abort.clicked.connect(self.slot_abort)

        # 应用参数配置并重新读取数据
        self.button_apply_param.clicked.connect(self.slot_apply_param)
        # 重置参数配置
        self.button_reset_param.clicked.connect(self.slot_reset_param)

        # 翻页
        self.button_next.clicked.connect(self.slot_page_down)
        self.button_previous.clicked.connect(self.slot_page_up)

        # 数据相关信号槽
        self.thread_data_process.finished.connect(self.slot_page_filter_finished)
        self.signal_progress_update.connect(self.slot_progress_update)

        # 异常处理
        self.signal_error.connect(self.slot_error_handle)

    # def communicate_data_related(self):
    #     for checkbox in self.checkboxes_field:
    #         checkbox.stateChanged.connect(lambda status, text=checkbox.text(): self.slot_field_status(status, text))

    ## 选择数据结构文件
    def slot_data_struct_file_select(self):
        file_path = self.dialog_data_struct_file.getOpenFileName(self, "Open file", self.current_data_struct_path, "Json (*.json)")[0]
        if len(file_path) == 0:
            return

        self.data_struct_path = file_path

        self.data_struct_reset()
        self.data_struct_init()

        # 缓存
        with open(file="./cache.json", mode='w+') as cache_json:
            json.dump(obj={'data_struct_path': self.data_struct_path, 'data_path': self.data_path}, fp=cache_json)

        # self.redraw_data_related()
        self.current_data_struct_path = file_path

    ## 选择数据文件
    def slot_file_select(self):
        file_path = self.dialog_data_file.getOpenFileName(self, "Open file", self.current_data_path, "Excel (*.csv)")[0]
        if len(file_path) == 0:
            return

        self.input_data_path.setText(file_path)
        self.redraw_data_related()

        # 缓存
        with open(file="./cache.json", mode='w+') as cache_json:
            json.dump(obj={'data_struct_path': self.data_struct_path, 'data_path': self.data_path}, fp=cache_json)

        self.current_data_path = file_path

    def slot_has_header(self, state):
        self.has_header = state
        self.button_generate_data_struct.setEnabled(state)

        if self.max_line_num > 0:
            if state:
                self.max_line_num -= 1
            else:
                self.max_line_num += 1

        self.max_section = math.ceil(self.max_line_num / self.page_capacity)
        self.label_max_line_num.setText(str(self.max_line_num))

    def slot_execute_filter(self):
        # 重置所有异常提示
        for label in self.label_condition_cache.values():
            label.setText('')

        if not self.is_initialized:
            self.label_status.setText("Please import data file first.")
            return

        # self.page_init()
        self.slot_apply_param()

        # self.thread_data_process.start()
        self.run_page_filter()
        self.label_status.setText("Filtering...")

    def slot_exit_filter(self):
        self.label_status.setText("Finished.")

    def slot_configure_filter(self):
        self.label_status.setText("Configuring filter...")

    def slot_exit_editor(self):
        self.label_status.setText("Finished.")

    def slot_configure_editor(self):
        self.label_status.setText("Configuring editor...")

    def slot_current_line_update(self, row, column):
        self.label_current_line_num.setText(str(self.current_line_num + row))

    def slot_between_selected(self, text, input, data_type):
        if text == "between":
            input.setPlaceholderText('例如：(' + data_type + ',' + data_type + ']')
        else:
            input.setPlaceholderText(str(data_type))

    def slot_field_display(self, state, combobox, input, field):
        # 条件选项配置是否可用
        combobox.setEnabled(state)
        input.setEnabled(state)

        # 字段筛选
        if state:
            self.table_hidden_column.remove(field)
        else:
            self.table_hidden_column.append(field)

    def slot_select_all(self, state):
        for checkbox in self.checkbox_condition_cache.values():
            checkbox.setChecked(state)

    def slot_reset_filter(self):
        self.checkbox_select_all.setChecked(True)

        for key in self.data_struct.keys():
            # self.checkbox_condition_cache[key].setChecked(True)
            self.combobox_condition_cache[key].setCurrentIndex(0)
            self.input_condition_cache[key].setText('')

    def slot_recover_filter(self):
        for key in self.data_struct.keys():
            if len(self.checkbox_condition_recover_cache) > 0:
                self.checkbox_condition_cache[key].setChecked(self.checkbox_condition_recover_cache[key])
            if len(self.combobox_condition_recover_cache) > 0:
                self.combobox_condition_cache[key].setCurrentIndex(self.combobox_condition_recover_cache[key])
            if len(self.input_condition_recover_cache) > 0:
                self.input_condition_cache[key].setText(self.input_condition_recover_cache[key])

    def slot_save_struct(self):
        for key in self.data_struct.keys():
            self.data_struct[key] = self.combobox_edit_cache[key].currentText()
            self.combobox_edit_recover_cache[key] = self.data_struct[key]
            # 筛选界面占位符修改
            if len(self.input_condition_cache) > 0:
                self.input_condition_cache[key].setPlaceholderText(self.data_struct[key])

        with open(self.data_struct_path, mode="w+") as data_struct_file:
            json.dump(obj=self.data_struct, fp=data_struct_file)

        self.data_struct_init()

    def slot_recover_struct(self):
        if len(self.combobox_edit_recover_cache) > 0:
            for (key, value) in self.data_struct.items():
                self.combobox_edit_cache[key].setCurrentText(self.combobox_edit_recover_cache[key])

    def slot_page_down(self):
        if self.current_section >= self.max_section:
            self.label_status.setText("Already the last page.")
            return

        self.page_num += 1
        self.label_page_num.setText(str(self.page_num))

        if len(self.page_sections) >= self.page_num:
            self.current_section = self.page_sections[self.page_num-1]
            # self.current_line_num = self.page_line_amounts[self.page_num - 1]

        # self.page_filter()
        # self.redraw_table_widget()
        self.run_page_filter()

    def slot_page_up(self):
        if self.page_num == 1:
            self.label_status.setText("Already the first page.")
            return

        self.page_num -= 1
        self.label_page_num.setText(str(self.page_num))

        self.current_section = self.page_sections[self.page_num-1]
        # self.current_line_num = self.page_line_amounts[self.page_num-1]

        # self.thread_data_process.start()
        self.run_page_filter()

    def slot_page_filter_finished(self):
        # 界面复位
        self.progress_bar_filter.setMaximum(1)
        self.progress_bar_filter.setValue(1)

        self.enable_buttons(True)

        self.redraw_table_widget()
        self.label_status.setText("Finished.")

        self.is_abort = False

    def slot_data_init_complete(self, errcode):
        self.page_init()
        if errcode < 0:
            self.is_initialized = False
            self.redraw_table_widget()
            self.progress_bar_filter.hide()
            self.enable_buttons(True)
            return
        if errcode > 0:
            QMessageBox.warning(self, "Warning", "The data file contains incomplete lines.")
        self.is_initialized = True
        self.spinbox_page_capacity.setValue(self.page_capacity)
        self.spinbox_begin_section.setMaximum(self.max_section)
        self.spinbox_begin_section.setValue(self.begin_section)
        self.spinbox_end_section.setMaximum(self.max_section)
        self.spinbox_end_section.setValue(self.end_section)

        self.run_page_filter()

        # self.button_previous.setEnabled(True)
        # self.button_next.setEnabled(True)

    def slot_progress_update(self, row_count):
        self.progress_bar_filter.setValue(row_count)
        self.label_section_progress.setText(str(self.current_section) + '/' + str(self.max_section))
        self.label_max_line_num.setText(str(self.max_line_num))
        if self.current_section > 0:
            self.current_line_num = (self.page_num - 1) * self.page_capacity + 1
            self.label_current_line_num.setText(str(self.current_line_num))

    def slot_error_handle(self, error_info):
        self.label_status.setText(error_info)

    def slot_abort(self):
        self.is_abort = True

    def slot_apply_param(self):
        if self.page_capacity != self.spinbox_page_capacity.value():
            self.page_capacity = self.spinbox_page_capacity.value()
            self.redraw_data_related()
        elif self.begin_section != self.spinbox_begin_section.value() or self.end_section != self.spinbox_end_section.value():
            if self.spinbox_begin_section.value() > self.spinbox_end_section.value():
                self.label_status.setText("Bad parameters. The begin section should be equal or less than the end section.")
                return
            self.begin_section = self.spinbox_begin_section.value()
            self.end_section = self.spinbox_end_section.value()

            self.run_page_filter()

        self.page_init()
        self.label_status.setText("Finished.")

    def slot_reset_param(self):
        self.page_capacity = 1000
        self.begin_section = 1
        self.end_section = self.max_section

        self.spinbox_page_capacity.setValue(self.page_capacity)
        self.spinbox_begin_section.setValue(self.begin_section)
        self.spinbox_end_section.setValue(self.end_section)

        self.slot_apply_param()

    def slot_generate_data_struct(self):
        if len(self.input_data_path.text()) == 0:
            self.label_status.setText("Please import data file first.")
            return
        try:
            header = list(pd.read_csv(self.input_data_path.text(), nrows=0))
            sample = list(pd.read_csv(self.input_data_path.text(), nrows=1).dtypes)

            # 设置表头
            data_struct = {}
            for i in range(len(header)):
                if sample[i] == 'O':
                    data_struct[header[i]] = "str"
                else:
                    data_struct[header[i]] = str(sample[i])

            with open("generate_data_struct.json", mode="w") as generate_file:
                json.dump(obj=data_struct, fp=generate_file)

            self.current_data_struct_path = "generate_data_struct.json"
            self.data_struct_path = "generate_data_struct.json"
            # 缓存
            with open(file="./cache.json", mode='w+') as cache_json:
                json.dump(obj={'data_struct_path': self.data_struct_path, 'data_path': self.data_path}, fp=cache_json)

            self.data_struct_reset()
            self.data_struct_init()

        except FileNotFoundError:
            self.label_status.setText("Path error.")

    # 工具方法
    ## 计算文件总行数
    def compute_max_line_num(self):
        self.begin_section = 1
        self.max_section = 0
        self.max_line_num = 0
        chunks = pd.read_csv(self.data_path, header=None, names=self.table_labels, dtype=self.data_struct, iterator=True, chunksize=self.page_capacity)
        for chunk in chunks:
            self.max_line_num += len(chunk.index)
            self.max_section += 1
            self.signal_progress_update.emit(0)

        if self.has_header and self.max_line_num > 0:
            self.max_line_num -= 1
            self.max_section = math.ceil(self.max_line_num / self.page_capacity)

    def run_page_filter(self):
        # 界面初始化
        self.enable_buttons(False)
        self.is_abort = False

        # 重置进度条
        self.progress_bar_filter.setMaximum(self.page_capacity)
        self.progress_bar_filter.setValue(0)

        # 启动线程
        self.thread_data_process.start()
        self.label_status.setText("Filtering...")

    ## 执行条件筛选（分页）
    def page_filter(self):
        if not self.is_initialized:
            return

        combobox_condition_cache_values = list(self.combobox_condition_cache.values())
        input_condition_cache_values = list(self.input_condition_cache.values())
        data_fields = list(self.data_struct.keys())
        data_types = list(self.data_struct.values())

        row_count = 0
        while not self.is_abort and row_count < self.page_capacity and self.current_section < self.end_section:
            data_view = self.data_read(self.current_section * self.page_capacity, self.page_capacity)

            if len(data_view) == 0:
                break

            if len(self.page_sections) < self.page_num:
                self.page_sections.append(self.current_section)
                # self.page_line_amounts.append(row_count)

            # 逐条筛选
            for i in range(len(self.input_condition_cache)):
                # 缓存最近一次筛选条件
                self.checkbox_condition_recover_cache[data_fields[i]] = self.checkbox_condition_cache[
                    data_fields[i]].isChecked()
                self.combobox_condition_recover_cache[data_fields[i]] = self.combobox_condition_cache[
                    data_fields[i]].currentIndex()
                self.input_condition_recover_cache[data_fields[i]] = self.input_condition_cache[data_fields[i]].text()

                # 跳过禁用项
                if not input_condition_cache_values[i].isEnabled():
                    continue

                if len(input_condition_cache_values[i].text()) == 0:
                    continue

                if combobox_condition_cache_values[i].currentText() == "between":
                    if data_types[i] == "str":
                        data_view = self.dict_str_condition["between"](data_view,
                                                                       input_condition_cache_values[i].text(), i)
                    # 数值
                    else:
                        data_view = self.dict_decimal_condition["between"](data_view,
                                                                           input_condition_cache_values[i].text(), i)
                else:
                    # 字符串
                    if data_types[i] == "str":
                        data_view = self.dict_str_condition[combobox_condition_cache_values[i].currentText()](data_view,
                                                                                input_condition_cache_values[
                                                                                i].text(),
                                                                                i)
                    # 数值
                    else:
                        data_view = self.dict_decimal_condition[combobox_condition_cache_values[i].currentText()](
                            data_view, float(input_condition_cache_values[i].text()), i)

            if row_count == 0:
                self.data_view = data_view
            elif len(data_view.values) > 0:
                self.data_view = pd.concat([self.data_view, data_view])

            row_count = len(self.data_view)
            self.current_section += 1

            self.signal_progress_update.emit(row_count)

    ## between表达式解析
    def between_expression_parse(self, df, expression, index):
        if len(expression) > 0:
            section_symbols = ['[', '(', ')', ']']

            left = expression[0]
            right = expression[-1]

            # 获取区间起点与终点
            section = str(expression[1:-1]).split(',')
            if len(section) != 2 or section[0] == '' or section[1] == '':
                list(self.label_condition_cache.values())[index].setText("需要区间起点值与终点值，并以 \',\' 分割")
                return df

            data_type = list(self.data_struct.values())[index]
            try:
                if data_type == "str":
                    begin = str(section[0])
                    end = str(section[1])
                else:
                    begin = float(section[0])
                    end = float(section[1])
            except ValueError:
                list(self.label_condition_cache.values())[index].setText("区间起点与终点应为数值类型(Decimal)")
                return df

            # 区间判断
            if section_symbols.count(left) == 0 or section_symbols.count(right) == 0:
                list(self.label_condition_cache.values())[index].setText("区间应以 '[]' 或 '()' 修饰")
            else:
                if left == '[':
                    df = df[df[list(self.data_struct.keys())[index]] >= begin]
                elif left == '(':
                    df = df[df[list(self.data_struct.keys())[index]] > begin]

                if right == ']':
                    df = df[df[list(self.data_struct.keys())[index]] <= end]
                elif right == ')':
                    df = df[df[list(self.data_struct.keys())[index]] < end]

                list(self.label_condition_cache.values())[index].setText('')

            return df

def dump_error(error_info):
    with open("log.txt", mode="a+") as log:
        log.write(error_info)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    try:
        view = View()
        view.showMaximized()
        sys.exit(app.exec())
    except Exception:
        dump_error(traceback.format_exc())


from datetime import timedelta
from airflow.models import Variable

def get_default_dag_args():
    owner_name = Variable.get('owner')
    return {
    'owner': owner_name,
    'depends_on_past': False,
    'start_date': "2015-09-30T16:41:54+00:00",
    'retries': 5,
    'retry_delay': timedelta(minutes=5),
}


import socket
import struct
import json
sock=socket.socket()
sock.connect(('127.0.0.1',8080))
while True:
    msg=input('>>:').strip()
    if not msg: continue  # 如果输入是空这进入下次循环
    str_len=len(msg)
    data = {'size': str_len}
    json_str=json.dumps(data)
    # 发送报头
    json_len = struct.pack('i', len(json_str))
    sock.send(json_len)
    #发送json
    sock.send(json_str.encode('utf8'))
    #发送内容
    sock.send(msg.encode('utf8'))
sock.close()

网络编程和并发/练习/通客户端.py


from django.urls import path, include
from rest_framework.routers import SimpleRouter

from highm_server.custom_lookup_type_router import CustomLookupTypeRouter
from .views_for_student import (
    LessonViewSet,
)
from .views_for_teacher import (
    TeacherLessonViewSet,
    GroupStatementViewSet,
    LessonResultViewSet,
)

root_router = CustomLookupTypeRouter()
root_router.register('lesson', LessonViewSet, basename='lesson')

group_router = SimpleRouter()
group_router.register('lesson', TeacherLessonViewSet,
                      basename='teacher-lesson')
group_router.register('statement', GroupStatementViewSet,
                      basename='group-statement')

lesson_router = SimpleRouter()
lesson_router.register('result', LessonResultViewSet,
                       basename='lesson-result')

urlpatterns = [
    path('', include(root_router.urls)),
    path('teacher-panel/group/<str:group>/', include(group_router.urls)),
    path('teacher-panel/group/<str:group>/lesson/<int:lesson>/',
         include(lesson_router.urls)),
]


# -*- coding: utf-8 -*-

from skimage import io
import numpy as np

centers = np.load('codebook_tiger.npy')

c_image = io.imread('compressed_tiger.png')

image = np.zeros((c_image.shape[0],c_image.shape[1],3),dtype=np.uint8 )
for i in range(c_image.shape[0]):
    for j in range(c_image.shape[1]):
            image[i,j,:] = centers[c_image[i,j],:]

io.imsave('reconstructed_tiger.png',image);
io.imshow(image)
io.show()


import PySimpleGUI as sg
import os
import subprocess
import platform

pf = platform.system()
if 'Darwin' != pf:
    print(f"あなたが使用しているOSは {pf} ですが,\nこれはMac専用のプログラムのため何もせず終了します.")
    input("\n続行するにはなにかキーを押してください.")
    exit(0)

# PySimpleGUIの確認
freeze_output = subprocess.run("pip3 freeze", capture_output=True, text=True, shell=True).stdout
if not ("PySimpleGUI" in freeze_output):
    subprocess.run("pip3 install PySimpleGUI", shell=True)  # 無ければインストール

# ホームディレクトリへのパス
path_to_HOME = os.environ['HOME']

# アーキテクチャの確認
arch = subprocess.run("arch", capture_output=True, text=True, shell=True).stdout.replace("\n", "")

def Homebrew():
    """Homebrewがインストールされてるかチェックする関数"""
    if arch == "arm64":
        path_to_brew = "/opt/homebrew/bin"  # ファイルチェック
    else:
        path_to_brew = "/usr/local/bin/brew"

    if os.path.exists(path_to_brew):
        comment = "Homebrewは(おそらく)インストール済みです."
    else:
        cmd = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
        subprocess.run(cmd, shell=True)
        comment = "Homebrewはインストールされていなかったので,インストールしておきました."

    return comment

def Homebrewecho():
    """Homebrewのzshrcの設定状況をチェックする関数"""
    if arch != "arm64":
        return "あなたの使っているMacは古いので, Homebrewの設定は必要ありません.\n(M1では設定が必要です.)"

    path_to_zshrc = path_to_HOME + "/.zshrc"   # ファイルチェック
    if not os.path.exists(path_to_zshrc):   # 無ければ作って
        with open(path_to_zshrc, mode='w+') as f:
            f.write('export PATH="/opt/homebrew/bin:$PATH"\n\n')
            return "そもそもなんも作業してないでしょう？こちらで設定しておきました."  # return

    # .zshrcが存在する状況で
    with open(path_to_zshrc, mode='r') as f:  # 既存ファイル読み取り
        path_check = False
        for line in f:
            if 'export PATH="/opt/homebrew/bin:$PATH"' in line:  # 設定チェック
                path_check = True
    if path_check:
        return 'Homebrewは(おそらく)ちゃんと設定されています.\n他の不具合がある場合は,ターミナルを開いて\n      $ open -t ~/.zshrc \nを実行して\n【export PATH="/opt/homebrew/bin:$PATH"】\nという行が書き込まれているか自分で確認してください'
    else:
        with open(path_to_zshrc, mode='a') as f:  # 設定がなければ書き込んでしまう
            f.write('\nexport PATH="/opt/homebrew/bin:$PATH"\n\n')
        return 'export PATH=????? が設定されていなかったので設定しておきました.\nもし他の不具合がある場合は,ターミナルを開いて\n      $ open -t ~/.zshrc \nを実行して\n【export PATH="/opt/homebrew/bin:$PATH"】\nという行が書き込まれているか自分で確認してください'

def Brave():
    """Braveがインストールされているかチェックする関数"""
    # ファイルチェック
    cmd = "ls /Applications"
    output = subprocess.run(cmd, capture_output=True, text=True, shell=True).stdout
    if "Brave" in output:
        return "Braveはインストール済みです."
    else:
        cmd = "brew install --cask brave-browser"
        subprocess.run(cmd, shell=True)
        return "Braveがインストールされていなかったので,こちらでやっておきました."

def Java():
    """JDKがインストールされてるかチェックする関数"""
    if arch == "arm64":
        path_to_jdk = "/opt/homebrew/opt/openjdk/libexec/openjdk.jdk"  # PATHチェック
    else:
        path_to_jdk = "/usr/local/opt/openjdk/libexec/openjdk.jdk"

    if not os.path.exists(path_to_jdk):
        cmd = "brew install openjdk"
        subprocess.run(cmd, shell=True)
        return "Javaがインストールされていなかったので,インストールしておきました."
    else:
        # javacで確認
        javac = f"{path_to_jdk}/Contents/Home/bin/javac --version"
        proc = subprocess.run(javac, shell=True)
        if proc.returncode != 0:
            cmd = "brew install openjdk"
            subprocess.run(cmd, shell=True)
            return "Javaがインストールされていなかったので,インストールしておきました."
        else:
            return "Javaはちゃんとインストールされています."

def JavaSetting():
    # Javaの設定確認1
    # リンクのチェック
    path_to_link = "/Library/Java/JavaVirtualMachines/openjdk.jdk"
    if arch == "arm64":
        path_to_jdk = "/opt/homebrew/opt/openjdk/libexec/openjdk.jdk"  # PATHチェック
    else:
        path_to_jdk = "/usr/local/opt/openjdk/libexec/openjdk.jdk"

    if not os.path.exists(path_to_link):
        print("Macにログインするときのパスワードを入力してください.")
        cmd = f"sudo ln -sfn {path_to_jdk} {path_to_link}"
        subprocess.run(cmd, shell=True)
        comment = "「Java を書きやすくする設定 1」は正しく実行できていませんでしたので,\nこちらでやっておきました.\n\n"
    else:
        comment = "「Java を書きやすくする設定 1」は正しく実行されています.\n\n"

    # Javaの設定確認2
    # .zshrcのチェック
    # JAVA_HOMEの設定
    path_to_zshrc = path_to_HOME + "/.zshrc"
    if not os.path.exists(path_to_zshrc):
        with open(path_to_zshrc, mode='w+') as f:
            f.write(
                '\nexport JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk"\nexport PATH="${JAVA_HOME}/Contents/Home/bin:${PATH}"\n\n')
            comment = comment + \
                'そもそもなんもzshrcの設定がなされていないです.\n【export JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk"】\n【export PATH="${JAVA_HOME}/Contents/Home/bin:${PATH}"】の2行 はこちらで書き込みました.\n\n'
    else:  # JAVA_HOMEの設定 (.zshrc自体はある)
        with open(path_to_zshrc, mode='r') as f:  # 読み込み
            javahome_check = False  # 内容のチェック
            for line in f:
                if 'export JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk"' in line:
                    javahome_check = True
        if javahome_check:
            comment = comment + \
                '「Java を書きやすくする設定 2」は(おそらく)正しく行われています.\n\nもし他の不具合がある場合は,ターミナルを開いて\n      open -t ~/.zshrc \nを実行して中身をチェックしてみてください.\n\n'
        else:
            with open(path_to_zshrc, mode='a') as f:   # 追記
                f.write('\nexport JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk"\n')
                comment = comment + '「Java を書きやすくする設定 2」は正しく実行されていませんでした.\n【export JAVA_HOME="/Library/Java/JavaVirtualMachines/openjdk.jdk"】 という行をこちらで書き込みました.\nもし他の不具合がある場合は,ターミナルを開いて\n      open -t ~/.zshrc \nを実行して中身をチェックしてみてください.\n\n'

    # Javaの設定確認3
    # PATHを通す export PATH="${{JAVA_HOME}}/Contents/Home/bin:$PATH"
    with open(path_to_zshrc, mode='r') as f:  # .zshrcのチェック
        path_check = False
        for line in f:
            if 'export PATH="${JAVA_HOME}/Contents/Home/bin:${PATH}"' in line:
                path_check = True
    if not path_check:
        with open(path_to_zshrc, mode='a') as f:  # 追記
            f.write('\nexport PATH="${JAVA_HOME}/Contents/Home/bin:${PATH}"\n')
            comment = comment + \
                'Java を書きやすくする設定 3. は実行されていませんでした.\n【export PATH="${JAVA_HOME}/Contents/Home/bin:${PATH}"】 という行をこちらで書き込みました.\nもし他の不具合がある場合は,ターミナルを開いて\n      open -t ~/.zshrc \nを実行して中身をチェックしてみてください.'
    else:
        comment = comment + \
            '「Java を書きやすくする設定 3」は(おそらく)正しく行われています.\nもし他の不具合がある場合は,ターミナルを開いて\n      open -t ~/.zshrc \nを実行して中身をチェックしてみてください.'

    return comment

questions = {
    'Homebrew': 'Homebrewはちゃんとインストールされてる？',
    'Homebrewecho': 'Homebrewインストール後の\n【 echo \'export PATH="/opt/homebrew/bin:$PATH"\' >> ${HOME}/.zshrc 】\nはちゃんと入力できてる？',
    'Brave': 'Brave(Webブラウザ)はインストールされてる？',
    'Java': 'Javaはインストールされてる？',
    'JavaSetting': 'Javaを書きやすくする設定はちゃんと出来てる？'}

msg = """
これは「低学年が高専沼にどっぷりハマるための Mac OS 初期設定 」の答え合わせプログラムです.

特に初心者が「うまく行っているか自分でわかりにくい」ターミナルでの設定の答え合わせを行います.

↓ から答え合わせをしたい項目を選んでください
"""
sg.theme('Dark Brown')

# 位置
left = 0
right = 0
top = 50

bottom = 50
layout = [
    [sg.Text(msg, size=(80, 10), font=('Arial', 15))],
    [sg.Checkbox('Homebrewはちゃんとインストールされてる？', default=True, font=('Arial', 15), key='Homebrew')],
    [sg.Checkbox('Homebrewインストール後の【 echo \'export PATH???】はちゃんと入力できてる？', font=('Arial', 15), key='Homebrewecho')],
    [sg.Checkbox('Brave(Webブラウザ)はインストールされてる？', font=('Arial', 15), key='Brave')],
    [sg.Checkbox('Javaはインストールされてる？', font=('Arial', 15), key='Java')],
    [sg.Checkbox('Javaを書きやすくする設定はちゃんと出来てる？', font=('Arial', 15), key='JavaSetting')],
    [sg.Push(), sg.Button('答え合わせする', font=('Arial', 15), size=(30, 2), pad=((left, right), (top, bottom)), key='-Btn-'), sg.Push()]
]

window = sg.Window('Mac初期設定答え合わせプログラム', layout)

while True:

    event, value = window.read()  # イベントの入力を待つ

    if event == '-Btn-':
        for q in value:
            if value[q]:
                func = eval(q)
                comment = func()
                sg.popup(comment, font=('Noto Serif CJK JP', 18))
        break

    elif event is None:
        break

window.close()


from torch.utils.data import Dataset,DataLoader
import skimage
import numpy as np
import imageio
import os
import matplotlib
import matplotlib.colors
import skimage.transform
import random
import torchvision
import torch
import torchvision.transforms as transforms
import cv2
import matplotlib.pyplot as plt

image_h = 480
image_w = 640

class RGBD_NYU_dataset(Dataset):
    def __init__(self,rgb_path:str,dep_path:str,lab_path:str,mode:str,transform=None):
        self.rgb_path = rgb_path
        self.dep_path = dep_path
        self.lab_path = lab_path
        self.rgb_dir = os.listdir(self.rgb_path)
        self.dep_dir = os.listdir(self.dep_path)
        self.lab_dir = os.listdir(self.lab_path)

        self.mode = mode
        self.transform = transform
    def __len__(self):
        return len(self.rgb_dir)

    def __getitem__(self, item):
        rgb_name = os.path.join(self.rgb_path, self.rgb_dir[item])
        dep_name = os.path.join(self.dep_path, self.dep_dir[item])
        lab_name = os.path.join(self.lab_path, self.lab_dir[item])

        #opencv read type:H,W,C
        lab = imageio.imread(lab_name)
        dep = imageio.imread(dep_name)
        rgb = imageio.imread(rgb_name)

        sample = {'image': rgb, 'depth': dep, 'label': lab}

        if self.mode == "train":
            sample = self.transform(sample)

        return sample

class RandomHSV(object):
    """
        Args:
            h_range (float tuple): random ratio of the hue channel,
                new_h range from h_range[0]*old_h to h_range[1]*old_h.
            s_range (float tuple): random ratio of the saturation channel,
                new_s range from s_range[0]*old_s to s_range[1]*old_s.
            v_range (int tuple): random bias of the value channel,
                new_v range from old_v-v_range to old_v+v_range.
        Notice:
            h range: 0-1
            s range: 0-1
            v range: 0-255
        """

    def __init__(self, h_range, s_range, v_range):
        assert isinstance(h_range, (list, tuple)) and \
               isinstance(s_range, (list, tuple)) and \
               isinstance(v_range, (list, tuple))
        self.h_range = h_range
        self.s_range = s_range
        self.v_range = v_range

    def __call__(self, sample):
        img = sample['image']
        img_hsv = matplotlib.colors.rgb_to_hsv(img)
        img_h, img_s, img_v = img_hsv[:, :, 0], img_hsv[:, :, 1], img_hsv[:, :, 2]
        h_random = np.random.uniform(min(self.h_range), max(self.h_range))
        s_random = np.random.uniform(min(self.s_range), max(self.s_range))
        v_random = np.random.uniform(-min(self.v_range), max(self.v_range))
        img_h = np.clip(img_h * h_random, 0, 1)
        img_s = np.clip(img_s * s_random, 0, 1)
        img_v = np.clip(img_v + v_random, 0, 255)
        img_hsv = np.stack([img_h, img_s, img_v], axis=2)
        img_new = matplotlib.colors.hsv_to_rgb(img_hsv)

        return {'image': img_new, 'depth': sample['depth'], 'label': sample['label']}

class scaleNorm_depth(object):
    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        # Bi-linear
        image = skimage.transform.resize(image, (image_h, image_w), order=1,
                                         mode='reflect', preserve_range=True)
        # Nearest-neighbor
        depth = skimage.transform.resize(depth, (image_h, image_w), order=0,
                                         mode='reflect', preserve_range=True)
        label = skimage.transform.resize(label, (image_h, image_w), order=0,
                                         mode='reflect', preserve_range=True)

        return {'image': image, 'depth': depth, 'label': label}

class scaleNorm_hha(object):
    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        # Bi-linear
        image = skimage.transform.resize(image, (image_h, image_w), order=1,
                                         mode='reflect', preserve_range=True)
        depth = skimage.transform.resize(depth, (image_h, image_w), order=1,
                                         mode='reflect', preserve_range=True)

        # Nearest-neighbor
        label = skimage.transform.resize(label, (image_h, image_w), order=0,
                                         mode='reflect', preserve_range=True)

        return {'image': image, 'depth': depth, 'label': label}

class RandomScale_depth(object):
    def __init__(self, scale):
        self.scale_low = min(scale)
        self.scale_high = max(scale)

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        target_scale = random.uniform(self.scale_low, self.scale_high)
        # (H, W, C)
        target_height = int(round(target_scale * image.shape[0]))
        target_width = int(round(target_scale * image.shape[1]))
        # Bi-linear
        image = skimage.transform.resize(image, (target_height, target_width),
                                         order=1, mode='reflect', preserve_range=True)
        # Nearest-neighbor
        depth = skimage.transform.resize(depth, (target_height, target_width),
                                         order=0, mode='reflect', preserve_range=True)
        label = skimage.transform.resize(label, (target_height, target_width),
                                         order=0, mode='reflect', preserve_range=True)

        return {'image': image, 'depth': depth, 'label': label}

class RandomScale_hha(object):
    def __init__(self, scale):
        self.scale_low = min(scale)
        self.scale_high = max(scale)

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        target_scale = random.uniform(self.scale_low, self.scale_high)
        # (H, W, C)
        target_height = int(round(target_scale * image.shape[0]))
        target_width = int(round(target_scale * image.shape[1]))
        # Bi-linear
        image = skimage.transform.resize(image, (target_height, target_width),
                                         order=1, mode='reflect', preserve_range=True)

        depth = skimage.transform.resize(depth, (target_height, target_width),
                                         order=1, mode='reflect', preserve_range=True)

        # Nearest-neighbor
        label = skimage.transform.resize(label, (target_height, target_width),
                                         order=0, mode='reflect', preserve_range=True)

        return {'image': image, 'depth': depth, 'label': label}

class RandomCrop_depth(object):
    def __init__(self, th, tw):
        self.th = th
        self.tw = tw

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']
        h = image.shape[0]
        w = image.shape[1]
        i = random.randint(0, h - self.th)
        j = random.randint(0, w - self.tw)

        return {'image': image[i:i + image_h, j:j + image_w, :],
                'depth': depth[i:i + image_h, j:j + image_w],
                'label': label[i:i + image_h, j:j + image_w]}

class RandomCrop_hha(object):
    def __init__(self, th, tw):
        self.th = th
        self.tw = tw

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']
        h = image.shape[0]
        w = image.shape[1]
        i = random.randint(0, h - self.th)
        j = random.randint(0, w - self.tw)

        return {'image': image[i:i + image_h, j:j + image_w, :],
                'depth': depth[i:i + image_h, j:j + image_w, :],
                'label': label[i:i + image_h, j:j + image_w]}

class RandomFlip(object):
    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']
        if random.random() > 0.75:
            image = np.fliplr(image).copy()
            depth = np.fliplr(depth).copy()
            label = np.fliplr(label).copy()

        elif random.random() <= 0.75 and random.random() > 0.5:
            image = np.flipud(image).copy()
            depth = np.flipud(depth).copy()
            label = np.flipud(label).copy()

        elif random.random() <= 0.5 and random.random() > 0.25  :
            image = np.flipud(image).copy()
            depth = np.flipud(depth).copy()
            label = np.flipud(label).copy()
            image = np.fliplr(image).copy()
            depth = np.fliplr(depth).copy()
            label = np.fliplr(label).copy()

        return {'image': image, 'depth': depth, 'label': label}

# Transforms on torch.*Tensor
class Normalize_depth(object):
    def __call__(self, sample):
        image, depth = sample['image'], sample['depth']
        image = image / 255
        image = torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                                 std=[0.229, 0.224, 0.225])(image)
        depth = torchvision.transforms.Normalize(mean=[19050],
                                                 std=[9650])(depth)
        sample['image'] = image
        sample['depth'] = depth

        return sample

class Normalize_hha(object):
    def __call__(self, sample):
        image, depth = sample['image'], sample['depth']
        image = image / 255
        image = torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                                 std=[0.229, 0.224, 0.225])(image)
        depth = depth / 255
        depth = torchvision.transforms.Normalize(mean=[0.538, 0.444, 0.439],
                                                 std=[0.211, 0.254, 0.146])(depth)
        sample['image'] = image
        sample['depth'] = depth

        return sample

class ToTensor_depth(object):
    """Convert ndarrays in sample to Tensors."""

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        # Generate different label scales
        label2 = skimage.transform.resize(label, (label.shape[0] // 2, label.shape[1] // 2),
                                          order=0, mode='reflect', preserve_range=True)
        label3 = skimage.transform.resize(label, (label.shape[0] // 4, label.shape[1] // 4),
                                          order=0, mode='reflect', preserve_range=True)
        label4 = skimage.transform.resize(label, (label.shape[0] // 8, label.shape[1] // 8),
                                          order=0, mode='reflect', preserve_range=True)
        label5 = skimage.transform.resize(label, (label.shape[0] // 16, label.shape[1] // 16),
                                          order=0, mode='reflect', preserve_range=True)

        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        image = image.transpose((2, 0, 1))
        depth = np.expand_dims(depth, 0).astype(np.float)
        return {'image': torch.from_numpy(image).float(),
                'depth': torch.from_numpy(depth).float(),
                'label': torch.from_numpy(label/1.0).float(),
                'label2': torch.from_numpy(label2/1.0).float(),
                'label3': torch.from_numpy(label3/1.0).float(),
                'label4': torch.from_numpy(label4/1.0).float(),
                'label5': torch.from_numpy(label5/1.0).float()}

class ToTensor_hha(object):
    """Convert ndarrays in sample to Tensors."""

    def __call__(self, sample):
        image, depth, label = sample['image'], sample['depth'], sample['label']

        # Generate different label scales
        label2 = skimage.transform.resize(label, (label.shape[0] // 2, label.shape[1] // 2),
                                          order=0, mode='reflect', preserve_range=True)
        label3 = skimage.transform.resize(label, (label.shape[0] // 4, label.shape[1] // 4),
                                          order=0, mode='reflect', preserve_range=True)
        label4 = skimage.transform.resize(label, (label.shape[0] // 8, label.shape[1] // 8),
                                          order=0, mode='reflect', preserve_range=True)
        label5 = skimage.transform.resize(label, (label.shape[0] // 16, label.shape[1] // 16),
                                          order=0, mode='reflect', preserve_range=True)

        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        image = image.transpose((2, 0, 1))
        depth = depth.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image).float(),
                'depth': torch.from_numpy(depth).float(),
                'label': torch.from_numpy(label/1.0).float(),
                'label2': torch.from_numpy(label2/1.0).float(),
                'label3': torch.from_numpy(label3/1.0).float(),
                'label4': torch.from_numpy(label4/1.0).float(),
                'label5': torch.from_numpy(label5/1.0).float()}

def transforms_hha():
    return transforms.Compose([scaleNorm_hha(),
                               RandomScale_hha((1.0, 1.6)),
                               RandomHSV((0.9, 1.1),
                                         (0.9, 1.1),
                                         (25, 25)),
                               RandomCrop_hha(image_h, image_w),
                               RandomFlip(),
                               ToTensor_hha()])
def transforms_depth():
    return transforms.Compose([scaleNorm_depth(),
                               RandomScale_depth((1.0, 2.0)),
                               RandomHSV((0.9, 1.1),
                                         (0.9, 1.1),
                                         (25, 25)),
                               RandomCrop_depth(image_h, image_w),
                               RandomFlip(),
                               ToTensor_depth()])

if __name__ =='__main__':
    rgbpath = r'datasets\NYUV2\train_rgb'
    deppath = r'datasets\NYUV2\train_hha'
    labpath = r'datasets\NYUV2\train_class40'

    dataset = RGBD_NYU_dataset(rgb_path=rgbpath, dep_path=deppath, lab_path=labpath, mode= 'train',
                               transform=transforms_hha())

    dataloader = DataLoader(dataset=dataset, batch_size=1, shuffle=False)

    device = 'cpu'
    for batch_idx, sample in enumerate(dataloader):
        image = sample['image'].to(device)
        depth = sample['depth'].to(device)
        # target_scales = [sample[s].to(device) for s in ['label', 'label2', 'label3', 'label4', 'label5']]
        target_scales = [sample[s].to(device) for s in ['label']]
        print(image.shape,depth.shape, target_scales[0].shape)

        rgb = image.squeeze()
        rgb = rgb.permute(1,2,0)
        rgb = np.array(rgb).astype(np.uint8)
        plt.figure("dog1")

        dep = depth.squeeze()
        dep = dep.permute(1, 2, 0)
        dep = np.array(dep).astype(np.uint16)

        lab = target_scales[0].squeeze()
        lab = np.array(lab).astype(np.uint16)

        plt.imshow(rgb/255)
        plt.show()
        plt.imshow(dep/255)
        plt.show()
        plt.imshow(lab*5/255)
        plt.show()

        dep = depth.squeeze()
        dep = np.array(dep).astype(np.uint16)
        lab0 = target_scales[0].squeeze()
        lab0 = np.array(lab0*15).astype(np.uint8)
        lab1 = target_scales[1].squeeze()
        lab1 = np.array(lab1*15).astype(np.uint8)
        lab2 = target_scales[2].squeeze()
        lab2 = np.array(lab2*15).astype(np.uint8)
        lab3 = target_scales[3].squeeze()
        lab3 = np.array(lab3*15).astype(np.uint8)
        lab4 = target_scales[4].squeeze()
        lab4 = np.array(lab4*15).astype(np.uint8)

        cv2.imshow('rgb', rgb)
        cv2.imshow('dep', dep)
        cv2.imshow('lab0', lab0)
        cv2.imshow('lab1', lab1)
        cv2.imshow('lab2', lab2)
        cv2.imshow('lab3', lab3)
        cv2.imshow('lab4', lab4)
        cv2.waitKey(0)


# version Python 3.6.4
# 데이터 파씽 parsing 하는 test
# 명령행 인자를 받아서 실행되는 프로그램에서 인자를 파싱할때 사용.

# 옵션인자를 추가 옵션값 제한. [0, 1, 2]번의 선택 옵션.

import argparse

parser = argparse.ArgumentParser()                                                                          # 이용자에게 데이터 파싱을 받음.

parser.add_argument("square", type=int, help="This is display a square calculating of a number")            # 입력을 받으면 args.square에 파싱을 함.
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2], help="increase ouput verboiity")      # 옵션 인자 -v 또는 --verboisty 를 추가하면 옵션에 진입.
                                                                                # choice = [0, 1, 2]를 이용해서 옵션 3가지만 한정함. 그외는 오류.

args = parser.parse_args()
answer = args.square ** 2

if args.verbosity == 2:
    print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
    print("{}^2 == {}".format(args.square, answer))
else:
    print(answer)

'''
  >>> python 모듈이름.py
   -> 에러

  >>> python 모듈이름.py 4
   -> 16                            (기본 디폴트가 2의 제곱으로 되어있어 16만 출력) else조건.

  >>> python 모듈이름.py 4 -v 1     (옵션 0번)
   -> 4^2 == 16                     (elif 1번 모양으로 출력 )

  >>> python 모듈이름.py 4 -v 1     (옵션 1번)
   -> 4^2 == 16                     (elif 1번 모양으로 출력 )

  >>> python 모듈이름.py 4 -v 2     (옵션 2번)
  ->  the square of 4 equals 16     (elif 2번 모양으로 출력)

  >>> python 모듈이름.py 4 -v 4     (그외 -v 3,4,5... 등 을 파싱하면,)
  ->  에러

'''

4_옵션인자 제한.py


from pm4py.util.business_hours import BusinessHours
import datetime
from workalendar.europe import Italy

def execute_script():
    ts1 = 100000000
    ts2 = 110000000
    d1 = datetime.datetime.fromtimestamp(ts1)
    d2 = datetime.datetime.fromtimestamp(ts2)
    print(ts2-ts1)
    # default business hours: all the days of the week except Saturday and Sunday are working days.
    bh1 = BusinessHours(d1, d2, worktiming=[[7, 12.5], [13, 17]])
    print(bh1.getseconds())
    # let's calculate the business hours using a proper work calendar.
    bh2 = BusinessHours(d1, d2, worktiming=[[7, 12.25], [13.25, 17]], workcalendar=Italy())
    print(bh2.getseconds())

if __name__ == "__main__":
    execute_script()


from tkinter import *
import tkinter.messagebox
from PIL import Image, ImageTk
import socket, threading, sys, traceback, os
import time
import functools

from RtpPacket import RtpPacket

CACHE_FILE_NAME = "cache-"
CACHE_FILE_EXT = ".jpg"

class Client:
	INIT = 0
	SWITCH = 1
	READY = 2
	PLAYING = 3
	state = INIT

	SETUP = 0
	PLAY = 1
	PAUSE = 2
	TEARDOWN = 3
	DESCRIBE = 4
	LOAD = 5
	FASTER = 6
	LOWER = 7
	FORWARD = 8
	BACK = 9
	REWIND = 10

	# Initiation..
	def __init__(self, master, serveraddr, serverport, rtpport):
		self.master = master
		self.master.protocol("WM_DELETE_WINDOW", self.handler)
		self.createWidgets()
		self.serverAddr = serveraddr
		self.serverPort = int(serverport)
		self.rtpPort = int(rtpport)
		self.fileName = ''
		self.rtspSeq = 0
		self.sessionId = 0
		self.requestSent = -1
		self.teardownAcked = 0
		self.connectToServer()
		self.frameNbr = 0
		self.timePeriod = 0
		self.totalData = 0
		self.loss = 0
		self.maxFrame = 0
		self.secPerFrame = 0
		self.totalFrame = 0
		self.videos = []
		self.reset = False
		self.speed =20
		self.loadMovies()

	# THIS GUI IS JUST FOR REFERENCE ONLY, STUDENTS HAVE TO CREATE THEIR OWN GUI
	def createWidgets(self):
		"""Build GUI."""
		# Create Text
		self.ann = Text(self.master, width=40, padx=3, pady=3, height=10)
		self.ann.grid(row=4, columnspan=2)

		# Create Description
		self.des = Text(self.master, width=40, padx=3, pady=3, height=10)
		self.des.grid(row=4, columnspan=2, column=2)

		# Create Play button
		self.start = Button(self.master, width=20, padx=3, pady=3)
		self.start["text"] = "Play"
		self.start["command"] = self.playMovie
		self.start.grid(row=2, column=0, padx=0, pady=0)

		# Create Pause button
		self.pause = Button(self.master, width=20, padx=3, pady=3)
		self.pause["text"] = "Pause"
		self.pause["command"] = self.pauseMovie
		self.pause.grid(row=3, column=0, padx=0, pady=0)

		# Create Faster button
		self.setup = Button(self.master, width=20, padx=3, pady=3)
		self.setup["text"] = "Faster"
		self.setup["command"] = self.fasterMovie
		self.setup.grid(row=2, column=1, padx=2, pady=2)

		# Create Lower button
		self.setup = Button(self.master, width=20, padx=3, pady=3)
		self.setup["text"] = "Lower"
		self.setup["command"] = self.lowerMovie
		self.setup.grid(row=3, column=1, padx=2, pady=2)

		# Create Foward button
		self.teardown = Button(self.master, width=20, padx=3, pady=3)
		self.teardown["text"] = "Forward"
		self.teardown["command"] =  self.forwardMovie
		self.teardown.grid(row=2, column=2, padx=2, pady=2)

		# Create Back button
		self.describe = Button(self.master, width=20, padx=3, pady=3)
		self.describe["text"] = "Back"
		self.describe["command"] = self.backMovie
		self.describe.grid(row=3, column=2, padx=2, pady=2)

		# Create Teardown button
		self.teardown = Button(self.master, width=20, padx=3, pady=3)
		self.teardown["text"] = "Teardown"
		self.teardown["command"] =  self.exitClient
		self.teardown.grid(row=2, column=3, padx=2, pady=2)

		# Describe
		self.describe = Button(self.master, width=20, padx=3, pady=3)
		self.describe["text"] = "Describe"
		self.describe["command"] = self.describeMovie
		self.describe.grid(row=3, column=3, padx=2, pady=2)

		# Create a label to display the movie
		self.label = Label(self.master, height=19)
		self.label.grid(row=0, column=0, columnspan=6, sticky=W+E+N+S, padx=5, pady=5)

		self.frameContainer = Frame(self.master, width = 200)
		self.frameContainer.grid(column=4, row= 1, rowspan = 4)

	def loadMovies(self):
		"""Setup button handler."""
		if self.state == self.INIT:
			self.sendRtspRequest(self.LOAD)

	def setupMovie(self):
		"""Setup button handler."""
		self.reset = False
		if self.state == self.SWITCH:
			self.sendRtspRequest(self.SETUP)

	def exitClient(self):
		"""Teardown button handler."""
		self.sendRtspRequest(self.TEARDOWN)
		# Close the gui window
		self.master.destroy()
		# Delete the cache image from video
		try:
			os.remove(CACHE_FILE_NAME + str(self.sessionId) + CACHE_FILE_EXT)
		except:
			print("No such cache file to delete")

	def pauseMovie(self):
		"""Pause button handler."""
		if self.state == self.PLAYING:
			self.sendRtspRequest(self.PAUSE)

	def fasterMovie(self):
		"""Faster button handler."""
		self.sendRtspRequest(self.FASTER)

	def lowerMovie(self):
		"""Lower button handler."""
		self.sendRtspRequest(self.LOWER)

	def forwardMovie(self):
		"""Forward button handler."""
		self.sendRtspRequest(self.FORWARD)

	def backMovie(self):
		"""Back button handler."""
		self.sendRtspRequest(self.BACK)

	def describeMovie(self):
		"""Describe button handler."""
		self.sendRtspRequest(self.DESCRIBE)

	def playMovie(self):
		"""Play button handler."""
		if self.state == self.SWITCH and self.fileName != '' or self.reset:
			self.frameNbr = 0
			self.reset = False
			self.sendRtspRequest(self.SETUP)
		elif self.state == self.READY:
			self.startRecv = time.time()
			# Create a new thread to connect to server and listen to the change on server
			threading.Thread(target=self.listenRtp).start()
			# Create a variable to save the next event after click on the button "Play"
			self.playEvent = threading.Event()

			# Block thread until the request PLAY send to server and client receive the response
			self.playEvent.clear()
			# Send request to server
			self.sendRtspRequest(self.PLAY)

	def listenRtp(self):
		"""Listen for RTP packets."""
		while True:
			if (self.frameNbr >= self.maxFrame or self.reset == True):
				self.sendRtspRequest(self.PAUSE)
			try:
				data, _ = self.rtpSocket.recvfrom(20480) # load all bytes need to display

				if data:
					self.totalFrame += 1
					rtpData = RtpPacket()
					rtpData.decode(data)

					seqNum = rtpData.seqNum()
					if seqNum > self.frameNbr: # Discard the late packet
						self.endRecv = time.time()
						if (self.endRecv < self.startRecv):
							print("TIME ERROR")
						if seqNum > self.frameNbr + 1 and self.tempFrameNum != -1:
							self.loss += seqNum - self.frameNbr
						self.timePeriod += self.endRecv - self.startRecv
						self.totalData += len(rtpData.getPayload())
						self.startRecv = time.time()

						self.frameNbr = seqNum
						if self.teardownAcked != 1:
							self.updateMovie(self.writeFrame(rtpData.getPayload())) # send cache name to update movie to change content
						else:
							self.rtpSocket.shutdown(socket.SHUT_RDWR)
							self.rtpSocket.close()
							self.state = self.READY
							break

			except:
				if self.playEvent.isSet():
					self.state = self.READY
					break

				if self.teardownAcked == 1:
					self.rtpSocket.shutdown(socket.SHUT_RDWR)
					self.rtpSocket.close()
					self.state = self.READY
					break

	def writeFrame(self, data):
		"""Write the received frame to a temp image file. Return the image file."""
		cachename = CACHE_FILE_NAME + str(self.sessionId) + CACHE_FILE_EXT # name of cache file
		file = open(cachename, "wb") # open file with authorization: write and the standard file is binary
		file.write(data)
		file.close()

		return cachename

	def updateMovie(self, imageFile):
		"""Update the image file as video frame in the GUI."""
		photo = ImageTk.PhotoImage(Image.open(imageFile)) # read the data and transger to variable "photo" by using Tk package
		self.label.configure(image = photo, height=288)
		self.label.image = photo # update screen
		self.ann.delete("1.0", END)

		self.ann.insert(INSERT, "Video data recieved: "+str(self.totalData))
		self.ann.insert(INSERT, "\nRTP packet loss rate: " + str(0 if self.frameNbr == 0 else self.loss/(self.totalFrame + self.loss)))
		self.ann.insert(INSERT, "\nVideo data rate: " + str(0 if self.timePeriod == 0 else self.totalData/self.timePeriod))
		self.ann.insert(INSERT, "\nFPS: " + str(0 if self.timePeriod == 0 else self.speed))
		# self.setTime()

	def connectToServer(self):
		self.rtspSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		try:
			self.rtspSocket.connect((self.serverAddr, self.serverPort))
		except:
			tkinter.messagebox.showwarning('Connection Failed', 'Connection to \'%s\' failed.' %self.serverAddr)

	def sendRtspRequest(self, requestCode):
		"""Send RTSP request to the server."""
		#-------------
		# TO COMPLETE
		#-------------
		if requestCode == self.LOAD and self.state == self.INIT:
			threading.Thread(target=self.recvRtspReply).start()
			self.rtspSeq += 1
			msg = 'LOAD ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)
			self.requestSent = self.LOAD

		elif requestCode == self.SETUP and self.state != self.INIT:
			# update rptspSeq = self.[action]
			self.rtspSeq += 1
			# save the content of action
			msg = 'SETUP ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nTransport: RTP/UDP; client_port= ' + str(self.rtpPort)
			# keep track the request sent to server
			self.requestSent = self.SETUP
			self.state = self.READY
		# Play request
		elif requestCode == self.PLAY and self.state == self.READY:
			self.rtspSeq += 1

			msg = 'PLAY ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)
			self.requestSent = self.PLAY
			self.state = self.PLAYING
		# Pause request
		elif requestCode == self.PAUSE and self.state == self.PLAYING:
			self.rtspSeq += 1

			msg = 'PAUSE ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)
			self.requestSent = self.PAUSE
			self.state = self.READY
		# Teardown request
		elif requestCode == self.TEARDOWN and (self.state == self.PLAYING or self.state == self.READY or self.state == self.SWITCH):
			self.rtspSeq += 1

			msg = 'TEARDOWN ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)
			self.requestSent = self.TEARDOWN
			self.state = self.INIT

		elif requestCode == self.DESCRIBE and (self.state == self.PLAYING or self.state == self.READY):
			self.rtspSeq += 1

			msg = 'DESCRIBE ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)
			self.requestSent = self.DESCRIBE

		# Faster request
		elif requestCode == self.FASTER :
			#Update speed
			self.speed *=2

			# Update RTSP sequence number.
			self.rtspSeq+=1

			# Write the RTSP request to be sent.
			msg = 'FASTER ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)

			# Keep track of the sent request.
			self.requestSent = self.FASTER
			# Lower request
		elif requestCode == self.LOWER:
			#Update speed
			self.speed /=2

			# Update RTSP sequence number.
			self.rtspSeq+=1

			# Write the RTSP request to be sent.
			msg = 'LOWER ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId)

			# Keep track of the sent request.
			self.requestSent = self.LOWER
			# Forward request
		elif requestCode == self.FORWARD :
			# Update RTSP sequence number.
			self.rtspSeq+=1

			# Update frame
			self.frameNbr +=30
			if self.frameNbr > self.maxFrame:
				self.frameNbr = self.maxFrame

			# Write the RTSP request to be sent.
			msg = 'FORWARD ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId) + '\nFrame: ' + str(self.frameNbr)

			# Keep track of the sent request.
			self.requestSent = self.FORWARD
			# Back request
		elif requestCode == self.BACK :
			# Update RTSP sequence number.
			self.rtspSeq+=1

			# # Update frame
			self.frameNbr -=30
			if self.frameNbr < 0:
				self.frameNbr = 0

			# Write the RTSP request to be sent.
			msg = 'BACK ' + self.fileName + ' RTSP/1.0\nCSeq: ' + str(self.rtspSeq) + '\nSession: ' + str(self.sessionId) + '\nFrame: ' + str(self.frameNbr)

			# Keep track of the sent request.
			self.requestSent = self.BACK
		else:
			return

		# Send request to server using rtspSocket
		self.rtspSocket.sendall(bytes(msg, 'utf8'))

	def recvRtspReply(self):
		"""Receive RTSP reply from the server."""
		while True:
			try:
				data = self.rtspSocket.recv(1024) # each request will be received 1024 bytes
			except:
				print("Error 1")
			if data:
				# try:
				self.parseRtspReply(data)
				# except:
				# 	print("Error 2")
			if self.requestSent == self.TEARDOWN:
				self.rtspSocket.shutdown(socket.SHUT_RDWR)
				self.rtspSocket.close()
				break

	def parseRtspReply(self, data):
		"""Parse the RTSP reply from the server."""
		lines = data.split(b'\n')
		seqNum = int(lines[1].split(b' ')[1])
		# Process only if the server reply's sequence number is the same as the request's
		if seqNum == self.rtspSeq:
			session = int(lines[2].split(b' ')[1])
			# New RTSP session ID
			if self.sessionId == 0:
				self.sessionId = session

			# Process only if the session ID is the same
			if self.sessionId == session:
				if int(lines[0].split(b' ')[1]) == 200: # The status code 200 is OK
					if self.requestSent == self.LOAD:
						temp = lines[3].decode()[8: ].split(',')
						self.videos = temp
						self.setList()
						self.state = self.SWITCH

					elif self.requestSent == self.SETUP:
						self.maxFrame = int(lines[3].decode().split(' ')[1])
						self.secPerFrame = float(lines[4].decode().split(' ')[1])
						# Update RTSP state.
						self.state = self.READY
						# Open RTP port.
						self.openRtpPort()
						self.playMovie()
					elif self.requestSent == self.PLAY:
						self.state = self.PLAYING
					elif self.requestSent == self.PAUSE:
						self.state = self.READY
						# The play thread exits. A new thread is created on resume.
						self.playEvent.set()
					elif self.requestSent == self.TEARDOWN:
						self.state = self.INIT
						# Flag the teardownAcked to close the socket.
						self.teardownAcked = 1
					elif self.requestSent == self.DESCRIBE:
						temp = lines[3].decode()
						for i in range(4, len(lines)):
							temp += '\n' + lines[i].decode()
						self.des.insert(INSERT, temp + '\n\n')

	def openRtpPort(self):
		"""Open RTP socket binded to a specified port."""
		#-------------
		# TO COMPLETE
		#-------------
		# Create a new datagram socket to receive RTP packets from the server
		# self.rtpSocket = ...

		# Set the timeout value of the socket to 0.5sec
		# ...
		self.rtpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		# Set the timeout value of the socket to 0.5sec
		self.rtpSocket.settimeout(0.5)

		try:
			# Bind the socket to the address using the RTP port given by the client user
			self.rtpSocket.bind(("", self.rtpPort))
		except:
			tkinter.messagebox.showwarning('Unable to Bind', 'Unable to bind PORT=%d' %self.rtpPort)

	def handler(self):
		"""Handler on explicitly closing the GUI window."""
		self.pauseMovie()
		if tkinter.messagebox.askokcancel("Quit?", "Are you sure you want to quit?"):
			self.exitClient()
		else: # When the user presses cancel, resume playing.
			self.playMovie()

	def setList(self):
		def func(name):
			self.fileName = name
			self.reset = True
			self.des.insert(INSERT, "Switch to file " + name + '\n\n')
		for item in self.videos:
			button = Button(self.frameContainer, text=item, width=20, padx=2, pady=2, command=functools.partial(func,item))
			button.pack(side=TOP)


import torch
import utils

def torch_eval(val_fn):
    @torch.no_grad()
    def decorated(self, gen, *args, **kwargs):
        gen.eval()
        ret = val_fn(self, gen, *args, **kwargs)
        gen.train()

        return ret

    return decorated

class Evaluator:
    def __init__(self, writer):
        torch.backends.cudnn.benchmark = True
        self.writer = writer

    @torch_eval
    def comparable_val_saveimg(self, gen, loader, step, n_row, tag='val'):
        compare_batches = self.infer_fact_loader(gen, loader)
        comparable_grid = utils.make_comparable_grid(*compare_batches[::-1], nrow=n_row)
        saved_path = self.writer.add_image(tag, comparable_grid, global_step=step)

        return comparable_grid, saved_path

    @torch_eval
    def infer_fact_loader(self, gen, loader, save_dir=None):
        outs = []
        trgs = []

        for batch in loader:
            style_imgs = batch["style_imgs"].cuda()
            char_imgs = batch["source_imgs"].unsqueeze(1).cuda()

            out = gen.gen_from_style_char(style_imgs, char_imgs)
            outs.append(out.detach().cpu())
            if "trg_imgs" in batch:
                trgs.append(batch["trg_imgs"])

        outs = torch.cat(outs).float()
        ret = (outs,)
        if trgs:
            trgs = torch.cat(trgs)
            ret += (trgs,)

        return ret