class Ui_MainWindow(QtWidgets.QMainWindow): def setupUi(self): self.setObjectName("MainWindow") self.resize(850, 550) self.setWindowTitle("Rastrigin") self.centralwidget = QtWidgets.QWidget(self) self.centralwidget.setObjectName("centralwidget") self.frameChart = QChartView(self.centralwidget) self.frameChart.setGeometry(QtCore.QRect(10, 10, 620, 500)) self.frameChart.setFrameShape(QtWidgets.QFrame.Box) self.frameChart.setFrameShadow(QtWidgets.QFrame.Sunken) self.frameChart.setRenderHint(QPainter.Antialiasing) self.frameChart.setObjectName("frameChart") self.genParams = QtWidgets.QGroupBox(self.centralwidget) self.genParams.setGeometry(QtCore.QRect(650, 10, 161, 110)) self.genParams.setObjectName("genParams") self.genParams.setTitle("General parameters") self.label1 = QtWidgets.QLabel(self.genParams) self.label1.setGeometry(QtCore.QRect(10, 20, 61, 16)) self.label1.setObjectName("label1") self.label1.setText("Population:") self.label2 = QtWidgets.QLabel(self.genParams) self.label2.setGeometry(QtCore.QRect(10, 50, 91, 16)) self.label2.setObjectName("label2") self.label2.setText("No. generations:") self.label3 = QtWidgets.QLabel(self.genParams) self.label3.setGeometry(QtCore.QRect(10, 80, 81, 16)) self.label3.setObjectName("label3") self.label3.setText("No. dimensions:") self.tbxPopulation = QtWidgets.QLineEdit(self.genParams) self.tbxPopulation.setGeometry(QtCore.QRect(100, 20, 51, 20)) self.tbxPopulation.setObjectName("tbxPopulation") self.tbxGenerations = QtWidgets.QLineEdit(self.genParams) self.tbxGenerations.setGeometry(QtCore.QRect(100, 50, 51, 20)) self.tbxGenerations.setObjectName("tbxGenerations") self.tbxDimensions = QtWidgets.QLineEdit(self.genParams) self.tbxDimensions.setGeometry(QtCore.QRect(100, 80, 51, 20)) self.tbxDimensions.setObjectName("tbxDimensions") self.gaParams = QtWidgets.QGroupBox(self.centralwidget) self.gaParams.setGeometry(QtCore.QRect(650, 130, 191, 105)) self.gaParams.setObjectName("gaParams") self.gaParams.setTitle("GA parameters") self.label4 = QtWidgets.QLabel(self.gaParams) self.label4.setGeometry(QtCore.QRect(10, 20, 61, 16)) self.label4.setObjectName("label4") self.label4.setText("Mutation:") self.label5 = QtWidgets.QLabel(self.gaParams) self.label5.setGeometry(QtCore.QRect(10, 50, 91, 16)) self.label5.setObjectName("label5") self.label5.setText("Elite members:") self.label9 = QtWidgets.QLabel(self.gaParams) self.label9.setGeometry(QtCore.QRect(10, 80, 61, 16)) self.label9.setObjectName("label9") self.label9.setText("Max abs.:") self.tbxMutation = QtWidgets.QLineEdit(self.gaParams) self.tbxMutation.setGeometry(QtCore.QRect(100, 20, 51, 20)) self.tbxMutation.setObjectName("tbxMutation") self.tbxElite = QtWidgets.QLineEdit(self.gaParams) self.tbxElite.setGeometry(QtCore.QRect(100, 50, 51, 20)) self.tbxElite.setObjectName("tbxElite") self.tbxMaxAbs = QtWidgets.QLineEdit(self.gaParams) self.tbxMaxAbs.setGeometry(QtCore.QRect(100, 80, 51, 20)) self.tbxMaxAbs.setObjectName("tbxMAxAbs") self.psoParams = QtWidgets.QGroupBox(self.centralwidget) self.psoParams.setGeometry(QtCore.QRect(650, 240, 161, 110)) self.psoParams.setObjectName("psoParams") self.psoParams.setTitle("PSO parameters") self.label6 = QtWidgets.QLabel(self.psoParams) self.label6.setGeometry(QtCore.QRect(10, 20, 61, 16)) self.label6.setObjectName("label6") self.label6.setText("Inertia factor:") self.label7 = QtWidgets.QLabel(self.psoParams) self.label7.setGeometry(QtCore.QRect(10, 50, 91, 16)) self.label7.setObjectName("label7") self.label7.setText("Personal factor:") self.label8 = QtWidgets.QLabel(self.psoParams) self.label8.setGeometry(QtCore.QRect(10, 80, 81, 16)) self.label8.setObjectName("label8") self.label8.setText("Social factor:") self.tbxInertia = QtWidgets.QLineEdit(self.psoParams) self.tbxInertia.setGeometry(QtCore.QRect(100, 20, 51, 20)) self.tbxInertia.setObjectName("tbxInertia") self.tbxPersonal = QtWidgets.QLineEdit(self.psoParams) self.tbxPersonal.setGeometry(QtCore.QRect(100, 50, 51, 20)) self.tbxPersonal.setObjectName("tbxPersonal") self.tbxSocial = QtWidgets.QLineEdit(self.psoParams) self.tbxSocial.setGeometry(QtCore.QRect(100, 80, 51, 20)) self.tbxSocial.setObjectName("tbxSocial") self.cbxNoVis = QtWidgets.QCheckBox(self.centralwidget) self.cbxNoVis.setGeometry(QtCore.QRect(650, 350, 170, 17)) self.cbxNoVis.setObjectName("cbxNoVis") self.cbxNoVis.setText("No visualization per generation") self.btnStartGA = QtWidgets.QPushButton(self.centralwidget) self.btnStartGA.setGeometry(QtCore.QRect(650, 370, 75, 23)) self.btnStartGA.setObjectName("btnStartGA") self.btnStartGA.setText("Start GA") self.btnStartPSO = QtWidgets.QPushButton(self.centralwidget) self.btnStartPSO.setGeometry(QtCore.QRect(650, 400, 75, 23)) self.btnStartPSO.setObjectName("btnStartPSO") self.btnStartPSO.setText("Start PSO") self.btnStop = QtWidgets.QPushButton(self.centralwidget) self.btnStop.setEnabled(False) self.btnStop.setGeometry(QtCore.QRect(740, 370, 75, 53)) self.btnStop.setObjectName("btnStop") self.btnStop.setText("Stop") self.btnSaveChart = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChart.setGeometry(QtCore.QRect(650, 450, 121, 41)) self.btnSaveChart.setObjectName("btnSaveChart") self.btnSaveChart.setText("Save chart as image") self.btnSaveChartSeries = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChartSeries.setGeometry(QtCore.QRect(650, 500, 121, 41)) self.btnSaveChartSeries.setObjectName("btnSaveChartSeries") self.btnSaveChartSeries.setText("Save chart as series") self.setCentralWidget(self.centralwidget) QtCore.QMetaObject.connectSlotsByName(self) #Connect events self.btnStartGA.clicked.connect(self.btnStartGA_Click) self.btnStartPSO.clicked.connect(self.btnStartPSO_Click) self.btnStop.clicked.connect(self.btnStop_Click) self.btnSaveChart.clicked.connect(self.btnSaveChart_CLick) self.btnSaveChartSeries.clicked.connect(self.btnSaveChartSeries_Click) #Set default variables self.tbxGenerations.insert(str(NGEN)) self.tbxPopulation.insert(str(POP_SIZE)) self.tbxDimensions.insert(str(NO_DIMS)) self.tbxMutation.insert(str(GA_MUTPB)) self.tbxElite.insert(str(GA_NELT)) self.tbxMaxAbs.insert(str(GA_MAX_ABS)) self.tbxInertia.insert(str(PSO_INERTIA)) self.tbxPersonal.insert(str(PSO_PERSONAL)) self.tbxSocial.insert(str(PSO_SOCIAL)) def btnStartGA_Click(self): global combination_series # List of lists containing min_series of 5 results -- Added by Denis Lazor global parameter_name # Name of parameter used for writing its data to .csv file -- Added by Denis Lazor global best_fit_values # List containing best fitness values for every of 5 experiments per combination -- Added by Denis Lazor global DIM_SIZES global ELITE_SIZES global MAX_ABS_SIZES global MUTATION_SIZES # Checking if files are empty or not -- Added by Denis Lazor csv_contains_ga = os.stat("graphs_csv/original_ga.csv").st_size != 0 # Clearing non empty files if we are trying to write to them -- Added by Denis Lazor if csv_contains_ga: clear_all_csv("ga") parameter_name = "original" n = 5000 print("GA:\n") # Automation for all necessary combinations -- Added by Denis Lazor for d in DIM_SIZES: MUTATION_SIZES = [0.05, 0.1, 0.2] ELITE_SIZES = [4, 8, 16] MAX_ABS_SIZES = [0.4] for m in MUTATION_SIZES: for e in ELITE_SIZES: for ma in MAX_ABS_SIZES: for i in range(5): # Set global variables global stop_evolution global q_min_series global q_max_series global q_avg_series stop_evolution = False q_min_series.clear() q_max_series.clear() q_avg_series.clear() # Set global variables from information on UI global NGEN global POP_SIZE global GA_MUTPB global GA_NELT global GA_MAX_ABS NGEN = int(self.tbxGenerations.text()) POP_SIZE = int(self.tbxPopulation.text()) GA_MUTPB = m GA_NELT = e GA_MAX_ABS = ma ####Initialize deap GA objects#### # Make creator that minimize. If it would be 1.0 instead od -1.0 than it would be maxmize self.creator = creator self.creator.create("FitnessMin", base.Fitness, weights=(-1.0, )) # Create an individual (a blueprint for cromosomes) as a list with a specified fitness type self.creator.create( "Individual", list, fitness=self.creator.FitnessMin) # Create base toolbox for finishing creation of a individual (cromosome) self.toolbox = base.Toolbox() # Define what type of data (number, gene) will it be in the cromosome self.toolbox.register("attr_float", random.uniform, F_MIN, F_MAX) # Initialization procedure (initRepeat) for the cromosome. For the individual to be completed we need to run initRepeat for the amaout of genes the cromosome includes self.toolbox.register("individual", tools.initRepeat, self.creator.Individual, self.toolbox.attr_float, n=NO_DIMS) # Create a population of individuals (cromosomes). The population is then created by toolbox.population(n=300) where 'n' is the number of cromosomes in population self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) # Register evaluation function self.toolbox.register("evaluate", evaluateInd) # Register what genetic operators to use # Standard coding self.toolbox.register( "mate", tools.cxTwoPoint ) # Use two point recombination self.toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=GA_MAX_ABS, indpb=0.5) self.toolbox.register( "select", tools.selTournament, tournsize=3) # Use tournament selection ################################## # Generate initial poplation. Will be a member variable so we can easely pass everything to new thread self.pop = self.toolbox.population(n=POP_SIZE) # Evaluate initial population, we map() the evaluation function to every individual and then assign their respective fitness, map runs evaluate function for each individual in pop fitnesses = list( map(self.toolbox.evaluate, self.pop)) for ind, fit in zip(self.pop, fitnesses): ind.fitness.values = fit # Assign calcualted fitness value to individuals # Extracting all the fitnesses of all individuals in a population so we can monitor and evovlve the algorithm until it reaches 0 or max number of generation is reached self.fits = [ ind.fitness.values[0] for ind in self.pop ] # Disable start and enable stop self.btnStartGA.setEnabled(False) self.btnStartPSO.setEnabled(False) self.btnStop.setEnabled(False) self.genParams.setEnabled(False) self.gaParams.setEnabled(False) self.psoParams.setEnabled(False) self.cbxNoVis.setEnabled(False) # Start evolution self.evolveGA() # Best fitness value -- Added by Denis Lazor best_fit = np.array(min(best_fit_values))[0] mean_fit = np.array( min(best_fit_values, key=lambda x: abs(x - statistics.mean( np.asarray(best_fit_values).flatten()))) )[0] # Index of best fitness value -- Added by Denis Lazor mean_fit_idx = best_fit_values.index(mean_fit) write_to_file(combination_series[mean_fit_idx], parameter_name, "ga") # First name will be "original", second one "max_abs" -- Added by Denis Lazor parameter_name = "max_abs" print_results_GA(POP_SIZE, m, e, ma, mean_fit, best_fit, NGEN, d) # Clearing past lists -- Added by Denis Lazor combination_series = [] best_fit_values = [] # Reducing number of combinations and changing .csv file for writing -- Added by Denis Lazor MAX_ABS_SIZES = MAX_ABS_SIZES[0:1] parameter_name = "elites" ELITE_SIZES = ELITE_SIZES[0:1] parameter_name = "mutation" MUTATION_SIZES = MUTATION_SIZES[0:1] parameter_name = "original" def btnStartPSO_Click(self): global combination_series # List of lists containing min_series of 5 results -- Added by Denis Lazor global parameter_name # Name of parameter used for writing its data to .csv file -- Added by Denis Lazor global best_fit_values # List containing best fitness values for every of 5 experiments per combination -- Added by Denis Lazor global DIM_SIZES global INERTIA_SIZES global PERSONAL_F_SIZES global SOCIAL_F_SIZES # Checking if files are empty or not -- Added by Denis Lazor csv_contains_pso = os.stat("graphs_csv/original_pso.csv").st_size != 0 # Clearing non empty files if we are trying to write to them -- Added by Denis Lazor if csv_contains_pso: clear_all_csv("pso") n = 5000 parameter_name = "original" print("PSO:\n") # Automation for all necessary combinations -- Added by Denis Lazor for d in DIM_SIZES: INERTIA_SIZES = [0.0, 0.37, 0.74] PERSONAL_F_SIZES = [0.5, 1.0, 1.5] SOCIAL_F_SIZES = [0.5, 1.0, 1.5] for in_f in INERTIA_SIZES: for pers_f in PERSONAL_F_SIZES: for soc_f in SOCIAL_F_SIZES: for i in range(5): # Set global variables global stop_evolution global q_min_series global q_max_series global q_avg_series stop_evolution = False q_min_series.clear() q_max_series.clear() q_avg_series.clear() # Set global variables from information on UI global NGEN global POP_SIZE global PSO_INERTIA global PSO_PERSONAL global PSO_SOCIAL NGEN = int(self.tbxGenerations.text()) POP_SIZE = int(self.tbxPopulation.text()) PSO_INERTIA = in_f PSO_PERSONAL = pers_f PSO_SOCIAL = soc_f ####Initialize deap PSO objects#### # Make creator that minimize. If it would be 1.0 instead od -1.0 than it would be maxmize self.creator = creator self.creator.create("FitnessMin", base.Fitness, weights=(-1.0, )) # Create an individual (a blueprint for cromosomes) as a list with a specified fitness type self.creator.create( "Particle", list, fitness=self.creator.FitnessMin, speed=list, best=None) # Create base toolbox for finishing creation of a individual (particle) and population self.toolbox = base.Toolbox() # Particle initialization self.toolbox.register("particle", generateParticle, cr=self.creator, size=NO_DIMS, min_val=F_MIN, max_val=F_MAX) # Create a population of individuals (particles). The population is then created by e.g. toolbox.population(n=300) where 'n' is the number of particles in population self.toolbox.register("population", tools.initRepeat, list, self.toolbox.particle) # Update function for each particle self.toolbox.register("update", updateParticle) # Evaluation function for each particle self.toolbox.register("evaluate", evaluateInd) ################################## # Create population self.pop = self.toolbox.population(n=POP_SIZE) # Evaluate initial population, we map() the evaluation function to every individual and then assign their respective fitness, map runs emaluet function for each individual in pop fitnesses = list( map(self.toolbox.evaluate, self.pop)) for ind, fit in zip(self.pop, fitnesses): ind.fitness.values = fit # Extracting all the fitnesses of all individuals in a population so we can monitor and evovlve the algorithm until it reaches 0 or max number of generation is reached self.fits = [ ind.fitness.values[0] for ind in self.pop ] # Extraction current best position self.global_best_position = tools.selBest( self.pop, 1)[0][:] # Disable start and enable stop self.btnStartGA.setEnabled(False) self.btnStartPSO.setEnabled(False) self.btnStop.setEnabled(False) self.genParams.setEnabled(False) self.gaParams.setEnabled(False) self.psoParams.setEnabled(False) self.cbxNoVis.setEnabled(False) # Start evolution self.evolvePSO() # Best fitness value -- Added by Denis Lazor best_fit = np.array(min(best_fit_values))[0] mean_fit = np.array( min(best_fit_values, key=lambda x: abs(x - statistics.mean( np.asarray(best_fit_values).flatten()))) )[0] # Index of best fitness value -- Added by Denis Lazor mean_fit_idx = best_fit_values.index(mean_fit) write_to_file(combination_series[mean_fit_idx], parameter_name, "pso") # First name will be "original", second one "social_factor" -- Added by Denis Lazor parameter_name = "social_factor" print_results_PSO(POP_SIZE, in_f, pers_f, soc_f, mean_fit, best_fit, NGEN, d) # Clearing past lists -- Added by Denis Lazor print(best_fit_values) combination_series = [] best_fit_values = [] # Reducing number of combinations and changing .csv file for writing -- Added by Denis Lazor SOCIAL_F_SIZES = SOCIAL_F_SIZES[0:1] parameter_name = "personal_factor" PERSONAL_F_SIZES = PERSONAL_F_SIZES[0:1] parameter_name = "inertia" INERTIA_SIZES = INERTIA_SIZES[0:1] parameter_name = "original" def btnStop_Click(self): global stop_evolution stop_evolution = True #Disable stop and enable start self.btnStartGA.setEnabled(True) self.btnStartPSO.setEnabled(True) self.btnStop.setEnabled(False) self.genParams.setEnabled(True) self.gaParams.setEnabled(True) self.psoParams.setEnabled(True) self.cbxNoVis.setEnabled(True) #Function for GA evolution def evolveGA(self): global q_min_series global q_max_series global q_avg_series global combination_series global best_fit_values combination_current_series = [ ] # Clearing fitness values series -- Added by Denis Lazor # Variable for keeping track of the number of generations curr_g = 0 # Begin the evolution till goal is reached or max number of generation is reached while min(self.fits) != 0 and curr_g < NGEN: #Check if evolution and thread need to stop if stop_evolution: break #Break the evolution loop # A new generation curr_g = curr_g + 1 # print("-- Generation %i --" % curr_g) # Select the next generation individuals #Select POP_SIZE - NELT number of individuals. Since recombination is between neigbours, not two naighbours should be the clone of the same individual offspring = [] offspring.append(self.toolbox.select( self.pop, 1)[0]) #add first selected individual for i in range( POP_SIZE - GA_NELT - 1 ): # -1 because the first seleceted individual is already added while True: new_o = self.toolbox.select(self.pop, 1)[0] if new_o != offspring[len( offspring ) - 1]: #if it is different than the last inserted then add to offspring and break offspring.append(new_o) break # Clone the selected individuals because all of the changes are inplace offspring = list(map(self.toolbox.clone, offspring)) # Apply crossover on the selected offspring for child1, child2 in zip(offspring[::2], offspring[1::2]): self.toolbox.mate(child1, child2) #inplace recombination #Invalidate new children fitness values del child1.fitness.values del child2.fitness.values #Apply mutation on the offspring for mutant in offspring: if random.random() < GA_MUTPB: self.toolbox.mutate(mutant) del mutant.fitness.values #Add elite individuals #Is clonning needed? offspring.extend( list(map(self.toolbox.clone, tools.selBest(self.pop, GA_NELT)))) # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = map(self.toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit # print(" Evaluated %i individuals" % len(invalid_ind)) #Replace population with offspring self.pop[:] = offspring # Gather all the fitnesses in one list and print the stats self.fits = [ind.fitness.values[0] for ind in self.pop] length = len(self.pop) mean = sum(self.fits) / length sum2 = sum(x * x for x in self.fits) std = abs(sum2 / length - mean**2)**0.5 q_min_series.append(curr_g, min(self.fits)) q_max_series.append(curr_g, max(self.fits)) q_avg_series.append(curr_g, mean) combination_current_series.append( min(self.fits) ) # Saving min_series fitness values of an experiment -- Added by Denis Lazor # print(" Min %s" % q_min_series.at(q_min_series.count()-1).y()) # print(" Max %s" % q_max_series.at(q_max_series.count()-1).y()) # print(" Avg %s" % mean) # print(" Std %s" % std) # if self.cbxNoVis.isChecked(): app.processEvents() else: self.chart = QChart() self.chart.addSeries(q_min_series) self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("Fitness value over time") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) self.frameChart.repaint() app.processEvents() #Printing best individual best_ind = tools.selBest(self.pop, 1)[0] # print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values)) #Visulaize final solution if self.cbxNoVis.isChecked(): self.chart = QChart() self.chart.addSeries(q_min_series) self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("Fitness value over time") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) self.frameChart.repaint() #Disable stop and enable start self.btnStartGA.setEnabled(True) self.btnStartPSO.setEnabled(True) self.btnStop.setEnabled(False) self.genParams.setEnabled(True) self.gaParams.setEnabled(True) self.psoParams.setEnabled(True) self.cbxNoVis.setEnabled(True) app.processEvents() combination_series.append(combination_current_series ) # Saving 5 results -- Added by Denis Lazor best_fit_values.append( best_ind.fitness.values ) # Adding best fitness value of experiment -- Added by Denis Lazor #Function for GA evolution def evolvePSO(self): global q_min_series global q_max_series global q_avg_series global combination_series global best_fit_values combination_current_series = [ ] # Clearing fitness values series -- Added by Denis Lazor # Variable for keeping track of the number of generations curr_g = 0 while min(self.fits) != 0.0 and curr_g < NGEN: #Check if evolution and thread need to stop if stop_evolution: break #Break the evolution loop # A new generation curr_g = curr_g + 1 # print("-- Generation %i --" % curr_g) #Update particle position and evaluate particle for particle in self.pop: #Update self.toolbox.update(particle, self.global_best_position, PSO_INERTIA, PSO_PERSONAL, PSO_SOCIAL) #Evaluate fit = self.toolbox.evaluate(particle) #Update best position if fit[0] < particle.fitness.values[0]: particle.best = particle[:] #Update fitness particle.fitness.values = fit #Extracting all the fitnesses of all individuals in a population so we can monitor and evovlve the algorithm until it reaches 0 or max number of generation is reached self.fits = [ind.fitness.values[0] for ind in self.pop] #Extraction current best position self.global_best_position = tools.selBest(self.pop, 1)[0][:] #Stats length = len(self.pop) mean = sum(self.fits) / length sum2 = sum(x * x for x in self.fits) std = abs(sum2 / length - mean**2)**0.5 q_min_series.append(curr_g, min(self.fits)) q_max_series.append(curr_g, max(self.fits)) q_avg_series.append(curr_g, mean) combination_current_series.append( min(self.fits) ) # Saving min_series fitness values of an experiment -- Added by Denis Lazor # print(" Min %s" % q_min_series.at(q_min_series.count()-1).y()) # print(" Max %s" % q_max_series.at(q_max_series.count()-1).y()) # print(" Avg %s" % mean) # print(" Std %s" % std) # if self.cbxNoVis.isChecked(): app.processEvents() else: self.chart = QChart() self.chart.addSeries(q_min_series) self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("Fitness value over time") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) self.frameChart.repaint() app.processEvents() #Printing best individual best_ind = tools.selBest(self.pop, 1)[0] # print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values)) #Visulaize final solution if self.cbxNoVis.isChecked(): self.chart = QChart() self.chart.addSeries(q_min_series) self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("Fitness value over time") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) self.frameChart.repaint() #Disable stop and enable start self.btnStartGA.setEnabled(True) self.btnStartPSO.setEnabled(True) self.btnStop.setEnabled(False) self.genParams.setEnabled(True) self.gaParams.setEnabled(True) self.psoParams.setEnabled(True) self.cbxNoVis.setEnabled(True) app.processEvents() combination_series.append(combination_current_series ) # Saving 5 results -- Added by Denis Lazor best_fit_values.append( best_ind.fitness.values ) # Adding best fitness value of experiment -- Added by Denis Lazor def btnSaveChart_CLick(self): p = self.frameChart.grab() filename, _ = QFileDialog.getSaveFileName( None, "Save series chart as a image", "", "Image Files (*.png)") p.save(filename, "PNG") print("Chart series image saved to: ", filename) def btnSaveChartSeries_Click(self): global q_min_series global q_max_series global q_avg_series filename, _ = QFileDialog.getSaveFileName(None, "Save series to text file", "", "Text Files (*.txt, *.csv)") with open(filename, 'w') as dat: for i in range(q_min_series.count()): dat.write('%f,%f,%f\n' % (q_min_series.at(i).y(), q_avg_series.at(i).y(), q_max_series.at(i).y())) print("Chart series saved to: ", filename)
class Ui_MainWindow(QtWidgets.QMainWindow): def setupUi(self): self.setObjectName("MainWindow") self.resize(600, 830) self.setWindowTitle("GA - Queens") self.centralwidget = QtWidgets.QWidget(self) self.centralwidget.setObjectName("centralwidget") self.frameWorld = MyQFrame(self.centralwidget) self.frameWorld.img = QPixmap(1000, 1000) self.frameWorld.setGeometry(QtCore.QRect(10, 10, 400, 400)) self.frameWorld.setFrameShape(QtWidgets.QFrame.Box) self.frameWorld.setFrameShadow(QtWidgets.QFrame.Sunken) self.frameWorld.setObjectName("frameWorld") self.frameChart = QChartView(self.centralwidget) self.frameChart.setGeometry(QtCore.QRect(10, 420, 400, 400)) self.frameChart.setFrameShape(QtWidgets.QFrame.Box) self.frameChart.setFrameShadow(QtWidgets.QFrame.Sunken) self.frameChart.setRenderHint(QPainter.Antialiasing) self.frameChart.setObjectName("frameChart") self.gaParams = QtWidgets.QGroupBox(self.centralwidget) self.gaParams.setGeometry(QtCore.QRect(430, 10, 161, 171)) self.gaParams.setObjectName("gaParams") self.gaParams.setTitle("GA parameters") self.label1 = QtWidgets.QLabel(self.gaParams) self.label1.setGeometry(QtCore.QRect(10, 20, 61, 16)) self.label1.setObjectName("label1") self.label1.setText("Population:") self.label2 = QtWidgets.QLabel(self.gaParams) self.label2.setGeometry(QtCore.QRect(10, 50, 47, 16)) self.label2.setObjectName("label2") self.label2.setText("Mutation:") self.label3 = QtWidgets.QLabel(self.gaParams) self.label3.setGeometry(QtCore.QRect(10, 80, 81, 16)) self.label3.setObjectName("label3") self.label3.setText("Elite members:") self.label4 = QtWidgets.QLabel(self.gaParams) self.label4.setGeometry(QtCore.QRect(10, 110, 91, 16)) self.label4.setObjectName("label4") self.label4.setText("No. generations:") self.cbxPermutation = QtWidgets.QCheckBox(self.gaParams) self.cbxPermutation.setGeometry(QtCore.QRect(35, 140, 91, 17)) self.cbxPermutation.setObjectName("cbxPermutation") self.cbxPermutation.setText("Permutation") self.tbxPopulation = QtWidgets.QLineEdit(self.gaParams) self.tbxPopulation.setGeometry(QtCore.QRect(100, 20, 51, 20)) self.tbxPopulation.setObjectName("tbxPopulation") self.tbxMutation = QtWidgets.QLineEdit(self.gaParams) self.tbxMutation.setGeometry(QtCore.QRect(100, 50, 51, 20)) self.tbxMutation.setObjectName("tbxMutation") self.tbxElite = QtWidgets.QLineEdit(self.gaParams) self.tbxElite.setGeometry(QtCore.QRect(100, 80, 51, 20)) self.tbxElite.setObjectName("tbxElite") self.tbxGenerations = QtWidgets.QLineEdit(self.gaParams) self.tbxGenerations.setGeometry(QtCore.QRect(100, 110, 51, 20)) self.tbxGenerations.setObjectName("tbxGenerations") self.label5 = QtWidgets.QLabel(self.centralwidget) self.label5.setGeometry(QtCore.QRect(440, 190, 61, 16)) self.label5.setObjectName("label5") self.label5.setText("No. queens:") self.tbxNoQueens = QtWidgets.QLineEdit(self.centralwidget) self.tbxNoQueens.setGeometry(QtCore.QRect(510, 190, 51, 20)) self.tbxNoQueens.setObjectName("tbxNoQueens") self.cbxNoVis = QtWidgets.QCheckBox(self.centralwidget) self.cbxNoVis.setGeometry(QtCore.QRect(420, 215, 170, 17)) self.cbxNoVis.setObjectName("cbxNoVis") self.cbxNoVis.setText("No visualization per generation") self.btnStart = QtWidgets.QPushButton(self.centralwidget) self.btnStart.setGeometry(QtCore.QRect(430, 250, 75, 23)) self.btnStart.setObjectName("btnStart") self.btnStart.setText("Start") self.btnStop = QtWidgets.QPushButton(self.centralwidget) self.btnStop.setEnabled(False) self.btnStop.setGeometry(QtCore.QRect(510, 250, 75, 23)) self.btnStop.setObjectName("btnStop") self.btnStop.setText("Stop") self.btnSaveWorld = QtWidgets.QPushButton(self.centralwidget) self.btnSaveWorld.setGeometry(QtCore.QRect(430, 370, 121, 41)) self.btnSaveWorld.setObjectName("btnSaveWorld") self.btnSaveWorld.setText("Save world as image") self.btnSaveChart = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChart.setGeometry(QtCore.QRect(430, 730, 121, 41)) self.btnSaveChart.setObjectName("btnSaveChart") self.btnSaveChart.setText("Save chart as image") self.btnSaveChartSeries = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChartSeries.setGeometry(QtCore.QRect(430, 780, 121, 41)) self.btnSaveChartSeries.setObjectName("btnSaveChartSeries") self.btnSaveChartSeries.setText("Save chart as series") self.setCentralWidget(self.centralwidget) QtCore.QMetaObject.connectSlotsByName(self) #Connect events self.btnStart.clicked.connect(self.btnStart_Click) self.btnStop.clicked.connect(self.btnStop_Click) self.btnSaveWorld.clicked.connect(self.btnSaveWorld_Click) self.btnSaveChart.clicked.connect(self.btnSaveChart_CLick) self.btnSaveChartSeries.clicked.connect(self.btnSaveChartSeries_Click) #Set default GA variables self.tbxNoQueens.insert(str(NO_QUEENS)) self.tbxGenerations.insert(str(NGEN)) self.tbxPopulation.insert(str(POP_SIZE)) self.tbxMutation.insert(str(MUTPB)) self.tbxElite.insert(str(NELT)) self.new_image = QPixmap(1000, 1000) def btnStart_Click(self): global success # Number of times when fitness function reached 0 -- Added by Denis Lazor global generations # Number of times when fitness function reached 0 -- Added by Denis Lazor global combination_series # List of lists containing min_series of 5 correct results -- Added by Denis Lazor global parameter_name # Name of parameter used for writing its data to .csv file -- Added by Denis Lazor global ELITE_SIZES global BOARD_SIZES global POPULATION_SIZES global MUTATION_SIZES # Checking if files are empty or not -- Added by Denis Lazor csv_contains = os.stat("graphs_csv/original_.csv").st_size != 0 csv_contains_permutation = os.stat( "graphs_csv/original_permutation.csv").st_size != 0 permutation_checked = self.cbxPermutation.isChecked() # Clearing non empty files if we are trying to write to them -- Added by Denis Lazor if csv_contains_permutation and permutation_checked: clear_all_csv("p") if csv_contains and not permutation_checked: clear_all_csv("np") BOARD_SIZES = [12, 24] n = 5000 # Automation for all necessary combinations -- Added by Denis Lazor for b in BOARD_SIZES: # Because we use slicing we need to restore parameters lists after changing board size POPULATION_SIZES = [50, 100, 200] MUTATION_SIZES = [0.04, 0.08, 0.16] ELITE_SIZES = [4, 8, 16] for p in POPULATION_SIZES: for m in MUTATION_SIZES: for e in ELITE_SIZES: success = 0 trials = 0 # Number of tries needed to find 5 'correct' results -- Added by Denis Lazor while success < 5: # Doing same combination till we get 5 'correct' results -- Added by Denis Lazor trials = trials + 1 # Set global variables global stop_evolution global q_min_series global q_max_series global q_avg_series stop_evolution = False q_min_series.clear() q_max_series.clear() q_avg_series.clear() # Set global variables from information on UI global NO_QUEENS global NGEN global POP_SIZE global MUTPB global NELT NO_QUEENS = b NGEN = n POP_SIZE = p MUTPB = m NELT = e # Painting chess table self.img = QPixmap(1000, 1000) self.img.fill() painter = QPainter(self.img) painter.setPen(QPen(Qt.black, 10, Qt.SolidLine)) width = 1000 / NO_QUEENS cur_width = 0 for i in range( NO_QUEENS + 1 ): # +1 in order to draw the last line as well painter.drawLine(cur_width, 0, cur_width, 1000) painter.drawLine(0, cur_width, 1000, cur_width) cur_width += width painter.end() self.frameWorld.img = self.img # Redrawing frames self.frameWorld.repaint() app.processEvents() ####Initialize deap GA objects#### # Make creator that minimize. If it would be 1.0 instead od -1.0 than it would be maxmize creator.create("FitnessMin", base.Fitness, weights=(-1.0, )) # Create an individual (a blueprint for cromosomes) as a list with a specified fitness type creator.create("Individual", list, fitness=creator.FitnessMin) # Create base toolbox for finishing creation of a individual (cromosome) self.toolbox = base.Toolbox() # Define what type of data (number, gene) will it be in the cromosome if self.cbxPermutation.isChecked(): # Permutation coding self.toolbox.register("indices", random.sample, range(NO_QUEENS), NO_QUEENS) # initIterate requires that the generator of genes (such as random.sample) generates an iterable (a list) variable self.toolbox.register("individual", tools.initIterate, creator.Individual, self.toolbox.indices) else: # Standard coding self.toolbox.register( "attr_int", random.randint, 0, NO_QUEENS - 1 ) # number in cromosome is from 0 till IND_SIZE - 1 # Initialization procedure (initRepeat) for the cromosome. For the individual to be completed we need to run initRepeat for the amaout of genes the cromosome includes self.toolbox.register("individual", tools.initRepeat, creator.Individual, self.toolbox.attr_int, n=NO_QUEENS) # Create a population of individuals (cromosomes). The population is then created by toolbox.population(n=300) where 'n' is the number of cromosomes in population self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) # Register evaluation function self.toolbox.register("evaluate", evaluateInd) # Register what genetic operators to use if self.cbxPermutation.isChecked(): # Permutation coding self.toolbox.register( "mate", tools.cxUniformPartialyMatched, indpb=0.2 ) # Use uniform recombination for permutation coding self.toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.2) else: # Standard coding self.toolbox.register( "mate", tools.cxTwoPoint ) # Use two point recombination self.toolbox.register( "mutate", tools.mutUniformInt, low=0, up=NO_QUEENS - 1, indpb=0.2) # 20% that the gene will change self.toolbox.register( "select", tools.selTournament, tournsize=3) # Use tournament selection ################################## # Generate initial poplation. Will be a member variable so we can easely pass everything to new thread self.pop = self.toolbox.population(n=POP_SIZE) # Evaluate initial population, we map() the evaluation function to every individual and then assign their respective fitness, map runs evaluate function for each individual in pop fitnesses = list( map(self.toolbox.evaluate, self.pop)) for ind, fit in zip(self.pop, fitnesses): ind.fitness.values = fit # Assign calcualted fitness value to individuals # Extracting all the fitnesses of all individuals in a population so we can monitor and evovlve the algorithm until it reaches 0 or max number of generation is reached self.fits = [ ind.fitness.values[0] for ind in self.pop ] self.fits # Disable start and enable stop self.btnStart.setEnabled(False) self.btnStop.setEnabled(True) self.gaParams.setEnabled(False) self.tbxNoQueens.setEnabled(False) self.cbxNoVis.setEnabled(False) # Start evolution self.evolve() # Mean number of generations nedeed for finding 5 correct solutions -- Added by Denis Lazor mean_gen = min(generations, key=lambda x: abs(x - statistics.mean( generations))) # Index of mean_gen value -- Added by Denis Lazor mean_idx = generations.index(mean_gen) write_to_file( combination_series[mean_idx], parameter_name, permutation_checked ) # First name will be "original", second one "elites" -- Added by Denis Lazor parameter_name = "elites" print_results(b, p, m, e, trials, generations, mean_gen, NGEN) # Clearing past lists -- Added by Denis Lazor generations = [] combination_series = [] # Reducing number of combinations and changing .csv file for writing -- Added by Denis Lazor ELITE_SIZES = ELITE_SIZES[0:1] parameter_name = "mutation" MUTATION_SIZES = MUTATION_SIZES[0:1] parameter_name = "population" POPULATION_SIZES = POPULATION_SIZES[0:1] parameter_name = "original" n = 30000 # Increasing generation size for 24x24 board def btnStop_Click(self): global stop_evolution stop_evolution = True #Disable stop and enable start self.btnStop.setEnabled(False) self.btnStart.setEnabled(True) self.gaParams.setEnabled(True) self.tbxNoQueens.setEnabled(True) self.cbxNoVis.setEnabled(True) #Function for GA evolution def evolve(self): global q_min_series global q_max_series global q_avg_series global success global generations global combination_series global NO_QUEENS global NGEN global POP_SIZE global MUTPB global NELT combination_current_series = [] # Variable for keeping track of the number of generations curr_g = 0 # Begin the evolution till goal is reached or max number of generation is reached while min(self.fits) != 0 and curr_g < NGEN: #Check if evolution and thread need to stop if stop_evolution: break #Break the evolution loop # A new generation curr_g = curr_g + 1 #print("-- Generation %i --" % curr_g) # Select the next generation individuals #Select POP_SIZE - NELT number of individuals. Since recombination is between neigbours, not two naighbours should be the clone of the same individual offspring = [] offspring.append(self.toolbox.select( self.pop, 1)[0]) #add first selected individual for i in range( POP_SIZE - NELT - 1 ): # -1 because the first seleceted individual is already added while True: new_o = self.toolbox.select(self.pop, 1)[0] if new_o != offspring[len( offspring ) - 1]: #if it is different than the last inserted then add to offspring and break offspring.append(new_o) break # Clone the selected individuals because all of the changes are inplace offspring = list(map(self.toolbox.clone, offspring)) # Apply crossover on the selected offspring for child1, child2 in zip(offspring[::2], offspring[1::2]): self.toolbox.mate(child1, child2) #inplace recombination #Invalidate new children fitness values del child1.fitness.values del child2.fitness.values #Apply mutation on the offspring for mutant in offspring: if random.random() < MUTPB: self.toolbox.mutate(mutant) del mutant.fitness.values #Add elite individuals #Is clonning needed? offspring.extend( list(map(self.toolbox.clone, tools.selBest(self.pop, NELT)))) # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = map(self.toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit #print(" Evaluated %i individuals" % len(invalid_ind)) #Replace population with offspring self.pop[:] = offspring # Gather all the fitnesses in one list and print the stats self.fits = [ind.fitness.values[0] for ind in self.pop] length = len(self.pop) mean = sum(self.fits) / length sum2 = sum(x * x for x in self.fits) std = abs(sum2 / length - mean**2)**0.5 q_min_series.append(curr_g, min(self.fits)) q_max_series.append(curr_g, max(self.fits)) q_avg_series.append(curr_g, mean) combination_current_series.append( min(self.fits) ) # Saving min_series fitness values of an experiment -- Added by Denis Lazor # Checking if fitness value of 0 is reached -- Added by Denis Lazor for f in self.fits: if f == 0: success = success + 1 generations.append(curr_g) combination_series.append(combination_current_series) break #print(" Min %s" % q_min_series.at(q_min_series.count()-1).y()) #print(" Max %s" % q_max_series.at(q_max_series.count()-1).y()) #print(" Avg %s" % mean) #print(" Std %s" % std) if self.cbxNoVis.isChecked(): app.processEvents() else: #Draw queen positions of best individual on a image best_ind = tools.selBest(self.pop, 1)[0] self.updateWorldFrame(generateQueenImage(best_ind)) self.chart = QChart() #self.chart.addSeries(q_min_series) #self.chart.addSeries(q_max_series) #q_avg_series.setName("Board: " + str(b) + " Population: " + str(p) + " Elite: " + str(e) + " Mutation:" + str(m*100) + "% " + "Generations:" + str(NGSEN)) self.chart.addSeries(q_avg_series) self.chart.setTitle("QN: " + str(NO_QUEENS) + " POP: " + str(POP_SIZE) + " EL: " + str(NELT) + " MT: " + str(MUTPB * 100) + "% ") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) #Printing best individual best_ind = tools.selBest(self.pop, 1)[0] #print("Best individual is %s, %s \n" % (best_ind, best_ind.fitness.values)) #Visulaize final solution if self.cbxNoVis.isChecked(): #Draw queen positions of best individual on a image best_ind = tools.selBest(self.pop, 1)[0] self.updateWorldFrame(generateQueenImage(best_ind)) self.chart = QChart() #self.chart.addSeries(q_min_series) #self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("QN: " + str(NO_QUEENS) + " POP: " + str(POP_SIZE) + " EL: " + str(NELT) + " MT: " + str(MUTPB * 100) + "% ") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) #Disable stop and enable start self.btnStop.setEnabled(False) self.btnStart.setEnabled(True) self.gaParams.setEnabled(True) self.tbxNoQueens.setEnabled(True) self.cbxNoVis.setEnabled(True) def updateWorldFrame(self, queens_img): #new_image = QPixmap(1000,1000) self.new_image.fill() #White color is default painter = QPainter(self.new_image) #First draw the table painter.drawPixmap(self.new_image.rect(), self.img) #Then draw the queens painter.drawImage(self.new_image.rect(), queens_img) painter.end() #Set new image to the frame self.frameWorld.img = self.new_image #Redrawing frames self.frameWorld.repaint() self.frameChart.repaint() app.processEvents() def btnSaveWorld_Click(self): filename, _ = QFileDialog.getSaveFileName(None, "Save world as a image", "", "Image Files (*.png)") self.frameWorld.img.save(filename, "PNG") print("World image saved to: ", filename) def btnSaveChart_CLick(self): p = self.frameChart.grab() filename, _ = QFileDialog.getSaveFileName( None, "Save series chart as a image", "", "Image Files (*.png)") p.save(filename, "PNG") print("Chart series image saved to: ", filename) def btnSaveChartSeries_Click(self): global q_min_series global q_max_series global q_avg_series filename, _ = QFileDialog.getSaveFileName(None, "Save series to text file", "", "Text Files (*.txt, *.csv)") with open(filename, 'w') as dat: for i in range(q_min_series.count()): dat.write('%f,%f,%f\n' % (q_min_series.at(i).y(), q_avg_series.at(i).y(), q_max_series.at(i).y())) print("Chart series saved to: ", filename)
class Ui(mainwindow.Ui_MainWindow): def __init__(self, MainWindow): #super(Ui, self).__init__(M) super(Ui, self).setupUi(MainWindow) #uic.loadUi('UI/mainwindow.ui', self) self.MainWindow = MainWindow self.initialize() def close(self): self.MainWindow.close() def style(self): return self.MainWindow.style() def initialize(self): self.tabWidget.setCurrentIndex(0) self.actionExit.triggered.connect(self.close) self.action_Plot.setEnabled(False) self.actionNext.setIcon( self.style().standardIcon( QtWidgets.QStyle.SP_ArrowForward)) self.actionPrevious.setIcon( self.style().standardIcon( QtWidgets.QStyle.SP_ArrowBack)) self.action_Open.setIcon( self.style().standardIcon( QtWidgets.QStyle.SP_DialogOpenButton)) self.actionSave.setIcon( self.style().standardIcon( QtWidgets.QStyle.SP_DriveFDIcon)) self.actionSave.triggered.connect(self.save) self.action_Open.triggered.connect(self.getOpenFilename) self.actionNext.triggered.connect(self.nextPacket) self.actionPrevious.triggered.connect(self.previousPacket) self.actionAbout.triggered.connect(self.about) self.actionPrevious.setEnabled(False) self.actionNext.setEnabled(False) self.actionSave.setEnabled(False) self.action_Plot.setEnabled(False) self.actionPaste.triggered.connect(self.onPasteTriggered) # self.actionLog.triggered.connect(self.dockWidget_2.show) self.actionSet_IDB.triggered.connect(self.onSetIDBClicked) self.plotButton.clicked.connect(self.onPlotButtonClicked) self.exportButton.clicked.connect(self.onExportButtonClicked) self.action_Plot.triggered.connect(self.onPlotActionClicked) self.actionLoad_mongodb.triggered.connect(self.onLoadMongoDBTriggered) self.mdb=None self.current_row = 0 self.data=[] self.x=[] self.y=[] self.xlabel='x' self.ylabel='y' self.chart = QChart() self.chart.layout().setContentsMargins(0,0,0,0) self.chart.setBackgroundRoundness(0) self.savePlotButton.clicked.connect(self.savePlot) self.chartView = QChartView(self.chart) self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15) # IDB location self.settings = QtCore.QSettings('FHNW', 'stix_parser') def onExportButtonClicked(self): if self.y: filename = str(QtWidgets.QFileDialog.getSaveFileName( None, "Save file", "", "*.csv")[0]) if filename: with open(filename,'w') as f: f.write('{},{}\n'.format(self.xlabel,self.ylabel)) for xx,yy in zip(self.x,self.y): f.write('{},{}\n'.format(xx,yy)) self.showMessage('The data has been written to {}'.format(filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('Plot first!') msgBox.setWindowTitle("Warning") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def savePlot(self): #if self.figure.get_axes(): if self.chart: filename = str(QtWidgets.QFileDialog.getSaveFileName( None, "Save file", "", "*.png *.jpg")[0]) if filename: if not filename.endswith(('.png','.jpg')): filename+='.png' #self.figure.savefig(filename) p=self.chartView.grab() p.save(filename) self.showMessage(('Saved to %s.' % filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('The canvas is empty!') msgBox.setWindowTitle("STIX DATA VIEWER") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def onPasteTriggered(self): pass def showMessageBox(self, message, content): msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Critical) msg.setText("Error") msg.setInformativeText(message) msg.setWindowTitle("Error") msg.setDetailedText(content) msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) retval = msg.exec_() def showMessage(self, msg): # if destination != 1: self.statusbar.showMessage(msg) # if destination !=0 : # self.listWidget_2.addItem(msg) def onSetIDBClicked(self): pass def save(self): pass def setListViewSelected(self, row): #index = self.model.createIndex(row, 0); # if index.isValid(): # self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select) pass def about(self): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText("STIX raw data parser and viewer, [email protected]") msgBox.setWindowTitle("Stix data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def nextPacket(self): self.current_row += 1 length=len(self.data) if self.current_row>=length: self.current_row=length-1 self.showMessage('No more packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def previousPacket(self): self.current_row -= 1 if self.current_row <0: self.current_row=0 self.showMessage('Reach the first packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def getOpenFilename(self): pass def openFile(self, filename): pass def onDataLoaded(self, data,clear=True): if not clear: self.data.append(data) else: self.data = data self.displayPackets(clear) if self.data: self.actionPrevious.setEnabled(True) self.actionNext.setEnabled(True) self.actionSave.setEnabled(True) self.action_Plot.setEnabled(True) def displayPackets(self,clear=True): if clear: self.packetTreeWidget.clear() t0=0 for p in self.data: if type(p) is not dict: continue header = p['header'] root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget) if t0==0: t0=header['time'] root.setText(0, '{:.2f}'.format(header['time']-t0)) root.setText(1, ('TM({},{}) - {}').format(header['service_type'], header['service_subtype'], header['DESCR'])) self.total_packets = len(self.data) self.showMessage((('%d packets loaded') % (self.total_packets))) self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected) self.showPacket(0) def onLoadMongoDBTriggered(self): diag=QtWidgets.QDialog() diag_ui=mongo_dialog.Ui_Dialog() diag_ui.setupUi(diag) diag_ui.pushButton.setFocus(True) #self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.mongo_server= self.settings.value('mongo_server', [], str) self.mongo_port= self.settings.value('mongo_port', [], str) self.mongo_user= self.settings.value('mongo_user', [], str) self.mongo_pwd= self.settings.value('mongo_pwd', [], str) if self.mongo_server: diag_ui.serverLineEdit.setText(self.mongo_server) if self.mongo_port: diag_ui.portLineEdit.setText(self.mongo_port) if self.mongo_user: diag_ui.userLineEdit.setText(self.mongo_user) if self.mongo_pwd: diag_ui.pwdLineEdit.setText(self.mongo_pwd) diag_ui.pushButton.clicked.connect(partial(self.loadRunsFromMongoDB,diag_ui)) diag_ui.buttonBox.accepted.connect(partial(self.loadDataFromMongoDB,diag_ui,diag)) diag.exec_() def loadRunsFromMongoDB(self,dui): server=dui.serverLineEdit.text() port=dui.portLineEdit.text() user=dui.userLineEdit.text() pwd=dui.pwdLineEdit.text() self.showMessage('saving setting...') if self.mongo_server!=server: self.settings.setValue('mongo_server', server) if self.mongo_port!=port: self.settings.setValue('mongo_port', port) if self.mongo_user!=user: self.settings.setValue('mongo_user', user) if self.mongo_pwd!=pwd: self.settings.setValue('mongo_pwd', pwd) self.showMessage('connecting Mongo database ...') self.mdb=mgdb.MongoDB(server,int(port),user,pwd) dui.treeWidget.clear() self.showMessage('Fetching data...') for run in self.mdb.get_runs(): root = QtWidgets.QTreeWidgetItem(dui.treeWidget) root.setText(0, str(run['_id'])) root.setText(1, run['file']) root.setText(2, run['date']) root.setText(3, str(run['start'])) root.setText(4, str(run['end'])) self.showMessage('Runs loaded!') def loadDataFromMongoDB(self,dui,diag): selected_runs=[] for item in dui.treeWidget.selectedItems(): selected_runs.append(item.text(0)) if not selected_runs: self.showMessage('Run not selected!') if selected_runs: diag.done(0) self.showMessage('Loading data ...!') data=self.mdb.get_packets(selected_runs[0]) if data: self.onDataLoaded(data,clear=True) else: self.showMessage('No packets found!') #close def onPacketSelected(self, cur, pre): self.current_row = self.packetTreeWidget.currentIndex().row() self.showMessage((('Packet #%d selected') % self.current_row)) self.showPacket(self.current_row) def showPacket(self, row): if not self.data: return header = self.data[row]['header'] self.showMessage( (('Packet %d / %d %s ') % (row, self.total_packets, header['DESCR']))) self.paramTreeWidget.clear() header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) header_root.setText(0, "Header") rows = len(header) for key, val in header.items(): root = QtWidgets.QTreeWidgetItem(header_root) root.setText(0, key) root.setText(1, str(val)) params = self.data[row]['parameters'] param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) param_root.setText(0, "Parameters") self.showParameterTree(params, param_root) self.paramTreeWidget.expandItem(param_root) self.paramTreeWidget.expandItem(header_root) def showParameterTree(self, params, parent): for p in params: root = QtWidgets.QTreeWidgetItem(parent) if not p: continue try: param_name=p['name'] desc='' scos_desc='' try: desc=param_desc.PCF[param_name] scos_desc=param_desc.SW[param_name] except KeyError: pass root.setToolTip(1,scos_desc) root.setText(0, param_name) root.setText(1, desc) root.setText(2, str(p['raw'])) root.setText(3, str(p['value'])) if 'child' in p: if p['child']: self.showParameterTree(p['child'], root) except KeyError: self.showMessage( ('[Error ]: keyError occurred when adding parameter')) self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked) def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0): if not params: return timestamp = header['time'] #parameters=[p for p in params if p['name'] == name] for p in params: if type(p) is not dict: continue #for p in parameters: if name == p['name']: values = None #print('data type:{}'.format(data_type)) if data_type == 0: values = p['raw'] else: values = p['value'] try: yvalue = None if (type(values) is tuple) or (type(values) is list): yvalue = float(values[0]) else: yvalue = float(values) ret_y.append(yvalue) if xaxis == 1: ret_x.append(timestamp) else: self.showMessage((('Can not plot %s ') % str(yvalue))) except Exception as e: self.showMessage((('%s ') % str(e))) if 'child' in p: if p['child']: self.walk( name, p['child'], header, ret_x, ret_y, xaxis, data_type) def onPlotButtonClicked(self): if self.chart: self.chart.removeAllSeries() if not self.data: return self.showMessage('Preparing plot ...') name = self.paramNameEdit.text() packet_selection = self.comboBox.currentIndex() xaxis_type = self.xaxisComboBox.currentIndex() data_type = self.dataTypeComboBox.currentIndex() timestamp = [] self.y = [] packet_id = self.current_row params = self.data[packet_id]['parameters'] header = self.data[packet_id]['header'] current_spid=header['SPID'] if packet_selection == 0: self.walk( name, params, header, timestamp, self.y, xaxis_type, data_type) elif packet_selection == 1: for packet in self.data: header = packet['header'] if packet['header']['SPID'] != current_spid: continue #only look for parameters in the packets of the same type params = packet['parameters'] self.walk( name, params, header, timestamp, self.y, xaxis_type, data_type) self.x = [] if not self.y: self.showMessage('No data points') elif self.y: style = self.styleEdit.text() if not style: style = '-' title = '%s' % str(name) desc = self.descLabel.text() if desc: title += '- %s' % desc self.chart.setTitle(title) ylabel = 'Raw value' xlabel = name if data_type == 1: ylabel = 'Engineering value' if xaxis_type == 0: xlabel = "Packet #" self.x = range(0, len(self.y)) if xaxis_type == 1: self.x = [t - timestamp[0] for t in timestamp] xlabel = 'Time -T0 (s)' #if xaxis_type != 2: if True: series = QLineSeries() series2 = None # print(y) # print(x) for xx, yy in zip(self.x, self.y): series.append(xx, yy) if 'o' in style: series2 = QScatterSeries() for xx, yy in zip(self.x, self.y): series2.append(xx, yy) self.chart.addSeries(series2) self.chart.addSeries(series) self.showMessage('plotted!') #self.chart.createDefaultAxes() axisX = QValueAxis() axisX.setTitleText(xlabel) axisY = QValueAxis() axisY.setTitleText(ylabel) self.chart.setAxisX(axisX) self.chart.setAxisY(axisY) series.attachAxis(axisX) series.attachAxis(axisY) # histogram #else: # nbins = len(set(self.y)) # ycounts, xedges = np.histogram(self.y, bins=nbins) # series = QLineSeries() # for i in range(0, nbins): # meanx = (xedges[i] + xedges[i + 1]) / 2. # series.append(meanx, ycounts[i]) # # series.append(dataset) # self.chart.addSeries(series) # #self.chart.createDefaultAxes() # self.showMessage('Histogram plotted!') # axisX = QValueAxis() # axisX.setTitleText(name) # axisY = QValueAxis() # axisY.setTitleText("Counts") # self.chart.setAxisY(axisY) # self.chart.setAxisX(axisX) ## series.attachAxis(axisX) # series.attachAxis(axisY) # self.widget.setChart(self.chart) self.xlabel=xlabel self.ylabel=ylabel self.chartView.setRubberBand(QChartView.RectangleRubberBand) self.chartView.setRenderHint(QtGui.QPainter.Antialiasing) def plotParameter(self, name=None, desc=None): self.tabWidget.setCurrentIndex(1) if name: self.paramNameEdit.setText(name) if desc: self.descLabel.setText(desc) def onPlotActionClicked(self): self.tabWidget.setCurrentIndex(1) self.plotParameter() def onTreeItemClicked(self, it, col): #print(it, col, it.text(0)) self.plotParameter(it.text(0), it.text(1)) def error(self, msg, description=''): self.showMessage((('Error: %s - %s') % (msg, description))) def warning(self, msg, description=''): self.showMessage((('Warning: %s - %s') % (msg, description))) def info(self, msg, description=''): self.showMessage((('Info: %s - %s') % (msg, description)))
class Ui(mainwindow.Ui_MainWindow): def __init__(self, MainWindow): super(Ui, self).setupUi(MainWindow) self.MainWindow = MainWindow self.socketPacketReceiver = None self.timmer_is_on = False self.hexParser = StixHexStringParser() self.socketPacketReceiver = StixSocketPacketReceiver() self.socketPacketServer = StixSocketPacketServer() self.dataReader = StixFileReader() slots = { 'info': self.onDataReaderInfo, 'warning': self.onDataReaderWarning, 'error': self.onDataReaderError, 'critical': self.onDataReaderCritical, 'dataLoaded': self.onDataReady, 'packetArrival': self.onPacketArrival, 'progress': self.onProgressUpdated } self.socketPacketReceiver.connectSignalSlots(slots) self.dataReader.connectSignalSlots(slots) self.hexParser.connectSignalSlots(slots) self.socketPacketServer.connectSignalSlots(slots) self.tabWidget.setCurrentIndex(0) self.actionExit.triggered.connect(self.close) self.actionPlot.setEnabled(False) self.actionNext.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowForward)) self.actionPrevious.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowBack)) self.actionOpen.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DialogOpenButton)) self.actionSave.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DriveFDIcon)) self.actionSave.triggered.connect(self.save) self.actionOpen.triggered.connect(self.getOpenFilename) self.filterPattern = None self.actionNext.triggered.connect(self.nextPacket) self.actionPrevious.triggered.connect(self.previousPacket) self.actionAbout.triggered.connect(self.about) self.actionPrevious.setEnabled(False) self.actionNext.setEnabled(False) self.actionSave.setEnabled(False) self.actionPlot.setEnabled(False) self.actionCopy.triggered.connect(self.onCopyTriggered) self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected) self.actionCopy.setEnabled(False) self.actionPaste.triggered.connect(self.onPasteTriggered) self.actionLog.triggered.connect(self.dockWidget.show) self.actionSetIDB.triggered.connect(self.onSetIDBClicked) self.plotButton.clicked.connect( partial(self.onPlotButtonClicked, None)) #self.progressBar = QtWidgets.QProgressBar() #self.statusbar.addPermanentWidget(self.progressBar) self.progressDiag = None #self.progressBar.hide() self.actionPacketServer.triggered.connect(self.startPacketServer) self.exportButton.clicked.connect(self.onExportButtonClicked) self.actionPlot.triggered.connect(self.onPlotActionClicked) self.actionLoadMongodb.triggered.connect(self.onLoadMongoDBTriggered) self.actionConnectTSC.triggered.connect(self.onConnectTSCTriggered) self.actionPacketFilter.triggered.connect(self.filter) self.actionPlugins.triggered.connect(self.onPluginTriggered) self.actionOnlineHelp.triggered.connect(self.onOnlineHelpTriggered) self.actionViewBinary.triggered.connect(self.onViewBinaryTriggered) self.actionTimestampConvertor.triggered.connect(self.onTimestampConvertorTriggered) #self.actionPythonConsole.triggered.connect(self.startPythonConsole) self.autoUpdateButton.clicked.connect( self.onPlotAutoUpdateButtonClicked) self.packetTreeWidget.customContextMenuRequested.connect( self.packetTreeContextMenuEvent) #self.statusListWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) #self.statusListWidget.customContextMenuRequested.connect(self.statusListContextMenuEvent) self.mdb = None self.current_row = 0 self.data = [] self.x = [] self.y = [] self.xlabel = 'x' self.ylabel = 'y' self.buttons_enabled = False self.chart = QChart() self.chart.layout().setContentsMargins(0, 0, 0, 0) self.chart.setBackgroundRoundness(0) self.savePlotButton.clicked.connect(self.savePlot) self.chartView = QChartView(self.chart) self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15) self.selected_services = SELECTED_SERVICES self.selected_SPID = [] self.selected_tmtc = 3 # IDB location self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.idb_filename = self.settings.value('idb_filename', [], str) if self.idb_filename: STIX_IDB.reload(self.idb_filename) if not STIX_IDB.is_connected(): self.showMessage('IDB has not been set!') else: idb_filename = STIX_IDB.get_idb_filename() self.showMessage('IDB loaded from : {} '.format(idb_filename), 1) if idb_filename != self.idb_filename: self.settings.setValue('idb_filename', idb_filename) self.idb_filename = idb_filename #def startPythonConsole(self): # console.start({'packets': self.data}) def close(self): self.MainWindow.close() def style(self): return self.MainWindow.style() def startPacketServer(self): host = 'localhost' port = 9096 self.socketPacketServer.connect(host, port) self.socketPacketServer.setData(self.current_row, self.data) self.socketPacketServer.start() abspath = os.path.dirname(os.path.abspath(__file__)) template = ( "import sys\nsys.path.append('{}')\nimport client_packet_request as req\n" "packets=req.request(query_str='len', host='{}',port={}, verbose_level=1)\n" "#a query_string can be \n" "# - a python slice notation, for example, ':' '0:-1', 3:-1\n" "# - 'len', to get the total number of packets,\n" "# - index , to get a packet of the given index" "#set verbose_level to 0, to suppress print output ").format( abspath, host, port) cb = QtWidgets.QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(template, mode=cb.Clipboard) msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Information) msg.setText( "Packet server started and a template to request packet has been copied to your clipboard!" ) retval = msg.exec_() def onPlotAutoUpdateButtonClicked(self): if not self.timmer_is_on: if not self.data: return num_packets = len(self.data) if num_packets > 200: packets = self.data[-200:-1] else: packets = self.data self.timer = QTimer() self.timer.timeout.connect( partial(self.onPlotButtonClicked, packets)) self.timer.start(2000) self.timmer_is_on = True self.autoUpdateButton.setText('Stop Auto Update') else: if self.timer: self.timer.stop() self.timmer_is_on = False self.autoUpdateButton.setText('Start Auto Update') #def statusListContextMenuEvent(self,pos): # menu = QtWidgets.QMenu() # clearLogAction= menu.addAction('Empty log') # clearLogAction.triggered.connect(self.clearLog) #def clearLog(self): # self.statusListWidget.clear() def packetTreeContextMenuEvent(self, pos): menu = QtWidgets.QMenu() filterAction = menu.addAction('Filter') menu.addSeparator() rawDataAction = menu.addAction('Raw binary data') menu.addSeparator() copyPacketAction = menu.addAction('Copy packet') menu.addSeparator() deleteAllAction = menu.addAction('Delete all packets') self.current_row = self.packetTreeWidget.currentIndex().row() rawDataAction.triggered.connect(self.onViewBinaryTriggered) filterAction.triggered.connect(self.filter) copyPacketAction.triggered.connect(self.onCopyTriggered) deleteAllAction.triggered.connect(self.onDeleteAllTriggered) action = menu.exec_(self.packetTreeWidget.viewport().mapToGlobal(pos)) def filter(self): text, okPressed = QtWidgets.QInputDialog.getText( None, "Packet filtering", "Filtering by SPID or description (! to exclude):", QtWidgets.QLineEdit.Normal, "") if okPressed: self.filterPattern = text self.addPacketsToView(self.data, True, show_stat=False) def onDeleteAllTriggered(self): self.data.clear() self.current_row = 0 self.packetTreeWidget.clear() self.paramTreeWidget.clear() #def onPacketTreeItemDoubleClicked(self): # self.onViewBinaryTriggered() def onViewBinaryTriggered(self): diag = QtWidgets.QDialog() diag_ui = raw_viewer.Ui_Dialog() diag_ui.setupUi(diag) if self.data: try: raw = self.data[self.current_row]['bin'] header = self.data[self.current_row]['header'] diag_ui.setPacketInfo('{}({},{}) {}'.format( header['TMTC'], header['service_type'], header['service_subtype'], header['descr'])) diag_ui.displayRaw(raw) except (IndexError, KeyError): diag_ui.setText('Raw data not available.') diag.exec_() def onOnlineHelpTriggered(self): webbrowser.open( 'https://github.com/i4Ds/STIX-python-data-parser', new=2) def onTimestampConvertorTriggered(self): diag = QtWidgets.QDialog() diag_ui = timestamp_convertor.Ui_Dialog() diag_ui.setupUi(diag) diag.exec_() def onPluginTriggered(self): self.plugin_location = self.settings.value('plugin_location', [], str) diag = QtWidgets.QDialog() diag_ui = plugin.Ui_Dialog() diag_ui.setupUi(diag) if self.plugin_location: diag_ui.setPluginLocation(self.plugin_location) diag_ui.setData(self.data, self.current_row) diag.exec_() location = diag_ui.getPluginLocation() if location != self.plugin_location: self.settings.setValue('plugin_location', location) def onPacketFilterTriggered(self): diag = QtWidgets.QDialog() diag_ui = packet_filter.Ui_Dialog() diag_ui.setupUi(diag) self.filterPattern = '' #empty search string diag_ui.setSelectedServices(self.selected_services) diag_ui.buttonBox.accepted.connect( partial(self.applyServiceFilter, diag_ui)) diag.exec_() def applyServiceFilter(self, diag_ui): self.selected_SPID = diag_ui.getSelectedSPID() self.selected_services = diag_ui.getSelectedServices() self.selected_tmtc = diag_ui.getTMTC() self.showMessage('Applying filter...') self.addPacketsToView(self.data, True, show_stat=False) def onExportButtonClicked(self): if self.y: filename = str( QtWidgets.QFileDialog.getSaveFileName( None, "Save data to file", "", "CSV(*.csv)")[0]) if filename: with open(filename, 'w') as f: f.write('{},{}\n'.format(self.xlabel, self.ylabel)) for xx, yy in zip(self.x, self.y): f.write('{},{}\n'.format(xx, yy)) self.showMessage( 'The data has been written to {}'.format(filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('Plot first!') msgBox.setWindowTitle("Warning") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def savePlot(self): # if self.figure.get_axes(): if self.chart: filetypes = "PNG (*.png);;JPEG (*.jpg)" filename = str( QtWidgets.QFileDialog.getSaveFileName( None, "Save plot to file", "", filetypes)[0]) if filename: if not filename.endswith(('.png', '.jpg')): filename += '.png' # self.figure.savefig(filename) p = self.chartView.grab() p.save(filename) self.showMessage(('Has been saved to %s.' % filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('No figure to save') msgBox.setWindowTitle("STIX raw data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def onCopyTriggered(self): packet_id = self.current_row try: packet = self.data[packet_id] ss = pprint.pformat(packet) cb = QtWidgets.QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(ss, mode=cb.Clipboard) msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Information) msg.setText( "The data of the selected packet has been copied to the clipboard." ) msg.setWindowTitle("Information") msg.setStandardButtons(QtWidgets.QMessageBox.Ok) retval = msg.exec_() except Exception as e: self.showMessage(str(e), 0) def onPasteTriggered(self): raw_hex = QtWidgets.QApplication.clipboard().text() if len(raw_hex) < 16: self.showMessage('No data in the clipboard.') return self.hexParser.setHex(raw_hex) self.hexParser.start() def showMessage(self, msg, where=0): if where != 1: self.statusbar.showMessage(msg) if where != 0: self.statusListWidget.addItem(msg) def onSetIDBClicked(self): self.idb_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', '.', 'IDB file(*.db *.sqlite *.sqlite3)')[0] if not self.idb_filename: return STIX_IDB.reload(self.idb_filename) if STIX_IDB.is_connected(): #settings = QtCore.QSettings('FHNW', 'stix_parser') self.settings.setValue('idb_filename', self.idb_filename) self.showMessage( 'IDB location: {} '.format(STIX_IDB.get_idb_filename()), 1) def save(self): filetypes = 'python compressed pickle (*.pklz);; python pickle file (*.pkl);; binary data (*.dat)' self.output_filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save packets to", "", filetypes)[0]) if not self.output_filename.endswith(('.pklz', '.pkl', '.dat')): msg = 'unsupported file format !' self.showMessage(msg) return msg = 'Writing data to file %s' % self.output_filename self.showMessage(msg) if self.output_filename.endswith(('.pklz', '.pkl')): stw = stix_writer.StixPickleWriter(self.output_filename) stw.register_run(str(self.input_filename)) stw.write_all(self.data) elif self.output_filename.endswith('.dat'): stw = stix_writer.StixBinaryWriter(self.output_filename) stw.write_all(self.data) num_ok = stw.get_num_sucess() msg = ( 'The binary data of {} packets written to file {}, total packets {}' .format(num_ok, self.output_filename, len(self.data))) self.showMessage(msg) msg = 'Packets have been written to %s' % self.output_filename self.showMessage(msg) def setListViewSelected(self, row): #index = self.model.createIndex(row, 0); # if index.isValid(): # self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select) pass def about(self): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText("STIX raw data parser and viewer, [email protected]") msgBox.setWindowTitle("Stix data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def nextPacket(self): self.current_row += 1 length = len(self.data) if self.current_row >= length: self.current_row = length - 1 self.showMessage('No more packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def previousPacket(self): self.current_row -= 1 if self.current_row < 0: self.current_row = 0 self.showMessage('Reach the first packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def getOpenFilename(self): location = self.settings.value('location', [], str) if not location: location = '.' filetypes = ( 'Supported file (*.dat *.bin *.binary *.pkl *.pklz *.xml *ascii *BDF *txt) ;; All(*)' ) self.input_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', location, filetypes)[0] if not self.input_filename: return self.settings.setValue('location', os.path.abspath(self.input_filename)) diag = QtWidgets.QDialog() diag_ui = packet_filter.Ui_Dialog() diag_ui.setupUi(diag) diag_ui.setSelectedServices(SELECTED_SERVICES) diag_ui.buttonBox.accepted.connect( partial(self.onOpenFile, self.input_filename, diag_ui)) diag.exec_() def onOpenFile(self, input_filename, diag): self.selected_SPID = diag.getSelectedSPID() self.selected_services = diag.getSelectedServices() self.openFile(input_filename, self.selected_services, self.selected_SPID) def openFile(self, filename, selected_services=None, selected_SPID=None): msg = 'Loading file %s ...' % filename self.progressDiag = QtWidgets.QProgressDialog() #self.showMessage(msg) self.progressDiag.setLabelText(msg) self.progressDiag.setWindowTitle('Loading data') self.progressDiag.setCancelButtonText('Cancel') self.progressDiag.setRange(0, 100) self.progressDiag.setMinimumWidth(300) self.progressDiag.canceled.connect(self.stopParsing) self.filterPattern = '' self.dataReader.setPacketFilter(selected_services, selected_SPID) self.dataReader.setFilename(filename) self.dataReader.start() self.progressDiag.show() def stopParsing(self): if self.dataReader: self.dataReader.stopParsing() self.progressDiag.hide() def onProgressUpdated(self, progress): if not self.progressDiag: return self.progressDiag.setValue(progress) if progress >=99: self.progressDiag.hide() def onDataReaderCritical(self, msg): self.showMessage(msg, 1) def onDataReaderInfo(self, msg): self.showMessage(msg, 0) def onDataReaderWarning(self, msg): self.showMessage(msg, 1) def onDataReaderError(self, msg): self.showMessage(msg, 1) def onDataReady(self, data, clear=True, show_stat=True): #self.progressBar.hide() if not clear: self.data.extend(data) else: self.data = data if data: self.addPacketsToView(data, clear=clear, show_stat=show_stat) self.enableButtons() else: self.showMessage('No packet loaded') def enableButtons(self): if not self.buttons_enabled: self.actionPrevious.setEnabled(True) self.actionNext.setEnabled(True) self.actionSave.setEnabled(True) self.actionCopy.setEnabled(True) self.actionPlot.setEnabled(True) self.actionViewBinary.setEnabled(True) self.buttons_enabled = True def addPacketsToView(self, data, clear=True, show_stat=True): if clear: self.packetTreeWidget.clear() for p in data: if not isinstance(p, dict): continue header = p['header'] root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget) colors = {2: '#FFA500', 1: '#000080', 3: '#FF0000', 4: '#800000'} tc_color = '#78281F' if header['TMTC'] == 'TC': root.setForeground(0, QtGui.QBrush(QtGui.QColor(tc_color))) root.setForeground(1, QtGui.QBrush(QtGui.QColor(tc_color))) else: if header['service_type'] == 5: if header['service_subtype'] in colors.keys(): root.setForeground( 0, QtGui.QBrush( QtGui.QColor( colors[header['service_subtype']]))) root.setForeground( 1, QtGui.QBrush( QtGui.QColor( colors[header['service_subtype']]))) timestamp_str = stix_datetime.format_datetime(header['unix_time']) root.setText(0, timestamp_str) description = '{}({},{}) - {}'.format( header['TMTC'], header['service_type'], header['service_subtype'], header['descr']) root.setText(1, description) hidden = False if self.selected_SPID: if header['TMTC'] == 'TC': hidden = True elif -int(header['SPID']) in self.selected_SPID or int( header['SPID']) not in self.selected_SPID: hidden = True else: if int(header['service_type']) not in self.selected_services: hidden = True TMTC = header['TMTC'] if TMTC == 'TM' and self.selected_tmtc in [2, 0]: hidden = True if TMTC == 'TC' and self.selected_tmtc in [1, 0]: hidden = True if self.filterPattern: to_exclude = False pattern = self.filterPattern.strip() if pattern.startswith('!'): to_exclude = True pattern = pattern[1:] try: spid = int(pattern) hidden = to_exclude == (header['SPID'] == spid) #XNOR operation except (TypeError, ValueError): hidden = to_exclude == (pattern in description) root.setHidden(hidden) if show_stat: total_packets = len(self.data) self.showMessage(('Total packet(s): %d' % total_packets)) def onConnectTSCTriggered(self): diag = QtWidgets.QDialog() diag_ui = tsc_connection.Ui_Dialog() diag_ui.setupUi(diag) self.tsc_host = self.settings.value('tsc_host', [], str) self.tsc_port = self.settings.value('tsc_port', [], str) if self.tsc_host: diag_ui.serverLineEdit.setText(self.tsc_host) if self.tsc_port: diag_ui.portLineEdit.setText(self.tsc_port) diag_ui.buttonBox.accepted.connect(partial(self.connectToTSC, diag_ui)) diag.exec_() def connectToTSC(self, dui): host = dui.serverLineEdit.text() port = dui.portLineEdit.text() self.showMessage('Connecting to TSC...') self.socketPacketReceiver.connect(host, int(port)) self.socketPacketReceiver.start() def onPacketArrival(self, packets): clear = False if packets: if len(self.data) > MAX_NUM_PACKET_IN_BUFFER: clear = True self.onDataReady(packets, clear=clear, show_stat=True) def onLoadMongoDBTriggered(self): diag = QtWidgets.QDialog() diag_ui = mongo_dialog.Ui_Dialog() diag_ui.setupUi(diag) #self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.mongo_server = self.settings.value('mongo_server', [], str) self.mongo_port = self.settings.value('mongo_port', [], str) self.mongo_user = self.settings.value('mongo_user', [], str) self.mongo_pwd = self.settings.value('mongo_pwd', [], str) if self.mongo_server: diag_ui.serverLineEdit.setText(self.mongo_server) if self.mongo_port: diag_ui.portLineEdit.setText(self.mongo_port) if self.mongo_user: diag_ui.userLineEdit.setText(self.mongo_user) if self.mongo_pwd: diag_ui.pwdLineEdit.setText(self.mongo_pwd) diag_ui.pushButton.clicked.connect( partial(self.loadRunsFromMongoDB, diag_ui)) diag_ui.buttonBox.accepted.connect( partial(self.loadDataFromMongoDB, diag_ui, diag)) diag.exec_() def loadRunsFromMongoDB(self, dui): server = dui.serverLineEdit.text() port = dui.portLineEdit.text() user = dui.userLineEdit.text() pwd = dui.pwdLineEdit.text() self.showMessage('saving setting...') if self.mongo_server != server: self.settings.setValue('mongo_server', server) if self.mongo_port != port: self.settings.setValue('mongo_port', port) if self.mongo_user != user: self.settings.setValue('mongo_user', user) if self.mongo_pwd != pwd: self.settings.setValue('mongo_pwd', pwd) self.showMessage('connecting Mongo database ...') self.mdb = mgdb.MongoDB(server, int(port), user, pwd) if not self.mdb.is_connected(): self.showMessage('Failed to connect to MongoDB') return dui.treeWidget.clear() self.showMessage('Fetching data...') for run in self.mdb.select_all_runs(): root = QtWidgets.QTreeWidgetItem(dui.treeWidget) root.setText(0, str(run['_id'])) root.setText(1, run['filename']) root.setText(2, stix_datetime.format_datetime(run['date'])) root.setText(3, stix_datetime.format_datetime(run['data_start_unix_time'])) root.setText(4, stix_datetime.format_datetime(run['data_stop_unix_time'])) def loadDataFromMongoDB(self, dui, diag): self.showMessage('Loading packets ...') selected_runs = [] for item in dui.treeWidget.selectedItems(): selected_runs.append(item.text(0)) if not selected_runs: self.showMessage('Run not selected!') if selected_runs: diag.done(0) self.showMessage('Loading data ...!') data = list(self.mdb.select_packets_by_run(selected_runs[0])) if data: self.onDataReady(data, clear=True) else: self.showMessage('No packets found!') # close def onPacketSelected(self, cur, pre): self.current_row = self.packetTreeWidget.currentIndex().row() self.showMessage(('Packet #%d selected' % self.current_row)) self.showPacket(self.current_row) def showPacket(self, row): if not self.data: return header = self.data[row]['header'] total_packets = len(self.data) self.showMessage( ('Packet %d / %d %s ' % (row, total_packets, header['descr']))) self.paramTreeWidget.clear() header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) header_root.setText(0, "Header") rows = len(header) for key, val in header.items(): root = QtWidgets.QTreeWidgetItem(header_root) root.setText(0, key) root.setText(1, str(val)) params = self.data[row]['parameters'] param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) param_root.setText(0, "Parameters") self.showParameterTree(params, param_root) self.paramTreeWidget.expandItem(param_root) self.paramTreeWidget.expandItem(header_root) self.current_row = row def showParameterTree(self, params, parent, parent_id=[]): if not params: return for i, p in enumerate(params): root = QtWidgets.QTreeWidgetItem(parent) if not p: continue param = Parameter(p) param_name = param['name'] desc = param['desc'] current_ids = parent_id[:] current_ids.append(i) root.setText(0, param_name) root.setText(1, desc) root.setText(2, str(param['raw'])) tip='parameter'+''.join(['[{}]'.format(x) for x in current_ids]) root.setToolTip(0, tip) long_desc = STIX_IDB.get_scos_description(param_name) if long_desc: root.setToolTip(1, long_desc) try: root.setToolTip(2, hex(param['raw_int'])) except: pass unit=STIX_IDB.get_parameter_unit(param_name) eng=str(param['eng']) root.setText(3, eng) root.setText(4, unit) if 'NIXG' in param_name: root.setHidden(True) #groups should not be shown if param.children: self.showParameterTree(param['children'], root, current_ids) self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked) def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0): if not params: return timestamp = header['unix_time'] for p in params: if not p: continue param = Parameter(p) if name == param.name: values = None if data_type == 0: values = param.raw else: values = param.eng try: yvalue = float(values) ret_y.append(yvalue) if xaxis == 1: ret_x.append(timestamp) else: self.showMessage(('Can not plot %s ' % str(yvalue))) except Exception as e: self.showMessage(('%s ' % str(e))) if param.children: self.walk(name, param.children, header, ret_x, ret_y, xaxis, data_type) def onPlotButtonClicked(self, packets=None): if self.chart: self.chart.removeAllSeries() if packets is None: packets = self.data if not packets: return self.showMessage('Preparing plot ...') name = self.paramNameEdit.text() packet_selection = self.comboBox.currentIndex() xaxis_type = self.xaxisComboBox.currentIndex() data_type = self.dataTypeComboBox.currentIndex() timestamp = [] self.y = [] params = self.paramNameEdit.text() header = packets[0]['header'] current_spid = 0 spid_text = self.spidLineEdit.text() if spid_text: current_spid = int(spid_text) selected_packets=[] if packet_selection == 0: selected_packets=[packets[self.current_row]] elif packet_selection == 1: selected_packets=packets for packet in selected_packets: header = packet['header'] if packet['header']['SPID'] != current_spid: continue params = packet['parameters'] self.walk(name, params, header, timestamp, self.y, xaxis_type, data_type) self.x = [] if not self.y: self.showMessage('No data points') elif self.y: style = self.styleEdit.text() if not style: style = '-' title = '%s' % str(name) desc = self.descLabel.text() if desc: title += '- %s' % desc self.chart.setTitle(title) ylabel = 'Raw value' xlabel = name if data_type == 1: ylabel = 'Engineering / Decompressed value' if xaxis_type == 0: if packet_selection == 1: xlabel = "Packet #" else: xlabel = "Repeat #" self.x = range(0, len(self.y)) if xaxis_type == 1: self.x = [t - timestamp[0] for t in timestamp] xlabel = 'Time -T0 (s)' if xaxis_type != 2: series = QLineSeries() series2 = None for xx, yy in zip(self.x, self.y): series.append(xx, yy) if 'o' in style: series2 = QScatterSeries() for xx, yy in zip(self.x, self.y): series2.append(xx, yy) self.chart.addSeries(series2) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(xlabel) axisY = QValueAxis() axisY.setTitleText(ylabel) self.chart.setAxisX(axisX) self.chart.setAxisY(axisY) series.attachAxis(axisX) series.attachAxis(axisY) else: nbins = len(set(self.y)) ycounts, xedges = np.histogram(self.y, bins=nbins) series = QLineSeries() for i in range(0, nbins): meanx = (xedges[i] + xedges[i + 1]) / 2. series.append(meanx, ycounts[i]) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(name) axisY = QValueAxis() axisY.setTitleText("Counts") self.chart.setAxisY(axisY) self.chart.setAxisX(axisX) series.attachAxis(axisX) series.attachAxis(axisY) self.xlabel = xlabel self.ylabel = ylabel self.chartView.setRubberBand(QChartView.RectangleRubberBand) self.chartView.setRenderHint(QtGui.QPainter.Antialiasing) msg = 'Number of data points: {}, Ymin: {}, Ymax: {}'.format( len(self.y), min(self.y), max(self.y)) self.showMessage(msg, 1) self.showMessage('The canvas updated!') def plotParameter(self, SPID=None, pname=None, desc=None): self.tabWidget.setCurrentIndex(1) if pname: self.paramNameEdit.setText(pname) if desc: self.descLabel.setText(desc) if SPID: self.spidLineEdit.setText(str(SPID)) def onPlotActionClicked(self): self.tabWidget.setCurrentIndex(1) self.plotParameter() def onTreeItemClicked(self, it, col): SPID = None try: SPID = self.data[self.current_row]['header']['SPID'] except IndexError: pass self.plotParameter(SPID, it.text(0), it.text(1))
class widgetFDChart(QtWidgets.QWidget, qdesignFDChart.Ui_Form): def __init__(self, parent=None): super(widgetFDChart, self).__init__(parent) self.setupUi(self) tribchartmenustyle(self) # SetUp for Chart to Display Distribution curves and inputs. self.chart = QChart() self.chart.legend().setVisible(False) self.chart.axisX = QValueAxis() self.chart.axisY = QValueAxis() self.chart.axisX.setTickCount(10) self.chart.axisY.setTickCount(10) self.chart.axisX.setTitleText("Value") self.chart.axisY.setTitleText("Probability") self.chartview = QChartView(self.chart) self.verticalLayout.addWidget(self.chartview) # self.setAxesMinMax(-3,3,0.01,1.5) self.chartview.setRenderHint(QPainter.Antialiasing) # self.legend().setVisible(True) # self.setAnimationOptions(QChart.SeriesAnimations) # self.legend().setAlignment(Qt.AlignBottom) # Connect Buttons self.pushButtonExportPNG.pressed.connect(self._onActionSavePNG) def axesMinMax(self): # returns a length 4 list of the axes min and max values [x1,x2,y1,y2] return [ self.chart.axisX.min(), self.chart.axisX.max(), self.chart.axisY.min(), self.chart.axisY.max() ] def setAxes(self, series): # assigns a series to the chart default axes self.chart.setAxisX(self.chart.axisX, series) self.chart.setAxisY(self.chart.axisY, series) def setAxesMinMax(self, x1, x2, y1, y2): # sets the min max values in X and Y self.chart.axisX.setMin(x1) self.chart.axisX.setMax(x2) self.chart.axisY.setMin(y1) self.chart.axisY.setMax(y2) def setTitle(self, title): self.chart.setTitle(title) def addDistrLine(self, type, n, kstats): data = distr.distrpdf(type, n, **kstats) xmin = min(data['X']) xmax = max(data['X']) ymin = min(data['Y']) ymax = max(data['Y']) self.lineDist = XLineSeries(data, xkey='X')[0] self.chart.addSeries(self.lineDist) self.setAxes(self.lineDist) xscal = 0.1 * (xmax - xmin) yscal = 0.1 * (ymax - ymin) self.setAxesMinMax(xmin - xscal, xmax + xscal, ymin, ymax + yscal) def updateChart(self, tabledata): self.activeDist = tabledata[0] self.kstats = tabledata[1] self.chart.removeAllSeries() self.addDistrLine(self.activeDist, 100, self.kstats) @pyqtSlot(str) def paintChart(self, filename): #pixmap = QPixmap() pixmap = self.chartview.grab() pixmap.save(filename) def _onActionSavePNG(self): pngfile = QtWidgets.QFileDialog.getSaveFileName( self, caption='Export PNG As', directory='~', filter='*.png') #try: self.paintChart(pngfile[0])
class graficoBarras(QWidget): def __init__(self, parent=None): super(graficoBarras, self).__init__(parent) self.initUI() def initUI(self): comboBoxColor = QComboBox() comboBoxColor.addItems([ "Color del fondo del CharView", "Color del fondo del Chart", "Color del título del Chart", "Color de las etiquetas del eje X", "Color de las etiquetas del eje Y", "Color de las etiquetas de la leyenda" ]) checkBoxVisibilidadFondoChart = QCheckBox("Visibilidad fondo (chart)") checkBoxMargenesChart = QCheckBox("Margenes del chart") checkBoxEsquinasChart = QCheckBox("Esquinas del chart") buttonGuardar = QPushButton("Guardar gráfico") # Crear gráficos. self.vistaGrafico = QChartView(self.crearGraficoBarras()) self.vistaGrafico.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.vistaGrafico.setRenderHint(QPainter.Antialiasing, True) # ========================== DISEÑO ========================== disenioConfiguracion = QVBoxLayout() disenioConfiguracion.addWidget(QLabel("Cambiar colores:")) disenioConfiguracion.addWidget(comboBoxColor) disenioConfiguracion.addWidget(checkBoxVisibilidadFondoChart) disenioConfiguracion.addWidget(checkBoxMargenesChart) disenioConfiguracion.addWidget(checkBoxEsquinasChart) disenioConfiguracion.setSpacing(4) disenioConfiguracion.addStretch() disenioConfiguracion.addWidget(buttonGuardar) baseDisenio = QGridLayout() baseDisenio.addLayout(disenioConfiguracion, 0, 0, 0, 1) baseDisenio.addWidget(self.vistaGrafico, 0, 1, 0, 4) baseDisenio.setSpacing(10) baseDisenio.setContentsMargins(10, 10, 10, 10) self.setLayout(baseDisenio) # ===================== CONECTAR SEÑALES ===================== comboBoxColor.activated.connect(self.Color) checkBoxVisibilidadFondoChart.toggled.connect( self.visibilidadFondoChart) checkBoxMargenesChart.toggled.connect(self.margenesChart) checkBoxEsquinasChart.toggled.connect(self.esquinasChart) buttonGuardar.clicked.connect(self.Guardar) # Establecer los valores predeterminados comboBoxColor.setCurrentIndex(-1) checkBoxVisibilidadFondoChart.setChecked(True) checkBoxMargenesChart.setChecked(True) checkBoxEsquinasChart.setChecked(True) # ========================== FUNCIONES =========================== def crearGraficoBarras(self): paises = [ "EEUU", "China", "Japon", "Alemania", "Reino Unido", "Resto del mundo" ] valores = [24.32, 14.85, 8.91, 12.54, 7.85, 31.53] colores = [ Qt.blue, Qt.red, Qt.darkYellow, Qt.gray, Qt.black, Qt.darkCyan ] grafico = QChart() grafico.setMargins(QMargins(30, 30, 30, 30)) grafico.setTheme(QChart.ChartThemeLight) grafico.setTitle("% Distribución del PIB global") grafico.setAnimationOptions(QChart.SeriesAnimations) for i in range(len(paises)): series = QBarSeries() barSet = QBarSet(paises[i]) barSet.setColor(colores[i]) barSet.setLabelColor(Qt.yellow) barSet.append(valores[i]) series.append(barSet) series.setLabelsVisible(True) series.setLabelsAngle(-90) # series.setLabelsPrecision(2) series.setLabelsFormat("@value %") series.setLabelsPosition(QAbstractBarSeries.LabelsCenter) grafico.addSeries(series) axisX = QBarCategoryAxis() axisX.append(paises) axisY = QValueAxis() axisY.setRange(0, 31.53) axisY.setTickCount(10) axisY.setLabelFormat("%.2f %") grafico.createDefaultAxes() grafico.setAxisX(axisX, None) grafico.setAxisY(axisY, None) grafico.legend().setVisible(True) grafico.legend().setAlignment(Qt.AlignBottom) return grafico def Color(self, item): if item == 0: color = (self.vistaGrafico.backgroundBrush().color() if self.vistaGrafico.backgroundBrush().color().isValid() else QColor(Qt.white)) elif item == 1: color = ( self.vistaGrafico.chart().backgroundBrush().color() if self.vistaGrafico.chart().backgroundBrush().color().isValid() else QColor(Qt.white)) elif item == 2: color = (self.vistaGrafico.chart().titleBrush().color() if self.vistaGrafico.chart().titleBrush().color().isValid() else QColor(Qt.black)) elif item == 3: color = (self.vistaGrafico.chart().axisX().labelsBrush().color() if self.vistaGrafico.chart().axisX().labelsBrush().color( ).isValid() else QColor(Qt.black)) elif item == 4: color = (self.vistaGrafico.chart().axisY().labelsBrush().color() if self.vistaGrafico.chart().axisY().labelsBrush().color( ).isValid() else QColor(Qt.black)) elif item == 5: color = (self.vistaGrafico.chart().legend().labelColor() if self.vistaGrafico.chart().legend().labelColor().isValid() else QColor(Qt.black)) color = QColorDialog.getColor(color, self) if color.isValid(): if item == 0: self.vistaGrafico.setBackgroundBrush(color) elif item == 1: self.vistaGrafico.chart().setBackgroundBrush(color) elif item == 2: self.vistaGrafico.chart().setTitleBrush(color) elif item == 3: self.vistaGrafico.chart().axisX().setLabelsBrush(color) elif item == 4: self.vistaGrafico.chart().axisY().setLabelsBrush(color) elif item == 5: self.vistaGrafico.chart().legend().setLabelColor(color) def visibilidadFondoChart(self, bool): self.vistaGrafico.chart().setBackgroundVisible(bool) def margenesChart(self, bool): if bool: self.vistaGrafico.chart().layout().setContentsMargins(9, 9, 9, 9) else: self.vistaGrafico.chart().layout().setContentsMargins(0, 0, 0, 0) def esquinasChart(self, bool): if bool: self.vistaGrafico.chart().setBackgroundRoundness(5) else: self.vistaGrafico.chart().setBackgroundRoundness(0) def Guardar(self): nombre, extension = QFileDialog.getSaveFileName( self, "Guardar como", "Gráfico de barras", "JPG (*.jpg);;PNG (*.png)", options=QFileDialog.Options()) if nombre: guardar = QPixmap(self.vistaGrafico.grab()) guardar.save(nombre, quality=100) if guardar: QMessageBox.information(self, "Guardar gráfico", "Gráfico guardado con éxito.", QMessageBox.Ok) else: QMessageBox.critical(self, "Guardar gráfico", "Error al guardar el gráfico.", QMessageBox.Ok)
class Ui(mainwindow.Ui_MainWindow): def __init__(self, MainWindow): super(Ui, self).setupUi(MainWindow) self.MainWindow = MainWindow self.stix_tctm_parser = stix_parser.StixTCTMParser() self.initialize() def close(self): self.MainWindow.close() def style(self): return self.MainWindow.style() def initialize(self): self.tabWidget.setCurrentIndex(0) self.actionExit.triggered.connect(self.close) self.actionPlot.setEnabled(False) self.actionNext.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowForward)) self.actionPrevious.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_ArrowBack)) self.actionOpen.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DialogOpenButton)) self.actionSave.setIcon(self.style().standardIcon( QtWidgets.QStyle.SP_DriveFDIcon)) self.actionSave.triggered.connect(self.save) self.actionOpen.triggered.connect(self.getOpenFilename) self.actionNext.triggered.connect(self.nextPacket) self.actionPrevious.triggered.connect(self.previousPacket) self.actionAbout.triggered.connect(self.about) self.actionPrevious.setEnabled(False) self.actionNext.setEnabled(False) self.actionSave.setEnabled(False) self.actionPlot.setEnabled(False) self.actionCopy.triggered.connect(self.onCopyTriggered) self.packetTreeWidget.currentItemChanged.connect(self.onPacketSelected) self.actionCopy.setEnabled(False) self.actionPaste.triggered.connect(self.onPasteTriggered) self.actionLog.triggered.connect(self.dockWidget.show) self.actionSetIDB.triggered.connect(self.onSetIDBClicked) self.plotButton.clicked.connect(self.onPlotButtonClicked) self.exportButton.clicked.connect(self.onExportButtonClicked) self.actionPlot.triggered.connect(self.onPlotActionClicked) self.actionLoadMongodb.triggered.connect(self.onLoadMongoDBTriggered) self.actionConnectTSC.triggered.connect(self.onConnectTSCTriggered) self.actionPacketFilter.triggered.connect(self.onPacketFilterTriggered) self.actionPlugins.triggered.connect(self.onPluginTriggered) self.actionOnlineHelp.triggered.connect(self.onOnlineHelpTriggered) self.actionViewBinary.triggered.connect(self.onViewBinaryTriggered) self.packetTreeWidget.customContextMenuRequested.connect( self.packetTreeContextMenuEvent) self.mdb = None self.current_row = 0 self.data = [] self.x = [] self.y = [] self.xlabel = 'x' self.ylabel = 'y' self.buttons_enabled = False self.chart = QChart() self.chart.layout().setContentsMargins(0, 0, 0, 0) self.chart.setBackgroundRoundness(0) self.savePlotButton.clicked.connect(self.savePlot) self.chartView = QChartView(self.chart) self.gridLayout.addWidget(self.chartView, 1, 0, 1, 15) #self.packetTreeWidget.itemDoubleClicked.connect(self.onPacketTreeItemDoubleClicked) self.selected_services = SELECTED_SERVICES self.selected_SPID = [] # IDB location self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.idb_filename = self.settings.value('idb_filename', [], str) if self.idb_filename: idb._stix_idb.reload(self.idb_filename) if not idb._stix_idb.is_connected(): self.showMessage('IDB has not been set!') else: self.showMessage( 'IDB location: {} '.format(idb._stix_idb.get_idb_filename()), 1) def packetTreeContextMenuEvent(self, pos): menu = QtWidgets.QMenu() rawDataAction = menu.addAction('Show raw data') menu.addSeparator() filterAction = menu.addAction('Filter packets') copyPacketAction = menu.addAction('Copy packet') menu.addSeparator() deleteAllAction = menu.addAction('Delete all packets') self.current_row = self.packetTreeWidget.currentIndex().row() rawDataAction.triggered.connect(self.onViewBinaryTriggered) filterAction.triggered.connect(self.onPacketFilterTriggered) copyPacketAction.triggered.connect(self.onCopyTriggered) deleteAllAction.triggered.connect(self.onDeleteAllTriggered) action = menu.exec_(self.packetTreeWidget.viewport().mapToGlobal(pos)) def onDeleteAllTriggered(self): self.data.clear() self.packetTreeWidget.clear() self.paramTreeWidget.clear() #def onPacketTreeItemDoubleClicked(self): # self.onViewBinaryTriggered() def onViewBinaryTriggered(self): diag = QtWidgets.QDialog() diag_ui = raw_viewer.Ui_Dialog() diag_ui.setupUi(diag) if self.data: try: raw = self.data[self.current_row]['bin'] header = self.data[self.current_row]['header'] diag_ui.setPacketInfo('{}({},{}) {}'.format( header['TMTC'], header['service_type'], header['service_subtype'], header['DESCR'])) diag_ui.displayRaw(raw) except (IndexError, KeyError): diag_ui.setText('Raw data not available.') diag.exec_() def onOnlineHelpTriggered(self): webbrowser.open('https://github.com/i4Ds/STIX-dataviewer', new=2) def onPluginTriggered(self): self.plugin_location = self.settings.value('plugin_location', [], str) diag = QtWidgets.QDialog() diag_ui = plugin.Ui_Dialog() diag_ui.setupUi(diag) if self.plugin_location: diag_ui.setPluginLocation(self.plugin_location) diag_ui.setData(self.data, self.current_row) diag.exec_() location = diag_ui.getPluginLocation() if location != self.plugin_location: self.settings.setValue('plugin_location', location) def onPacketFilterTriggered(self): diag = QtWidgets.QDialog() diag_ui = packet_filter.Ui_Dialog() diag_ui.setupUi(diag) diag_ui.setSelectedServices(self.selected_services) diag_ui.buttonBox.accepted.connect( partial(self.applyServiceFilter, diag_ui)) diag.exec_() def applyServiceFilter(self, diag_ui): self.selected_SPID = diag_ui.getSelectedSPID() self.selected_services = diag_ui.getSelectedServices() self.showMessage('Applying filter...') self.addPacketsToView(self.data, True, show_stat=False) def onExportButtonClicked(self): if self.y: filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save data to file", "", "CSV(*.csv)")[0]) if filename: with open(filename, 'w') as f: f.write('{},{}\n'.format(self.xlabel, self.ylabel)) for xx, yy in zip(self.x, self.y): f.write('{},{}\n'.format(xx, yy)) self.showMessage( 'The data has been written to {}'.format(filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('Plot first!') msgBox.setWindowTitle("Warning") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def savePlot(self): # if self.figure.get_axes(): if self.chart: filetypes = "PNG (*.png);;JPEG (*.jpg)" filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save plot to file", "", filetypes)[0]) if filename: if not filename.endswith(('.png', '.jpg')): filename += '.png' # self.figure.savefig(filename) p = self.chartView.grab() p.save(filename) self.showMessage(('Saved to %s.' % filename)) else: msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText('No figure to save') msgBox.setWindowTitle("STIX raw data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def onCopyTriggered(self): packet_id = self.current_row try: packet = self.data[packet_id] ss = pprint.pformat(packet) cb = QtWidgets.QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(ss, mode=cb.Clipboard) msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Information) msg.setText( "The data of the selected packet has been copied to the clipboard." ) msg.setWindowTitle("Information") msg.setStandardButtons(QtWidgets.QMessageBox.Ok) retval = msg.exec_() except Exception as e: self.showMessage(str(e), 0) def onPasteTriggered(self): raw_hex = QtWidgets.QApplication.clipboard().text() if len(raw_hex) < 16: self.showMessage('No data in the clipboard.') return data_hex = re.sub(r"\s+", "", raw_hex) try: data_binary = binascii.unhexlify(data_hex) packets = self.stix_tctm_parser.parse_binary(data_binary, 0, store_binary=True) if not packets: return self.showMessage('%d packets read from the clipboard' % len(packets)) self.onDataReady(packets, clear=False, show_stat=False) except Exception as e: self.showMessageBox(str(e), data_hex) def showMessageBox(self, message, content): msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Critical) msg.setText("Error") msg.setInformativeText(message) msg.setWindowTitle("Error") msg.setDetailedText(content) msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) retval = msg.exec_() def showMessage(self, msg, where=0): if where != 1: self.statusbar.showMessage(msg) if where != 0: self.statusListWidget.addItem(msg) # if destination !=0 : # self.listWidget_2.addItem(msg) def onSetIDBClicked(self): self.idb_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', '.', 'IDB file(*.db *.sqlite *.sqlite3)')[0] if not self.idb_filename: return idb._stix_idb.reload(self.idb_filename) if idb._stix_idb.is_connected(): #settings = QtCore.QSettings('FHNW', 'stix_parser') self.settings.setValue('idb_filename', self.idb_filename) self.showMessage( 'IDB location: {} '.format(idb._stix_idb.get_idb_filename()), 1) def save(self): filetypes = 'python compressed pickle (*.pklz);; python pickle file (*.pkl);; binary data (*.dat)' self.output_filename = str( QtWidgets.QFileDialog.getSaveFileName(None, "Save packets to", "", filetypes)[0]) if not self.output_filename.endswith(('.pklz', '.pkl', '.dat')): msg = 'unsupported file format !' self.showMessage(msg) return msg = 'Writing data to file %s' % self.output_filename self.showMessage(msg) if self.output_filename.endswith(('.pklz', '.pkl')): stw = stix_writer.StixPickleWriter(self.output_filename) stw.register_run(str(self.input_filename)) stw.write_all(self.data) #stw.done() elif self.output_filename.endswith('.dat'): stw = stix_writer.StixBinaryWriter(self.output_filename) stw.write_all(self.data) num_ok = stw.get_num_sucess() msg = ( 'The binary data of {} packets written to file {}, total packets {}' .format(num_ok, self.output_filename, len(self.data))) self.showMessage(msg) def setListViewSelected(self, row): #index = self.model.createIndex(row, 0); # if index.isValid(): # self.model.selectionModel().select( index, QtGui.QItemSelectionModel.Select) pass def about(self): msgBox = QtWidgets.QMessageBox() msgBox.setIcon(QtWidgets.QMessageBox.Information) msgBox.setText("STIX raw data parser and viewer, [email protected]") msgBox.setWindowTitle("Stix data viewer") msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok) msgBox.exec_() def nextPacket(self): self.current_row += 1 length = len(self.data) if self.current_row >= length: self.current_row = length - 1 self.showMessage('No more packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def previousPacket(self): self.current_row -= 1 if self.current_row < 0: self.current_row = 0 self.showMessage('Reach the first packet!') self.showPacket(self.current_row) self.setListViewSelected(self.current_row) def getOpenFilename(self): filetypes = ( 'STIX raw data(*.dat *.bin *.binary);; python pickle files (*.pkl *pklz);;' 'ESA xml files (*xml);;' 'ESA ascii files(*.ascii);; CMDVS archive files (*.BDF);; All(*)') self.input_filename = QtWidgets.QFileDialog.getOpenFileName( None, 'Select file', '.', filetypes)[0] if not self.input_filename: return self.openFile(self.input_filename) def openFile(self, filename): self.packetTreeWidget.clear() msg = 'Loading file %s ...' % filename self.showMessage(msg) self.dataReader = StixFileReader(filename) #self.dataReader.dataLoaded.connect(self.onDataReady) self.dataReader.packetArrival.connect(self.onPacketArrival) self.dataReader.error.connect(self.onDataReaderError) self.dataReader.info.connect(self.onDataReaderInfo) self.dataReader.warn.connect(self.onDataReaderWarn) self.dataReader.start() def onDataReaderInfo(self, msg): self.showMessage(msg, 0) def onDataReaderWarn(self, msg): self.showMessage(msg, 1) def onDataReaderError(self, msg): self.showMessage(msg, 1) def onDataReady(self, data, clear=True, show_stat=True): if not clear: self.data.extend(data) else: self.data = data if data: self.addPacketsToView(data, clear=clear, show_stat=show_stat) self.enableButtons() def enableButtons(self): if not self.buttons_enabled: self.actionPrevious.setEnabled(True) self.actionNext.setEnabled(True) self.actionSave.setEnabled(True) self.actionCopy.setEnabled(True) self.actionPlot.setEnabled(True) self.actionViewBinary.setEnabled(True) self.buttons_enabled = True def addPacketsToView(self, data, clear=True, show_stat=True): if clear: self.packetTreeWidget.clear() #t0 = 0 for p in data: if type(p) is not dict: continue header = p['header'] root = QtWidgets.QTreeWidgetItem(self.packetTreeWidget) # if t0 == 0: # t0 = header['time'] timestamp_str = '' try: timestamp_str = header['utc'] except KeyError: timestamp_str = '{:.2f}'.format(header['time']) #- t0) root.setText(0, timestamp_str) root.setText( 1, '{}({},{}) - {}'.format(header['TMTC'], header['service_type'], header['service_subtype'], header['DESCR'])) if not self.selected_SPID: #not set then apply service if header['service_type'] not in self.selected_services: root.setHidden(True) else: if header['SPID'] not in self.selected_SPID: root.setHidden(True) if show_stat: total_packets = len(self.data) self.showMessage(('Total packet(s): %d' % total_packets)) def onConnectTSCTriggered(self): diag = QtWidgets.QDialog() diag_ui = tsc_connection.Ui_Dialog() diag_ui.setupUi(diag) self.tsc_host = self.settings.value('tsc_host', [], str) self.tsc_port = self.settings.value('tsc_port', [], str) if self.tsc_host: diag_ui.serverLineEdit.setText(self.tsc_host) if self.tsc_port: diag_ui.portLineEdit.setText(self.tsc_port) diag_ui.buttonBox.accepted.connect(partial(self.connectToTSC, diag_ui)) diag.exec_() def connectToTSC(self, dui): host = dui.serverLineEdit.text() port = dui.portLineEdit.text() self.showMessage('Connecting to TSC...') self.socketPacketReceiver = StixSocketPacketReceiver(host, int(port)) self.socketPacketReceiver.packetArrival.connect(self.onPacketArrival) self.socketPacketReceiver.error.connect(self.onDataReaderError) self.socketPacketReceiver.info.connect(self.onDataReaderInfo) self.socketPacketReceiver.warn.connect(self.onDataReaderWarn) self.socketPacketReceiver.start() def onPacketArrival(self, packets): if packets: self.onDataReady(packets, clear=False, show_stat=True) def onLoadMongoDBTriggered(self): diag = QtWidgets.QDialog() diag_ui = mongo_dialog.Ui_Dialog() diag_ui.setupUi(diag) #self.settings = QtCore.QSettings('FHNW', 'stix_parser') self.mongo_server = self.settings.value('mongo_server', [], str) self.mongo_port = self.settings.value('mongo_port', [], str) self.mongo_user = self.settings.value('mongo_user', [], str) self.mongo_pwd = self.settings.value('mongo_pwd', [], str) if self.mongo_server: diag_ui.serverLineEdit.setText(self.mongo_server) if self.mongo_port: diag_ui.portLineEdit.setText(self.mongo_port) if self.mongo_user: diag_ui.userLineEdit.setText(self.mongo_user) if self.mongo_pwd: diag_ui.pwdLineEdit.setText(self.mongo_pwd) diag_ui.pushButton.clicked.connect( partial(self.loadRunsFromMongoDB, diag_ui)) diag_ui.buttonBox.accepted.connect( partial(self.loadDataFromMongoDB, diag_ui, diag)) diag.exec_() def loadRunsFromMongoDB(self, dui): server = dui.serverLineEdit.text() port = dui.portLineEdit.text() user = dui.userLineEdit.text() pwd = dui.pwdLineEdit.text() self.showMessage('saving setting...') if self.mongo_server != server: self.settings.setValue('mongo_server', server) if self.mongo_port != port: self.settings.setValue('mongo_port', port) if self.mongo_user != user: self.settings.setValue('mongo_user', user) if self.mongo_pwd != pwd: self.settings.setValue('mongo_pwd', pwd) self.showMessage('connecting Mongo database ...') self.mdb = mgdb.MongoDB(server, int(port), user, pwd) if not self.mdb.is_connected(): return dui.treeWidget.clear() self.showMessage('Fetching data...') for run in self.mdb.get_runs(): root = QtWidgets.QTreeWidgetItem(dui.treeWidget) root.setText(0, str(run['_id'])) root.setText(1, run['filename']) root.setText(2, run['date']) root.setText(3, str(run['start'])) root.setText(4, str(run['end'])) def loadDataFromMongoDB(self, dui, diag): self.showMessage('Loading packets ...') selected_runs = [] for item in dui.treeWidget.selectedItems(): selected_runs.append(item.text(0)) if not selected_runs: self.showMessage('Run not selected!') if selected_runs: diag.done(0) self.showMessage('Loading data ...!') data = self.mdb.get_packets(selected_runs[0]) if data: self.onDataReady(data, clear=True) else: self.showMessage('No packets found!') # close def onPacketSelected(self, cur, pre): self.current_row = self.packetTreeWidget.currentIndex().row() self.showMessage(('Packet #%d selected' % self.current_row)) self.showPacket(self.current_row) def showPacket(self, row): if not self.data: return header = self.data[row]['header'] total_packets = len(self.data) self.showMessage( ('Packet %d / %d %s ' % (row, total_packets, header['DESCR']))) self.paramTreeWidget.clear() header_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) header_root.setText(0, "Header") rows = len(header) for key, val in header.items(): root = QtWidgets.QTreeWidgetItem(header_root) root.setText(0, key) root.setText(1, str(val)) params = self.data[row]['parameters'] param_root = QtWidgets.QTreeWidgetItem(self.paramTreeWidget) param_root.setText(0, "Parameters") self.showParameterTree(params, param_root) self.paramTreeWidget.expandItem(param_root) self.paramTreeWidget.expandItem(header_root) self.current_row = row def showParameterTree(self, params, parent): if not params: return for p in params: root = QtWidgets.QTreeWidgetItem(parent) if not p: continue try: param_name = p['name'] desc = '' #description of parameter if 'desc' in p: desc = p['desc'] if not desc: desc = idb._stix_idb.get_PCF_description(param_name) scos_desc = idb._stix_idb.get_scos_description(param_name) if scos_desc: root.setToolTip(1, scos_desc) root.setText(0, param_name) root.setText(1, desc) root.setText(2, str(p['raw'])) try: root.setToolTip(2, hex(p['raw'][0])) except: pass root.setText(3, str(p['eng'])) if 'children' in p: if p['children']: self.showParameterTree(p['children'], root) except KeyError: self.showMessage( '[Error ]: keyError occurred when adding parameter') self.paramTreeWidget.itemDoubleClicked.connect(self.onTreeItemClicked) def walk(self, name, params, header, ret_x, ret_y, xaxis=0, data_type=0): if not params: return timestamp = header['time'] for p in params: if type(p) is not dict: continue if name == p['name']: values = None #print('data type:{}'.format(data_type)) if data_type == 0: values = p['raw'] else: values = p['eng'] try: yvalue = None if (type(values) is tuple) or (type(values) is list): yvalue = float(values[0]) else: yvalue = float(values) ret_y.append(yvalue) if xaxis == 1: ret_x.append(timestamp) else: self.showMessage(('Can not plot %s ' % str(yvalue))) except Exception as e: self.showMessage(('%s ' % str(e))) if 'children' in p: if p['children']: self.walk(name, p['children'], header, ret_x, ret_y, xaxis, data_type) def onPlotButtonClicked(self): if self.chart: self.chart.removeAllSeries() if not self.data: return self.showMessage('Preparing plot ...') name = self.paramNameEdit.text() packet_selection = self.comboBox.currentIndex() xaxis_type = self.xaxisComboBox.currentIndex() data_type = self.dataTypeComboBox.currentIndex() timestamp = [] self.y = [] packet_id = self.current_row params = self.data[packet_id]['parameters'] header = self.data[packet_id]['header'] current_spid = header['SPID'] if packet_selection == 0: self.walk(name, params, header, timestamp, self.y, xaxis_type, data_type) elif packet_selection == 1: for packet in self.data: header = packet['header'] if packet['header']['SPID'] != current_spid: continue params = packet['parameters'] self.walk(name, params, header, timestamp, self.y, xaxis_type, data_type) self.x = [] if not self.y: self.showMessage('No data points') elif self.y: style = self.styleEdit.text() if not style: style = '-' title = '%s' % str(name) desc = self.descLabel.text() if desc: title += '- %s' % desc self.chart.setTitle(title) ylabel = 'Raw value' xlabel = name if data_type == 1: ylabel = 'Engineering value' if xaxis_type == 0: if packet_selection == 1: xlabel = "Packet #" else: xlabel = "Repeat #" self.x = range(0, len(self.y)) if xaxis_type == 1: self.x = [t - timestamp[0] for t in timestamp] xlabel = 'Time -T0 (s)' if xaxis_type != 2: series = QLineSeries() series2 = None for xx, yy in zip(self.x, self.y): series.append(xx, yy) if 'o' in style: series2 = QScatterSeries() for xx, yy in zip(self.x, self.y): series2.append(xx, yy) self.chart.addSeries(series2) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(xlabel) axisY = QValueAxis() axisY.setTitleText(ylabel) self.chart.setAxisX(axisX) self.chart.setAxisY(axisY) series.attachAxis(axisX) series.attachAxis(axisY) else: nbins = len(set(self.y)) ycounts, xedges = np.histogram(self.y, bins=nbins) series = QLineSeries() for i in range(0, nbins): meanx = (xedges[i] + xedges[i + 1]) / 2. series.append(meanx, ycounts[i]) self.chart.addSeries(series) axisX = QValueAxis() axisX.setTitleText(name) axisY = QValueAxis() axisY.setTitleText("Counts") self.chart.setAxisY(axisY) self.chart.setAxisX(axisX) series.attachAxis(axisX) series.attachAxis(axisY) self.xlabel = xlabel self.ylabel = ylabel self.chartView.setRubberBand(QChartView.RectangleRubberBand) self.chartView.setRenderHint(QtGui.QPainter.Antialiasing) msg = 'Y length: {}, min-Y: {}, max-Y: {}'.format( len(self.y), min(self.y), max(self.y)) self.showMessage(msg, 1) self.showMessage('The canvas updated!') def plotParameter(self, name=None, desc=None): self.tabWidget.setCurrentIndex(1) if name: self.paramNameEdit.setText(name) if desc: self.descLabel.setText(desc) def onPlotActionClicked(self): self.tabWidget.setCurrentIndex(1) self.plotParameter() def onTreeItemClicked(self, it, col): self.plotParameter(it.text(0), it.text(1))
class Ui_MainWindow(QtWidgets.QMainWindow): def setupUi(self): self.setObjectName("MainWindow") self.resize(850, 1080) self.setWindowTitle("GA - Queens") self.centralwidget = QtWidgets.QWidget(self) self.centralwidget.setObjectName("centralwidget") self.frameWorld = MyQFrame(self.centralwidget) self.frameWorld.img = QPixmap(1000, 1000) self.frameWorld.setGeometry(QtCore.QRect(10, 10, 620, 600)) self.frameWorld.setFrameShape(QtWidgets.QFrame.Box) self.frameWorld.setFrameShadow(QtWidgets.QFrame.Sunken) self.frameWorld.setObjectName("frameWorld") self.frameChart = QChartView(self.centralwidget) self.frameChart.setGeometry(QtCore.QRect(10, 620, 620, 400)) self.frameChart.setFrameShape(QtWidgets.QFrame.Box) self.frameChart.setFrameShadow(QtWidgets.QFrame.Sunken) self.frameChart.setRenderHint(QPainter.Antialiasing) self.frameChart.setObjectName("frameChart") self.gaParams = QtWidgets.QGroupBox(self.centralwidget) self.gaParams.setGeometry(QtCore.QRect(650, 10, 161, 145)) self.gaParams.setObjectName("gaParams") self.gaParams.setTitle("GA parameters") self.label1 = QtWidgets.QLabel(self.gaParams) self.label1.setGeometry(QtCore.QRect(10, 20, 61, 16)) self.label1.setObjectName("label1") self.label1.setText("Population:") self.label2 = QtWidgets.QLabel(self.gaParams) self.label2.setGeometry(QtCore.QRect(10, 50, 47, 16)) self.label2.setObjectName("label2") self.label2.setText("Mutation:") self.label3 = QtWidgets.QLabel(self.gaParams) self.label3.setGeometry(QtCore.QRect(10, 80, 81, 16)) self.label3.setObjectName("label3") self.label3.setText("Elite members:") self.label4 = QtWidgets.QLabel(self.gaParams) self.label4.setGeometry(QtCore.QRect(10, 110, 91, 16)) self.label4.setObjectName("label4") self.label4.setText("No. generations:") self.tbxPopulation = QtWidgets.QLineEdit(self.gaParams) self.tbxPopulation.setGeometry(QtCore.QRect(100, 20, 51, 20)) self.tbxPopulation.setObjectName("tbxPopulation") self.tbxMutation = QtWidgets.QLineEdit(self.gaParams) self.tbxMutation.setGeometry(QtCore.QRect(100, 50, 51, 20)) self.tbxMutation.setObjectName("tbxMutation") self.tbxElite = QtWidgets.QLineEdit(self.gaParams) self.tbxElite.setGeometry(QtCore.QRect(100, 80, 51, 20)) self.tbxElite.setObjectName("tbxElite") self.tbxGenerations = QtWidgets.QLineEdit(self.gaParams) self.tbxGenerations.setGeometry(QtCore.QRect(100, 110, 51, 20)) self.tbxGenerations.setObjectName("tbxGenerations") self.cbxNoVis = QtWidgets.QCheckBox(self.centralwidget) self.cbxNoVis.setGeometry(QtCore.QRect(650, 170, 170, 17)) self.cbxNoVis.setObjectName("cbxNoVis") self.cbxNoVis.setText("No visualization per generation") self.cbxBorder = QtWidgets.QCheckBox(self.centralwidget) self.cbxBorder.setGeometry(QtCore.QRect(650, 200, 100, 17)) self.cbxBorder.setObjectName("cbxBorder") self.cbxBorder.setText("Border patrol") self.btnStart = QtWidgets.QPushButton(self.centralwidget) self.btnStart.setGeometry(QtCore.QRect(650, 230, 75, 23)) self.btnStart.setObjectName("btnStart") self.btnStart.setText("Start") self.btnStop = QtWidgets.QPushButton(self.centralwidget) self.btnStop.setEnabled(False) self.btnStop.setGeometry(QtCore.QRect(730, 230, 75, 23)) self.btnStop.setObjectName("btnStop") self.btnStop.setText("Stop") self.btnSaveWorld = QtWidgets.QPushButton(self.centralwidget) self.btnSaveWorld.setGeometry(QtCore.QRect(650, 570, 121, 41)) self.btnSaveWorld.setObjectName("btnSaveWorld") self.btnSaveWorld.setText("Save world as image") self.btnSaveChart = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChart.setGeometry(QtCore.QRect(650, 930, 121, 41)) self.btnSaveChart.setObjectName("btnSaveChart") self.btnSaveChart.setText("Save chart as image") self.btnSaveChartSeries = QtWidgets.QPushButton(self.centralwidget) self.btnSaveChartSeries.setGeometry(QtCore.QRect(650, 980, 121, 41)) self.btnSaveChartSeries.setObjectName("btnSaveChartSeries") self.btnSaveChartSeries.setText("Save chart as series") self.setCentralWidget(self.centralwidget) QtCore.QMetaObject.connectSlotsByName(self) #Connect events self.btnStart.clicked.connect(self.btnStart_Click) self.btnStop.clicked.connect(self.btnStop_Click) self.btnSaveWorld.clicked.connect(self.btnSaveWorld_Click) self.btnSaveChart.clicked.connect(self.btnSaveChart_CLick) self.btnSaveChartSeries.clicked.connect(self.btnSaveChartSeries_Click) #Set default GA variables self.tbxGenerations.insert(str(NGEN)) self.tbxPopulation.insert(str(POP_SIZE)) self.tbxMutation.insert(str(MUTPB)) self.tbxElite.insert(str(NELT)) self.new_image = QPixmap(1000, 1000) def btnStart_Click(self): global combination_series # List of lists containing min_series of 5 correct results -- Added by Denis Lazor global parameter_name # Name of parameter used for writing its data to .csv file -- Added by Denis Lazor global best_fit_values # List containing best fitness values for every of 5 experiments per combination -- Added by Denis Lazor global best_individual # Saving best individuals for drawing the best solution -- Added by Denis Lazor global ELITE_SIZES global POPULATION_SIZES global MUTATION_SIZES # Checking if files are empty or not -- Added by Denis Lazor csv_contains = os.stat("graphs_csv/original.csv").st_size != 0 if csv_contains: clear_all_csv() n = 5000 # Automation for all necessary combinations -- Added by Denis Lazor for p in POPULATION_SIZES: for m in MUTATION_SIZES: for e in ELITE_SIZES: for i in range(5): # Set global variables global stop_evolution global q_min_series global q_max_series global q_avg_series stop_evolution = False q_min_series.clear() q_max_series.clear() q_avg_series.clear() # Set global variables from information on UI global NGEN global POP_SIZE global MUTPB global NELT NGEN = n POP_SIZE = p MUTPB = m NELT = e global border_check border_check = self.cbxBorder.isChecked() # Loading Croatia map self.img = QPixmap(620, 600) self.img.load('Croatia620.png') self.frameWorld.img = self.img # Drawing towns painter = QPainter(self.img) painter.setPen(QPen(Qt.black, 10, Qt.SolidLine)) painter.setFont(QFont('Arial', 12)) for i in range(len(gradovi)): x, y = GlobToImgCoords(sirine[i], duzine[i]) painter.drawPoint(x, y) painter.drawText(x + 5, y + 5, gradovi[i]) painter.end() # Redrawing frames self.frameWorld.repaint() app.processEvents() ####Initialize deap GA objects#### # Make creator that minimize. If it would be 1.0 instead od -1.0 than it would be maxmize creator.create("FitnessMin", base.Fitness, weights=(-1.0, )) # Create an individual (a blueprint for cromosomes) as a list with a specified fitness type creator.create("Individual", list, fitness=creator.FitnessMin) # Create base toolbox for finishing creation of a individual (cromosome) self.toolbox = base.Toolbox() # This is if we want a permutation coding of genes in the cromosome self.toolbox.register("indices", random.sample, range(IND_SIZE), IND_SIZE) # initIterate requires that the generator of genes (such as random.sample) generates an iterable (a list) variable self.toolbox.register("individual", tools.initIterate, creator.Individual, self.toolbox.indices) # Create a population of individuals (cromosomes). The population is then created by toolbox.population(n=300) where 'n' is the number of cromosomes in population self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) # Register evaluation function self.toolbox.register("evaluate", evaluateInd) # Register what genetic operators to use self.toolbox.register( "mate", tools.cxUniformPartialyMatched, indpb=0.2 ) # Use uniform recombination for permutation coding # Permutation coding self.toolbox.register("mutate", tools.mutShuffleIndexes, indpb=0.2) self.toolbox.register( "select", tools.selTournament, tournsize=3) # Use tournament selection ################################## # Generate initial poplation. Will be a member variable so we can easely pass everything to new thread self.pop = self.toolbox.population(n=POP_SIZE) # Evaluate initial population, we map() the evaluation function to every individual and then assign their respective fitness, map runs evaluate function for each individual in pop fitnesses = list(map(self.toolbox.evaluate, self.pop)) for ind, fit in zip(self.pop, fitnesses): ind.fitness.values = fit # Assign calcualted fitness value to individuals # Extracting all the fitnesses of all individuals in a population so we can monitor and evovlve the algorithm until it reaches 0 or max number of generation is reached self.fits = [ind.fitness.values[0] for ind in self.pop] # Disable start and enable stop self.btnStart.setEnabled(False) self.btnStop.setEnabled(True) self.gaParams.setEnabled(False) self.cbxBorder.setEnabled(False) self.cbxNoVis.setEnabled(False) # Start evolution self.evolve() # Best fitness value -- Added by Denis Lazor best_fit = np.array(min(best_fit_values))[0] mean_fit = min( best_fit_values, key=lambda x: abs(x - statistics.mean( (np.asarray(best_fit_values)).flatten())))[0] # Index of best fitness value -- Added by Denis Lazor best_fit_idx = best_fit_values.index(best_fit) write_to_file(combination_series[best_fit_idx], parameter_name) # First name will be "original", second one "elites" -- Added by Denis Lazor parameter_name = "elites" print_results(p, m, e, best_fit, mean_fit, NGEN) # Clearing past lists -- Added by Denis Lazor combination_series = [] best_fit_values = [] # Reducing number of combinations and changing .csv file for writing -- Added by Denis Lazor ELITE_SIZES = ELITE_SIZES[0:1] parameter_name = "mutation" MUTATION_SIZES = MUTATION_SIZES[0:1] parameter_name = "population" print("Best individual: " + str(best_individual)) self.updateWorldFrame(generateWorldImage( best_individual)) # Drawing best solution -- Added by Denis Lazor def btnStop_Click(self): global stop_evolution stop_evolution = True #Disable stop and enable start self.btnStop.setEnabled(False) self.btnStart.setEnabled(True) self.gaParams.setEnabled(True) self.cbxBorder.setEnabled(True) self.cbxNoVis.setEnabled(True) #Function for GA evolution def evolve(self): global q_min_series global q_max_series global q_avg_series global best_fit_values global combination_series global best_individual combination_current_series = [ ] # Clearing fitness values series -- Added by Denis Lazor # Variable for keeping track of the number of generations curr_g = 0 # Begin the evolution till goal is reached or max number of generation is reached while min(self.fits) != 0 and curr_g < NGEN: #Check if evolution and thread need to stop if stop_evolution: break #Break the evolution loop # A new generation curr_g = curr_g + 1 #print("-- Generation %i --" % curr_g) # Select the next generation individuals #Select POP_SIZE - NELT number of individuals. Since recombination is between neigbours, not two naighbours should be the clone of the same individual offspring = [] offspring.append(self.toolbox.select( self.pop, 1)[0]) #add first selected individual for i in range( POP_SIZE - NELT - 1 ): # -1 because the first seleceted individual is already added while True: new_o = self.toolbox.select(self.pop, 1)[0] if new_o != offspring[len( offspring ) - 1]: #if it is different than the last inserted then add to offspring and break offspring.append(new_o) break # Clone the selected individuals because all of the changes are inplace offspring = list(map(self.toolbox.clone, offspring)) # Apply crossover on the selected offspring for child1, child2 in zip(offspring[::2], offspring[1::2]): self.toolbox.mate(child1, child2) #inplace recombination #Invalidate new children fitness values del child1.fitness.values del child2.fitness.values #Apply mutation on the offspring for mutant in offspring: if random.random() < MUTPB: self.toolbox.mutate(mutant) del mutant.fitness.values #Add elite individuals #Is clonning needed? offspring.extend( list(map(self.toolbox.clone, tools.selBest(self.pop, NELT)))) # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = map(self.toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit #print(" Evaluated %i individuals" % len(invalid_ind)) #Replace population with offspring self.pop[:] = offspring # Gather all the fitnesses in one list and print the stats self.fits = [ind.fitness.values[0] for ind in self.pop] length = len(self.pop) mean = sum(self.fits) / length sum2 = sum(x * x for x in self.fits) std = abs(sum2 / length - mean**2)**0.5 q_min_series.append(curr_g, min(self.fits)) q_max_series.append(curr_g, max(self.fits)) q_avg_series.append(curr_g, mean) combination_current_series.append( min(self.fits) ) # Saving min_series fitness values of an experiment -- Added by Denis Lazor #print(" Min %s" % q_min_series.at(q_min_series.count()-1).y()) #print(" Max %s" % q_max_series.at(q_max_series.count()-1).y()) #print(" Avg %s" % mean) #print(" Std %s" % std) if self.cbxNoVis.isChecked(): app.processEvents() else: self.chart = QChart() self.chart.addSeries(q_min_series) self.chart.addSeries(q_max_series) self.chart.addSeries(q_avg_series) self.chart.setTitle("Fitness value over time") self.chart.setAnimationOptions(QChart.NoAnimation) self.chart.createDefaultAxes() self.frameChart.setChart(self.chart) #Draw queen positions of best individual on a image best_ind = tools.selBest(self.pop, 1)[0] self.updateWorldFrame(generateWorldImage(best_ind)) #Printing best individual best_ind = tools.selBest(self.pop, 1)[0] #print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values)) combination_series.append( combination_current_series ) # Saving best 5 results -- Added by Denis Lazor if not best_individual: best_individual = best_ind elif best_ind.fitness.values < best_individual.fitness.values: # Saving best individual in all combinations -- Added by Denis Lazor best_individual = best_ind best_fit_values.append( best_ind.fitness.values ) # Adding best fitness value of experiment -- Added by Denis Lazor # #Visulaize final solution # if self.cbxNoVis.isChecked(): # self.chart = QChart() # self.chart.addSeries(q_min_series) # self.chart.addSeries(q_max_series) # self.chart.addSeries(q_avg_series) # self.chart.setTitle("Fitness value over time") # self.chart.setAnimationOptions(QChart.NoAnimation) # self.chart.createDefaultAxes() # self.frameChart.setChart(self.chart) # # #Draw queen positions of best individual on a image # best_ind = tools.selBest(self.pop, 1)[0] # self.updateWorldFrame(generateWorldImage(best_ind)) #Disable stop and enable start self.btnStop.setEnabled(False) self.btnStart.setEnabled(True) self.gaParams.setEnabled(True) self.cbxBorder.setEnabled(True) self.cbxNoVis.setEnabled(True) def updateWorldFrame(self, best_individual_img): #new_image = QPixmap(1000,1000) self.new_image.fill() #White color is default painter = QPainter(self.new_image) #First draw the map with towns painter.drawPixmap(self.new_image.rect(), self.img) #Then draw the best individual painter.drawImage(self.new_image.rect(), best_individual_img) painter.end() #Set new image to the frame self.frameWorld.img = self.new_image #Redrawing frames self.frameWorld.repaint() self.frameChart.repaint() app.processEvents() def btnSaveWorld_Click(self): filename, _ = QFileDialog.getSaveFileName(None, "Save world as a image", "", "Image Files (*.png)") self.frameWorld.img.save(filename, "PNG") print("World image saved to: ", filename) def btnSaveChart_CLick(self): p = self.frameChart.grab() filename, _ = QFileDialog.getSaveFileName( None, "Save series chart as a image", "", "Image Files (*.png)") p.save(filename, "PNG") print("Chart series image saved to: ", filename) def btnSaveChartSeries_Click(self): global q_min_series global q_max_series global q_avg_series filename, _ = QFileDialog.getSaveFileName(None, "Save series to text file", "", "Text Files (*.txt, *.csv)") with open(filename, 'w') as dat: for i in range(q_min_series.count()): dat.write('%f,%f,%f\n' % (q_min_series.at(i).y(), q_avg_series.at(i).y(), q_max_series.at(i).y())) print("Chart series saved to: ", filename)