如何使用多线程改进我的程序
How to improve my program with Multi-threading
我写了一个更长的代码,读取天平的重量并将它们打印在 excel 文件中。现在我想添加一个 "plot" 函数,绘制每个余额的最后十个值。我遇到的问题是程序只会在测量 运行ds 后绘制,这可能需要更长的时间。现在我发现,多线程可以避免这个问题。但我不知道如何在我现有的代码中实现它。也许有人可以告诉我如何在右边添加 类 。因此,当主循环将它们写入 excel 文件时,我将能够绘制一些其他值。
以下是我的基本代码,很抱歉代码太长了,但我无法在不丢失整个上下文的情况下显示更小的版本。我删掉了天平 2 到 10 的测量命令,而且我只留下了一个绘图命令。因为他们都在工作并且与其他人非常相似。
测量功能"def Measurement():"将花费最长的时间,并且在当前运行正在前台活跃的部分
测量功能"def Measurement():"将花费最长的时间,并且在当前运行正在前台活跃的部分。这将阻止 GUI 中的所有其他命令,并导致其他命令排队。例如:我按下"plot"或"Help"按钮,它们会在一个运行测量循环后打开。
# -*- coding: utf-8 -*-
Created on Thu Aug 8 15:22:25 2019
The following program, is planed and designed to collect the weight information form 10 OHAUS SCOUT SKX2202 balances.
The program generates an GUI for the user to input all needed information and dates for the measurements.
After collecting, the program writes all needed and important information to an Excel file (`.xlsx`). Also the program should give the possibility to plot some weight values while the measurement is running.
# import all needed basic libraries
import time
import matplotlib.pyplot as plt
import os
import os.path
import string
import serial
import sys
import xlsxwriter
from openpyxl import Workbook
from datetime import datetime
from os import path
#import all needed libraries for the graphic user interface
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QGridLayout
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QProgressBar
from PyQt5.QtWidgets import QVBoxLayout,QRadioButton, QPlainTextEdit
#creating the main window, show the code to be an application
app = QApplication([])
app.setStyle('Fusio')
app.setStyleSheet("QPushButton {margin: 10ex;}")
window = QWidget()
window.setWindowTitle('Balance Messurements')
#creating the basic window elements
#all buttons for the main window (text added to the button will show their use)
buttonOkName = QPushButton('Confirm User Name')
buttonOkAnzahl = QPushButton('Confirm input ')
buttonOkZeit = QPushButton('Confirm time ')
buttonStarter = QPushButton("Start")
buttonhelp = QPushButton("Help")
buttonClose = QPushButton("close")
buttonOpenFile = QPushButton("Open File")
buttonPlot = QPushButton("Plot Graph")
chooseTime = QLabel("Choose if your Input Time are Minutes or Seconds")
minWahl = QRadioButton("Minutes")
sekWahl = QRadioButton("Seconds")
#user input mask for the user name, only accept Letters, no numbers, no space
user_name = QLineEdit()
user_name.setPlaceholderText("User Name")
user_name.setInputMask("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaa")
#user input mask for number of measurements, only accept numbers , no Letters, no spaces
user_anzahl = QLineEdit()
user_anzahl.setPlaceholderText("Number")
user_anzahl.setInputMask('99999')
#user input mask for timeinterval, only accept numbers , no Letters, no spaces
user_zeit = QLineEdit()
user_zeit.setPlaceholderText("Timeintervall")
user_zeit.setInputMask('9999999999999')
#user input for the project name (written to the Excel file)
user_project=QLineEdit()
#the labels to name the single inputfields
user_name_label = QLabel("User Name:")
user_anzahl_label= QLabel("Number of measurements:")
user_zeit_label = QLabel("Break interval:")
Savepath =QLabel("Saving Path: ")
nameoffile = QLabel("Filename: ")
proname = QLabel("Project Name: ")
#progressbar, get values at the lower code
progcounter = QProgressBar()
#implememting the single parts to a full layout
firstlay = QHBoxLayout()
firstlay.addWidget(user_name_label)
firstlay.addWidget(user_name)
firstlay.addWidget (buttonOkName)
firstlay.addWidget(buttonhelp)
secondlay = QHBoxLayout()
secondlay.addWidget(user_anzahl_label)
secondlay.addWidget(user_anzahl)
secondlay.addWidget(buttonOkAnzahl)
layer10 = QHBoxLayout()
warning = QLabel("A breakinterval under 12 seconds can lead to irregular measurements.")
layer10.addWidget(warning)
layer10.addWidget(chooseTime)
layer11 = QHBoxLayout()
layer11.addWidget(minWahl)
layer11.addWidget(sekWahl)
layer11.addWidget(buttonOkZeit)
thirdlay = QHBoxLayout()
thirdlay.addWidget(user_zeit_label)
thirdlay.addWidget(user_zeit)
layer4 = QHBoxLayout()
notes = QLabel("Remarks/notes")
text_notes = QLineEdit()
layer4.addWidget(notes)
layer4.addWidget(text_notes)
layer5 =QHBoxLayout()
progres = QLabel("Progres: ")
layer5.addWidget(progres)
layer5.addWidget(progcounter)
layer5.addWidget(buttonStarter)
layer6= QHBoxLayout()
layer6.addWidget(proname)
layer6.addWidget(user_project)
layer7 = QHBoxLayout()
layer7.addWidget(Savepath)
layer8 = QHBoxLayout()
layer8.addWidget(nameoffile)
layer9 = QHBoxLayout()
layer9.addWidget(buttonPlot)
layer12 = QVBoxLayout()
layer12.addWidget(buttonClose)
#implementierung aller elemente in das zu zeugende Fenster
mainLayout = QGridLayout()
mainLayout.addLayout(firstlay, 1, 1, 1, 1)
mainLayout.addLayout(layer7, 2, 1, 1, 1)
mainLayout.addLayout(secondlay, 3 ,1, 1, 1)
mainLayout.addLayout(layer10, 5, 1, 1,1)
mainLayout.addLayout(layer11, 6, 1, 1, 1)
mainLayout.addLayout(thirdlay, 4,1, 1, 1)
mainLayout.addLayout(layer6, 7, 1, 1, 1)
mainLayout.addLayout(layer8, 9, 1, 1, 1)
mainLayout.addLayout(layer4, 8, 1, 1, 1)
mainLayout.addLayout(layer5, 10, 1, 1, 1)
mainLayout.addLayout(layer9, 11, 1, 1, 1)
mainLayout.addLayout(layer12,12,1,1,1)
#befehl das Fenster zu zeigen
window.setLayout(mainLayout)
window.show()
# funktion um Eingabe auf "NUr Letter" zu überprüfen
allowed_alpha = string.ascii_letters + string.whitespace
#following all functions will be defined
def on_minWahl_clicked():
global zeit
zeit_vorab = int(user_zeit.text())
zeit = zeit_vorab * 60
global Pause
Pause_vorab = int(user_zeit.text())
Pause = Pause_vorab * 60
global Zeitangabe
Zeitangabe = " minutes"
global intervall
intervall = 'Time [min]'
def on_sekWahl_clicked():
global zeit
zeit_vorab = int(user_zeit.text())
zeit = zeit_vorab
global Pause
Pause = int(user_zeit.text())
global Zeitangabe
Zeitangabe = " seconds"
global intervall
intervall = 'Time [sec]'
#function to confirm the user name
def on_OkName_clicked(self):
#convert the input to a global string for all other functions
name = str(user_name.text())
user = str(name)
# function to confirm the number of measurements
def on_OkAnzahl_clicked():
while True:
try:
#convert the input to a global integer for all other functions
global Anzahl
Anzahl = int(user_anzahl.text())
break
except Exception as inst:
print (type(inst) ) # the exception instance
print (inst.args) # arguments stored in .args
print (inst) # __str__ allows args to be printed directly
x, y = inst.args
print ('x =', x)
print ('y =', y)
#function to confirm the time for the break interval
def on_OkZeit_clicked():
while True:
try:
break
except Exception as inst:
print (type(inst) ) # the exception instance
print (inst.args) # arguments stored in .args
print (inst) # __str__ allows args to be printed directly
x, y = inst.args
print ('x =', x)
print ('y =', y)
intzeit = user_zeit.text()
if intzeit < 12:
alert = QMessageBox()
alert.setText('Intervall needs to be 12 seconds or longer!')
alert.exec_()
# start the main function/measurement function of this program by click on start button
def on_start_clicked():
while True: #function to run the measurements
try:
Measurement() #start the measurements
break
except IOError: # return information if an error appears
alert = QMessageBox()
alert.setText('Failed start the measurements!')
alert.exec_()
break
# function to check the saving path
def check_path():
dt = datetime.now()
global save_path
global user
user = user_name.text()
global filename
filename = filename = 'data_'+ dt.strftime("%Y%m%d_%I%M%S") + '.xlsx' # generates the file name
save_path = ('C:/Users/satzh/.spyder-py3/CPU Temp Measurements/' +user+'/') #generates the full saving path
a = os.path.exists(save_path) # function to found out if path exist
if a == True: # path exist = use the existing path
saveText = QLabel("Savepath: "+ save_path) #show saving path in the main window
layer7.addWidget(saveText)
#shows the saving path in a message box
alert = QMessageBox()
alert.setText("Userpath exist, Data will be saved into: %s"%save_path)
alert.exec_()
else: #path does not excist,
os.makedirs(save_path) # creats a new folder for the user, as save path
saveText = QLabel("Savepath: "+ save_path) # shows the new savepath in main window
layer7.addWidget(saveText)
#shows the saving path in a message box
alert = QMessageBox()
alert.setText("Path does not exist, file will be generated at:\n%s" %save_path)
alert.exec_()
name_end = QLabel(filename)
layer8.addWidget(name_end)
#check if their is a file withe the same name (not really probably, cause it's always named for the current time )
while True:
try:
path.exists(save_path,filename)
global wb # open an new excel workbook/file
wb = Workbook()
global sheet # creates in the workbook a new sheet
sheet = wb.active
sheet.title = "Book1"
wb.save(save_path+filename) # saves the workbook/file at the given path
#if the file exist, adding a 1 to the name and produce a new file
new_filename = 'filename'+'1'
global workbook
workbook = xlsxwriter.Workbook(save_path+ new_filename)
global worksheet
worksheet = workbook.add_worksheet()
break #if an error appears, it is creating a provisionally file
except TypeError:
workbook = xlsxwriter.Workbook(save_path + filename)
worksheet = workbook.add_worksheet()
break
else:
os.makedirs(save_path)
saveText = QLabel("Savepath: "+ save_path)
layer7.addWidget(saveText)
workbook = xlsxwriter.Workbook(save_path+filename)
worksheet = workbook.add_worksheet()
# measurement main function, collect values form the balances and write this in an Excel file
def Measurement():
c = 0
row = 2
col = 0
n = 0
# generates the title line and basic information for the Excel file
worksheet.write(0,0, 'Project Name:')
worksheet.write(0,1, user_project.text())
worksheet.write(0,2, "Remarks/Notes:")
worksheet.write(0,3, text_notes.text())
worksheet.write(0,4,"Number of measurements: %d"%Anzahl)
worksheet.write(0,5, "Breakinterval: %d"%Pause)
worksheet.write(1,0, 'Date' )
worksheet.write(1,1, 'Balance 1')
worksheet.write(1,2, 'Balance 2')
worksheet.write(1,3, 'Balance 3')
worksheet.write(1,4, 'Balance 4')
worksheet.write(1,5, 'Balance 5')
worksheet.write(1,6, 'Balance 6')
worksheet.write(1,7, 'Balance 7')
worksheet.write(1,8, 'Balance 8')
worksheet.write(1,9, 'Balance 9')
worksheet.write(1,10, 'Balance 10')
worksheet.set_column('A:B', 30)
worksheet.set_column('B:L',15)
#generates and show a message box that the measurements are started
alert = QMessageBox()
alert.setText("Measurements in progress")
alert.exec_()
# generating the value list for each balance for the print command
global lb1,lb2,lb3,lb4,lb5,lb6,lb7,lb8,lb9,lb10
lb1 =[]
lb2=[]
lb3=[]
lb4=[]
lb5=[]
lb6=[]
lb7=[]
lb8=[]
lb9=[]
lb10=[]
# measurments loop,
while c <= Anzahl:
dt = datetime.now() #create a shortcut for the date and time value
date_format = workbook.add_format({'num_format': 'd.mm.yyyy hh:mm:ss'}) #create the format which is written to the Excel file
worksheet.write_datetime(row,col, dt, date_format)
# the following notes are for all 10 measurements loops the same:
#Measurement loop for balance 1
while True:
try:
bal1 = serial.Serial('COM7' ,9600, bytesize=8, stopbits=1, timeout =0.1) #opens the serial/com-port for balance 1
#and set the bal1 variable for the port name/class
break #ends the loop when balance was opened
except OSError: #except Error for failing to open the serial port
alert = QMessageBox() #generates a messagebox to say that an error appears
alert.setText("Failed to open Balance 1")
alert.exec_()
break
while True:
try:
bal1.write(b'Z\r\n') #sends the first few characters to the balance, to activate her and make her responding to the Computer
s=bal1.read(17) #reads out the weight information
bal1.write(b'\x50') #sends the "print" command to the balance
t=list(str(s)) # creating a list of strings of the measurement values
liste = t
satz = "".join(liste [3:13] ) # reads the numbers out of the list and generate a new combined string
zwischen_string = satz # generates the values for the plotting lists
lb1.append(zwischen_string) # connect the value to the print list
gewicht = zwischen_string.replace('.', ',') #change the dot to a comma for the exported Value to excel
worksheet.write(row, 1 , gewicht) # writes the value to the Excel file
bal1.close() # close the serial port
break
except OSError:
alert = QMessageBox()
alert.setText("Failed to open collect and write the information form balance 1")
alert.exec_()
break
row += 1 #say the program, to use a new row for the next measuremnt in the Excel file
c +=1 # counter for measurements +1, next measurements for the while loop
time.sleep(Pause) # set the break, which is given by the user
#loop to set the maximum number of list elements to 10
#for the direct printing of the values
def check_plotlists():
if len(lb1)>=10:
del lb1[0]
if len(lb2)>=10:
del lb2[0]
if len(lb3)>=10:
del lb3[0]
if len(lb4)>=10:
del lb4[0]
if len(lb5)>=10:
del lb5[0]
if len(lb6)>=10:
del lb6[0]
if len(lb7)>=10:
del lb7[0]
if len(lb8)>=10:
del lb8[0]
if len(lb9)>=10:
del lb9[0]
if len(lb10)>=10:
del lb10[0]
app.processEvents() #allows to send back the percent value vor the processbar
prozent =(n/Anzahl)*100 #creating the percent value
progcounter.setValue(prozent) # gives back the percent value to the processbar
n +=1 #counter to generate the percent value
workbook.close() # close the excel file
layer9.addWidget(buttonOpenFile) # add the "open file" button
# öffnet am ende die angelegt datei
def on_OpenFile_clicked():
os.chdir
os.system('start excel.exe "%s%s"' % (save_path,filename ))
#helpFuntion, reads text from Help Instructions.txt file
#generating the help window, by clicking on the Help button
def open_help():
f = open('C:\Users\satzh\.spyder-py3\Help Instructions.txt',"r") #source for the help text
x = f.read() #reads the text from the txt. file
global windowhelp
windowhelp = QWidget()
windowhelp.setWindowTitle('Help')
buttonClose = QPushButton("Close")
buttonClose.clicked.connect(on_close_clicked)
helplayer1 = QVBoxLayout()
help_text= QPlainTextEdit()
help_text.appendPlainText(x)
help_text.zoomIn(4)
helplayer1.addWidget(help_text)
helplayer1.addWidget(buttonClose)
HelpLayout = QGridLayout()
HelpLayout.addLayout(helplayer1,1,1,1,1)
windowhelp.setLayout(HelpLayout)
windowhelp.setGeometry(300,300 ,800,480 )
windowhelp.show()
# close function for help window
def on_close_clicked():
windowhelp.close()
#generating plot menu
def on_plot_clicked():
#window generating
global printwindow
printwindow = QWidget()
printwindow.setWindowTitle('Plott')
#button generating
Balance1=QPushButton("Print Balance 1")
Balance2=QPushButton("Print Balance 2")
Balance3=QPushButton("Print Balance 3")
Balance4=QPushButton("Print Balance 4")
Balance5=QPushButton("Print Balance 5")
Balance6=QPushButton("Print Balance 6")
Balance7=QPushButton("Print Balance 7")
Balance8=QPushButton("Print Balance 8")
Balance9=QPushButton("Print Balance 9")
Balance10=QPushButton("Print Balance 10")
Balance11=QPushButton("Print All together")
Pclose = QPushButton("Close")
#generate all buttons for the print menu
printLayout1 = QHBoxLayout()
printLayout1.addWidget(Balance1)
printLayout1.addWidget(Balance2)
printLayout1.addWidget(Balance3)
printLayout2 = QHBoxLayout()
printLayout2.addWidget(Balance4)
printLayout2.addWidget(Balance5)
printLayout2.addWidget(Balance6)
printLayout3 = QHBoxLayout()
printLayout3.addWidget(Balance7)
printLayout3.addWidget(Balance8)
printLayout3.addWidget(Balance9)
printLayout4 = QHBoxLayout()
printLayout4.addWidget(Balance10)
printLayout4.addWidget(Balance11)
printLayout5 = QHBoxLayout()
printLayout5.addWidget(Pclose)
#generate the printlayout
printFinal = QVBoxLayout()
printFinal.addLayout(printLayout1)
printFinal.addLayout(printLayout2)
printFinal.addLayout(printLayout3)
printFinal.addLayout(printLayout4)
printFinal.addLayout(printLayout5)
#connect the print buttons to print orders
printwindow.setLayout(printFinal)
printwindow.show()
Balance1.clicked.connect(print_1)
Balance2.clicked.connect(print_2)
Balance3.clicked.connect(print_3)
Balance1.clicked.connect(print_4)
Balance5.clicked.connect(print_5)
Balance6.clicked.connect(print_6)
Balance7.clicked.connect(print_7)
Balance8.clicked.connect(print_8)
Balance9.clicked.connect(print_9)
Balance10.clicked.connect(print_10)
Balance11.clicked.connect(print_11)
Pclose.clicked.connect(on_pclose_clicked)
#closing the print menue
def on_pclose_clicked():
printwindow.close()
#printing for balancce 1
#following notes are for all print funtions the same
def print_1():
fig = plt.figure() #generates the basic figure for the plot, like the new window
plt.plot(lb1) #give the dates which should be printed
plt.title('Hydrogen Evolution') #generates the head-title
plt.ylabel('Weight [g]') # name fo the y cooridnates
plt.xlabel(intervall) # name for the x intervall (is defined by the choose between seconds and minutes functions above)
ax = plt.subplot(111)
ax.legend()
plt.ylim(-1, 425) # defined the intervall on the y coordinates
xmax= Anzahl*Pause #defined the intervall on the x coordinates
plt.xlim(0, xmax )
plt.show()
timestamp = time.strftime("%d%m%Y-%H%M%S") #generates part of the saving name
command = "fig.savefig('"+timestamp+"_h2evo_multiplot.png', dpi=200)" # saves the figure
exec(command)
fig.savefig('multiplot.png' , dpi=200)
#closing the Mainwindow and main Process
def close_all():
window.close()
sys.exit(app.exec_())
exit()
#connect the Buttons with their functions
buttonOkName.clicked.connect(on_OkName_clicked) #confirm name with checking user name
buttonOkName.clicked.connect( check_path) #confrim name with function to check saving path
buttonOkAnzahl.clicked.connect(on_OkAnzahl_clicked) #confirm number of messurements
buttonOkZeit.clicked.connect(on_OkZeit_clicked) #confirm time input
buttonStarter.clicked.connect(on_start_clicked)
buttonOpenFile.clicked.connect(on_OpenFile_clicked)
minWahl.toggled.connect(on_minWahl_clicked)
sekWahl.toggled.connect(on_sekWahl_clicked)
buttonhelp.clicked.connect(open_help)
buttonPlot.clicked.connect(on_plot_clicked)
buttonClose.clicked.connect(close_all)
app.exec_()
在这里的另一个线程中,我发现了以下内容:QThread 可以 运行 事件循环,QRunnable 没有,所以不要将它用于设计为具有事件循环的任务。来自:C++/Qt - QThread vs QRunnable
假设您想要运行一个名为my_func的函数使用一个线程。
import threading
my_thread = threading.Thread(target=my_func, args=())
my_thread.start()
如果你想让线程在整个程序退出后退出,在开始之前输入这个
my_thread.setDaemon(True)
如果你想传递函数需要的变量(参数)。假设你有一个函数 add(num1, num2) returns num1+num2,你在列表的 args 中传递 num1 和 num2,如下所示:
add_thread = threading.Thread(target=add, args=([4, 6]))
像Quamash这样的库应该用来防止Qt UI线程中断进程相关的线程执行。
自定义 QEventLoop 实现可能是做类似事情的替代且耗时的方式。
如果 python 线程和 Qt UI 线程不受管理,代码开始生成意外的 errors/behaviors
import sys
import asyncio
import time
from PyQt5.QtWidgets import QApplication, QProgressBar
from quamash import QEventLoop, QThreadExecutor
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop) # NEW must set the event loop
progress = QProgressBar()
progress.setRange(0, 99)
progress.show()
async def master():
await first_50()
with QThreadExecutor(1) as exec:
await loop.run_in_executor(exec, last_50)
# TODO announce completion?
async def first_50():
for i in range(50):
progress.setValue(i)
await asyncio.sleep(.1)
def last_50():
for i in range(50,100):
loop.call_soon_threadsafe(progress.setValue, i)
time.sleep(.1)
with loop: ## context manager calls .close() when loop completes, and releases all resources
loop.run_until_complete(master())
我写了一个更长的代码,读取天平的重量并将它们打印在 excel 文件中。现在我想添加一个 "plot" 函数,绘制每个余额的最后十个值。我遇到的问题是程序只会在测量 运行ds 后绘制,这可能需要更长的时间。现在我发现,多线程可以避免这个问题。但我不知道如何在我现有的代码中实现它。也许有人可以告诉我如何在右边添加 类 。因此,当主循环将它们写入 excel 文件时,我将能够绘制一些其他值。
以下是我的基本代码,很抱歉代码太长了,但我无法在不丢失整个上下文的情况下显示更小的版本。我删掉了天平 2 到 10 的测量命令,而且我只留下了一个绘图命令。因为他们都在工作并且与其他人非常相似。
测量功能"def Measurement():"将花费最长的时间,并且在当前运行正在前台活跃的部分
测量功能"def Measurement():"将花费最长的时间,并且在当前运行正在前台活跃的部分。这将阻止 GUI 中的所有其他命令,并导致其他命令排队。例如:我按下"plot"或"Help"按钮,它们会在一个运行测量循环后打开。
# -*- coding: utf-8 -*-
Created on Thu Aug 8 15:22:25 2019
The following program, is planed and designed to collect the weight information form 10 OHAUS SCOUT SKX2202 balances.
The program generates an GUI for the user to input all needed information and dates for the measurements.
After collecting, the program writes all needed and important information to an Excel file (`.xlsx`). Also the program should give the possibility to plot some weight values while the measurement is running.
# import all needed basic libraries
import time
import matplotlib.pyplot as plt
import os
import os.path
import string
import serial
import sys
import xlsxwriter
from openpyxl import Workbook
from datetime import datetime
from os import path
#import all needed libraries for the graphic user interface
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QLabel
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QGridLayout
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QProgressBar
from PyQt5.QtWidgets import QVBoxLayout,QRadioButton, QPlainTextEdit
#creating the main window, show the code to be an application
app = QApplication([])
app.setStyle('Fusio')
app.setStyleSheet("QPushButton {margin: 10ex;}")
window = QWidget()
window.setWindowTitle('Balance Messurements')
#creating the basic window elements
#all buttons for the main window (text added to the button will show their use)
buttonOkName = QPushButton('Confirm User Name')
buttonOkAnzahl = QPushButton('Confirm input ')
buttonOkZeit = QPushButton('Confirm time ')
buttonStarter = QPushButton("Start")
buttonhelp = QPushButton("Help")
buttonClose = QPushButton("close")
buttonOpenFile = QPushButton("Open File")
buttonPlot = QPushButton("Plot Graph")
chooseTime = QLabel("Choose if your Input Time are Minutes or Seconds")
minWahl = QRadioButton("Minutes")
sekWahl = QRadioButton("Seconds")
#user input mask for the user name, only accept Letters, no numbers, no space
user_name = QLineEdit()
user_name.setPlaceholderText("User Name")
user_name.setInputMask("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaa")
#user input mask for number of measurements, only accept numbers , no Letters, no spaces
user_anzahl = QLineEdit()
user_anzahl.setPlaceholderText("Number")
user_anzahl.setInputMask('99999')
#user input mask for timeinterval, only accept numbers , no Letters, no spaces
user_zeit = QLineEdit()
user_zeit.setPlaceholderText("Timeintervall")
user_zeit.setInputMask('9999999999999')
#user input for the project name (written to the Excel file)
user_project=QLineEdit()
#the labels to name the single inputfields
user_name_label = QLabel("User Name:")
user_anzahl_label= QLabel("Number of measurements:")
user_zeit_label = QLabel("Break interval:")
Savepath =QLabel("Saving Path: ")
nameoffile = QLabel("Filename: ")
proname = QLabel("Project Name: ")
#progressbar, get values at the lower code
progcounter = QProgressBar()
#implememting the single parts to a full layout
firstlay = QHBoxLayout()
firstlay.addWidget(user_name_label)
firstlay.addWidget(user_name)
firstlay.addWidget (buttonOkName)
firstlay.addWidget(buttonhelp)
secondlay = QHBoxLayout()
secondlay.addWidget(user_anzahl_label)
secondlay.addWidget(user_anzahl)
secondlay.addWidget(buttonOkAnzahl)
layer10 = QHBoxLayout()
warning = QLabel("A breakinterval under 12 seconds can lead to irregular measurements.")
layer10.addWidget(warning)
layer10.addWidget(chooseTime)
layer11 = QHBoxLayout()
layer11.addWidget(minWahl)
layer11.addWidget(sekWahl)
layer11.addWidget(buttonOkZeit)
thirdlay = QHBoxLayout()
thirdlay.addWidget(user_zeit_label)
thirdlay.addWidget(user_zeit)
layer4 = QHBoxLayout()
notes = QLabel("Remarks/notes")
text_notes = QLineEdit()
layer4.addWidget(notes)
layer4.addWidget(text_notes)
layer5 =QHBoxLayout()
progres = QLabel("Progres: ")
layer5.addWidget(progres)
layer5.addWidget(progcounter)
layer5.addWidget(buttonStarter)
layer6= QHBoxLayout()
layer6.addWidget(proname)
layer6.addWidget(user_project)
layer7 = QHBoxLayout()
layer7.addWidget(Savepath)
layer8 = QHBoxLayout()
layer8.addWidget(nameoffile)
layer9 = QHBoxLayout()
layer9.addWidget(buttonPlot)
layer12 = QVBoxLayout()
layer12.addWidget(buttonClose)
#implementierung aller elemente in das zu zeugende Fenster
mainLayout = QGridLayout()
mainLayout.addLayout(firstlay, 1, 1, 1, 1)
mainLayout.addLayout(layer7, 2, 1, 1, 1)
mainLayout.addLayout(secondlay, 3 ,1, 1, 1)
mainLayout.addLayout(layer10, 5, 1, 1,1)
mainLayout.addLayout(layer11, 6, 1, 1, 1)
mainLayout.addLayout(thirdlay, 4,1, 1, 1)
mainLayout.addLayout(layer6, 7, 1, 1, 1)
mainLayout.addLayout(layer8, 9, 1, 1, 1)
mainLayout.addLayout(layer4, 8, 1, 1, 1)
mainLayout.addLayout(layer5, 10, 1, 1, 1)
mainLayout.addLayout(layer9, 11, 1, 1, 1)
mainLayout.addLayout(layer12,12,1,1,1)
#befehl das Fenster zu zeigen
window.setLayout(mainLayout)
window.show()
# funktion um Eingabe auf "NUr Letter" zu überprüfen
allowed_alpha = string.ascii_letters + string.whitespace
#following all functions will be defined
def on_minWahl_clicked():
global zeit
zeit_vorab = int(user_zeit.text())
zeit = zeit_vorab * 60
global Pause
Pause_vorab = int(user_zeit.text())
Pause = Pause_vorab * 60
global Zeitangabe
Zeitangabe = " minutes"
global intervall
intervall = 'Time [min]'
def on_sekWahl_clicked():
global zeit
zeit_vorab = int(user_zeit.text())
zeit = zeit_vorab
global Pause
Pause = int(user_zeit.text())
global Zeitangabe
Zeitangabe = " seconds"
global intervall
intervall = 'Time [sec]'
#function to confirm the user name
def on_OkName_clicked(self):
#convert the input to a global string for all other functions
name = str(user_name.text())
user = str(name)
# function to confirm the number of measurements
def on_OkAnzahl_clicked():
while True:
try:
#convert the input to a global integer for all other functions
global Anzahl
Anzahl = int(user_anzahl.text())
break
except Exception as inst:
print (type(inst) ) # the exception instance
print (inst.args) # arguments stored in .args
print (inst) # __str__ allows args to be printed directly
x, y = inst.args
print ('x =', x)
print ('y =', y)
#function to confirm the time for the break interval
def on_OkZeit_clicked():
while True:
try:
break
except Exception as inst:
print (type(inst) ) # the exception instance
print (inst.args) # arguments stored in .args
print (inst) # __str__ allows args to be printed directly
x, y = inst.args
print ('x =', x)
print ('y =', y)
intzeit = user_zeit.text()
if intzeit < 12:
alert = QMessageBox()
alert.setText('Intervall needs to be 12 seconds or longer!')
alert.exec_()
# start the main function/measurement function of this program by click on start button
def on_start_clicked():
while True: #function to run the measurements
try:
Measurement() #start the measurements
break
except IOError: # return information if an error appears
alert = QMessageBox()
alert.setText('Failed start the measurements!')
alert.exec_()
break
# function to check the saving path
def check_path():
dt = datetime.now()
global save_path
global user
user = user_name.text()
global filename
filename = filename = 'data_'+ dt.strftime("%Y%m%d_%I%M%S") + '.xlsx' # generates the file name
save_path = ('C:/Users/satzh/.spyder-py3/CPU Temp Measurements/' +user+'/') #generates the full saving path
a = os.path.exists(save_path) # function to found out if path exist
if a == True: # path exist = use the existing path
saveText = QLabel("Savepath: "+ save_path) #show saving path in the main window
layer7.addWidget(saveText)
#shows the saving path in a message box
alert = QMessageBox()
alert.setText("Userpath exist, Data will be saved into: %s"%save_path)
alert.exec_()
else: #path does not excist,
os.makedirs(save_path) # creats a new folder for the user, as save path
saveText = QLabel("Savepath: "+ save_path) # shows the new savepath in main window
layer7.addWidget(saveText)
#shows the saving path in a message box
alert = QMessageBox()
alert.setText("Path does not exist, file will be generated at:\n%s" %save_path)
alert.exec_()
name_end = QLabel(filename)
layer8.addWidget(name_end)
#check if their is a file withe the same name (not really probably, cause it's always named for the current time )
while True:
try:
path.exists(save_path,filename)
global wb # open an new excel workbook/file
wb = Workbook()
global sheet # creates in the workbook a new sheet
sheet = wb.active
sheet.title = "Book1"
wb.save(save_path+filename) # saves the workbook/file at the given path
#if the file exist, adding a 1 to the name and produce a new file
new_filename = 'filename'+'1'
global workbook
workbook = xlsxwriter.Workbook(save_path+ new_filename)
global worksheet
worksheet = workbook.add_worksheet()
break #if an error appears, it is creating a provisionally file
except TypeError:
workbook = xlsxwriter.Workbook(save_path + filename)
worksheet = workbook.add_worksheet()
break
else:
os.makedirs(save_path)
saveText = QLabel("Savepath: "+ save_path)
layer7.addWidget(saveText)
workbook = xlsxwriter.Workbook(save_path+filename)
worksheet = workbook.add_worksheet()
# measurement main function, collect values form the balances and write this in an Excel file
def Measurement():
c = 0
row = 2
col = 0
n = 0
# generates the title line and basic information for the Excel file
worksheet.write(0,0, 'Project Name:')
worksheet.write(0,1, user_project.text())
worksheet.write(0,2, "Remarks/Notes:")
worksheet.write(0,3, text_notes.text())
worksheet.write(0,4,"Number of measurements: %d"%Anzahl)
worksheet.write(0,5, "Breakinterval: %d"%Pause)
worksheet.write(1,0, 'Date' )
worksheet.write(1,1, 'Balance 1')
worksheet.write(1,2, 'Balance 2')
worksheet.write(1,3, 'Balance 3')
worksheet.write(1,4, 'Balance 4')
worksheet.write(1,5, 'Balance 5')
worksheet.write(1,6, 'Balance 6')
worksheet.write(1,7, 'Balance 7')
worksheet.write(1,8, 'Balance 8')
worksheet.write(1,9, 'Balance 9')
worksheet.write(1,10, 'Balance 10')
worksheet.set_column('A:B', 30)
worksheet.set_column('B:L',15)
#generates and show a message box that the measurements are started
alert = QMessageBox()
alert.setText("Measurements in progress")
alert.exec_()
# generating the value list for each balance for the print command
global lb1,lb2,lb3,lb4,lb5,lb6,lb7,lb8,lb9,lb10
lb1 =[]
lb2=[]
lb3=[]
lb4=[]
lb5=[]
lb6=[]
lb7=[]
lb8=[]
lb9=[]
lb10=[]
# measurments loop,
while c <= Anzahl:
dt = datetime.now() #create a shortcut for the date and time value
date_format = workbook.add_format({'num_format': 'd.mm.yyyy hh:mm:ss'}) #create the format which is written to the Excel file
worksheet.write_datetime(row,col, dt, date_format)
# the following notes are for all 10 measurements loops the same:
#Measurement loop for balance 1
while True:
try:
bal1 = serial.Serial('COM7' ,9600, bytesize=8, stopbits=1, timeout =0.1) #opens the serial/com-port for balance 1
#and set the bal1 variable for the port name/class
break #ends the loop when balance was opened
except OSError: #except Error for failing to open the serial port
alert = QMessageBox() #generates a messagebox to say that an error appears
alert.setText("Failed to open Balance 1")
alert.exec_()
break
while True:
try:
bal1.write(b'Z\r\n') #sends the first few characters to the balance, to activate her and make her responding to the Computer
s=bal1.read(17) #reads out the weight information
bal1.write(b'\x50') #sends the "print" command to the balance
t=list(str(s)) # creating a list of strings of the measurement values
liste = t
satz = "".join(liste [3:13] ) # reads the numbers out of the list and generate a new combined string
zwischen_string = satz # generates the values for the plotting lists
lb1.append(zwischen_string) # connect the value to the print list
gewicht = zwischen_string.replace('.', ',') #change the dot to a comma for the exported Value to excel
worksheet.write(row, 1 , gewicht) # writes the value to the Excel file
bal1.close() # close the serial port
break
except OSError:
alert = QMessageBox()
alert.setText("Failed to open collect and write the information form balance 1")
alert.exec_()
break
row += 1 #say the program, to use a new row for the next measuremnt in the Excel file
c +=1 # counter for measurements +1, next measurements for the while loop
time.sleep(Pause) # set the break, which is given by the user
#loop to set the maximum number of list elements to 10
#for the direct printing of the values
def check_plotlists():
if len(lb1)>=10:
del lb1[0]
if len(lb2)>=10:
del lb2[0]
if len(lb3)>=10:
del lb3[0]
if len(lb4)>=10:
del lb4[0]
if len(lb5)>=10:
del lb5[0]
if len(lb6)>=10:
del lb6[0]
if len(lb7)>=10:
del lb7[0]
if len(lb8)>=10:
del lb8[0]
if len(lb9)>=10:
del lb9[0]
if len(lb10)>=10:
del lb10[0]
app.processEvents() #allows to send back the percent value vor the processbar
prozent =(n/Anzahl)*100 #creating the percent value
progcounter.setValue(prozent) # gives back the percent value to the processbar
n +=1 #counter to generate the percent value
workbook.close() # close the excel file
layer9.addWidget(buttonOpenFile) # add the "open file" button
# öffnet am ende die angelegt datei
def on_OpenFile_clicked():
os.chdir
os.system('start excel.exe "%s%s"' % (save_path,filename ))
#helpFuntion, reads text from Help Instructions.txt file
#generating the help window, by clicking on the Help button
def open_help():
f = open('C:\Users\satzh\.spyder-py3\Help Instructions.txt',"r") #source for the help text
x = f.read() #reads the text from the txt. file
global windowhelp
windowhelp = QWidget()
windowhelp.setWindowTitle('Help')
buttonClose = QPushButton("Close")
buttonClose.clicked.connect(on_close_clicked)
helplayer1 = QVBoxLayout()
help_text= QPlainTextEdit()
help_text.appendPlainText(x)
help_text.zoomIn(4)
helplayer1.addWidget(help_text)
helplayer1.addWidget(buttonClose)
HelpLayout = QGridLayout()
HelpLayout.addLayout(helplayer1,1,1,1,1)
windowhelp.setLayout(HelpLayout)
windowhelp.setGeometry(300,300 ,800,480 )
windowhelp.show()
# close function for help window
def on_close_clicked():
windowhelp.close()
#generating plot menu
def on_plot_clicked():
#window generating
global printwindow
printwindow = QWidget()
printwindow.setWindowTitle('Plott')
#button generating
Balance1=QPushButton("Print Balance 1")
Balance2=QPushButton("Print Balance 2")
Balance3=QPushButton("Print Balance 3")
Balance4=QPushButton("Print Balance 4")
Balance5=QPushButton("Print Balance 5")
Balance6=QPushButton("Print Balance 6")
Balance7=QPushButton("Print Balance 7")
Balance8=QPushButton("Print Balance 8")
Balance9=QPushButton("Print Balance 9")
Balance10=QPushButton("Print Balance 10")
Balance11=QPushButton("Print All together")
Pclose = QPushButton("Close")
#generate all buttons for the print menu
printLayout1 = QHBoxLayout()
printLayout1.addWidget(Balance1)
printLayout1.addWidget(Balance2)
printLayout1.addWidget(Balance3)
printLayout2 = QHBoxLayout()
printLayout2.addWidget(Balance4)
printLayout2.addWidget(Balance5)
printLayout2.addWidget(Balance6)
printLayout3 = QHBoxLayout()
printLayout3.addWidget(Balance7)
printLayout3.addWidget(Balance8)
printLayout3.addWidget(Balance9)
printLayout4 = QHBoxLayout()
printLayout4.addWidget(Balance10)
printLayout4.addWidget(Balance11)
printLayout5 = QHBoxLayout()
printLayout5.addWidget(Pclose)
#generate the printlayout
printFinal = QVBoxLayout()
printFinal.addLayout(printLayout1)
printFinal.addLayout(printLayout2)
printFinal.addLayout(printLayout3)
printFinal.addLayout(printLayout4)
printFinal.addLayout(printLayout5)
#connect the print buttons to print orders
printwindow.setLayout(printFinal)
printwindow.show()
Balance1.clicked.connect(print_1)
Balance2.clicked.connect(print_2)
Balance3.clicked.connect(print_3)
Balance1.clicked.connect(print_4)
Balance5.clicked.connect(print_5)
Balance6.clicked.connect(print_6)
Balance7.clicked.connect(print_7)
Balance8.clicked.connect(print_8)
Balance9.clicked.connect(print_9)
Balance10.clicked.connect(print_10)
Balance11.clicked.connect(print_11)
Pclose.clicked.connect(on_pclose_clicked)
#closing the print menue
def on_pclose_clicked():
printwindow.close()
#printing for balancce 1
#following notes are for all print funtions the same
def print_1():
fig = plt.figure() #generates the basic figure for the plot, like the new window
plt.plot(lb1) #give the dates which should be printed
plt.title('Hydrogen Evolution') #generates the head-title
plt.ylabel('Weight [g]') # name fo the y cooridnates
plt.xlabel(intervall) # name for the x intervall (is defined by the choose between seconds and minutes functions above)
ax = plt.subplot(111)
ax.legend()
plt.ylim(-1, 425) # defined the intervall on the y coordinates
xmax= Anzahl*Pause #defined the intervall on the x coordinates
plt.xlim(0, xmax )
plt.show()
timestamp = time.strftime("%d%m%Y-%H%M%S") #generates part of the saving name
command = "fig.savefig('"+timestamp+"_h2evo_multiplot.png', dpi=200)" # saves the figure
exec(command)
fig.savefig('multiplot.png' , dpi=200)
#closing the Mainwindow and main Process
def close_all():
window.close()
sys.exit(app.exec_())
exit()
#connect the Buttons with their functions
buttonOkName.clicked.connect(on_OkName_clicked) #confirm name with checking user name
buttonOkName.clicked.connect( check_path) #confrim name with function to check saving path
buttonOkAnzahl.clicked.connect(on_OkAnzahl_clicked) #confirm number of messurements
buttonOkZeit.clicked.connect(on_OkZeit_clicked) #confirm time input
buttonStarter.clicked.connect(on_start_clicked)
buttonOpenFile.clicked.connect(on_OpenFile_clicked)
minWahl.toggled.connect(on_minWahl_clicked)
sekWahl.toggled.connect(on_sekWahl_clicked)
buttonhelp.clicked.connect(open_help)
buttonPlot.clicked.connect(on_plot_clicked)
buttonClose.clicked.connect(close_all)
app.exec_()
在这里的另一个线程中,我发现了以下内容:QThread 可以 运行 事件循环,QRunnable 没有,所以不要将它用于设计为具有事件循环的任务。来自:C++/Qt - QThread vs QRunnable
假设您想要运行一个名为my_func的函数使用一个线程。
import threading
my_thread = threading.Thread(target=my_func, args=())
my_thread.start()
如果你想让线程在整个程序退出后退出,在开始之前输入这个
my_thread.setDaemon(True)
如果你想传递函数需要的变量(参数)。假设你有一个函数 add(num1, num2) returns num1+num2,你在列表的 args 中传递 num1 和 num2,如下所示:
add_thread = threading.Thread(target=add, args=([4, 6]))
像Quamash这样的库应该用来防止Qt UI线程中断进程相关的线程执行。 自定义 QEventLoop 实现可能是做类似事情的替代且耗时的方式。 如果 python 线程和 Qt UI 线程不受管理,代码开始生成意外的 errors/behaviors
import sys
import asyncio
import time
from PyQt5.QtWidgets import QApplication, QProgressBar
from quamash import QEventLoop, QThreadExecutor
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop) # NEW must set the event loop
progress = QProgressBar()
progress.setRange(0, 99)
progress.show()
async def master():
await first_50()
with QThreadExecutor(1) as exec:
await loop.run_in_executor(exec, last_50)
# TODO announce completion?
async def first_50():
for i in range(50):
progress.setValue(i)
await asyncio.sleep(.1)
def last_50():
for i in range(50,100):
loop.call_soon_threadsafe(progress.setValue, i)
time.sleep(.1)
with loop: ## context manager calls .close() when loop completes, and releases all resources
loop.run_until_complete(master())