class InputComponent(QGroupBox): analysisNameStateChanged = Signal(bool) def __init__(self): super().__init__() self.setTitle("Entrée") self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) main_layout = QFormLayout(self) main_layout.setHorizontalSpacing(20) main_layout.setVerticalSpacing(14) self._name_text = QLineEdit() self._name_validator = QRegExpValidator( QRegExp("^[a-zA-Z0-9_-#éèêëàîï ]{5,30}$")) self._name_text.setValidator(self._name_validator) self._name_text.inputRejected.connect(self._analysisNameError) self._name_text.textChanged.connect(self._analysisNameChanged) main_layout.addRow("Nom de l'analyse :", self._name_text) self._filepath_text = QLineEdit() self._filepath_button = QPushButton(" Parcourir... ") filepath_layout = QHBoxLayout() filepath_layout.setSpacing(8) filepath_layout.addWidget(self._filepath_text) filepath_layout.addWidget(self._filepath_button) main_layout.addRow("Dossier d'images à analyser :", filepath_layout) self.setLayout(main_layout) # Button slots self._filepath_button.clicked.connect(self.filepathBrowse) def reset(self, parameters: Parameters): self._name_text.setText(parameters.name()) self._filepath_text.setText(parameters.srcFolder()) def updateParameters(self, parameters: Parameters): parameters.setName(self._name_text.text()) parameters.setSrcFolder(self._filepath_text.text()) @Slot(str) def _analysisNameChanged(self, text: str): self.analysisNameStateChanged.emit( self._name_text.hasAcceptableInput()) @Slot() def _analysisNameError(self): text = "Caractères autorisés : alphanumérique, espace, #, - et _\nLongueur maximale : 30 caractères" QToolTip.showText( self._name_text.mapToGlobal(QPoint()) + self._name_text.cursorRect().topLeft(), text, self) @Slot() def filepathBrowse(self): path = None if AppInfo.isDevMode(): path = "./data/images" else: img_dir = QStandardPaths.writableLocation( QStandardPaths.PicturesLocation) path = QFileDialog.getExistingDirectory(self, "Sélectionner un dossier", img_dir) self._filepath_text.setText(path)
class App(QWidget): def __init__(self): super().__init__() self.title = 'Thermodynamic data' qd = QDesktopWidget() screen = qd.screenGeometry(qd) width = 600 height = 300 x = screen.width() / 2 - width / 2 y = screen.height() / 2 - height / 2 self.width = width self.height = height self.left = x self.top = y self.ignore_events = False self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.tableView1 = QTableView() self.tableWidget1 = QTableWidget() self.model = thTableModel() self.cas_filter = QLineEdit() self.name_filter = QLineEdit() self.formula_filter = QLineEdit() self.phase_filter = QComboBox() self.delete_selected_button = QPushButton() self.copy_selected_button = QPushButton() self.phases_vol_button = QPushButton() self.temp_text = QLineEdit() self.plot_window = PlotWindow() self.cp_button = QPushButton() self.psat_button = QPushButton() self.tableView1.setModel(self.model) self.tableWidget1.setColumnCount( self.tableView1.model().columnCount() + 1 + 1 + 1 + 1 + 1) self.widget_col_names = ['z_i', 'h_ig', 's_ig', 'g_ig', 'cp_ig' ] + self.tableView1.model().column_names self.widget_col_units = ['-', 'J/mol', 'J/mol/K', 'J/mol', 'J/mol/K' ] + self.tableView1.model().column_units self.widget_col_names_units = [ self.widget_col_names[i] + '\n' + self.widget_col_units[i] for i in range(len(self.widget_col_names)) ] self.tableWidget1.setHorizontalHeaderLabels( self.widget_col_names_units) self.phase_filter.addItems(['', 'G', 'L', 'S', 'C']) self.tableWidget1.setEnabled(False) self.tableWidget1.setSelectionBehavior(QTableWidget.SelectRows) self.tableWidget1.installEventFilter(self) self.delete_selected_button.setText('delete selected') self.copy_selected_button.setText('copy selected') self.phases_vol_button.setText('ph, v') self.temp_text.setValidator(QDoubleValidator(0, 6000, 16)) self.temp_text.setPlaceholderText('1000 K') # Add box layout, add table to box layout and add box layout to widget self.layout = QGridLayout() self.layout.addWidget(self.tableView1, 1, 1, 1, 4) self.layout.addWidget(self.cas_filter, 3, 1, 1, 1) self.layout.addWidget(self.name_filter, 3, 2, 1, 1) self.layout.addWidget(self.formula_filter, 3, 3, 1, 1) self.layout.addWidget(self.phase_filter, 3, 4, 1, 1) self.layout.addWidget(QLabel('find by: cas'), 2, 1, 1, 1) self.layout.addWidget(QLabel('name'), 2, 2, 1, 1) self.layout.addWidget(QLabel('formula'), 2, 3, 1, 1) self.layout.addWidget(QLabel('phase'), 2, 4, 1, 1) self.layout.addWidget(QLabel('selection'), 4, 1, 1, 4) self.layout.addWidget(self.tableWidget1, 5, 1, 1, 4) self.layout.addWidget(self.delete_selected_button, 6, 4, 1, 1) self.layout.addWidget(self.copy_selected_button, 6, 1, 1, 1) self.layout.addWidget(self.phases_vol_button, 6, 2, 1, 1) self.layout.addWidget(self.temp_text, 6, 3, 1, 1) self.setLayout(self.layout) for item in [self.cas_filter, self.name_filter, self.formula_filter]: item.textChanged.connect(self.apply_filters) self.phase_filter.currentTextChanged.connect(self.apply_filters) self.tableView1.selectionModel().selectionChanged.connect( partial(self.add_selection_to_widget)) self.tableWidget1.cellChanged.connect(partial(self.update_props)) self.delete_selected_button.clicked.connect( partial(self.delete_selected)) self.copy_selected_button.clicked.connect(partial(self.copy_selection)) self.phases_vol_button.clicked.connect(partial(self.phases_vol)) self.temp_text.editingFinished.connect(partial(self.update_temp)) #self.tableView1.horizontalHeader().setClickable(False) self.props_i = self.tableView1.model().df.iloc[[]] self.props_i['z_i'] = zeros(0) self.props_i['h_ig'] = zeros(0) self.props_i['s_ig'] = zeros(0) self.props_i['g_ig'] = zeros(0) self.props_i['cp_ig'] = zeros(0) # Show widget self.show() def apply_filters(self): cas_filter = self.cas_filter.text().upper().strip().replace(' ', '') name_filter = self.name_filter.text().upper().strip() formula_filter = self.formula_filter.text().upper().strip() phase_filter = self.phase_filter.currentText() self.model.apply_filter(cas_filter, name_filter, formula_filter, phase_filter) def add_selection_to_widget(self, selected, deselected): # indexes = self.tableView1.selectedIndexes() indexes = selected.indexes() index = indexes[0] column_of_cas = self.tableView1.model().column_names.index('cas_no') column_of_phase = self.tableView1.model().column_names.index('phase') phase = self.tableView1.model().index(index.row(), column_of_phase).data() cas = self.tableView1.model().index(index.row(), column_of_cas).data() already_in_table = False for item in self.tableWidget1.findItems(cas, Qt.MatchExactly): if phase == self.tableWidget1.item( item.row(), column_of_phase + 1 + 1 + 1 + 1 + 1).text(): already_in_table = True if not already_in_table: row_index = int(self.tableView1.model().headerData( index.row(), Qt.Vertical)) column_names = self.tableView1.model().column_names header_to_add = QTableWidgetItem(str(row_index)) # save df with new props z_i_orig = self.props_i['z_i'] h_ig_orig = self.props_i['h_ig'] s_ig_orig = self.props_i['s_ig'] g_ig_orig = self.props_i['g_ig'] cp_ig_orig = self.props_i['cp_ig'] index_orig = self.props_i.index self.props_i = self.tableView1.model().df.loc[[ int(self.tableWidget1.verticalHeaderItem(i).text()) for i in range(self.tableWidget1.rowCount()) ] + [row_index], :] self.props_i['z_i'] = zeros(len(self.props_i)) self.props_i['h_ig'] = zeros(len(self.props_i)) self.props_i['s_ig'] = zeros(len(self.props_i)) self.props_i['g_ig'] = zeros(len(self.props_i)) self.props_i['cp_ig'] = zeros(len(self.props_i)) self.props_i.loc[index_orig, 'z_i'] = z_i_orig self.props_i.loc[index_orig, 'h_ig'] = h_ig_orig self.props_i.loc[index_orig, 's_ig'] = s_ig_orig self.props_i.loc[index_orig, 'g_ig'] = g_ig_orig self.props_i.loc[index_orig, 'cp_ig'] = cp_ig_orig self.props_i.loc[row_index, 'z_i'] = float(0) # add item to widget self.tableWidget1.setRowCount(self.tableWidget1.rowCount() + 1) self.tableWidget1.setVerticalHeaderItem( self.tableWidget1.rowCount() - 1, header_to_add) for i in range(len(column_names)): # columns in TableWidget shifted by 1+1+1+1+1 vs. Tableview due to first columns z_i, h_ig, s_ig, g_ig, cp_ig data = self.tableView1.model().index(index.row(), i).data() if isinstance(data, str) or data is None: item_to_add = QTableWidgetItem(data) else: item_to_add = QTableWidgetItem(locale.str(data)) item_to_add.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable) self.tableWidget1.setItem(self.tableWidget1.rowCount() - 1, i + 1 + 1 + 1 + 1 + 1, item_to_add) # additional column with z_i item_to_add = QTableWidgetItem(locale.str(0)) item_to_add.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable) self.tableWidget1.setItem(self.tableWidget1.rowCount() - 1, 0, item_to_add) if len(indexes) > 0 or self.tableWidget1.rowCount() > 0: self.tableWidget1.setEnabled(True) def update_props(self, row, col): if col == 0: # change z_i df_index = int(self.tableWidget1.verticalHeaderItem(row).text()) new_value = float( self.tableWidget1.item(row, col).text().replace(',', '.')) self.props_i.loc[df_index, 'z_i'] = new_value else: pass def update_temp(self): if self.temp_text.hasAcceptableInput(): self.temp_text.setPlaceholderText(self.temp_text.text() + ' K') t = float(self.temp_text.text().replace(',', '.')) # K self.temp_text.clear() else: t = 1000 # K self.temp_text.clear() mm_i = (self.props_i['poling_molwt'] / 1000).tolist() # kg/mol tc_i = self.props_i['poling_tc'].tolist() # K pc_i = (self.props_i['poling_pc'] * 1e5).tolist() # Pa omega_i = self.props_i['poling_omega'].tolist() vc_i = (self.props_i['poling_vc'] * 10**-6).tolist() # m^3/mol delhf0_poling = (self.props_i['poling_delhf0'] * 1000).tolist() # J/mol delgf0_poling = (self.props_i['poling_delgf0'] * 1000).tolist() # J/mol delsf0_poling = [(delhf0_poling[i] - delgf0_poling[i]) / 298.15 for i in range(len(self.props_i))] # J/mol/K a_low = [ self.props_i['a' + str(i) + '_low'].tolist() for i in range(1, 7 + 1) ] a_high = [ self.props_i['a' + str(i) + '_high'].tolist() for i in range(1, 7 + 1) ] cp_r_low = [ sum([a_low[j][i] * t**j for j in range(4 + 1)]) for i in range(len(self.props_i)) ] # cp/R cp_r_high = [ sum([a_high[j][i] * t**j for j in range(4 + 1)]) for i in range(len(self.props_i)) ] # cp/R if t > 1000: # poly a_low is for 200 - 1000 K; a_high is for 1000 - 6000 K a = a_high cp_ig = [8.3145 * cp_r_high[i] for i in range(len(self.props_i))] # J/mol/K else: a = a_low cp_ig = [8.3145 * cp_r_low[i] for i in range(len(self.props_i))] # J/mol/K s_cp_r_dt = [ sum([1 / (j + 1) * a[j][i] * t**(j + 1) for j in range(4 + 1)]) - sum([ 1 / (j + 1) * a_low[j][i] * 298.15**(j + 1) for j in range(4 + 1) ]) for i in range(len(self.props_i)) ] # int(Cp/R*dT,298,15K,T) # int(Cp/R/T*dT,298.15K,T) s_cp_r_t_dt = [ a[0][i] * log(t) + a[6][i] + sum([1 / (j) * a[j][i] * t**(j) for j in range(1, 3 + 1)]) for i in range(len(self.props_i)) ] # int(Cp/(RT)*dT,0,T) h_ig = [ delhf0_poling[i] + 8.3145 * s_cp_r_dt[i] for i in range(len(self.props_i)) ] s_ig = [8.3145 * s_cp_r_t_dt[i] for i in range(len(self.props_i))] g_ig = [h_ig[i] - t * s_ig[i] for i in range(len(self.props_i))] for i in range(len(self.props_i)): for j, col in enumerate([h_ig, s_ig, g_ig, cp_ig]): #print(col) item_to_add = QTableWidgetItem(locale.str(col[i])) item_to_add.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable) self.tableWidget1.setItem(i, j + 1, item_to_add) def copy_selection(self): selection = self.tableWidget1.selectedIndexes() if selection: rows = sorted(index.row() for index in selection) columns = range(len(self.widget_col_names)) rowcount = rows[-1] - rows[0] + 1 colcount = columns[-1] - columns[0] + 1 + 1 table = [[''] * colcount for _ in range(rowcount + 1)] table[0] = [] for i in range(len(self.widget_col_names)): text_to_add = self.widget_col_names[ i] + '/(' + self.widget_col_units[i] + ')' table[0] += [text_to_add] for index in selection: row = index.row() - rows[0] column = index.column() - columns[0] table[row + 1][column] = index.data().replace( chr(34), '' ) # ensure string can be read as csv by removing quotation mark (ascii character 34) table = table + [['T=' + self.temp_text.placeholderText()] + ['' for _ in range(colcount - 1)]] stream = io.StringIO() csv.writer(stream, delimiter=';', quoting=csv.QUOTE_NONE).writerows(table) QApplication.clipboard().setText(stream.getvalue()) def eventFilter(self, source, event): if self.ignore_events: pass elif (event.type() == QEvent.KeyPress and event.matches(QKeySequence.Copy)): self.copy_selection() return True elif (event.type() == QEvent.KeyPress and event.matches(QKeySequence.Delete)): if len(self.tableWidget1.selectedIndexes()) > 1: self.delete_selected() return True return super(App, self).eventFilter(source, event) def delete_selected(self): if len(self.tableWidget1.selectedIndexes()) > 0: while len(self.tableWidget1.selectedIndexes()) > 0: current_row = self.tableWidget1.selectedIndexes()[0].row() row_index = int( self.tableWidget1.verticalHeaderItem(current_row).text()) self.tableWidget1.removeRow(current_row) self.props_i = self.props_i.drop([row_index]) self.tableWidget1.selectRow(current_row) if self.tableWidget1.rowCount() == 0: self.tableWidget1.setEnabled(False) def phases_vol(self): self.plot_window.show() t = linspace(60, 220, 10) p = 1.01325 # bar phase_fraction = empty_like(t) v_l = empty_like(t) v_v = empty_like(t) z_i = self.props_i['z_i'] # normalize z_i sum_z_i = sum(z_i) if sum_z_i <= 0: z_i = 1 / len(z_i) * ones(len(z_i)) z_i = z_i.tolist() elif sum_z_i != 1.0: z_i = z_i / sum_z_i z_i = z_i.to_list() else: z_i = z_i.to_list() mm_i = (self.props_i['poling_molwt'] / 1000).tolist() # kg/mol tc_i = self.props_i['poling_tc'].tolist() # K pc_i = (self.props_i['poling_pc']).tolist() # bar omega_i = self.props_i['poling_omega'].tolist() vc_i = (self.props_i['poling_vc'] * 10**-6).tolist() # m^3/mol state = State(t[0], p, z_i, mm_i, tc_i, pc_i, omega_i, 'pr') for i in range(len(t)): state.set_t(t[i]) phase_fraction[i] = state.v_f v_l[i] = state.v_l v_v[i] = state.v_v self.plot_window.ax[0].plot(t, phase_fraction) self.plot_window.ax[0].set_xlabel('T / K') self.plot_window.ax[0].set_xlabel('V / F') self.plot_window.ax[1].semilogy(t, v_l, label='v_l') self.plot_window.ax[1].semilogy(t, v_v, label='v_v') self.plot_window.ax[1].set_xlabel('T / K') self.plot_window.ax[1].set_xlabel(r'$\frac{V}{m^3 / mol}$') self.plot_window.ax[1].legend() self.plot_window.fig.tight_layout()