From 4a34f2a85a73c4eddf05ae820533f8a803464bfa Mon Sep 17 00:00:00 2001 From: "nazarchuk.as" Date: Fri, 4 Nov 2022 18:18:40 +0300 Subject: [PATCH] add third window and class for data --- Automation/Main.py | 460 ++++++++++++++++++++++++++++++++++++++++++ Automation/arrow.png | Bin 0 -> 11486 bytes Automation/j/data.csv | 3 + 3 files changed, 463 insertions(+) create mode 100644 Automation/Main.py create mode 100644 Automation/arrow.png create mode 100644 Automation/j/data.csv diff --git a/Automation/Main.py b/Automation/Main.py new file mode 100644 index 0000000..d6f3309 --- /dev/null +++ b/Automation/Main.py @@ -0,0 +1,460 @@ +import os +import sys +import csv +import numpy as np +import matplotlib.pyplot as plt +from scipy.optimize import curve_fit +import time +from PyQt6 import QtCore +from PyQt6.QtGui import QAction, QIcon, QPixmap +from PyQt6.QtWidgets import (QHBoxLayout, + QApplication, + QCheckBox, + QComboBox, + QDateEdit, + QDateTimeEdit, + QDial, + QDoubleSpinBox, + QFontComboBox, + QLabel, + QLCDNumber, + QLineEdit, + QMainWindow, + QProgressBar, + QPushButton, + QRadioButton, + QSlider, + QSpinBox, + QTimeEdit, + QVBoxLayout, + QWidget, + QTableWidget, + QGridLayout, + QMenu, + QTableWidgetItem, + QHeaderView, + QTextBrowser, + ) + + +class AbstractWindow(QMainWindow): + def __init__(self): + super().__init__() + self.create_actions() + self.create_menuBar() + + def create_menuBar(self): + menuBar = self.menuBar() + self.fileMenu = QMenu("&Файл", self) + menuBar.addMenu(self.fileMenu) + self.helpMenu = QMenu("&Помощь") + menuBar.addMenu(self.helpMenu) + self.fileMenu.addAction(self.exitAction) + self.helpMenu.addAction(self.helpContentAction) + self.helpMenu.addAction(self.aboutAction) + + def create_actions(self): + self.exitAction = QAction("&Выход", self) + self.exitAction.triggered.connect(self.exit_click) + self.helpContentAction = QAction("&Инструкция", self) + self.helpContentAction.triggered.connect(self.help_click) + self.aboutAction = QAction("&О программе", self) + self.aboutAction.triggered.connect(self.about_click) + + def exit_click(self): + self.close() + + def help_click(self): + print('Help') + # TODO + + def about_click(self): + print('About') + # TODO + + +class Start: + def __init__(self): + self.number = 0 + self.foldername = '' + if not QApplication.instance(): + self.app = QApplication(sys.argv) + else: + self.app = QApplication.instance() + self.window = StartWindow(self) + self.draw() + self.app.exec() + + def __del__(self): + print('destruct') + + def draw(self): + self.window.show() + + def change_number(self): + if self.number == 20: + self.window.close() + self.window = MainExperimentDataWindow(self) + if self.number == 21: + self.window.close() + self.window = MainExperimentChartWindow(self) + + if self.number == 10: + pass + self.draw() + + +class StartWindow(AbstractWindow): + def __init__(self, parent): + super().__init__() + + self.setWindowTitle('Эффект Холла в полупроводниках') + self.parent = parent + self.centralwidget = QWidget() + self.resize(1400, 800) + self.setCentralWidget(self.centralwidget) + self.lineEdit = QLineEdit(placeholderText='Введите фамилию') + self.lineEdit.returnPressed.connect(self.enter_name) + + self.flow = QPushButton('измерение потока') + self.flow.clicked.connect(self.flow_click) + self.flow.setEnabled(False) + self.main = QPushButton('основной эксперимент') + self.main.clicked.connect(self.main_click) + self.main.setEnabled(False) + + self.hbox_layout = QGridLayout(self.centralwidget) + self.hbox_layout.setRowStretch(1, 1) + self.hbox_layout.addWidget(self.lineEdit, 1, 0, 1, 2) + self.hbox_layout.addWidget(self.flow, 2, 0) + self.hbox_layout.addWidget(self.main, 2, 1) + + def flow_click(self): + self.parent.number = 10 + self.parent.change_number() + + def main_click(self): + self.parent.number = 20 + self.parent.change_number() + + def enter_name(self): + # make folder + self.parent.foldername = self.lineEdit.text() + self.parent.folder = os.path.join(os.getcwd(), self.parent.foldername) + if not os.path.exists(self.parent.folder): + os.mkdir(self.parent.folder) + + self.flow.setEnabled(True) + self.main.setEnabled(True) + self.lineEdit.setReadOnly(True) + + +class ThreadData(QtCore.QThread): + signal = QtCore.pyqtSignal(str) + + def __init__(self, parent): + QtCore.QThread.__init__(self) + self.running = False + self.parent = parent + + def run(self): + self.running = True + while self.running: + self.parent.no_data() + self.sleep(1) + + +class MainExperimentDataWindow(AbstractWindow): + def __init__(self, parent): + # TODO : clean code + super().__init__() + + self.setWindowTitle('Основной эксперимент. Получение данных') + self.start_time = round(time.time()*1000) + self.parent = parent + self.data_thread = ThreadData(self) + self.resize(1400, 800) + + # make masthead + self.dataname = 'data.csv' + head_1 = 'I_0,mA' + head_2 = 'U_34,mV' + head_3 = 't,s' + with open(os.path.join(self.parent.folder, self.dataname), 'w') as file: + wr = csv.writer(file) + wr.writerow([head_1, head_2, head_3]) + + self.centralwidget = QWidget() + self.setCentralWidget(self.centralwidget) + + self.start = QPushButton('Старт') + self.start.clicked.connect(self.start_clicked) + self.start.setEnabled(False) + + self.stop = QPushButton('Стоп') + self.stop.clicked.connect(self.stop_clicked) + self.stop.setEnabled(False) + + self.next = QPushButton(self) + self.next.setIcon(QIcon('arrow.png')) + self.next.setEnabled(False) + self.next.clicked.connect(self.next_clicked) + + grid_layout = QGridLayout(self.centralwidget) + + self.table = QTableWidget(self) # Create a self.table + self.table.setColumnCount(3) # Set three columns + self.table.setRowCount(0) + + self.table.setHorizontalHeaderLabels([head_1, head_2, head_3]) + + header = self.table.horizontalHeader() + header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) + self.table.setItem(0, 0, QTableWidgetItem("Text in column 1")) + self.table.setItem(0, 1, QTableWidgetItem("Text in column 2")) + self.table.setItem(0, 2, QTableWidgetItem("Text in column 3")) + self.table.resizeColumnsToContents() + + self.lineEdit = QLineEdit(placeholderText='Введите что-то') + self.lineEdit.returnPressed.connect(self.enter_smth) + + # Adding the table to the grid + grid_layout.addWidget(self.table, 0, 0, -1, 1) + grid_layout.addWidget(self.lineEdit, 0, 2, -1, -1) + grid_layout.addWidget(self.next, 2, 2, -1, -1) + grid_layout.addWidget(self.start, 1, 2) + grid_layout.addWidget(self.stop, 1, 3) + + self.parent.draw() + + def enter_smth(self): + # TODO + self.parent.smth = self.lineEdit.text() + self.start.setEnabled(True) + self.lineEdit.setReadOnly(True) + + def start_clicked(self): + self.stop.setEnabled(True) + self.start.setEnabled(False) + if not self.data_thread.isRunning(): + self.data_thread.start() + + def no_data(self): + current_time = round(time.time()*1000) + v = str((current_time-self.start_time)/60) + a = v + t = str(current_time-self.start_time) + with open(os.path.join(self.parent.folder, self.dataname), 'a') as file: + wr = csv.writer(file) + wr.writerow([v, a, str(current_time-self.start_time)]) + self.table.insertRow(self.table.rowCount()) + self.table.setItem(self.table.rowCount()-1, 0, QTableWidgetItem(v)) + self.table.setItem(self.table.rowCount()-1, 1, QTableWidgetItem(a)) + self.table.setItem(self.table.rowCount()-1, 2, QTableWidgetItem(t)) + + def stop_clicked(self): + self.data_thread.running = False + self.stop.setEnabled(False) + self.next.setEnabled(True) + + def next_clicked(self): + self.parent.number = 21 + self.parent.change_number() + + def take_data(self): + # measure voltage and current + volt_name = os.path.join('/dev', 'usbtmc1') + f_volt = open(volt_name, 'w') + f_volt.write('Measure:Voltage:DC?\n') + f_volt.close() + amp_name = os.path.join('/dev', 'usbtmc2') + f_amp = open(amp_name, 'w') + f_amp.write('Measure:Current:DC?\n') + f_amp.close() + + f_volt = open(volt_name, 'r') + v = f_volt.read(15) + f_amp = open(amp_name, 'r') + a = f_amp.read(15) + f_volt.close() + f_amp.close() + current_time = round(time.time()*1000) + with open(os.path.join(self.parent.folder, self.dataname), 'a') as file: + wr = csv.writer(file) + wr.writerow([v, a, str(current_time-self.start_time)]) + + +class MainExperimentChartWindow(AbstractWindow): + def __init__(self, parent): + super().__init__() + + self.setWindowTitle('Основной эксперимент. Обработка данных') + self.parent = parent + self.resize(1400, 800) + + self.centralwidget = QWidget() + self.setCentralWidget(self.centralwidget) + + pixmap = QPixmap('arrow.png') + self.label = QLabel(self) + self.label.setPixmap(pixmap) + self.label.resize(pixmap.width(), pixmap.height()) + + self.text = QTextBrowser() + self.text.setText('text') + + self.hbox_layout = QGridLayout(self.centralwidget) + self.hbox_layout.addWidget(self.label, 0, 0) + self.hbox_layout.addWidget(self.text, 0, 1) + + +class Data: + def __init__(self, x, y, xlabel, ylabel, caption, xerr, yerr, through_0, + data_filename, color=None, centering=None, size=15, + coefficient=[0.9, 1.1]): + self.x = x + self.y = y + self.xlabel = xlabel + self.ylabel = ylabel + self.caption = caption + if type(yerr) == float or type(yerr) == int: + self.yerr = [yerr for _ in self.y] + else: + self.yerr = yerr + if type(xerr) == float or type(xerr) == int: + self.xerr = [xerr for _ in self.x] + else: + self.xerr = xerr + self.through_0 = through_0 + if not self.color: + self.color = ['limegreen', 'indigo'] + else: + self.color = color + self.centering = centering + self.size = size + self.coefficient = coefficient + self.data_filename=data_filename + + def read_csv(self): + with open(self.data_filename) as file: + reader = list(csv.reader(file, delimiter=';', + quotechar=',', quoting=csv.QUOTE_MINIMAL)) + data = np.array(reader) + data = np.transpose(data) + dic = {} + for i in range(len(data)): + dic[data[i][0]] = np.array(data[i][1:]).astype(np.float) + data = dic + self.data=data + + def make_point_grafic(self): + + if self.xerr[1] != 0 or self.yerr[1] != 0: + plt.errorbar(self.x, self.y, yerr=self.yerr, xerr=self.xerr, linewidth=4, + linestyle='', label=self.caption, color=self.color, + ecolor=self.color, elinewidth=1, capsize=3.4, + capthick=1.4) + else: + plt.scatter(self.x, self.y, linewidth=0.005, label=self.caption, + color=self.color, edgecolor='black', s=self.size) + + if not self.centering: + plt.xlabel(self.xlabel) + plt.ylabel(self.ylabel) + else: + self.ax = plt.gca() + self.ax.spines['top'].set_visible(False) + self.ax.spines['right'].set_visible(False) + self.ax.spines['bottom'].set_position('zero') + self.ax.spines['left'].set_position('zero') + self.ax.set_xlabel(self.ylabel, labelpad=-180, fontsize=14) + self.ax.set_ylabel(self.xlabel, labelpad=-260, + rotation=0, fontsize=14) + + def make_line_grafic(self, k, b,): + if min(self.x) > 0: + xmin = min(self.x)*self.coefficient[0] + else: + xmin = min(self.x)*self.coefficient[1] + + if max(self.x) > 0: + xmax = max(self.x)*self.coefficient[1] + else: + xmax = max(self.x)*self.coefficient[0] + x = np.arange(xmin, xmax, (xmax-xmin)/10000) + plt.plot(x, k*x+b, label=self.caption, linewidth=2.4, + linestyle='-') + + def make_graffic(self, named_by_points=True): + + if named_by_points: + cap_point = self.caption + line_point = None + else: + line_point = self.caption + cap_point = None + self.make_point_grafic() + k, b, sigma = self.approx() + sigma[0] = abs(k*((sigma[0]/k)**2+(np.mean(self.yerr)/np.mean(self.y))**2 + + (np.mean(self.xerr)/np.mean(self.x))**2)**0.5) + if (b != 0): + sigma[1] = abs(b*((sigma[1]/b)**2+(np.mean(self.yerr)/np.mean(self.y))**2 + + (np.mean(self.xerr)/np.mean(self.x))**2)**0.5) + else: + sigma[1] = 0 + + self.make_line_grafic() + plt.legend() + return k, b, sigma + + def approx(self): + if self.yerr[0] != 0: + sigma_y = [1/i**2 for i in self.yerr] + else: + sigma_y = np.array([1 for _ in self.yerr]) + if self.through_0 == 0: + def f(x, k): + return k*x + k, sigma = curve_fit(f, xdata=self.x, ydata=self.y, sigma=sigma_y) + sigma = np.sqrt(np.diag(sigma)) + return k[0], 0, [sigma[0], 0] + else: + def f(x, k, b): + return x*k + b + k, sigma = curve_fit(f, xdata=self.x, ydata=self.y, sigma=sigma_y) + sigma_b = np.sqrt(sigma[1][1]) + b = k[1] + k = k[0] + sigma = np.sqrt(sigma[0][0]) + return k, b, [sigma, sigma_b] + + +start = Start() + +# ============================================================================= +# import time +# import serial +# +# ser=serial.Serial( +# port='/dev/ttyUSB0', +# baudrate=9600, +# timeout=1 +# ) +# ser.isOpen() +# +# msg='SYSTem:REMote\n' +# ser.write(msg.encode('ascii')) +# +# while 1: +# +# msg='Read?\n' +# ser.write(msg.encode('ascii')) +# time.sleep(1) +# +# bytesToRead=ser.inWaiting() +# data=ser.read(bytesToRead) +# print(data) +# +# ============================================================================= diff --git a/Automation/arrow.png b/Automation/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..b72358d05c4a072c97d50b66991d1dbee872df82 GIT binary patch literal 11486 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 i*nfuj|G!3z65wXTA17R|!V7QskGXNnh9X