class TabMNConf_gui(QWidget): def __init__(self, masternode_alias=None, *args, **kwargs): QWidget.__init__(self) self.initConfigForm(masternode_alias) ###-- Compose tab2 layout mainVertical2 = QVBoxLayout() mainVertical2.setSpacing(10) mainVertical2.addWidget(self.configForm) vBox = QVBoxLayout() vBox.addStretch(1) mainVertical2.addLayout(vBox) mainVertical2.addLayout(self.footer) self.setLayout(mainVertical2) def clearConfigForm(self): self.testnetCheck.setChecked(False) self.edt_name.setText('') self.edt_masternodeIp.setText('') self.edt_masternodePort.setValue(51472) self.edt_mnPrivKey.setText('') self.edt_hwAccount.setValue(0) self.edt_address.setText('') self.edt_spath.setValue(0) self.edt_pubKey.setText('') self.edt_txid.setText('') self.edt_txidn.setValue(0) self.configForm.setTitle('New Masternode') def fillConfigForm(self, masternode): self.edt_name.setText(masternode['name']) self.edt_masternodeIp.setText(masternode['ip']) self.edt_masternodePort.setValue(masternode['port']) self.edt_mnPrivKey.setText(masternode['mnPrivKey']) self.edt_hwAccount.setValue(masternode['hwAcc']) if masternode['isTestnet'] == 1: self.testnetCheck.setChecked(True) else: self.testnetCheck.setChecked(False) self.edt_address.setText(masternode['collateral'].get('address')) self.edt_spath.setValue(masternode['collateral'].get('spath')) self.edt_pubKey.setText(masternode['collateral'].get('pubKey')) self.edt_txid.setText(masternode['collateral'].get('txid')) self.edt_txidn.setValue(masternode['collateral'].get('txidn')) self.configForm.setTitle("Edit Masternode") def initConfigForm(self, masternode_alias=None): self.configForm = QGroupBox() if not masternode_alias: self.configForm.setTitle("New Masternode") else: self.configForm.setTitle("Edit Masternode [%s]" % masternode_alias) layout = QFormLayout() layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) layout.setContentsMargins(10, 20, 10, 10) layout.setSpacing(13) ##--- ROW 1 self.edt_name = QLineEdit() self.edt_name.setToolTip( "masternode Alias.\n-- example: My Masternode 1") layout.addRow(QLabel("Name"), self.edt_name) ##--- ROW 2 line1 = QHBoxLayout() self.edt_masternodeIp = QLineEdit() self.edt_masternodeIp.setToolTip( "masternode IP address\n-- example [IPv4] 88.172.23.1\n-- example [IPv6] 2001:db8:85a3::8a2e:370:7334" ) line1.addWidget(self.edt_masternodeIp) line1.addWidget(QLabel("IP Port")) self.edt_masternodePort = QSpinBox() self.edt_masternodePort.setRange(1, 65535) self.edt_masternodePort.setValue(51472) self.edt_masternodePort.setToolTip( "remote masternode tcp port \n-- example: 51472") self.edt_masternodePort.setFixedWidth(180) line1.addWidget(self.edt_masternodePort) layout.addRow(QLabel("IP Address"), line1) ##--- ROW 3 self.edt_mnPrivKey = QLineEdit() self.edt_mnPrivKey.setToolTip( "masternode private key \n-- output of 'masternode genkey' command" ) self.btn_genKey = QPushButton("Generate") self.btn_genKey.setToolTip( "generate masternode privKey from hardware wallet") hBox2 = QHBoxLayout() hBox2.addWidget(self.edt_mnPrivKey) hBox2.addWidget(self.btn_genKey) ## Testnet check self.testnetCheck = QCheckBox() self.testnetCheck.setToolTip("check for TESTNET masternode setup") hBox2.addWidget(QLabel("testnet")) hBox2.addWidget(self.testnetCheck) layout.addRow(QLabel("MN Priv Key"), hBox2) ##--- ROW 4/5 layout.addRow(QFrame()) layout.addRow(QLabel("<em>Masternode Collateral</em>")) hBox3 = QHBoxLayout() self.edt_hwAccount = QSpinBox() self.edt_hwAccount.setFixedWidth(50) self.edt_hwAccount.setToolTip( "account number of the hardware wallet.\nIf unsure put 0") self.edt_hwAccount.setValue(0) hBox3.addWidget(self.edt_hwAccount) hBox3.addWidget(QLabel("PIVX Address")) self.edt_address = QLineEdit() self.edt_address.setToolTip("the address containing 10000 PIV") self.edt_spath = QSpinBox() self.edt_spath.setToolTip("BIP44 spath for the address") self.edt_spath.setFixedWidth(75) self.edt_spath.setValue(0) self.btn_spathToAddress = QPushButton("<<") self.btn_spathToAddress.setToolTip( "find address and public key of given account/spath_id") self.btn_addressToSpath = QPushButton(">>") self.btn_addressToSpath.setToolTip( "find spath_id and public key of given account/address") hBox3.addWidget(self.edt_address) hBox3.addWidget(self.btn_spathToAddress) hBox3.addWidget(self.btn_addressToSpath) hBox3.addWidget(QLabel("spath_id")) hBox3.addWidget(self.edt_spath) layout.addRow(QLabel("Account HW"), hBox3) ##--- ROW 6 self.edt_pubKey = QLineEdit() self.edt_pubKey.setToolTip("public key corresponding to address") self.edt_pubKey.setEnabled(False) layout.addRow(QLabel("Public Key"), self.edt_pubKey) ##--- ROW 7 hBox5 = QHBoxLayout() self.btn_findTxid = QPushButton("Lookup") self.btn_findTxid.setToolTip("look for txid and txidn on explorer") hBox5.addWidget(self.btn_findTxid) hBox5.addWidget(QLabel("/")) self.btn_editTxid = QPushButton("Edit") self.btn_editTxid.setToolTip("edit txid and txidn manually") hBox5.addWidget(self.btn_editTxid) hBox5.addWidget(QLabel("txid")) self.edt_txid = QLineEdit() self.edt_txid.setToolTip("txid for the collateral") self.edt_txid.setEnabled(False) hBox5.addWidget(self.edt_txid) hBox5.addWidget(QLabel("txidn")) self.edt_txidn = QSpinBox() self.edt_txidn.setFixedWidth(50) self.edt_txidn.setToolTip("txidn for the collateral") self.edt_txidn.setEnabled(False) hBox5.addWidget(self.edt_txidn) layout.addRow(QLabel("Transaction"), hBox5) ##--- Set Layout self.configForm.setLayout(layout) ##--- Footer self.footer = QHBoxLayout() self.footer.addStretch(1) self.btn_cancelMNConf = QPushButton('Cancel') self.btn_cancelMNConf.setToolTip( "cancel changes and go back to main list") self.footer.addWidget(self.btn_cancelMNConf) self.btn_saveMNConf = QPushButton('Save') self.btn_saveMNConf.setToolTip( "save configuration and go back to main list") self.footer.addWidget(self.btn_saveMNConf)
class TwoPlots(QWidget): ''' Widget to plot 2 curves x(t) & y(t), or Vx(t) & Vy(t)''' Ylabels = { "position": ("X [pixel]", "Y [pixel]"), "velocity": ("VX [pixel/s]", "VY [pixel/s]"), "position_mm": ("X [mm]", "Y [mm]"), "velocity_mm": ("VX [mm/s]", "VY [mm/s]") } CurveLabels = { "position": ("X(t) [{}]", "Y(t) [{}]"), "velocity": ("VX(t) {}", "VY(t) {}") } def __init__(self, mainWindow, quantity): # appel du constructeur de la classe de base : QWidget.__init__(self, mainWindow) self.mw = mainWindow # la fenêtre de l'application principale # Attributs (objets persistants) self.__quantity = quantity # "position" or "velocity" self.__data1 = None # data for the first plot self.__data2 = None # data for tthe second self.__figure = None # figure tracé self.__axes1 = None # système d'axes tracé 1 self.__axes2 = None # système d'axes tracé 2 self.__canvas = None # pour le tracé matplot.lib self.__toolbar = None # barre d'outils tracé self.__time = None # abcissa values for plot self.__xlim = None # xmin, xmay tracé self.__xlabel = None # étiquette axe X (n° image ou temps [s]) self.__ylim1 = None # ymin, ymax tracé x(t) self.__ylim2 = None # ymin, ymax tracé y(t) self.btn_imageSize = QRadioButton("ImageSize", self) self.btn_autoSize = QRadioButton("AutoSize", self) self.btn_smooth_Vx = QCheckBox("Lissage Vx", self) self.btn_smooth_Vy = QCheckBox("Lissage Vy", self) self.x_mav_nb_pts = QSpinBox(parent=self) # X velocity moving average self.y_mav_nb_pts = QSpinBox(parent=self) # Y velocity moving average self.__initUI() # Initialisation de l'interface utilisateur def __initUI(self): if self.__quantity == "position": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(False) w.setEnabled(False) group = QButtonGroup(self) group.addButton(self.btn_imageSize) group.addButton(self.btn_autoSize) self.btn_imageSize.toggled.connect(self.__ImageSizePlotXYLim) self.btn_imageSize.setEnabled(False) texte = "Tracé avec les bornes min et max de l'image" self.btn_imageSize.setStatusTip(texte) self.btn_imageSize.setChecked(True) self.btn_autoSize.toggled.connect(self.__AutoSizePlotXYLim) self.btn_autoSize.setEnabled(False) texte = "Tracé avec les bornes min et max de la " texte += "trajectoire calculée" self.btn_autoSize.setStatusTip(texte) elif self.__quantity == "velocity": for w in (self.btn_imageSize, self.btn_autoSize): w.setVisible(False) w.setEnabled(False) self.btn_smooth_Vx.stateChanged.connect(self.__smooth_Vx_wanted) self.btn_smooth_Vy.stateChanged.connect(self.__smooth_Vy_wanted) self.x_mav_nb_pts.setEnabled(False) self.y_mav_nb_pts.setEnabled(False) self.x_mav_nb_pts.setRange(3, 100) self.y_mav_nb_pts.setRange(3, 100) self.x_mav_nb_pts.setSingleStep(2) self.y_mav_nb_pts.setSingleStep(2) self.x_mav_nb_pts.valueChanged.connect(self.__smooth_vX) self.y_mav_nb_pts.valueChanged.connect(self.__smooth_vY) vbox = QVBoxLayout() self.setLayout(vbox) # Ligne 1 : tracé de l'image self.setLayout(vbox) self.__figure = Figure() self.__axes1 = self.__figure.add_subplot(211) self.__axes2 = self.__figure.add_subplot(212) self.__figure.subplots_adjust(left=0.120, right=0.99, bottom=0.11, top=0.98) self.__canvas = FigureCanvas(self.__figure) self.__toolbar = NavigationToolbar(self.__canvas, self) #self.__toolbar.setMinimumWidth(450) vbox.addWidget(self.__canvas) hbox = QHBoxLayout() hbox.addWidget(self.__toolbar) hbox.addStretch() if self.__quantity == "position": hbox.addWidget(self.btn_imageSize) hbox.addWidget(self.btn_autoSize) elif self.__quantity == "velocity": vb = QVBoxLayout() hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vx) hb.addWidget(self.x_mav_nb_pts) vb.addLayout(hb) hb = QHBoxLayout() hb.addWidget(self.btn_smooth_Vy) hb.addWidget(self.y_mav_nb_pts) vb.addLayout(hb) hbox.addLayout(vb) vbox.addLayout(hbox) def reset(self): if self.__quantity == "velocity": for w in (self.btn_smooth_Vx, self.btn_smooth_Vy, self.x_mav_nb_pts, self.y_mav_nb_pts): w.setVisible(True) w.setEnabled(True) self.x_mav_nb_pts.setValue(self.x_mav_nb_pts.minimum()) self.y_mav_nb_pts.setValue(self.y_mav_nb_pts.minimum()) self.btn_smooth_Vx.setCheckState(Qt.Unchecked) self.btn_smooth_Vy.setCheckState(Qt.Unchecked) def __smooth_Vx_wanted(self, checked): if checked: self.x_mav_nb_pts.setEnabled(True) else: self.x_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_Vy_wanted(self, checked): if checked: self.y_mav_nb_pts.setEnabled(True) else: self.y_mav_nb_pts.setEnabled(False) self.Plot() def __smooth_vX(self, nb_pts): if self.btn_smooth_Vx.isChecked(): self.Plot() else: pass def __smooth_vY(self, nb_pts): if self.btn_smooth_Vy.isChecked(): self.Plot() else: pass def __compute_velocity(self, U, plot_id): """Computes the velocity with the centered finite difference of order 1 : V[i] = (U[i+1] - U[i-1])/(T[i+1] - T[i-1]) for i in [1,N-1] V[0] = (U[1] - U[0])/(T[1] - T[0]) V[-1] = (U[-1] - U[-2])/(T[-1] - T[-2]) """ V = U.copy() T = self.__time V[0] = (U[1] - U[0]) / (T[1] - T[0]) V[-1] = (U[-1] - U[-2]) / (T[-1] - T[-2]) V[1:-1] = (U[2:] - U[:-2]) / (T[2:] - T[:-2]) if plot_id == "x": if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() V = self.__smooth_data(V, N) elif plot_id == "y": if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() V = self.__smooth_data(V, N) return V def __smooth_data(self, U, nb_pts): """Computes the nb_pts moving average on U.""" N = nb_pts V = U.copy() V.fill(np.nan) mav = deque(maxlen=N) # initialize the mav (moving average) for e in U[:N]: mav.append(e) # move! index, count = N // 2, 0 while count < V.shape[0] - N: V[index] = np.mean(mav) mav.append(U[N + count]) count += 1 index += 1 return V def Plot(self): target_pos = self.mw.target_pos if target_pos is None: return else: self.__data1, self.__data2, I = target_pos scale = self.mw.imageTab.pix_to_mm_coeff if self.__quantity == "position": self.btn_imageSize.setEnabled(True) self.btn_autoSize.setEnabled(True) elif self.__quantity == "velocity": pass # Effacement automatiqe si demandé à chaque nouveau tracé : if self.mw.flags["autoClearTraj"]: if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() # Récupération du nom de l'alagorithme de traitement : algo = self.mw.imageTab.btn_algo.currentText() # Récupération de la valeur de FP (Frame per seconde) pour calcul # du pas de temps et des abscisses : deltaT = None if self.mw.imageTab.video_FPS is not None: deltaT = 1. / self.mw.imageTab.video_FPS self.__time = np.array(I) * deltaT self.__xlabel = "temps [s]" else: self.__time = np.array(I) self.__xlabel = "image #" if self.__quantity == "velocity": if deltaT is not None: self.__data1 = self.__compute_velocity(self.__data1, "x") self.__data2 = self.__compute_velocity(self.__data2, "y") if self.btn_smooth_Vx.isChecked(): N = self.x_mav_nb_pts.value() if self.btn_smooth_Vy.isChecked(): N = self.y_mav_nb_pts.value() self.__AutoSizePlotXYLim() else: self.__data1, self.__data2 = None, None self.mw.target_veloc = np.array([self.__data1, self.__data2]) else: self.__ImageSizePlotXYLim() if self.__data1 is None or self.__data2 is None: return curveLabelX, curveLabelY = TwoPlots.CurveLabels[self.__quantity] if self.__quantity == "position": Xlabel, Ylabel = curveLabelX.format(algo), curveLabelY.format(algo) else: Xlabel, Ylabel = curveLabelX.format(""), curveLabelY.format("") color = 'b' if self.mw.target_RGB is None else self.mw.target_RGB / 255 # tracé de courbe x(t) self.__axes1.plot(self.__time, self.__data1 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Xlabel) self.__axes1.grid(True) #self.__axes1.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(-0.1, 1.1), loc='upper left') self.__axes1.legend(loc='best', fontsize=10) # tracé de courbe y(t) self.__axes2.plot(self.__time, self.__data2 * scale, color=color, marker='o', markersize=2, linewidth=.4, label=Ylabel) self.__axes2.grid(True) #self.__axes2.legend(fontsize=9, framealpha=0.7, # bbox_to_anchor=(1.1, 1.1), loc='upper right') self.__axes2.legend(loc='best', fontsize=10) self.__canvas.draw() def __AutoSizePlotXYLim(self): if self.mw.target_pos is None: return y1label, y2label = TwoPlots.Ylabels[self.__quantity] self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) scale = self.mw.imageTab.pix_to_mm_coeff if not self.btn_smooth_Vx.isChecked(): self.__ylim1 = np.array( [np.nanmin(self.__data1), np.nanmax(self.__data1)]) * scale offset = (self.__ylim1[1] - self.__ylim1[0]) / 10 self.__ylim1 += np.array([-offset, offset]) else: #self.__ylim1 = self.__axes1.get_ylim() pass if not self.btn_smooth_Vy.isChecked(): self.__ylim2 = np.array( [np.nanmin(self.__data2), np.nanmax(self.__data2)]) * scale offset = (self.__ylim2[1] - self.__ylim2[0]) / 10 self.__ylim2 += np.array([-offset, offset]) else: #self.__ylim2 = self.__axes2.get_ylim() pass if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def __ImageSizePlotXYLim(self): if self.mw.target_pos is None: return scale = self.mw.imageTab.pix_to_mm_coeff y1label, y2label = TwoPlots.Ylabels[self.__quantity] w, h = self.mw.imageTab.video_size self.__xlim = np.array( [np.nanmin(self.__time), np.nanmax(self.__time)]) self.__ylim1 = np.array([0, w - 1], dtype=float) * scale self.__ylim2 = np.array([0, h - 1], dtype=float) * scale if self.mw.imageTab.valid_scale: y1label, y2label = TwoPlots.Ylabels[self.__quantity + "_mm"] self.__axes1.set_xlim(*self.__xlim) self.__axes2.set_xlim(*self.__xlim) self.__axes1.set_ylim(*self.__ylim1) self.__axes2.set_ylim(*self.__ylim2) self.__axes1.set_ylabel(y1label) self.__axes2.set_ylabel(y2label) self.__axes2.set_xlabel(self.__xlabel) self.__canvas.draw() def ClearAxes(self): if self.__axes1 is not None: self.__axes1.clear() if self.__axes2 is not None: self.__axes2.clear() self.__canvas.draw()
class ConfigWidget(QWidget): def __init__(self, plugin_action): QWidget.__init__(self) self.plugin_action = plugin_action layout = QVBoxLayout(self) self.setLayout(layout) self.help_anchor = "configuration" title_layout = ImageTitleLayout(self, 'images/icon.png', 'Sony Utilities Options') layout.addLayout(title_layout) # c = plugin_prefs[STORE_NAME] library_config = get_library_config(self.plugin_action.gui.current_db) custom_column_group = QGroupBox(_('Custom Columns'), self) layout.addWidget(custom_column_group ) options_layout = QGridLayout() custom_column_group.setLayout(options_layout) avail_text_columns = self.get_text_custom_columns() avail_number_columns = self.get_number_custom_columns() avail_rating_columns = self.get_rating_custom_columns() avail_date_columns = self.get_date_custom_columns() # debug_print("avail_rating_columns=", avail_rating_columns) # debug_print("default columns=", self.plugin_action.gui.library_view.model().orig_headers) current_Location_column = library_config.get(KEY_CURRENT_LOCATION_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_CURRENT_LOCATION_CUSTOM_COLUMN]) precent_read_column = library_config.get(KEY_PERCENT_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_PERCENT_READ_CUSTOM_COLUMN]) rating_column = library_config.get(KEY_RATING_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_RATING_CUSTOM_COLUMN]) last_read_column = library_config.get(KEY_LAST_READ_CUSTOM_COLUMN, DEFAULT_LIBRARY_VALUES[KEY_LAST_READ_CUSTOM_COLUMN]) store_on_connect = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_ON_CONNECT) prompt_to_store = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_PROMPT_TO_STORE) store_if_more_recent = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_STORE_IF_MORE_RECENT) do_not_store_if_reopened = get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_DO_NOT_STORE_IF_REOPENED) # do_check_for_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_UPDATE_CHECK) # do_early_firmware_updates = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_DO_EARLY_FIRMWARE_CHECK) # self.update_check_last_time = get_plugin_pref(UPDATE_OPTIONS_STORE_NAME, KEY_LAST_FIRMWARE_CHECK_TIME) do_daily_backup = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_DO_DAILY_BACKUP) dest_directory = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_DEST_DIRECTORY) copies_to_keep = get_plugin_pref(BACKUP_OPTIONS_STORE_NAME, KEY_BACKUP_COPIES_TO_KEEP) # debug_print("current_Location_column=%s, precent_read_column=%s, rating_column=%s" % (current_Location_column, precent_read_column, rating_column)) current_Location_label = QLabel(_('Current Reading Location Column:'), self) current_Location_label.setToolTip(_("Select a custom column to store the current reading location. The column type must be 'text'. Leave this blank if you do not want to store or restore the current reading location.")) self.current_Location_combo = CustomColumnComboBox(self, avail_text_columns, current_Location_column) current_Location_label.setBuddy(self.current_Location_combo) options_layout.addWidget(current_Location_label, 0, 0, 1, 1) options_layout.addWidget(self.current_Location_combo, 0, 1, 1, 1) percent_read_label = QLabel(_('Percent Read Column:'), self) percent_read_label.setToolTip(_("Column used to store the current percent read. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the percentage read.")) self.percent_read_combo = CustomColumnComboBox(self, avail_number_columns, precent_read_column) percent_read_label.setBuddy(self.percent_read_combo) options_layout.addWidget(percent_read_label, 2, 0, 1, 1) options_layout.addWidget(self.percent_read_combo, 2, 1, 1, 1) rating_label = QLabel(_('Rating Column:'), self) rating_label.setToolTip(_("Column used to store the rating. The column type must be a 'integer'. Leave this blank if you do not want to store or restore the rating.")) self.rating_combo = CustomColumnComboBox(self, avail_rating_columns, rating_column) rating_label.setBuddy(self.rating_combo) options_layout.addWidget(rating_label, 3, 0, 1, 1) options_layout.addWidget(self.rating_combo, 3, 1, 1, 1) last_read_label = QLabel(_('Last Read Column:'), self) last_read_label.setToolTip(_("Column used to store when the book was last read. The column type must be a 'Date'. Leave this blank if you do not want to store the last read timestamp.")) self.last_read_combo = CustomColumnComboBox(self, avail_date_columns, last_read_column) last_read_label.setBuddy(self.last_read_combo) options_layout.addWidget(last_read_label, 4, 0, 1, 1) options_layout.addWidget(self.last_read_combo, 4, 1, 1, 1) auto_store_group = QGroupBox(_('Store on connect'), self) layout.addWidget(auto_store_group ) options_layout = QGridLayout() auto_store_group.setLayout(options_layout) self.store_on_connect_checkbox = QCheckBox(_("Store current bookmarks on connect"), self) self.store_on_connect_checkbox.setToolTip(_("When this is checked, the library will be updated with the current bookmark for all books on the device.")) self.store_on_connect_checkbox.setCheckState(Qt.Checked if store_on_connect else Qt.Unchecked) self.store_on_connect_checkbox.clicked.connect(self.store_on_connect_checkbox_clicked) options_layout.addWidget(self.store_on_connect_checkbox, 0, 0, 1, 3) self.prompt_to_store_checkbox = QCheckBox(_("Prompt to store any changes"), self) self.prompt_to_store_checkbox.setToolTip(_("Enable this to be prompted to save the changed bookmarks after an automatic store is done.")) self.prompt_to_store_checkbox.setCheckState(Qt.Checked if prompt_to_store else Qt.Unchecked) self.prompt_to_store_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.prompt_to_store_checkbox, 1, 0, 1, 1) self.store_if_more_recent_checkbox = QCheckBox(_("Only if more recent"), self) self.store_if_more_recent_checkbox.setToolTip(_("Only store the reading position if the last read timestamp on the device is more recent than in the library.")) self.store_if_more_recent_checkbox.setCheckState(Qt.Checked if store_if_more_recent else Qt.Unchecked) self.store_if_more_recent_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.store_if_more_recent_checkbox, 1, 1, 1, 1) self.do_not_store_if_reopened_checkbox = QCheckBox(_("Not if finished in library"), self) self.do_not_store_if_reopened_checkbox.setToolTip(_("Do not store the reading position if the library has the book as finished. This is if the percent read is 100%.")) self.do_not_store_if_reopened_checkbox.setCheckState(Qt.Checked if do_not_store_if_reopened else Qt.Unchecked) self.do_not_store_if_reopened_checkbox.setEnabled(store_on_connect) options_layout.addWidget(self.do_not_store_if_reopened_checkbox, 1, 2, 1, 1) # update_options_group = QGroupBox(_('Firmware Update Options'), self) # layout.addWidget(update_options_group) # options_layout = QGridLayout() # update_options_group.setLayout(options_layout) # # self.do_update_check = QCheckBox(_('Check for Sony firmware updates daily?'), self) # self.do_update_check.setToolTip(_('If this is selected the plugin will check for Sony firmware updates when your Sony device is plugged in, once per 24-hour period.')) # self.do_update_check.setCheckState(Qt.Checked if do_check_for_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_update_check, 0, 0, 1, 1) # # self.do_early_firmware_check = QCheckBox(_('Use early firmware adopter affiliate?'), self) # self.do_early_firmware_check.setToolTip(_('WARNING: THIS OPTION RISKS DOWNLOADING THE WRONG FIRMWARE FOR YOUR DEVICE! YOUR DEVICE MAY NOT FUNCTION PROPERLY IF THIS HAPPENS! Choose this option to attempt to download Sony firmware updates before they are officially available for your device.')) # self.do_early_firmware_check.setCheckState(Qt.Checked if do_early_firmware_updates else Qt.Unchecked) # options_layout.addWidget(self.do_early_firmware_check, 0, 1, 1, 1) backup_options_group = QGroupBox(_('Device Database Backup'), self) layout.addWidget(backup_options_group) options_layout = QGridLayout() backup_options_group.setLayout(options_layout) self.do_daily_backp_checkbox = QCheckBox(_('Backup the device database daily'), self) self.do_daily_backp_checkbox.setToolTip(_('If this is selected the plugin will backup the device database the first time it is connected each day.')) self.do_daily_backp_checkbox.setCheckState(Qt.Checked if do_daily_backup else Qt.Unchecked) self.do_daily_backp_checkbox.clicked.connect(self.do_daily_backp_checkbox_clicked) options_layout.addWidget(self.do_daily_backp_checkbox, 0, 0, 1, 3) self.dest_directory_label = QLabel(_("Destination:"), self) self.dest_directory_label.setToolTip(_("Select the destination the annotations files are to be backed up in.")) self.dest_directory_edit = QLineEdit(self) self.dest_directory_edit.setMinimumSize(150, 0) self.dest_directory_edit.setText(dest_directory) self.dest_directory_label.setBuddy(self.dest_directory_edit) self.dest_pick_button = QPushButton(_("..."), self) self.dest_pick_button.setMaximumSize(24, 20) self.dest_pick_button.clicked.connect(self._get_dest_directory_name) options_layout.addWidget(self.dest_directory_label, 1, 0, 1, 1) options_layout.addWidget(self.dest_directory_edit, 1, 1, 1, 1) options_layout.addWidget(self.dest_pick_button, 1, 2, 1, 1) self.copies_to_keep_checkbox = QCheckBox(_('Copies to keep'), self) self.copies_to_keep_checkbox.setToolTip(_("Select this to limit the number of backup kept. If not set, the backup files must be manually deleted.")) self.copies_to_keep_spin = QSpinBox(self) self.copies_to_keep_spin.setMinimum(2) self.copies_to_keep_spin.setToolTip(_("The number of backup copies of the database to keep. The minimum is 2.")) options_layout.addWidget(self.copies_to_keep_checkbox, 1, 3, 1, 1) options_layout.addWidget(self.copies_to_keep_spin, 1, 4, 1, 1) self.copies_to_keep_checkbox.clicked.connect(self.copies_to_keep_checkbox_clicked) if copies_to_keep == -1: self.copies_to_keep_checkbox.setCheckState(not Qt.Checked) else: self.copies_to_keep_checkbox.setCheckState(Qt.Checked) self.copies_to_keep_spin.setProperty('value', copies_to_keep) self.do_daily_backp_checkbox_clicked(do_daily_backup) other_options_group = QGroupBox(_('Other Options'), self) layout.addWidget(other_options_group ) options_layout = QGridLayout() other_options_group.setLayout(options_layout) library_default_label = QLabel(_('&Library Button default:'), self) library_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.library_default_combo = KeyComboBox(self, self.plugin_action.library_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_LIBRARY))) library_default_label.setBuddy(self.library_default_combo) options_layout.addWidget(library_default_label, 0, 0, 1, 1) options_layout.addWidget(self.library_default_combo, 0, 1, 1, 2) device_default_label = QLabel(_('&Device Button default:'), self) device_default_label.setToolTip(_('If plugin is placed as a toolbar button, choose a default action when clicked on')) self.device_default_combo = KeyComboBox(self, self.plugin_action.device_actions_map, unicode(get_plugin_pref(COMMON_OPTIONS_STORE_NAME, KEY_BUTTON_ACTION_DEVICE))) device_default_label.setBuddy(self.device_default_combo) options_layout.addWidget(device_default_label, 1, 0, 1, 1) options_layout.addWidget(self.device_default_combo, 1, 1, 1, 2) keyboard_shortcuts_button = QPushButton(_('Keyboard shortcuts...'), self) keyboard_shortcuts_button.setToolTip(_('Edit the keyboard shortcuts associated with this plugin')) keyboard_shortcuts_button.clicked.connect(self.edit_shortcuts) layout.addWidget(keyboard_shortcuts_button) layout.addStretch(1) def store_on_connect_checkbox_clicked(self, checked): self.prompt_to_store_checkbox.setEnabled(checked) self.store_if_more_recent_checkbox.setEnabled(checked) self.do_not_store_if_reopened_checkbox.setEnabled(checked) def do_daily_backp_checkbox_clicked(self, checked): self.dest_directory_edit.setEnabled(checked) self.dest_pick_button.setEnabled(checked) self.dest_directory_label.setEnabled(checked) self.copies_to_keep_checkbox.setEnabled(checked) self.copies_to_keep_checkbox_clicked(checked and self.copies_to_keep_checkbox.checkState() == Qt.Checked) def copies_to_keep_checkbox_clicked(self, checked): self.copies_to_keep_spin.setEnabled(checked) # Called by Calibre before save_settings def validate(self): # import traceback # traceback.print_stack() debug_print('BEGIN Validate') valid = True # Only save if we were able to get data to avoid corrupting stored data # if self.do_daily_backp_checkbox.checkState() == Qt.Checked and not len(self.dest_directory_edit.text()): # error_dialog(self, 'No destination directory', # 'If the automatic device backup is set, there must be a destination directory.', # show=True, show_copy_button=False) # valid = False debug_print('END Validate, status = %s' % valid) return valid def save_settings(self): new_prefs = {} new_prefs[KEY_BUTTON_ACTION_DEVICE] = unicode(self.device_default_combo.currentText()) new_prefs[KEY_BUTTON_ACTION_LIBRARY] = unicode(self.library_default_combo.currentText()) new_prefs[KEY_STORE_ON_CONNECT] = self.store_on_connect_checkbox.checkState() == Qt.Checked new_prefs[KEY_PROMPT_TO_STORE] = self.prompt_to_store_checkbox.checkState() == Qt.Checked new_prefs[KEY_STORE_IF_MORE_RECENT] = self.store_if_more_recent_checkbox.checkState() == Qt.Checked new_prefs[KEY_DO_NOT_STORE_IF_REOPENED] = self.do_not_store_if_reopened_checkbox.checkState() == Qt.Checked plugin_prefs[COMMON_OPTIONS_STORE_NAME] = new_prefs new_update_prefs = {} # new_update_prefs[KEY_DO_UPDATE_CHECK] = self.do_update_check.checkState() == Qt.Checked # new_update_prefs[KEY_DO_EARLY_FIRMWARE_CHECK] = self.do_early_firmware_check.checkState() == Qt.Checked # new_update_prefs[KEY_LAST_FIRMWARE_CHECK_TIME] = self.update_check_last_time plugin_prefs[UPDATE_OPTIONS_STORE_NAME] = new_update_prefs backup_prefs = {} backup_prefs[KEY_DO_DAILY_BACKUP] = self.do_daily_backp_checkbox.checkState() == Qt.Checked backup_prefs[KEY_BACKUP_DEST_DIRECTORY] = unicode(self.dest_directory_edit.text()) backup_prefs[KEY_BACKUP_COPIES_TO_KEEP] = int(unicode(self.copies_to_keep_spin.value())) if self.copies_to_keep_checkbox.checkState() == Qt.Checked else -1 plugin_prefs[BACKUP_OPTIONS_STORE_NAME] = backup_prefs db = self.plugin_action.gui.current_db library_config = get_library_config(db) library_config[KEY_CURRENT_LOCATION_CUSTOM_COLUMN] = self.current_Location_combo.get_selected_column() library_config[KEY_PERCENT_READ_CUSTOM_COLUMN] = self.percent_read_combo.get_selected_column() library_config[KEY_RATING_CUSTOM_COLUMN] = self.rating_combo.get_selected_column() library_config[KEY_LAST_READ_CUSTOM_COLUMN] = self.last_read_combo.get_selected_column() set_library_config(db, library_config) def get_number_custom_columns(self): column_types = ['float','int'] return self.get_custom_columns(column_types) def get_rating_custom_columns(self): column_types = ['rating','int'] custom_columns = self.get_custom_columns(column_types) ratings_column_name = self.plugin_action.gui.library_view.model().orig_headers['rating'] custom_columns['rating'] = {'name': ratings_column_name} return custom_columns def get_text_custom_columns(self): column_types = ['text'] return self.get_custom_columns(column_types) def get_date_custom_columns(self): column_types = ['datetime'] return self.get_custom_columns(column_types) def get_custom_columns(self, column_types): custom_columns = self.plugin_action.gui.library_view.model().custom_columns available_columns = {} for key, column in custom_columns.iteritems(): typ = column['datatype'] if typ in column_types and not column['is_multiple']: available_columns[key] = column return available_columns def help_link_activated(self, url): self.plugin_action.show_help(anchor="configuration") def edit_shortcuts(self): d = KeyboardConfigDialog(self.plugin_action.gui, self.plugin_action.action_spec[0]) if d.exec_() == d.Accepted: self.plugin_action.gui.keyboard.finalize() def _get_dest_directory_name(self): path = choose_dir(self, 'backup annotations destination dialog','Choose destination directory') if path: self.dest_directory_edit.setText(path)
class ImageDisplay(QWidget): video_infos = ['vidéo : {}','nb frames : {}','taille : {}','FPS : {}','durée : {:.2f} sec'] video_keys = ['videoname','nframes','size','fps','duration'] algo_traj = ['barycentre','minmax'] def __init__(self, mainWindow): # acall the base class constructor: super().__init__(mainWindow) self.mw = mainWindow # Attributs (objets persistants) self.img_lbl = QLabel(self) # to display the current image self.img_lbl.installEventFilter(self) # filter to catch evenements self.selectTargetRect = None # display a rectangle to show teh target color selection self.rubberBand = QRubberBand(QRubberBand.Line, self) # Boutons pour avancer/reculer self.btn_prev = QPushButton(QIcon("icones/go-prev.png"), "", self) self.btn_next = QPushButton(QIcon("icones/go-next.png"), "", self) self.btn_first = QPushButton(QIcon("icones/go-first.png"), "", self) self.btn_last = QPushButton(QIcon("icones/go-last.png"), "", self) self.btn_traj = QPushButton(QIcon("icones/extract.png"), "Extraire...", self) self.btn_clear = QPushButton(QIcon("icones/clear.png"), "Effacer courbes...", self) self.btn_exportCSV = QPushButton(QIcon("icones/exportCSV.png"), "Export CSV", self) self.btn_algo = QComboBox(self) self.image_index = QLabel(self) # widget QSpinBox self.images_step = QSpinBox(parent=self) self.images_firstRank = QSpinBox(parent=self) self.images_lastRank = QSpinBox(parent=self) # QLabel to display the target color self.target_color_label = QLabel("target color",parent=self) self.picked_color = QLabel(self) self.video_path = None # Chemin de la dernière vidéo self.images_dir = None # Dossier contenant les images self.__img_idx = None # Rang de l'image affichée self.img_path = None # nom du chemin de l'image courante self.nb_img = None # nombre d'images self.video_name = None # nom de la video ("aaaaaa.mp4") self.video_nframes = None # nombre d'images dans la video self.video_size = None # taille (width, height) des images self.video_FPS = None # nombre d'images par seconde self.video_duration = None # durée de la video en secondes self.videoLabels = [] # liste de QLabel contenant les infos vidéo self.dico_video = {} # dictionnaire des méta-données self.dico_unit = {} # dictionary "pixels", "mm" self.scale_pixel = None # nombre de pixels pour conversion en mm self.scale_mm = None # nbre de mm pour scale_pixel self.valid_scale = False # données de l'échelle valides ou pas self.pix_to_mm_coeff= 1. # le coefficient de converion pixels -> mm self.dicoScale = {} # dictionnaire des QWidget d'info scale self.lbl_epsilon = None # label epsilon self.epsi_spin = None # boite de choix de epsilon # créer l'onglet de visualisation image """ self.__initUI() self.scaleInfoVisible(False) self.__epsilonVisible(False) def __initUI(self): # Onglet "Visualisation images" vbox = QVBoxLayout() # Ligne 1 : extraction trajec self.picked_color.setFrameStyle(QFrame.StyledPanel | QFrame.Plain); line1 = QHBoxLayout() line1.addStretch(1) line1.addWidget(self.btn_algo) line1.addWidget(self.btn_traj) line1.addWidget(self.target_color_label) line1.addWidget(self.picked_color) line1.addWidget(self.btn_clear) line1.addWidget(self.btn_exportCSV) line1.addStretch(1) # Ligne 2 : infos video + visu image line2 = QHBoxLayout() # boîte d'infos sur la vidéo infoVBox = QVBoxLayout() for _ in ImageDisplay.video_infos: label = QLabel(self) label.setFrameStyle(QFrame.StyledPanel | QFrame.Plain); infoVBox.addWidget(label) self.videoLabels.append(label) infoVBox.addStretch() widget = QLabel("Conversion pixels -> mm", self) self.dicoScale['Pixels-mm'] = widget infoVBox.addWidget(widget) grid = QGridLayout() infoVBox.addLayout(grid) widget = QLabel("pixels ",self) self.dicoScale['pixels'] = widget grid.addWidget(widget,1,1) self.scale_pixel = QLineEdit(self) self.dicoScale['pixelsForMM'] = self.scale_pixel grid.addWidget(self.scale_pixel,1,2) widget = QLabel("millimètres ",self) self.dicoScale['millimeters'] = widget grid.addWidget(widget,2,1) self.scale_mm = QLineEdit(self) self.dicoScale['mmForPixels'] = self.scale_mm grid.addWidget(self.scale_mm,2,2) self.lbl_epsilon = QLabel("Epsilon ",self) grid.addWidget(self.lbl_epsilon,5,1) self.epsi_spin = QSpinBox(self) self.epsi_spin.setRange(1,50) self.epsi_spin.setSingleStep(1) self.epsi_spin.setValue(10) grid.addWidget(self.epsi_spin,5,2) infoVBox.addStretch() line2.addLayout(infoVBox) line2.addStretch(1) line2.addWidget(self.img_lbl) # le QLabel por afficher l'image line2.addStretch(1) # line 3 : navigation boutons self.image_index.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.image_index.setText(" ") line3 = QHBoxLayout() line3.addStretch(1) line3.addWidget(self.btn_first) line3.addWidget(self.btn_prev) line3.addWidget(self.image_index) line3.addWidget(self.btn_next) line3.addWidget(self.btn_last) line3.addStretch(1) # line 4 : first , step, last image selection line4 = QHBoxLayout() line4.addStretch(1) line4.addWidget(self.images_firstRank) line4.addWidget(self.images_step) line4.addWidget(self.images_lastRank) line4.addStretch(1) vbox.addLayout(line1) vbox.addStretch(1) vbox.addLayout(line2) vbox.addStretch(1) vbox.addLayout(line3) vbox.addLayout(line4) self.setLayout(vbox) self.buttonsState() self.__buttonsConnect() self.__setVideoLabelVisible(False) def __setVideoLabelVisible(self, state): for label in self.videoLabels: label.setVisible(state) def __buttonsConnect(self): self.btn_traj.clicked.connect(self.extract_trajectoire) self.btn_clear.clicked.connect(self.mw.clearPlots) self.btn_exportCSV.clicked.connect(self.mw.ExportCSV) self.btn_prev.clicked.connect(self.prev_image) self.btn_next.clicked.connect(self.next_image) self.btn_first.clicked.connect(self.first_image) self.btn_last.clicked.connect(self.last_image) self.images_step.valueChanged.connect(self.__step_changed) self.images_firstRank.valueChanged.connect(self.__first_rank_changed) self.images_lastRank.valueChanged.connect(self.__last_rank_changed) def buttonsState(self, importCSV=False): self.btn_traj.setEnabled(False) self.picked_color.setText("X") self.picked_color.setEnabled(False) self.btn_traj.setStatusTip('Extrait la trajectoire de la cible'+ 'dont la couleur a été choisie') self.target_color_label.setEnabled(False) self.picked_color.setStyleSheet('background-color : rgb(255, 255, 255)') self.btn_clear.setEnabled(False) self.btn_clear.setStatusTip('Nettoye tous les tracés des onglets'+ '<trajectoire> et <X(t), Y(t)>') self.btn_exportCSV.setEnabled(False) texte = "Export des données dans un fichier CSV" self.btn_exportCSV.setStatusTip(texte) if not importCSV: self.btn_algo.addItems(ImageDisplay.algo_traj) self.btn_algo.setEnabled(False) self.btn_prev.setEnabled(False) self.btn_prev.setStatusTip("affiche l'image précédente") self.btn_next.setEnabled(False) self.btn_next.setStatusTip("affiche l'image suivante") self.btn_first.setEnabled(False) self.btn_first.setStatusTip("affiche la première image à traiter") self.btn_last.setEnabled(False) self.btn_last.setStatusTip("affiche la dernière image à traiter") # SpinBoxes parameters: self.images_step.setRange(1,1000) self.images_step.setSingleStep(1) self.images_step.setValue(1) self.images_step.setPrefix("step: ") self.images_step.setEnabled(False) self.images_step.setStatusTip("Fixe le pas pour passer d'une image à l'autre") self.images_firstRank.setRange(1,1000) self.images_firstRank.setSingleStep(1) self.images_firstRank.setValue(1) self.images_firstRank.setPrefix("first: ") self.images_firstRank.setEnabled(False) self.images_firstRank.setStatusTip("Fixe le rang de la première image à traiter") self.images_lastRank.setRange(1,10000) self.images_lastRank.setSingleStep(1) self.images_lastRank.setValue(1) self.images_lastRank.setPrefix("last: ") self.images_lastRank.setEnabled(False) self.images_lastRank.setStatusTip("Fixe le rang de la dernière image à traiter") def __first_rank_changed(self, val): if self.img_idx is None: return if self.img_idx < val: self.img_idx = val self.show_image() def __last_rank_changed(self, val): if self.img_idx is None: return if self.img_idx > val: self.img_idx = val self.show_image() def __step_changed(self, val): if self.img_idx is None: return def setTextInfoVideoGrid(self): for field, name, key in zip(self.videoLabels, ImageDisplay.video_infos, ImageDisplay.video_keys): mess = name.format(self.dico_video.get(key,'?')) field.setText(mess) self.__setVideoLabelVisible(True) def scaleInfoVisible(self, state): for widget in self.dicoScale.values(): widget.setVisible(state) def __epsilonVisible(self, state): self.lbl_epsilon.setVisible(state) self.epsi_spin.setVisible(state) def open_video(self): '''Lance un sélecteur de fichier pour choisir la vidéo à traiter.''' fname = QFileDialog.getOpenFileName(None, 'Choisir une vidéo', self.mw.cur_dir, 'Fichier vidéo (*.mp4)') if fname[0] != '' : # un fichier vidéo a été chosi : vp = fname[0] if self.video_path == vp : name = os.path.basename(vp) rep = QMessageBox.question(self, # widget parent de QMessageBox 'Question', # texte du bandeau de la fenêtre 'Voulez-vous recharger le fichier video {} ?'.format(name), QMessageBox.Yes | QMessageBox.No, # afficher les boutons Yes et No QMessageBox.No) # bouton No sélectionné par défaut if rep == QMessageBox.No: return # fichier vidéo à traiter => faire le split des images : self.video_path = vp self.extract_images_from_video() def load_images_from_directory(self): '''Charge les images '*.png' contenue dans le répertoire des images choisi avec un sélecteur graphique.''' # Choix du répertoire avec un sélecteur de fichier : dname = QFileDialog.getExistingDirectory(None, 'Choisir un dossier images', self.mw.image_dir) if dname != '' : # un répertoire valide a été choisi : self.video_path = None self.images_dir = dname + "/" try: # Lecture du fichier ascii des méta-données with open(self.images_dir + "metaData.txt", "r") as F: data = F.read() exec('self.dico_video = '+data) except: rep = QMessageBox.critical( None, # widget parent de QMessageBox 'Erreur', # bandeau de la fenêtre 'Pas de fichier de méta-données dans le répertoire'+\ ' <{}>'.format(os.path.basename(dname)), QMessageBox.Ok) return print("méta données :", self.dico_video) self.parse_meta_data() self.setTextInfoVideoGrid() # Mettre à jour l'application avec les nouvelles images chargées : self.update_images() def extract_trajectoire(self): '''Méthode utilisée pour extraire la trajectoire du centre de la cible pour toutes les images de la vidéo.''' # Récupérer l'algorithme de calcul du centre de la cible : algo = self.btn_algo.currentText() # Définition de la liste dans laquelle on va récupérer les coordonnées # du centre cible pour toutes les images : target_pos = [] # Création d'un objet ProgressBar qui va lancer le travail # d'extraction de la cible dans les images tout en affichant une # barre d'avancement : first = self.images_firstRank.value() last = self.images_lastRank.value() step = self.images_step.value() last = last - (last - first) % step first_last_step = (first, last, step) pg = ProgressBar(self.images_dir, self) pg.configure_for_target_extraction(self.mw.target_RGB, algo, self.epsi_spin.value(), target_pos, first_last_step) ret = pg.exec_() # lance la barre et le travail d'extraction... print("retour de pg.exec_() :",ret) if ret != 0: self.mw.target_pos = None return target_pos = np.array(target_pos) width, height = self.video_size # l'axe verticale est retourné et decalé: target_pos[1] = height - target_pos[1] self.scale_XY() self.mw.target_pos = target_pos self.display_plots() # remettre le bouton extraire_trajectoire disabled: self.btn_exportCSV.setEnabled(True) def display_plots(self): self.mw.clearPlots() # Plot trajectory (X(t), Y(t)) : self.mw.onePlot.setEnabled(True) self.mw.onePlot.Plot() # Plot curves X(t) and Y(t) self.mw.twoPlots_xy.setEnabled(True) self.mw.tabs.setCurrentWidget(self.mw.twoPlots_xy) self.mw.twoPlots_xy.Plot() # Plot curves VX(t) and VY(t) self.mw.twoPlots_VxVy.setEnabled(True) self.mw.tabs.setCurrentWidget(self.mw.twoPlots_xy) self.mw.twoPlots_VxVy.Plot() def extract_images_from_video(self) : # name of the video file without path and suffix: videoname = os.path.basename(self.video_path)[:-4] # directory where to put extracted iamges: self.images_dir = self.mw.image_dir + videoname + "/" if os.path.isdir(self.images_dir) : print("Effacement de tous les fichiers de '{}'"\ .format(self.images_dir)) for fn in os.listdir(self.images_dir) : os.remove(self.images_dir+fn) else : os.mkdir(self.images_dir) video = cv2.VideoCapture(self.video_path) self.dico_video['nframes'] = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) self.dico_video['size'] = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))) self.dico_video['fps'] = int(video.get(cv2.CAP_PROP_FPS)) self.dico_video['duration'] = video.get(cv2.CAP_PROP_FRAME_COUNT)/video.get(cv2.CAP_PROP_FPS) self.dico_video['videoname'] = os.path.basename(self.video_path) self.parse_meta_data() self.dico_video["videoname"] = videoname+".mp4" self.setTextInfoVideoGrid() # Création d'un objet ProgressBar qui va lancer le travail # d'extraction des images tout en affichant une barre d'avancement : pg = ProgressBar(self.images_dir, self) pg.configure_for_video_extraction(video, self.mw.image_fmt) ret = pg.exec_() print("retour de pg.exec_() :", ret) if ret != 0: return # MAJ de la liste des fichiers images : self.update_images() # écriture des méta-data dans le fichier 'nom_video'.info with open(self.mw.image_dir+videoname+"/metaData.txt", "w") as F: F.write(str(self.dico_video)) def computeTargetColor(self, draw_selection=False): col_min,row_min,col_max,row_max = self.selection.getCoords() print("Pixels selectionnés : lignes [{},{}] colonnes [{},{}]".\ format(row_min, row_max, col_min, col_max)) tab = imread(self.img_path) self.target_pix = tab[row_min:row_max+1, col_min:col_max+1, :] R = round(self.target_pix[:,:,0].mean()) G = round(self.target_pix[:,:,1].mean()) B = round(self.target_pix[:,:,2].mean()) self.mw.target_RGB = np.array([R, G, B], dtype=int) print("RGB sélection dans <{}> :".format(os.path.basename(self.img_path)), self.mw.target_RGB) draw_selection = self.mw.flags["drawTargetSelection"] if draw_selection: self.show_image() print("drawTargetSelection") #if self.selectTargetRect is not None : del self.selectTargetRect # create painter instance with pixmap self.selectTargetRect = QPainter(self.img_lbl.pixmap()) # set rectangle color and thickness self.penRectangle = QPen(QColor(0,0,0)) self.penRectangle.setWidth(2) # draw rectangle on painter self.selectTargetRect.begin(self) self.selectTargetRect.setPen(self.penRectangle) self.selectTargetRect.drawRect(col_min,row_min, col_max-col_min,row_max-row_min) self.selectTargetRect.setOpacity(0.1) self.selectTargetRect.end() #self.show_image() self.btn_traj.setEnabled(True) self.btn_algo.setEnabled(True) self.btn_clear.setEnabled(True) self.target_color_label.setEnabled(True) self.picked_color.setStyleSheet('background-color : rgb({},{},{})'.format(R, G, B)) @property def img_idx(self): return self.__img_idx @img_idx.setter def img_idx(self, index): self.__img_idx = index self.image_index.setText(str(index)) def update_images(self) : '''Méthode à exécuter quand de nouvelles images sont apparues après une extraction d'images depuis une vidéo par exemple. Cette méthode : - met à jour des attributs qui dépendant de la liste des images, - met à jour l'état de certains boutons - fait afficher la première image et un message d'information.''' if self.images_dir is None : self.__img_idx = None #self.btn_prev.setEnabled(False) self.btn_prev.setStatusTip("") #self.btn_next.setEnabled(False) self.btn_next.setStatusTip("") self.images_step.setEnabled(False) self.images_firstRank.setEnabled(False) self.images_lastRank.setEnabled(False) else : self.buttonsState() self.mw.clearPlots() self.mw.twoPlots_VxVy.reset() # liste des noms des fichiers image pour avoir leur nombre : file_names = [ f for f in os.listdir(self.images_dir) \ if f.endswith('.png')] file_names.sort() self.nb_img = len(file_names) # Update spinBox: self.images_step.setEnabled(True) self.images_firstRank.setEnabled(True) self.images_lastRank.setEnabled(True) self.images_firstRank.setValue(1) self.images_step.setValue(1) self.images_lastRank.setValue(self.nb_img) self.images_firstRank.setMaximum(self.nb_img) self.images_lastRank.setMaximum(self.nb_img) self.images_step.setMaximum(self.nb_img) # MAJ des boutons prev et next self.img_idx = self.images_firstRank.value() self.btn_prev.setEnabled(True) self.btn_first.setEnabled(True) self.btn_prev.setStatusTip("charge l'image précédente") self.btn_next.setEnabled(True) self.btn_last.setEnabled(True) self.btn_next.setStatusTip("afficher "+self.mw.image_fmt.format(\ self.img_idx+self.images_step.value())) self.show_image() self.scaleInfoVisible(True) self.__epsilonVisible(True) self.mw.tabs.setCurrentWidget(self) self.scale_mm.clear() self.scale_mm.setText("???") self.scale_pixel.clear() try: text = str(self.video_size[1]) except: text = "" self.scale_pixel.setText(text) self.mw.twoPlots_VxVy.reset() if self.mw.flags["displayInfo"]: rep = QMessageBox.information( None, # widget parent de QMessageBox 'Information', # bandeau de la fenêtre 'Vous pouvez maintenant sélectionner une couleur de cible'+\ 'avec la souris...', QMessageBox.Ok) def show_image(self): '''Affiche l'image dont le numéro est donné par l'attribut 'img_idx'.''' if self.img_idx is None : self.img_path = '' else : self.img_path = self.images_dir+self.mw.image_fmt.format(self.img_idx) pixmap = QPixmap(self.img_path) self.img_lbl.setPixmap(pixmap) self.img_lbl.setStatusTip(os.path.basename(self.img_path)) def first_image(self) : if self.img_idx == None : return self.img_idx = self.images_firstRank.value() self.mw.statusBar().showMessage("") self.show_image() def prev_image(self) : if self.img_idx == None : return if self.img_idx >= self.images_firstRank.value() + self.images_step.value(): self.img_idx -= self.images_step.value() self.mw.statusBar().showMessage("") self.show_image() def last_image(self) : if self.img_idx == None : return self.img_idx = self.images_lastRank.value() # rank of last image to process self.mw.statusBar().showMessage("") self.show_image() def next_image(self) : if self.img_idx == None : return if self.img_idx <= self.images_lastRank.value()-self.images_step.value(): self.img_idx += self.images_step.value() self.mw.statusBar().showMessage("") self.show_image() def parse_meta_data(self): self.video_nframes = self.dico_video.get('nframes', None) self.video_size = self.dico_video.get('size', None) self.video_FPS = self.dico_video.get('fps', None) self.video_duration = self.dico_video.get('duration', None) self.video_name = self.dico_video.get('videoname',"none.mp4") if self.mw.flags["debug"]: info= " nb images : {},\n taille image : {},\n FPS : {} image/sec,\n durée : {.2f} sec." info = info.format(self.video_nframes, self.video_size, self.video_FPS, self.video_duration) QMessageBox.about(None, # widget parent de QMessageBox 'Informations video {}'.format(self.video_name), info) def eventFilter(self, object, event): if object == self.img_lbl : if event.type() == QEvent.MouseButtonPress: self.mousePressInLabel(event) return True elif event.type() == QEvent.MouseButtonRelease: self.mouseReleaseInLabel(event) return True elif event.type() == QEvent.MouseMove: self.mouseMoveInLabel(event) return True return False def mousePressInLabel(self, event): if event.button() == Qt.LeftButton: self.pt1 = event.pos() self.pt1_rect = self.img_lbl.mapTo(self, self.pt1) print("\nCoord. pt1 image :",self.pt1) self.rubberBand.setGeometry(QRect(self.pt1_rect, QSize())) self.rubberBand.show() self.mw.statusBar().showMessage('sélection en cours....') def mouseMoveInLabel(self, event): if not self.pt1.isNull(): pt = self.img_lbl.mapTo(self,event.pos()) self.rubberBand.setGeometry(QRect(self.pt1_rect, pt).normalized()) def mouseReleaseInLabel(self, event): if event.button() == Qt.LeftButton: self.pt2 = event.pos() print("Coord. pt2 image :", self.pt2) self.rubberBand.hide() self.selection = QRect(self.pt1, self.pt2).normalized() print(self.selection) self.computeTargetColor() def scale_XY(self): self.valid_scale = False self.pix_to_mm_coeff = 1. try : pixels = float(self.scale_pixel.text()) mm = float(self.scale_mm.text()) except : if self.mw.flags["displayInfo"]: info = 'Les données de conversion Pixels -> mm n\'ont pas été ' info += 'complétées.. les ordonnées des tracés seront en pixels.' rep = QMessageBox.information(None, # parent de QMessageBox 'Info', # bandeau de la fenêtre info, QMessageBox.Ok) return self.valid_scale = True self.pix_to_mm_coeff = mm/pixels print("valid scale : ", self.pix_to_mm_coeff)