#!/usr/bin/python3
import asyncio
import sys

# adapted from: https://stackoverflow.com/questions/57730010/python-asyncio-subprocess-write-stdin-and-read-stdout-stderr-continuously
async def read_stdout(stdout):
    print('read_stdout')
    while True:
        buf = await stdout.readline()
        if not buf:
            break

        print(f'stdout: { buf }')

async def read_stderr(stderr):
    print('read_stderr')
    while True:
        buf = await stderr.readline()
        if not buf:
            break

        print(f'stderr: { buf }')

async def write_stdin(stdin, what = sys.stdin):
    print('write_stdin')
    print('waiting for input...')
    for line in what:
        #print('reading!')
        #line = what.read(1)
        #print('got text')
        buf = line.encode()
        #buf = line
        print(f'stdin: { buf }')

        stdin.write(buf)
        await stdin.drain()
        print('waiting for input...')
        #await asyncio.sleep(0.01)
        await asyncio.sleep(0.1)
    print('done waiting...')

async def run(cmd):
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    await asyncio.gather(
        read_stderr(proc.stderr),
        read_stdout(proc.stdout),
        write_stdin(proc.stdin, sys.stdin))

# because python 3.6 doesn't have asyncio.run()
# THANK YOU https://stackoverflow.com/questions/55590343/asyncio-run-or-run-until-complete
def arun(aw):
    if sys.version_info >= (3, 7):
        return asyncio.run(aw)

    # Emulate asyncio.run() on older versions
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        return loop.run_until_complete(aw)
    finally:
        loop.close()
        asyncio.set_event_loop(None)

if __name__ == "__main__":
    args = sys.argv
    print(args)
    print("Removing first argument")
    del(args[0])

    command_to_run = ' '.join(args)
    print(command_to_run)

    arun(run(command_to_run))


"""PyTest configuration module. Defines useful fixtures, command line args."""

import logging
import os
import pathlib
import shutil

import pandas as pd
import pytest
import sqlalchemy as sa
import yaml

import pudl
from pudl import constants as pc
from pudl.output.pudltabl import PudlTabl
from pudl.workspace import datastore

logger = logging.getLogger(__name__)

START_DATE_EIA = pd.to_datetime(f"{min(pc.working_years['eia923'])}-01-01")
END_DATE_EIA = pd.to_datetime(f"{max(pc.working_years['eia923'])}-12-31")
START_DATE_FERC1 = pd.to_datetime(f"{min(pc.working_years['ferc1'])}-01-01")
END_DATE_FERC1 = pd.to_datetime(f"{max(pc.working_years['ferc1'])}-12-31")

@pytest.fixture(scope='session')
def data_scope(fast_tests, pudl_settings_fixture):
    """Define data scope for tests for CI vs. local use."""
    data_scope = {}
    test_dir = pathlib.Path(__file__).parent
    if fast_tests:
        settings_file = 'settings_datapackage_fast.yml'
        # the ferc1_dbf_tables are for the ferc1_engine. they refer to ferc1
        # dbf table names, not pudl table names. for the fast test, we only pull
        # in tables we need for pudl.
        data_scope['ferc1_dbf_tables'] = pc.ferc1_default_tables
    else:
        settings_file = 'settings_datapackage_test.yml'
        data_scope['ferc1_dbf_tables'] = [
            tbl for tbl in pc.ferc1_tbl2dbf if tbl not in pc.ferc1_huge_tables
        ]
    with open(pathlib.Path(test_dir, 'settings', settings_file),
              "r") as f:
        pkg_settings = yaml.safe_load(f)
    # put the whole settings dictionary
    data_scope.update(pkg_settings)
    # copy the etl parameters (years, tables, states) from the pkg dataset
    # settings into the data_scope so they are more easily available
    data_scope.update(pudl.etl.get_flattened_etl_parameters(
        pkg_settings['pkg_bundle_settings']))
    return data_scope

def pytest_addoption(parser):
    """Add a command line option for using the live FERC/PUDL DB."""
    parser.addoption("--live_ferc_db", action="store", default=False,
                     help="""Path to a live rather than temporary FERC DB.
                          Use --live_ferc_db=AUTO if FERC DB location concurs
                          with derived pudl_out.""")
    parser.addoption("--live_pudl_db", action="store", default=False,
                     help="""Path to a live rather than temporary PUDL DB.
                          Use --live_pudl_db=AUTO if PUDL DB location concurs
                          with derived pudl_out.""")
    parser.addoption("--pudl_in", action="store", default=False,
                     help="Path to the top level PUDL inputs directory.")
    parser.addoption("--pudl_out", action="store", default=False,
                     help="Path to the top level PUDL outputs directory.")
    parser.addoption("--fast", action="store_true", default=False,
                     help="Use minimal test data to speed up the tests.")
    parser.addoption("--clobber", action="store_true", default=False,
                     help="Clobber the existing datapackages if they exist")

@pytest.fixture(scope='session')
def live_ferc_db(request):
    """Use the live FERC DB or make a temporary one."""
    return request.config.getoption("--live_ferc_db")

@pytest.fixture(scope='session')
def live_pudl_db(request):
    """Fixture that tells use which PUDL DB to use (live vs. testing)."""
    return request.config.getoption("--live_pudl_db")

@pytest.fixture(scope='session')
def fast_tests(request):
    """
    Set a boolean flag indicating whether we are doing full or fast tests.

    We sometimes want to do a quick sanity check while testing locally, and
    that can be accomplished by setting the --fast flag on the command line.
    if fast_tests is true, then we only use 1 year of data, otherwise we use
    all available data (all the working_years for each dataset).

    Additionally, if we are on a CI platform, we *always* want to use the fast
    tests, regardless of what has been passed in on the command line with the
    --fast arguement.

    Returns:
        boolean

    """
    fast_tests = request.config.getoption("--fast")
    if os.getenv('CI'):
        logger.info("We're testing on CI platform, using minimal data.")
        fast_tests = True

    return fast_tests

@pytest.fixture(scope='session', params=['AS'], ids=['ferc1_annual'])
def pudl_out_ferc1(live_pudl_db, pudl_engine, request):
    """Define parameterized PudlTabl output object fixture for FERC 1 tests."""
    if not live_pudl_db:
        raise AssertionError("Output tests only work with a live PUDL DB.")
    return PudlTabl(pudl_engine=pudl_engine,
                    start_date=START_DATE_FERC1,
                    end_date=END_DATE_FERC1,
                    freq=request.param)

@pytest.fixture(
    scope="session",
    params=["AS", "MS"],
    ids=["eia_annual", "eia_monthly"]
)
def pudl_out_eia(live_pudl_db, pudl_engine, request):
    """Define parameterized PudlTabl output object fixture for EIA tests."""
    if not live_pudl_db:
        raise AssertionError("Output tests only work with a live PUDL DB.")
    return PudlTabl(pudl_engine=pudl_engine,
                    start_date=START_DATE_EIA,
                    end_date=END_DATE_EIA,
                    freq=request.param)

@pytest.fixture(scope='session')
def pudl_out_orig(live_pudl_db, pudl_engine):
    """Create an unaggregated PUDL output object for checking raw data."""
    if not live_pudl_db:
        raise AssertionError("Output tests only work with a live PUDL DB.")
    return PudlTabl(pudl_engine=pudl_engine,
                    start_date=START_DATE_EIA,
                    end_date=END_DATE_EIA)

@pytest.fixture(scope='session')
def ferc1_engine(live_ferc_db, pudl_settings_fixture,
                 datastore_fixture, data_scope):
    """
    Grab a connection to the FERC Form 1 DB clone.

    If we are using the test database, we initialize it from scratch first.
    If we're using the live database, then we just yield a conneciton to it.
    """
    if not live_ferc_db:
        pudl.extract.ferc1.dbf2sqlite(
            tables=data_scope['ferc1_dbf_tables'],
            years=data_scope['ferc1_years'],
            refyear=max(data_scope['ferc1_years']),
            pudl_settings=pudl_settings_fixture)
    # for now let's check if the ferc1 database has any tables...
    pudl.extract.ferc1.get_ferc1_meta(pudl_settings_fixture)

    # Grab a connection to the freshly populated database, and hand it off.
    engine = sa.create_engine(pudl_settings_fixture["ferc1_db"])
    yield engine

    logger.info(f'Engine: {engine}')

    if not live_ferc_db:
        # Clean up after ourselves by dropping the test DB tables.
        pudl.helpers.drop_tables(engine)

@pytest.fixture(scope='session')
def data_packaging(request, datastore_fixture, ferc1_engine,
                   pudl_settings_fixture, data_scope):
    """Generate limited packages for testing."""
    logger.info('setting up the data_packaging fixture')
    clobber = request.config.getoption("--clobber")
    pudl.etl.generate_data_packages(
        data_scope['pkg_bundle_settings'],
        pudl_settings_fixture,
        pkg_bundle_name=data_scope['pkg_bundle_name'],
        clobber=clobber)

@pytest.fixture(scope='session')
def data_packaging_to_sqlite(pudl_settings_fixture, data_scope, data_packaging):
    """Try flattening the data packages."""
    logger.info('setting up the data_packaging_to_sqlite fixture')
    pudl.convert.flatten_datapkgs.flatten_pudl_datapackages(
        pudl_settings_fixture,
        pkg_bundle_name=data_scope['pkg_bundle_name'],
        pkg_name='pudl-all')

    pudl.convert.datapkg_to_sqlite.pkg_to_sqlite_db(
        pudl_settings_fixture,
        pkg_bundle_name=data_scope['pkg_bundle_name'],
        pkg_name='pudl-all')

@pytest.fixture(scope='session')
def pudl_engine(ferc1_engine, live_pudl_db,
                pudl_settings_fixture,
                datastore_fixture, data_scope, request):
    """
    Grab a connection to the PUDL Database.

    If we are using the test database, we initialize the PUDL DB from scratch.
    If we're using the live database, then we just make a conneciton to it.
    """
    logger.info('setting up the pudl_engine fixture')
    if not live_pudl_db:
        clobber = request.config.getoption("--clobber")
        pudl.etl.generate_data_packages(
            data_scope['pkg_bundle_settings'],
            pudl_settings_fixture,
            pkg_bundle_name=data_scope['pkg_bundle_name'],
            clobber=clobber)

        pudl.convert.flatten_datapkgs.flatten_pudl_datapackages(
            pudl_settings_fixture,
            pkg_bundle_name=data_scope['pkg_bundle_name'],
            pkg_name='pudl-all')

        pudl.convert.datapkg_to_sqlite.pkg_to_sqlite_db(
            pudl_settings_fixture,
            pkg_bundle_name=data_scope['pkg_bundle_name'],
            pkg_name='pudl-all')
    # Grab a connection to the freshly populated PUDL DB, and hand it off.
    pudl_engine = sa.create_engine(pudl_settings_fixture["pudl_db"])
    logger.info(pudl_engine)
    yield pudl_engine

    if not live_pudl_db:
        # Clean up after ourselves by dropping the test DB tables.
        pudl.helpers.drop_tables(pudl_engine)

@pytest.fixture(scope='session')  # noqa: C901
def pudl_settings_fixture(request, tmpdir_factory, live_ferc_db, live_pudl_db):
    """Determine some settings (mostly paths) for the test session."""
    logger.info('setting up the pudl_settings_fixture')
    # Create a session scoped temporary directory.
    pudl_dir = tmpdir_factory.mktemp('pudl')

    # Grab the user configuration, if it exists:
    try:
        pudl_auto = pudl.workspace.setup.get_defaults()
    except FileNotFoundError:
        pass

    # Grab the input / output dirs specified on the command line, if any:
    pudl_in = request.config.getoption("--pudl_in")
    pudl_out = request.config.getoption("--pudl_out")

    # By default, we use the command line option. If that is left False, then
    # we use a temporary directory. If the command_line option is AUTO, then
    # we use whatever the user has configured in their $HOME/.pudl.yml file.
    if pudl_in is False:
        pudl_in = pudl_dir
    elif pudl_in == 'AUTO':
        pudl_in = pudl_auto['pudl_in']

    if pudl_out is False:
        pudl_out = pudl_dir
    elif pudl_out == 'AUTO':
        pudl_out = pudl_auto['pudl_out']

    logger.info(f"Using PUDL_IN={pudl_in}")
    logger.info(f"Using PUDL_OUT={pudl_out}")

    pudl_settings = pudl.workspace.setup.derive_paths(
        pudl_in=pudl_in,
        pudl_out=pudl_out)

    pudl.workspace.setup.init(pudl_in=pudl_in, pudl_out=pudl_out)

    if live_ferc_db == 'AUTO':
        pudl_settings['ferc1_db'] = pudl_auto['ferc1_db']
    elif live_ferc_db:
        live_ferc_db_path = pathlib.Path(live_ferc_db).expanduser().resolve()
        pudl_settings['ferc1_db'] = 'sqlite:///' + str(live_ferc_db_path)

    if live_pudl_db == 'AUTO':
        pudl_settings['pudl_db'] = pudl_auto['pudl_db']
    elif live_pudl_db:
        live_pudl_db_path = pathlib.Path(live_pudl_db).expanduser().resolve()
        pudl_settings['pudl_db'] = 'sqlite:///' + \
            str(live_pudl_db_path)

    logger.info(f'pudl_settings being used : {pudl_settings}')
    return pudl_settings

@pytest.fixture(scope='session')
def datastore_fixture(pudl_settings_fixture, data_scope):
    """
    Populate a minimal PUDL datastore for the Travis CI tests to access.

    Downloads from FERC & EPA have been... throttled or something, so we
    apparently can't pull the data from them directly any more. To deal with
    that we have checked a small amount of FERC Form 1 and EPA CEMS data into
    the PUDL repository.

    For the EIA 860 and EIA 923 data, we can use the datastore management
    library directly. It's important that we do this from within the tests,
    and not by using the update_datstore script during setup for the Travis
    tests, for two reasons:
     * the pudl module is not installed and importable until after the tox
       run has begun, so unless we import pudl from the filesystem, we can't
       use the script beforehand... and doing so would contaminate the
       environment.
     * Calling the datastore management functions from within the tests will
       add that code to our measurement of test coverage, which is good!
    """
    sources_to_update = [
        'epaipm',
        'eia860',
        'eia923'
    ]

    years_by_source = {
        'eia860': data_scope['eia860_years'],
        'eia923': data_scope['eia923_years'],
        'epacems': [],
        'epaipm': [None, ],
        'ferc1': [],
    }
    # Sadly, FERC & EPA only provide access to their data via FTP, and it's
    # not possible to use FTP from within the Travis CI environment:
    if os.getenv('TRAVIS'):
        logger.info(f"Building a special Travis CI datastore for PUDL.")
        # Simulate having downloaded the data...
        dl_dir = pathlib.Path(pudl_settings_fixture['data_dir'], 'tmp')

        epacems_files = (
            pathlib.Path(os.getenv('TRAVIS_BUILD_DIR'),
                         'test/data/epa/cems/epacems2017/').
            glob('*.zip')
        )
        # Copy the files over to the test-run proto-datastore:
        for file in epacems_files:
            logger.info(
                f"Faking download of {os.path.basename(file)} to {dl_dir}")
            shutil.copy(file, dl_dir)

        # The datastore knows what to do with a file it finds in this dir:
        datastore.organize(
            source='epacems',
            year=2017,
            states=['ID'],
            data_dir=pudl_settings_fixture['data_dir'],
            unzip=True
        )

        ferc1_files = (
            pathlib.Path(os.getenv('TRAVIS_BUILD_DIR'),
                         'test/data/ferc/form1/f1_2017/').
            glob('*.zip')
        )
        # Copy the files over to the test-run proto-datastore:
        for file in ferc1_files:
            logger.info(f"Faking the download of {file} to {dl_dir}")
            shutil.copy(file, dl_dir)

        # The datastore knows what to do with a file it finds in this dir:
        datastore.organize(
            source='ferc1',
            year=2017,
            states=None,
            data_dir=pudl_settings_fixture['data_dir'],
            unzip=True
        )
        states = []
    else:
        sources_to_update.extend(["ferc1", "epacems"])
        years_by_source["ferc1"] = data_scope["ferc1_years"]
        years_by_source["epacems"] = data_scope["epacems_years"]
        states = ["id"]

    # Download the test year for each dataset that we're downloading...
    datastore.parallel_update(
        sources=sources_to_update,
        years_by_source=years_by_source,
        states=states,
        data_dir=pudl_settings_fixture["data_dir"],
    )

    pudl.helpers.verify_input_files(
        ferc1_years=years_by_source["ferc1"],
        eia923_years=years_by_source["eia923"],
        eia860_years=years_by_source["eia860"],
        epacems_years=years_by_source["epacems"],
        epacems_states=states,
        pudl_settings=pudl_settings_fixture,
    )


import numpy as np
from PyQt5.QtCore import pyqtSlot, Qt, QItemSelectionModel, QModelIndex

from Common import show_information_message, show_error_message, init_tableview, get_row_model

from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QHeaderView, QGridLayout

from PyQt5.QtGui import QPainter, QPaintEvent, QPixmap, QStandardItem, QStandardItemModel

from ParamsSetWindow import ParamsSetWindow
from PlotWindow import PlotWindow
from SigninWindow import SigninWindow
from UI.Ui_MainWindow import Ui_MainWindow
from UserManageWindow import ManageWindow

from MapDisplay import MapDisplay

from ImportDateFromExcel import Thread_import_data_from_excel, ShowInfo
from ImportDatatoExcel import ImportDatatoExcel

from Util.Grade import Grade

from call_mainbar import ProgressBar
from graphyWindow import visual_all
from loginWindow import LoginWindow, User
from mapper.ChargeMapper import ChargeMapper
from mapper.ChargeMapper_test import ChargeMapper_test
from decimal import Decimal

from Util.Plot import Myplot2D

import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.UI = Ui_MainWindow()
        self.UI.setupUi(self)

        self.user = None

        self.setCentralWidget(self.UI.tabWidget)  # 将tabwidget设为中心控件
        self.UI.tabWidget.setVisible(True)  # 线隐藏tabwidget控件
        #self.UI.tabWidget.clear()  # 清除所有的页
        self.UI.tabWidget.setTabsClosable(True)  # Page有关闭按钮
        self.UI.tabWidget.setDocumentMode(True)  # 设置文档模式

        self.setWindowState(Qt.WindowMaximized)  # 窗口最大化显示
        self.setAutoFillBackground(False)  # 自动绘制背景
        self.setMinimumSize(1680, 1000)

        self.backgroundPix = QPixmap("Icon/00.png")
        self.pb = None #进度条窗口

        self.head = list(['充电站', '总数', '交流桩', '占比', '直流桩', '占比'])
        self.model = get_row_model(6, header=self.head)
        ###########################################################

        # add 利用信号的方式 （tableView的设置）
        # self.sense_station_change.connect(self.getValue)

        self.mapDisplay = MapDisplay(self)  # 为什么放在这，这就是解决点击按钮  弹框一下就关了的bug......... https://blog.csdn.net/veloi/article/details/115027549这里面的方法二
        self.mapDisplay.close()

        self.chargeMapper = ChargeMapper()  # 注入操作数据库类
        self.chargeMapper_test = ChargeMapper_test()  # 注入操作数据库类
        ##########################################################

        self.importdatatoexcel = ImportDatatoExcel()

        ##################################hyd  加tableView功能  huang######################
        self.init_model = None
        init_tableview(self.UI.tableView, hor_size=50, ver_size=50)
        self.get_initial_model(shuaxinchaxun=0)
        data_model = self.init_model
        selection_model = QItemSelectionModel(data_model)

        self.UI.tableView.setModel(data_model)
        self.UI.tableView.setSelectionModel(selection_model)
        # 设置表格充满这个布局QHeaderView
        self.UI.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 所有列自动拉伸，充满界面

        self.UI.tableView.horizontalHeader().setStyleSheet("QHeaderView::section{border-color: #142c58};")
        self.UI.tableView.verticalHeader().setStyleSheet("QHeaderView::section{border-color: #142c58};")

        #self.UI.tableView.horizontalHeader().setStyleSheet("QHeaderView::section{border-color: #142c58};")
        #self.UI.tableView.verticalHeader().setStyleSheet("QHeaderView::section{border-color: #142c58};")

        self.UI.tableView.selectionModel().currentRowChanged.connect(self.do_cur_row_change)
        ###########################hyd 设置主页面的饼状图显示##########################################
        self.fig_line = None
        self.fig_pie = None
        self.plot_type = None
        self.init_plot_frame()
        # x = ['a', 'b', 'c']
        # values = [20, 30, 50]
        x, values = self.get_plot_pie_row_and_line()
        self.plot_pie(x, values, '北京市各区域充电桩数据及比例显示')
        #############################################################
        # 启用登录窗口
        self.on_act_login_triggered()

    # 单击【登录】按钮
    @pyqtSlot()
    def on_act_login_triggered(self):
        login_window = LoginWindow()
        self.change_enabled([1, 2, 3, 4, 5, 6],
                            True)  # 发现的xiao bug 从页面中点击登录后，这块还得判断用户管理权限....... 也就是5不能乱开  待解决！！！2021/12/04  完美解决 如下，调整了顺序就可以 好好理解下信号传递 2021/12/06
        self.change_enabled([0], False)
        login_window.userSignal.connect(self.receive_user) #这个就是信号传递的函数，连接了自动会去执行receive_user()函数
        res_code = login_window.exec()  # 以模态的方式打开登录窗口
        if res_code == 0:
            self.change_enabled([1, 2, 3, 4, 5],
                            False)
            self.change_enabled([0], True)

    # 单击【登出】按钮
    @pyqtSlot()
    def on_act_logout_triggered(self):
        self.user = None
        self.change_enabled([0], on=True)
        self.change_enabled([1, 2, 3, 5], on=False)
        self.UI.tabWidget.setVisible(False) #关闭打开的子页面

    # 点击【导入数据】按钮
    #可能存在一个隐患 就是一次数据插入错误（不按标准格式），会导致后面的算法计算导致出错，如何设置一次插入失败全部回滚的操作？？？待解决 ：https://blog.csdn.net/weixin_29649815/article/details/114394636
    #已经解决 回滚问题 2021/12/26， 但是如果数据有问题  是否还得检查数据类型 以及数据范围的问题 判断是否回滚和接收？？？
    #解决导入数据重复问题 2021/12/26
    #解决数据导入与进度条问题，采用多线程的编写方式，以完美解决！！！2021/12/30
    @pyqtSlot()
    def on_act_import_data_triggered(self):
        file_path, *_ = QFileDialog.getOpenFileName(self, "选取文件", "", "excel Files (*.xlsx)")
        # 无意发现一个bug：点击导入数据的时候，直接×掉 会导致暂停程序,所以程序加一个空字符判断  已解决！
        if file_path == '':
            return
        data_df = self.read_excel(file_path)

        thread = Thread_import_data_from_excel(data_df)
        thread.show_info_signal.connect(self.receive_showInfo)
        thread.start() #开始线程
        # 调用进度条窗口
        self.pb = ProgressBar()
        self.pb.show()

        thread.exec() #保护子线程，否则主线程调用函数结束的时候子线程也被迫退出 一定得添加 不然就会报错，调试都调不出来的错误

    # 单击【数据可视化】按钮
    @pyqtSlot()
    def on_act_show_triggered(self):
        window = PlotWindow(self)
        window.setAttribute(Qt.WA_DeleteOnClose)
        curindex = self.UI.tabWidget.addTab(window, "可视化窗口")
        self.UI.tabWidget.setCurrentIndex(curindex)
        self.UI.tabWidget.setVisible(True)

    #  单击【参数设置】按钮
    @pyqtSlot()
    def on_actiongraph_triggered(self):
        window = ParamsSetWindow()
        window.setAttribute(Qt.WA_DeleteOnClose)
        #window.setWindowFlag(Qt.Window, True)  # 指示该窗口是一个独立的窗口
        curindex = self.UI.tabWidget.addTab(window, "参数设置")
        self.UI.tabWidget.setCurrentIndex(curindex)
        self.UI.tabWidget.setVisible(True)

    # 单击【用户管理】按钮
    @pyqtSlot()
    def on_act_manager_triggered(self):
        window = ManageWindow(self)
        window.setAttribute(Qt.WA_DeleteOnClose)
        curindex = self.UI.tabWidget.addTab(window, "用户管理")
        self.UI.tabWidget.setCurrentIndex(curindex)
        self.UI.tabWidget.setVisible(True)

    # 单击【注册】按钮
    @pyqtSlot()
    def on_act_signin_triggered(self):
        window = SigninWindow(self)  # 创建用户窗口
        window.setAttribute(Qt.WA_DeleteOnClose)  # 设置关闭删除实例
        window.setWindowFlag(Qt.Window, True)  # 指示该窗口是一个独立的窗口
        window.show()  # 非模态显示

    # 单击【退出】按钮
    @pyqtSlot()
    def on_act_quit_triggered(self):
        self.close()

    # 接收用户类槽函数
    @pyqtSlot(User)
    def receive_user(self, user: User):
        self.user = user
        self.check_grade()

    # 接受导入数据的出现的问题详情
    @pyqtSlot(ShowInfo)
    def receive_showInfo(self, showInfo: ShowInfo):
        if showInfo.info == -1:
            message = "插入的excel表重复！！！"
            show_information_message(self, message)
        elif showInfo.info == 0:
            message = "全部插入成功!!!"
            show_information_message(self, message)
            x, values = self.get_plot_pie_row_and_line()
            self.plot_pie(x, values, '北京市各区域充电桩数据及比例显示')
            self.get_initial_model(shuaxinchaxun=1)
            data_model = self.init_model
            selection_model = QItemSelectionModel(data_model)
            self.UI.tableView.setModel(data_model)
            self.UI.tableView.setSelectionModel(selection_model)
        elif showInfo.info == -2:
            message = "数据无效（无法计算出唯一解）！！！"
            show_information_message(self, message)
        else:
            message = "大约 " + str(showInfo.info) + "行数据插入失败！请检查，然后重新导入"
            show_information_message(self, message)
        self.pb.close() #数据不管导入成功与否，进度条页面都得关闭

    # 关闭分页请求函数
    @pyqtSlot(int)
    def on_tabWidget_tabCloseRequested(self, index):
        if index < 0:
            return
        else:
            # 获取关闭的页面
            page = self.UI.tabWidget.widget(index)
            # 关闭该页面
            page.close()

    # 审核当前用户权限
    def check_grade(self):
        if self.user is None:
            grade = 0
        else:
            grade = self.user.grade

        if grade == Grade.Administrator.value: #管理员
            pass
        elif grade == Grade.Normal_user.value: #普通用户
            self.change_enabled([5], on=False)

    # 改变控件使能
    def change_enabled(self, change_list, on):
        if not isinstance(change_list, list):
            # 如果不是int参数进行报错
            if not isinstance(change_list, int):
                show_error_message(self, "错误的参数:" + str(change_list))
                return

            change_list = [].append(change_list)

        for i in change_list:
            if i == 0:
                self.UI.act_login.setEnabled(on)
            elif i == 1:
                self.UI.act_logout.setEnabled(on)
            elif i == 2:
                self.UI.act_import_data.setEnabled(on)
            elif i == 3:
                self.UI.act_show.setEnabled(on)
            elif i == 4:
                self.UI.act_signin.setEnabled(on)
            elif i == 5:
                self.UI.act_manager.setEnabled(on)
            elif i == 6:
                self.UI.act_quit.setEnabled(on)

    # 读取excel数据
    def read_excel(self, file_path):
        import pandas as pd
        df = pd.read_excel(file_path, skiprows=1)
        return df

    # 重写【绘制事件】
    def paintEvent(self, event: QPaintEvent) -> None:
        painter = QPainter(self)
        pointx = 0  # 绘制的左上角
        pointy = self.UI.menubar.height() + self.UI.toolBar.height()  # 绘制的右上角
        # 绘制的图片宽度
        width = self.width()
        # 绘制的图片高度
        height = self.height() - self.statusBar().height() - self.UI.menubar.height() - self.UI.toolBar.height()
        painter.drawPixmap(pointx, pointy, width, height, self.backgroundPix)  # 绘制背景图片
        super(MainWindow, self).paintEvent(event)  # 向上传递QPaintEvent

    # 下载导出高风险等级充电桩的数据文件 .txt (hyd)
    @pyqtSlot()
    def on_act_output_triggered(self):

        file_path = QFileDialog.getSaveFileName(self, "save file", "E:/", "excel Files (*.xls)")  # 这里的file_name是tuple型
        #self.impo
        # rtdatatoexcel.export_to_excel('table_charge_station', file_path[0])
        # lists = [[1, 2, 3, 4], [45, 23, 456, 23, 54, 23], [12, 23, 23, 345, 23, 12]]
        if file_path[0] == '':
             return
        else:
            # self.Save_list(lists, file_path[0])
            # 取tuple第一个元素即为地址
            self.importdatatoexcel.export_to_excel('table_charge_pile', file_path[0])

    # 下载导出模板数据文件 .txt (hyd)
    @pyqtSlot()
    def on_actionact_formwork_triggered(self):
        file_path = QFileDialog.getSaveFileName(self, "save file", "E:/",
                                                "excel Files (*.xls)")  # 这里的file_name是tuple型
        if file_path[0] == '':
            return
        else:
        # self.Save_list(lists, file_path[0])
            # 取tuple第一个元素即为地址
            self.importdatatoexcel.export_to_excel_formwork('table_formwork', file_path[0])

    # 地图风险等级展示按钮  author：hyd
    @pyqtSlot()
    def on_act_map_triggered(self):
        '''
        charge_name = self.UI.comboBox_type_station.currentText()
        sid = self.chargeMapper.find_sid_by_charge_name(charge_name)
        # 先获得经纬度数据,是一个数组类型，可能需要将数据强转float
        longitude_and_latitude = self.chargeMapper.find_longitude_and_latitude_by_charge_name(sid)
        #获取该充电站下的pid以及对应的风险等级数据，返回一个字典类型
        pid_and_risk_level_dict = self.chargeMapper.find_pid_risk_level_by_sid(sid)
        '''
        # todo:外弹框 显示地图就ok，我需要把经纬度数据填

        location = self.chargeMapper_test.find_longitude_latitude_risk()
        if location is None:        # 修复没有数据的时候点击地图展示报错问题
            message = "请先插入数据！"
            show_information_message(self, message)
            return

        location1 = list([])  # 现在location是列表 需要把i i+1单独存成元组
        location2 = list([])

        for i in range(0, len(location)):
            location1.append(location[i][0])
            location2.append(location[i][1])
            # level.append(location[i][2])
        for k in range(0, len(location1)):
            location1[k] = float(Decimal(location1[k]))
            location2[k] = float(Decimal(location2[k]))
        local_real = [list(l) for l in zip(location2, location1)]

        a = 0
        list3 = np.array(list(set([tuple(t) for t in local_real])),
                         dtype='float').tolist()  # The truth value of an array
        # with more than one element is ambiguous. Use a.any() or a.all() 但显示在folium.Icon这块

        level = [[] for x in range(len(list3))]
        for k in range(0, len(list3)):
            if a == len(location):
                break
            for i in range(0, len(location)):
                if location2[i] == list3[k][0]:
                    # list1.append(location[i][2])  # 要把风险等级存入二维列表
                    level[k].append(location[i][2])
                    a = a + 1
                else:
                    j = k + 1
                    level[j].append(location[i][2])
                    a = a + 1

        addr = local_real[len(local_real)-1]
        data = visual_all(list3, addr, *level)  # list输入位置与风险等级

        # mapDisplay = MapDisplay(data)
        self.mapDisplay.trans_data(data)
        # self.mapDisplay.setAttribute(Qt.WA_DeleteOnClose)  # 设置关闭删除实例  应该是这句话一直出现了bug
        self.mapDisplay.setWindowFlag(Qt.Window, True)  # 指示该窗口是一个独立的窗口
        # mapDisplay.exec()  #Qwidths没有模态窗口选项
        self.mapDisplay.show()

    @pyqtSlot(QModelIndex, QModelIndex)
    def do_cur_row_change(self, cur: QModelIndex, pre: QModelIndex):
        self.cur_row = cur.row()

    # 获取最初的数据模型  huang
    def get_initial_model(self, shuaxinchaxun: int):
        new_id = self.chargeMapper.find_newest_sid()
        ret_data = self.chargeMapper.find_first_page_table(new_id, shuaxinchaxun=shuaxinchaxun)
        if ret_data is None:
            return list([])
        data = list([])
        for i in range(0, len(ret_data)):
            temp = list([])
            sum = ret_data[i][1] + ret_data[i][2]
            temp.append(ret_data[i][0])
            temp.append(sum)  # 充电站总数量
            temp.append(ret_data[i][1])  # 交流数量
            temp.append('{:.0f}%'.format(ret_data[i][1] / sum * 100))  # 交流数量占比
            temp.append(ret_data[i][2])  # 直流数量
            temp.append('{:.0f}%'.format(ret_data[i][2] / sum * 100))  # 直流数量占比
            data.append(temp)
        self.init_model = self.add_data(self.model, data)

    # 向模型添加数据
    def add_data(self, model: QStandardItemModel, data) -> QStandardItemModel:
        for i in data:
            row = []
            for j in range(len(i)):
                item = QStandardItem(str(i[j]))
                row.append(item)
            model.appendRow(row)
        return model

    # 初始化画图区域 没直接用plotsubwindow的是因为这里是frame 那个是frame_5 改了会影响数据可视化页面 hyd
    def init_plot_frame(self):
        self.fig_line = Myplot2D(plt0=10)
        #tool = NavigationToolbar(self.fig_line, self.UI.frame)
        layout = QGridLayout()
        layout.addWidget(self.fig_line)
        #layout.addWidget(tool)
        self.UI.frame.setLayout(layout)

    # 一个autopct指向的函数，lambda可以指向
    def func(self, pct, allvals):
        absolute = int(pct / 100. * np.sum(allvals))
        return "{:.1f}%\n({:d} 台)".format(pct, absolute)

    # 绘制饼状图
    def plot_pie(self, x: list, values: list, type: str, grid=False):
        if x is None:  # 没有数据的时候的判断
            return
        self.fig_line.axes.clear()

        self.fig_line.axes.pie(values,
                                labels=x,
                                autopct=lambda pct: self.func(pct, values),
                                shadow=True,
                                #colors=['y', 'g', 'b', 'r', 'w'],
                                startangle=100
                               )
        #self.fig_line.axes.legend(x, title="", loc='center left', fontsize=10, bbox_to_anchor=(1, 0, 0.5, 1))  # 有图例总是无法覆盖 暂时先去掉 加上新数据后再看
        self.fig_line.axes.legend()
        self.fig_line.axes.set_title(type + "饼状图", fontsize=20)
        self.fig_line.axes.axis('equal')
        self.fig_line.draw()

    def get_plot_pie_row_and_line(self):
        x, values = self.chargeMapper_test.find_plot_attr_by_sid_and_main_data()
        return x, values

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


from collections import deque

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left, self.right = None, None
    def traverse(self):
        queue = deque()
        queue.append(self)
        result = []
        flag = True
        while queue:
            currentLevel = deque()
            levelLength = len(queue)
            for i in range(levelLength):
                currentNode = queue.popleft()
                if flag == True:
                    currentLevel.append(currentNode.value)
                else:
                    currentLevel.appendleft(currentNode.value)
                if currentNode.left:
                    queue.append(currentNode.left)
                if currentNode.right:
                    queue.append(currentNode.right)
            flag = not flag
            result.append(currentLevel)
        return result

def main():
    root = TreeNode(12)
    root.left = TreeNode(7)
    root.right = TreeNode(1)
    root.left.left = TreeNode(9)
    root.right.left = TreeNode(10)
    root.right.right = TreeNode(5)
    root.right.left.left = TreeNode(20)
    root.right.left.right = TreeNode(17)
    print("Zigzag traversal: " + str(root.traverse()))

main()


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Thu Dec  6 11:24:49 2018

@author: rhou
"""

import warnings
warnings.filterwarnings("ignore")
import argparse
import pandas as pd
import os, sys, glob
from scipy import io

def CalCords(savefolder, em, visMethod):
    cordFile = os.path.join(savefolder, 'cords_%s.csv' % visMethod)
    if os.path.exists(cordFile):
        cords = pd.read_csv(cordFile, index_col=0, header=0)
        return cords

    x = em.T.values
    from sklearn.preprocessing import StandardScaler
    x = StandardScaler().fit_transform(x)
    from sklearn.decomposition import PCA
    pca = PCA(n_components=10)
    principalComponents = pca.fit_transform(x)

    if visMethod == 'PCA':
        cords = pd.DataFrame(data = principalComponents[:,:2], columns = ['x', 'y'], index = em.columns)
    elif visMethod == 'tSNE':
        from sklearn.manifold import TSNE
        tsneComponents = TSNE(n_components=2).fit_transform(principalComponents)
        cords = pd.DataFrame(data = tsneComponents, columns = ['x', 'y'], index = em.columns)
    elif visMethod == 'UMAP':
        import umap
        umapComponents = umap.UMAP(n_components=2).fit_transform(principalComponents)
        cords = pd.DataFrame(data = umapComponents, columns = ['x', 'y'], index = em.columns)
    cords.index.name = 'barcode'
    cords.to_csv(cordFile, index=True, header=True)
    return cords

def DrawScatters(savefolder, annoFile, visMethod, cords, annos):
    import plotly
    import plotly.graph_objs as go
    annText = os.path.basename(annoFile).split('.')[0]

    for kind in ['cell type', 'top sample']:
        if kind not in annos.columns:
            continue
        annotationList = sorted(list(set(annos.ix[:,kind])))

        import seaborn as sns
        colorList = sns.hls_palette(n_colors=len(annotationList))

        data = []
        annoLen = 0
        for annoIdx in range(len(annotationList)):
            annoNames = annotationList[annoIdx]
            if len(annoNames) > annoLen:
                annoLen = len(annoNames)
            indicesOfAnno = annos[kind]==annoNames
            text = []
            for idx in annos.index[indicesOfAnno]:
                show_text = '%s: %s, barcode: %s' % (kind, annoNames, idx)
                text.append(show_text)
            trace = go.Scatter(
                x = cords.ix[annos.index[indicesOfAnno],'x'],
                y = cords.ix[annos.index[indicesOfAnno],'y'],
                name = annoNames,
                mode = 'markers',
                marker=dict(
                    color='rgb(%s, %s, %s)' % colorList[annoIdx],
                    size=5,
                    symbol='circle',
                    line=dict(
                        color='rgb(204, 204, 204)',
                        width=1
                    ),
                    opacity=0.9
                ),
                text = text,
             )
            data.append(trace)
        if annoLen < 35:
            layout = go.Layout(legend=dict(orientation="v"),autosize=True,showlegend=True)
        else:
            layout = go.Layout(legend=dict(orientation="v"),autosize=True,showlegend=False)
        fig = go.Figure(data=data, layout=layout)
        fn = os.path.join(savefolder, '%s_%s_%s.html' % (annText, kind.replace(' ', '_'), visMethod))
        print('##########saving plot: %s' % fn)
        plotly.offline.plot(fig, filename=fn)

#start to visualise test dataset
def main(testFormat, testDS, annoFile, visMethod):
    #load test data
    print('##########loading test data')
    if testFormat == '10x':
        fileItem = glob.glob(os.path.join(testDS, "matrix.mtx"))[0]
        em = io.mmread(fileItem)
        em = em.tocsr().toarray()
        if os.path.exists(os.path.join(opt.testDS, 'genes.tsv')):
            row = pd.read_table(fileItem[:-10]+"genes.tsv", header=None, index_col=None)
        else:
            row = pd.read_table(fileItem[:-10]+"features.tsv", header=None, index_col=None)
        col = pd.read_table(fileItem[:-10]+"barcodes.tsv", header=None, index_col=None)
        em = pd.DataFrame(em, index=row.T.values[1], columns=col.T.values[0])
        savefolder = testDS
    else:
        em = pd.read_csv(testDS, index_col=0, header=0)
        savefolder = testDS[:-4]

    print('##########reducing dimensions')
    cords = CalCords(savefolder, em, visMethod)
    annos = pd.read_csv(annoFile, index_col=0, header=0)
    commonIdx = set(cords.index).intersection(set(annos.index))
    cords = cords.ix[commonIdx,]
    annos = annos.ix[commonIdx,]

    print('##########darwing the scatter plots in the folder: %s' % savefolder)
    DrawScatters(savefolder, annoFile, visMethod, cords, annos)
    print('##########DONE!')

if __name__ == "__main__":

    #process arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('--testDS', required=True, help='path to the folder of test dataset if dtype is 10x, otherwise, the path to the file')
    parser.add_argument('--dFormat', default='10x', help='10x (default) | csv')
    parser.add_argument('--annoFile', required=True, help='path to the annotation file generated by scMatch')
    parser.add_argument('--visMethod', default='p', help='p[ca] (default) | t[sne] | u[map]')

    opt = parser.parse_args()

    #check visMethod
    if opt.visMethod.lower() == "p":
        visMethod = 'PCA'
    elif opt.visMethod.lower() == "t":
        visMethod = 'tSNE'
    elif opt.visMethod.lower() == "u":
        visMethod = 'UMAP'
    else:
        sys.exit("The visMethod can only be 'p', 't' or 'u'.")

    #check dFormat and testDS
    if opt.dFormat.lower() == 'csv':
        if not os.path.exists(opt.testDS):
            sys.exit("The test dataset file does not exist.")
        if opt.testDS[-4:].lower() != '.csv':
            sys.exit("The test dataset file is not a CSV file.")
    elif opt.dFormat.lower() == '10x':
        if not os.path.exists(opt.testDS):
            sys.exit("The folder of test dataset does not exist.")
        if not os.path.exists(os.path.join(opt.testDS, 'matrix.mtx')):
            sys.exit("Cannot find 'matrix.mtx' file in the folder of test dataset.")
        if not os.path.exists(os.path.join(opt.testDS, 'genes.tsv')):
            if not os.path.exists(os.path.join(opt.testDS, 'features.tsv')):
                sys.exit("Cannot find 'genes.tsv' or 'features.tsv' file in the folder of test dataset.")
        if not os.path.exists(os.path.join(opt.testDS, 'barcodes.tsv')):
            sys.exit("Cannot find 'barcodes.tsv' file in the folder of test dataset.")

    #check annotation file
    if not os.path.exists(opt.annoFile):
        sys.exit("Cannot find annotation file for single cells.")

    #pass argument check, show input data
    print('===================================================')
    print('Input data:')
    print('The format of the test dataset: %s' % opt.dFormat)
    if opt.dFormat == '10x':
        print('Test data are in the folder: %s' % opt.testDS)
    else:
        print('Test data are in the file: %s' % opt.testDS)
    print('Annotation file: %s' % opt.annoFile)
    print('The visualisation method: %s' % visMethod)
    print('===================================================')

    #start to visualise test dataset
    main(opt.dFormat, opt.testDS, opt.annoFile, visMethod)
    #python visAnno.py --dFormat csv --testDS GSE81861_Cell_Line_COUNT.csv --annoFile GSE81861_Cell_Line_COUNT/annotation_result_keep_all_genes/human_Spearman_top_ann.csv


import pytest
from playwright.sync_api import Page

@pytest.mark.skip(reason="Just a different selectors example")
def test_text_selectors(page: Page):
    #  Test Locators: default matching is case-insensitive and searches for a substring
    page.locator("text=Log in").click()

    # Fill the input by targeting the label.
    page.fill('text=Password', 'secret')

    # select elements that have some text somewhere inside,
    # possibly in a descendant element. Matching is case-insensitive and searches for a substring
    page.locator("button", has_text="Click me").click()

@pytest.mark.skip(reason="Just a different selectors example")
def test_css_xpath_selectors(page: Page):
    # css engine pierces open shadow DOM by default.
    # Playwright adds custom pseudo-classes like :visible, :text and more.
    page.locator("button").click()
    page.locator("button:visible").click()
    page.locator("button:has-text('Log in')").click()

    # Clicks a <button> that has either a "Log in" or "Sign in" text.
    page.locator('button:has-text("Log in"), button:has-text("Sign in")').click()

    # Fill an input to the right of "Username".
    page.locator("input:right-of(:text(\"Username\"))").fill("value")

    # Selector starting with // or .. is assumed to be an xpath selector. (doesn't pierce shadow roots  )
    page.locator("//html/body")

@pytest.mark.skip(reason="Just a different selectors example")
def test_chained_selectors(page: Page):
    # Chaining selectors
    page.locator("css=article >> css=.bar > .baz >> css=span[attr=value]")

    # Click first button
    page.locator("button >> nth=0").click()

    # By default, chained selectors resolve to an element queried by the last selector.
    # A selector can be prefixed with * to capture elements that are queried by an intermediate selector.
    page.locator("css=article >> text=Hello")
    page.locator("*css=article >> text=Hello")

    # only select elements that have a descendant matching another locator.
    page.locator("article", has=page.locator("button.subscribe"))

@pytest.mark.skip(reason="Just a different selectors example")
def test_special_selectors(page: Page):
    # React selectors - match by component
    page.locator("_react=BookItem")
    page.locator("_react=BookItem[author = 'Steven King']")

    # Vue selectors
    page.locator("_vue=book-item")

    # Click an element with data-test-id "submit" (id, data-testid, data-test-id, data-test)
    page.locator("data-test-id=submit").click()