diff --git a/.coveragerc b/.coveragerc index 2899472773..49ec786eb5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -18,5 +18,7 @@ exclude_lines = def mouseReleaseEvent def on_import_samples_from_csv_action_triggered def show_open_dialog + def on_btn_choose_python2_interpreter_clicked + def on_btn_choose_gnuradio_directory_clicked def on_new_project_action_triggered def on_label_non_project_mode_link_activated diff --git a/.gitignore b/.gitignore index 92ed8e0fcf..ffa8e69983 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ src/urh/controller/__pycache__/ src/urh/cythonext/__pycache__/ src/urh/dev/__pycache__/ src/urh/dev/gr/__pycache__/ -src/urh/dev/gr/scripts/InputHandlerThread.pyc +*.pyc src/urh/dev/native/__pycache__/ src/urh/dev/native/lib/__pycache__/ src/urh/models/__pycache__/ diff --git a/.travis.yml b/.travis.yml index 5e6f73225c..53d414b0c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,9 @@ matrix: - DISPLAY=:99.0 - LIBOVERLAY_SCROLLBAR=0 +notifications: + email: false + branches: only: - master diff --git a/appveyor.yml b/appveyor.yml index a7da88b1e4..8abf2a1e09 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -48,7 +48,7 @@ install: build_script: # Build the compiled extension - - python setup.py build_ext --inplace + - python setup.py build_ext --inplace > build_log.txt test_script: - python src\urh\main.py --version @@ -68,6 +68,8 @@ artifacts: on_success: - IF "%APPVEYOR_REPO_TAG%" == "true" (twine upload --skip-existing dist/*.whl) +on_failure: + - type build_log.txt deploy: release: $(APPVEYOR_REPO_TAG_NAME) diff --git a/src/urh/controller/OptionsController.py b/src/urh/controller/OptionsController.py index 9163e81c8a..b5441cac2b 100644 --- a/src/urh/controller/OptionsController.py +++ b/src/urh/controller/OptionsController.py @@ -8,7 +8,7 @@ from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal, QSize from PyQt5.QtGui import QCloseEvent, QIcon, QPixmap from PyQt5.QtWidgets import QDialog, QHBoxLayout, QCompleter, QDirModel, QApplication, QHeaderView, QStyleFactory, \ - QRadioButton, QVBoxLayout, QPlainTextEdit + QRadioButton, QFileDialog from urh import constants, colormaps from urh.controller.PluginController import PluginController @@ -125,6 +125,8 @@ def __get_key_from_device_display_text(self, displayed_device_name): def create_connects(self): self.ui.doubleSpinBoxFuzzingPause.valueChanged.connect(self.on_spinbox_fuzzing_pause_value_changed) self.ui.lineEditPython2Interpreter.editingFinished.connect(self.on_python2_exe_path_edited) + self.ui.btnChoosePython2Interpreter.clicked.connect(self.on_btn_choose_python2_interpreter_clicked) + self.ui.btnChooseGnuRadioDirectory.clicked.connect(self.on_btn_choose_gnuradio_directory_clicked) self.ui.lineEditGnuradioDirectory.editingFinished.connect(self.on_gnuradio_install_dir_edited) self.ui.listWidgetDevices.currentRowChanged.connect(self.on_list_widget_devices_current_row_changed) self.ui.chkBoxDeviceEnabled.clicked.connect(self.on_chk_box_device_enabled_clicked) @@ -327,6 +329,24 @@ def on_spinbox_fuzzing_pause_value_changed(self, value: float): def on_python2_exe_path_edited(self): self.set_gnuradio_status() + @pyqtSlot() + def on_btn_choose_python2_interpreter_clicked(self): + if sys.platform == "win32": + dialog_filter = "Executable (*.exe);;All files (*.*)" + else: + dialog_filter = "" + filename, _ = QFileDialog.getOpenFileName(self, self.tr("Choose python2 interpreter"), filter=dialog_filter) + if filename: + self.ui.lineEditPython2Interpreter.setText(filename) + self.set_gnuradio_status() + + @pyqtSlot() + def on_btn_choose_gnuradio_directory_clicked(self): + directory = QFileDialog.getExistingDirectory(self, "Choose GNU Radio directory") + if directory: + self.ui.lineEditGnuradioDirectory.setText(directory) + self.set_gnuradio_status() + @pyqtSlot() def on_chk_box_device_enabled_clicked(self): self.selected_device.is_enabled = bool(self.ui.chkBoxDeviceEnabled.isChecked()) @@ -370,7 +390,7 @@ def on_gnuradio_install_dir_edited(self): @pyqtSlot() def on_btn_rebuild_native_clicked(self): library_dirs = None if not self.ui.lineEditLibDirs.text() \ - else list(map(str.strip, self.ui.lineEditLibDirs.text().split(","))) + else list(map(str.strip, self.ui.lineEditLibDirs.text().split(","))) num_natives = self.backend_handler.num_native_backends extensions = ExtensionHelper.get_device_extensions(use_cython=False, library_dirs=library_dirs) new_natives = len(extensions) - num_natives @@ -406,7 +426,6 @@ def on_btn_rebuild_native_clicked(self): except OSError: pass - @pyqtSlot() def on_btn_health_check_clicked(self): info = ExtensionHelper.perform_health_check() @@ -417,7 +436,6 @@ def on_btn_health_check_clicked(self): d = util.create_textbox_dialog(info, "Health check for native extensions", self) d.show() - @staticmethod def write_default_options(): settings = constants.SETTINGS diff --git a/src/urh/dev/BackendHandler.py b/src/urh/dev/BackendHandler.py index 3bbf2f5cd9..ef5cd9f694 100644 --- a/src/urh/dev/BackendHandler.py +++ b/src/urh/dev/BackendHandler.py @@ -164,11 +164,10 @@ def set_gnuradio_installed_status(self): self.gnuradio_installed = False return else: - # we are on a nice unix with gnuradio installed to default python path - devnull = "NUL" if os.name == "nt" else "/dev/null" if os.path.isfile(self.python2_exe) and os.access(self.python2_exe, os.X_OK): - # Subprocess.call gives memory error, so we use os.system - self.gnuradio_installed = os.system(self.python2_exe + " -c 'import gnuradio' 2>" + devnull) == 0 + # Use shell=True to prevent console window popping up on windows + self.gnuradio_installed = call('"{0}" -c "import gnuradio"'.format(self.python2_exe), + shell=True, stderr=DEVNULL) == 0 constants.SETTINGS.setValue("python2_exe", self.python2_exe) else: self.gnuradio_installed = False diff --git a/src/urh/dev/gr/AbstractBaseThread.py b/src/urh/dev/gr/AbstractBaseThread.py index 35238395c9..46d1847aa8 100644 --- a/src/urh/dev/gr/AbstractBaseThread.py +++ b/src/urh/dev/gr/AbstractBaseThread.py @@ -46,15 +46,20 @@ def __init__(self, freq, sample_rate, bandwidth, gain, if_gain, baseband_gain, r self.context = None self.socket = None + gnuradio_path_file = os.path.join(tempfile.gettempdir(), "gnuradio_path.txt") if constants.SETTINGS.value("use_gnuradio_install_dir", False, bool): gnuradio_dir = constants.SETTINGS.value("gnuradio_install_dir", "") - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "w") as f: + with open(gnuradio_path_file, "w") as f: f.write(gnuradio_dir) if os.path.isfile(os.path.join(gnuradio_dir, "gr-python27", "pythonw.exe")): self.python2_interpreter = os.path.join(gnuradio_dir, "gr-python27", "pythonw.exe") else: self.python2_interpreter = os.path.join(gnuradio_dir, "gr-python27", "python.exe") else: + try: + os.remove(gnuradio_path_file) + except OSError: + pass self.python2_interpreter = constants.SETTINGS.value("python2_exe", "") self.queue = Queue() diff --git a/src/urh/dev/gr/scripts/Initializer.py b/src/urh/dev/gr/scripts/Initializer.py new file mode 100644 index 0000000000..6211d9c7b1 --- /dev/null +++ b/src/urh/dev/gr/scripts/Initializer.py @@ -0,0 +1,19 @@ +import sys + +import os +import tempfile + + +def init_path(): + # Append script path at end to prevent conflicts in case of frozen interpreter + sys.path.append(sys.path.pop(0)) + + try: + with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: + gnuradio_path = f.read().strip() + + os.environ["PATH"] = os.path.join(gnuradio_path, "bin") + os.pathsep + os.environ["PATH"] + sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) + + except IOError: + pass diff --git a/src/urh/dev/gr/scripts/airspy_recv.py b/src/urh/dev/gr/scripts/airspy_recv.py index 8cb27f1bf3..b168f5731d 100644 --- a/src/urh/dev/gr/scripts/airspy_recv.py +++ b/src/urh/dev/gr/scripts/airspy_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser -import tempfile -import os -import sys +import Initializer -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/bladerf_recv.py b/src/urh/dev/gr/scripts/bladerf_recv.py index 60e3e1aa7c..2ed84ddd25 100644 --- a/src/urh/dev/gr/scripts/bladerf_recv.py +++ b/src/urh/dev/gr/scripts/bladerf_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/bladerf_send.py b/src/urh/dev/gr/scripts/bladerf_send.py index facf845325..8c1de81fb7 100644 --- a/src/urh/dev/gr/scripts/bladerf_send.py +++ b/src/urh/dev/gr/scripts/bladerf_send.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/funcube-dongle_recv.py b/src/urh/dev/gr/scripts/funcube-dongle_recv.py index 8efbe3d3e9..bc99ca8bad 100755 --- a/src/urh/dev/gr/scripts/funcube-dongle_recv.py +++ b/src/urh/dev/gr/scripts/funcube-dongle_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/hackrf_recv.py b/src/urh/dev/gr/scripts/hackrf_recv.py index e4ffc730f0..bde8336dd5 100755 --- a/src/urh/dev/gr/scripts/hackrf_recv.py +++ b/src/urh/dev/gr/scripts/hackrf_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/hackrf_send.py b/src/urh/dev/gr/scripts/hackrf_send.py index 900734b93c..afdba6bbb1 100755 --- a/src/urh/dev/gr/scripts/hackrf_send.py +++ b/src/urh/dev/gr/scripts/hackrf_send.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/rtl-sdr_recv.py b/src/urh/dev/gr/scripts/rtl-sdr_recv.py index c196edf0f9..936a2b137b 100755 --- a/src/urh/dev/gr/scripts/rtl-sdr_recv.py +++ b/src/urh/dev/gr/scripts/rtl-sdr_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/sdrplay_recv.py b/src/urh/dev/gr/scripts/sdrplay_recv.py index 5f7008cb3f..16369f4b2d 100644 --- a/src/urh/dev/gr/scripts/sdrplay_recv.py +++ b/src/urh/dev/gr/scripts/sdrplay_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio.eng_option import eng_option diff --git a/src/urh/dev/gr/scripts/usrp_recv.py b/src/urh/dev/gr/scripts/usrp_recv.py index 7cd035e2c8..3a5adc9ddb 100755 --- a/src/urh/dev/gr/scripts/usrp_recv.py +++ b/src/urh/dev/gr/scripts/usrp_recv.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio import uhd diff --git a/src/urh/dev/gr/scripts/usrp_send.py b/src/urh/dev/gr/scripts/usrp_send.py index 0b5d3d33ec..48b4c8d105 100755 --- a/src/urh/dev/gr/scripts/usrp_send.py +++ b/src/urh/dev/gr/scripts/usrp_send.py @@ -6,21 +6,9 @@ ################################################## from optparse import OptionParser +import Initializer -import tempfile -import os -import sys -try: - with open(os.path.join(tempfile.gettempdir(), "gnuradio_path.txt"), "r") as f: - gnuradio_path = f.read().strip() - - os.environ["PATH"] = os.path.join(gnuradio_path, "bin") - # Append script path at end to prevent conflicts in case of frozen interpreter - sys.path.append(sys.path.pop(0)) - sys.path.insert(0, os.path.join(gnuradio_path, "lib", "site-packages")) - -except IOError: - pass +Initializer.init_path() from gnuradio import gr from gnuradio import uhd diff --git a/src/urh/ui/ui_options.py b/src/urh/ui/ui_options.py index 150f2b2fb3..695d813737 100644 --- a/src/urh/ui/ui_options.py +++ b/src/urh/ui/ui_options.py @@ -100,10 +100,9 @@ def setupUi(self, DialogOptions): self.scrollAreaSpectrogramColormap.setWidgetResizable(True) self.scrollAreaSpectrogramColormap.setObjectName("scrollAreaSpectrogramColormap") self.scrollAreaWidgetSpectrogramColormapContents = QtWidgets.QWidget() - self.scrollAreaWidgetSpectrogramColormapContents.setGeometry(QtCore.QRect(0, 0, 644, 316)) + self.scrollAreaWidgetSpectrogramColormapContents.setGeometry(QtCore.QRect(0, 0, 616, 316)) self.scrollAreaWidgetSpectrogramColormapContents.setObjectName("scrollAreaWidgetSpectrogramColormapContents") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetSpectrogramColormapContents) - self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) self.verticalLayout_4.setObjectName("verticalLayout_4") self.scrollAreaSpectrogramColormap.setWidget(self.scrollAreaWidgetSpectrogramColormapContents) self.verticalLayout_2.addWidget(self.scrollAreaSpectrogramColormap) @@ -254,6 +253,12 @@ def setupUi(self, DialogOptions): self.radioButtonGnuradioDirectory = QtWidgets.QRadioButton(self.groupBox_3) self.radioButtonGnuradioDirectory.setObjectName("radioButtonGnuradioDirectory") self.gridLayout_2.addWidget(self.radioButtonGnuradioDirectory, 2, 0, 1, 1) + self.btnChoosePython2Interpreter = QtWidgets.QToolButton(self.groupBox_3) + self.btnChoosePython2Interpreter.setObjectName("btnChoosePython2Interpreter") + self.gridLayout_2.addWidget(self.btnChoosePython2Interpreter, 1, 2, 1, 1) + self.btnChooseGnuRadioDirectory = QtWidgets.QToolButton(self.groupBox_3) + self.btnChooseGnuRadioDirectory.setObjectName("btnChooseGnuRadioDirectory") + self.gridLayout_2.addWidget(self.btnChooseGnuRadioDirectory, 2, 2, 1, 1) self.verticalLayout_8.addWidget(self.groupBox_3) self.groupBoxNativeOptions = QtWidgets.QGroupBox(self.tabDevices) self.groupBoxNativeOptions.setObjectName("groupBoxNativeOptions") @@ -283,7 +288,7 @@ def setupUi(self, DialogOptions): self.verticalLayout_6.addWidget(self.tabWidget) self.retranslateUi(DialogOptions) - self.tabWidget.setCurrentIndex(1) + self.tabWidget.setCurrentIndex(4) QtCore.QMetaObject.connectSlotsByName(DialogOptions) def retranslateUi(self, DialogOptions): @@ -337,6 +342,8 @@ def retranslateUi(self, DialogOptions): self.radioButtonPython2Interpreter.setText(_translate("DialogOptions", "Python2 interpreter")) self.radioButtonGnuradioDirectory.setToolTip(_translate("DialogOptions", "

If you installed Gnuradio with a bundled python interpreter, you need to enter the site-packages path of the installation here. The path should be something like C:\\Program Files\\GNURadio-3.7.

")) self.radioButtonGnuradioDirectory.setText(_translate("DialogOptions", "Gnuradio Directory")) + self.btnChoosePython2Interpreter.setText(_translate("DialogOptions", "...")) + self.btnChooseGnuRadioDirectory.setText(_translate("DialogOptions", "...")) self.groupBoxNativeOptions.setTitle(_translate("DialogOptions", "Native options")) self.labelLibDirs.setText(_translate("DialogOptions", "Library directories:")) self.btnRebuildNative.setToolTip(_translate("DialogOptions", "

Rebuild the native device extensions. You need to restart URH after this, to use new extensions.

")) diff --git a/tests/test_send_recv_dialog_gui.py b/tests/test_send_recv_dialog_gui.py index 88a8676aae..addb69c3bd 100644 --- a/tests/test_send_recv_dialog_gui.py +++ b/tests/test_send_recv_dialog_gui.py @@ -189,7 +189,7 @@ def test_send(self): send_dialog.ui.spinBoxNRepeat.setValue(2) send_dialog.ui.btnStart.click() QApplication.instance().processEvents() - QTest.qWait(self.SEND_RECV_TIMEOUT) + QTest.qWait(5 * self.SEND_RECV_TIMEOUT) self.assertEqual(receive_dialog.device.current_index, 2 * self.signal.num_samples) self.assertTrue(np.array_equal(receive_dialog.device.data[:receive_dialog.device.current_index // 2], @@ -223,7 +223,7 @@ def test_continuous_send_dialog(self): continuous_send_dialog.ui.spinBoxNRepeat.setValue(2) continuous_send_dialog.ui.btnStart.click() QApplication.instance().processEvents() - QTest.qWait(10 * self.SEND_RECV_TIMEOUT) + QTest.qWait(15 * self.SEND_RECV_TIMEOUT) gframe = self.form.generator_tab_controller expected = np.zeros(gframe.total_modulated_samples, dtype=np.complex64) diff --git a/ui/options.ui b/ui/options.ui index a253236e2e..ea6844eee0 100644 --- a/ui/options.ui +++ b/ui/options.ui @@ -21,7 +21,7 @@ - 1 + 4 @@ -210,7 +210,7 @@ 0 0 - 644 + 616 316 @@ -546,6 +546,20 @@ + + + + ... + + + + + + + ... + + +