问题
I am trying to run a process in a separate thread but it is freezing my Gui and I cant understand why.
I am initialising the thread in the init function of my class:
self.cipher = Cipher()
self.cipher_thread = QThread()
self.cipher.moveToThread(self.cipher_thread)
self.cipher_thread.started.connect(lambda: self.cipher.encrypt(self.plaintext_file_path,
self.ciphertext_file_path,
self.init_vector,
self.key))
self.cipher_thread.start()
The encrypt method of the cipher class is:
def encrypt(self):
# check that both the key and the initialisation vector are 16 bytes long
if len(self.k) == self.key_byte_length and len(self.init_vector) == self.byte_length:
if not self.used:
self.used = True
# get the padding bytes and store in a list
self.padding_bytes = self.__getPaddingBytes()
# generate sub keys
# initial subkey is first four words from key
init_subkey_words = []
for i in range(0, self.key_byte_length-3,4):
init_subkey_words.append(self.k[i:i+4])
self.__genSubKeys(init_subkey_words)
# read file and append the padding to it
with open(self.plaintext_file_path, 'rb') as f:
self.plaintext_data = bytearray(f.read())
self.plaintext_data += self.padding_bytes
# set total size
self.total_size_bytes = len(self.plaintext_data)
# insert the initialisation vector as the first 16 bytes in the ciphertext data
self.ciphertext_data = self.init_vector
'''
begin encryption
--------------------------------------------------------------------------------------------------------
'''
self.start_time = datetime.datetime.now()
# loop through the file 16 bytes at a time
for i in range(0, int(len(self.plaintext_data)), self.byte_length): # i increases by 16 each loop
# if self.block_time is not None:
# print('block time is', datetime.datetime.now()-self.block_time)
self.block_time = datetime.datetime.now()
# set the 16 byte state - bytearray Object
state = copy.deepcopy(self.plaintext_data[i:i+self.byte_length])
# xor the state with the initialisation vector and first subkey
for j in range(self.byte_length):
state[j] ^= self.init_vector[j]
state[j] ^= self.sub_keys[0][j]
# round start
# --------------------------------------------------------------------------------------------------
for j in range(self.num_rounds):
self.current_round += 1 # increment current round counter
'''
arrange the data into a 4x4 matrix
[[1, 5, 9, 13],
[2, 6, 10, 14],
[3, 7, 11, 15],
[4, 8, 12, 16]]
'''
state_matrix = np.array(state)
state_matrix.resize(4, 4)
state_matrix.swapaxes(0, 1)
# byte substitution
# ----------------------------------------------------------------------------------------------
for row in state_matrix:
for byte in row:
byte = self.__sBoxSubstitution(byte)
# shift row - row k shifts left k places
# ----------------------------------------------------------------------------------------------
state_matrix = state_matrix.tolist()
for row in range(1, 4):
for l in range(0, row):
state_matrix[row].append(state_matrix[row].pop(0))
state_matrix = np.array(state_matrix)
# mix column - not included in last round
# ----------------------------------------------------------------------------------------------
if self.current_round is not self.num_rounds:
# swap axes of state matrix
state_matrix.swapaxes(0, 1)
# create temporary holder for the computed values
mixed_col_bytes = [[], [], [], []]
for k in range(4):
for l in range(4):
mixed_col_bytes[k].append(
self.__GFMult(self.MIX_COL_MATRIX[l][0], state_matrix[k][0]) ^
self.__GFMult(self.MIX_COL_MATRIX[l][1], state_matrix[k][1]) ^
self.__GFMult(self.MIX_COL_MATRIX[l][2], state_matrix[k][2]) ^
self.__GFMult(self.MIX_COL_MATRIX[l][3], state_matrix[k][3]))
# restore state matrix from temporary holder and swap axes back
state_matrix = np.array(copy.deepcopy(mixed_col_bytes))
state_matrix.swapaxes(0, 1)
# restore single bytearray state
state_matrix = state_matrix.flatten()
state_matrix = state_matrix.tolist()
state = bytearray(state_matrix)
# key addition
# ----------------------------------------------------------------------------------------------
for k in range(self.byte_length):
state[k] ^= self.sub_keys[self.current_round][k]
self.ciphertext_data += state # append state to ciphertext data
self.init_vector = self.ciphertext_data[-16:] # update the initialisation vector
self.current_round = 0 # reset current round number
self.completed_size_bytes += self.byte_length
self.percent_done = (self.completed_size_bytes/self.total_size_bytes)*100
self.updateProgressSig.emit(int(self.percent_done))
# finish encryption
self.__saveEncryptedData()
print('total encryption time:', datetime.datetime.now() - self.start_time)
# finish
self.finish(self.ciphertext_file_path)
# either the key of the initialisation vector are not the correct length
else:
print(' either the key length or initialisation vector is the wrong length')
print('---')
print('key length:', len(self.k))
print('iv length:', len(self.init_vector))
回答1:
The issue you are experiencing is that the function you are connecting to the started
signal is not run in the thread, it's run in the context of where it was set, which seems to be your UI thread.
Normally you would want to create a custom class which inherits from QThread, and any code that you want to be executed would be in the run()
function of that class. Like so:
class MyTask(QThread):
def __init__ (self):
QThread.__init__(self)
def run(self):
print("Code to run in the thread goes here.")
If that seems like overkill you could just set the value of self.cipher_thread.run
to your own function. Here's an example:
import time
from PySide.QtCore import QThread
from PySide import QtGui
app = QtGui.QApplication("")
def main():
task = SomeTask()
thread = QThread()
# Just some variables to pass into the task
a, b, c = (1, 2, 3)
thread.run = lambda: task.runTask(a, b, c)
print("Starting thread")
thread.start()
# Doing this so the application does not exit while we wait for the thread to complete.
while not thread.isFinished():
time.sleep(1)
print("Thread complete")
class SomeTask():
def runTask(self, a, b, c):
print a, b, c
print("runTask Started")
time.sleep(5)
print("runTask Complete")
if __name__ == "__main__":
main()
回答2:
As Ekhumoro suggested, I was running into issues with the GIL. using the Multiprocessing module has worked for me.
来源:https://stackoverflow.com/questions/33147725/qthread-locking-up-gui-pyside