Skip to content

Commit 716384d

Browse files
authored
Refactor FileOperator (#787)
1 parent 22f3cf6 commit 716384d

8 files changed

+109
-104
lines changed

src/urh/controller/CompareFrameController.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ def save_protocol(self):
857857
break
858858

859859
text = "protocol"
860-
filename = FileOperator.get_save_file_name("{0}.proto.xml".format(text), caption="Save protocol")
860+
filename = FileOperator.ask_save_file_name("{0}.proto.xml".format(text), caption="Save protocol")
861861

862862
if not filename:
863863
return

src/urh/controller/GeneratorTabController.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ def generate_file(self):
384384
except Exception as e:
385385
logger.exception(e)
386386
sample_rate = 1e6
387-
FileOperator.save_data_dialog("generated", modulated_samples, sample_rate=sample_rate, parent=self)
387+
FileOperator.ask_signal_file_name_and_save("generated", modulated_samples, sample_rate=sample_rate, parent=self)
388388
except Exception as e:
389389
Errors.exception(e)
390390
self.unsetCursor()
@@ -608,7 +608,7 @@ def on_btn_send_clicked(self):
608608

609609
@pyqtSlot()
610610
def on_btn_save_clicked(self):
611-
filename = FileOperator.get_save_file_name("profile.fuzz.xml", caption="Save fuzz profile")
611+
filename = FileOperator.ask_save_file_name("profile.fuzz.xml", caption="Save fuzzing profile")
612612
if filename:
613613
self.table_model.protocol.to_xml_file(filename,
614614
decoders=self.project_manager.decodings,

src/urh/controller/SimulatorTabController.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ def refresh_tree(self):
507507

508508
@pyqtSlot()
509509
def on_btn_save_clicked(self):
510-
filename = FileOperator.get_save_file_name(initial_name="myprofile.sim.xml", caption="Save simulator profile")
510+
filename = FileOperator.ask_save_file_name(initial_name="myprofile.sim.xml", caption="Save simulator profile")
511511
if filename:
512512
self.save_simulator_file(filename)
513513

src/urh/controller/dialogs/ReceiveDialog.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ def on_save_clicked(self):
103103

104104
initial_name = initial_name.replace(Formatter.local_decimal_seperator(), "_").replace("_000", "")
105105

106-
filename = FileOperator.save_data_dialog(initial_name, data,
107-
sample_rate=dev.sample_rate, parent=self)
106+
filename = FileOperator.ask_signal_file_name_and_save(initial_name, data,
107+
sample_rate=dev.sample_rate, parent=self)
108108
self.already_saved = True
109109
if filename is not None and filename not in self.recorded_files:
110110
self.recorded_files.append(filename)

src/urh/controller/dialogs/SendDialog.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def init_device(self):
9494

9595
@pyqtSlot()
9696
def on_graphics_view_save_as_clicked(self):
97-
filename = FileOperator.get_save_file_name("signal.complex")
97+
filename = FileOperator.ask_save_file_name("signal.complex")
9898
if filename:
9999
try:
100100
try:

src/urh/controller/dialogs/SimulatorDialog.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ def on_btn_save_rx_clicked(self):
418418
rx_device = self.simulator.sniffer.rcv_device
419419
if isinstance(rx_device.data, np.ndarray) or isinstance(rx_device.data, IQArray):
420420
data = IQArray(rx_device.data[:rx_device.current_index])
421-
filename = FileOperator.save_data_dialog("simulation_capture", data,
422-
sample_rate=rx_device.sample_rate, parent=self)
421+
filename = FileOperator.ask_signal_file_name_and_save("simulation_capture", data,
422+
sample_rate=rx_device.sample_rate, parent=self)
423423
if filename:
424424
data.tofile(filename)
425425
self.rx_file_saved.emit(filename)

src/urh/controller/widgets/SignalFrame.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,8 @@ def save_signal(self):
448448

449449
def save_signal_as(self):
450450
try:
451-
FileOperator.save_data_dialog(self.signal.name, self.signal.iq_array, self.signal.sample_rate,
452-
self.signal.wav_mode)
451+
FileOperator.ask_signal_file_name_and_save(self.signal.name, self.signal.iq_array, self.signal.sample_rate,
452+
self.signal.wav_mode)
453453
except Exception as e:
454454
Errors.exception(e)
455455

@@ -460,7 +460,7 @@ def export_demodulated(self):
460460
logger.exception(e)
461461
initial_name = "demodulated.complex"
462462

463-
filename = FileOperator.get_save_file_name(initial_name)
463+
filename = FileOperator.ask_save_file_name(initial_name)
464464
if filename:
465465
try:
466466
self.setCursor(Qt.WaitCursor)
@@ -1314,7 +1314,7 @@ def on_export_fta_wanted(self):
13141314
logger.exception(e)
13151315
initial_name = "spectrogram.ft"
13161316

1317-
filename = FileOperator.get_save_file_name(initial_name, caption="Export spectrogram")
1317+
filename = FileOperator.ask_save_file_name(initial_name, caption="Export spectrogram")
13181318
if not filename:
13191319
return
13201320
QApplication.setOverrideCursor(Qt.WaitCursor)

src/urh/util/FileOperator.py

+96-91
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,53 @@
88
from PyQt5.QtCore import QDir
99
from PyQt5.QtWidgets import QFileDialog, QMessageBox
1010

11-
from urh.models.FileIconProvider import FileIconProvider
1211
from urh.signalprocessing.IQArray import IQArray
1312

14-
VIEW_TYPES = ["Bits", "Hex", "ASCII"]
15-
1613
archives = {}
1714
""":type: dict of [str, str]
1815
:param: archives[extracted_filename] = filename"""
1916

2017
RECENT_PATH = QDir.homePath()
2118

22-
EXT = {np.int8: ".complex16s", np.uint8: ".complex16u", np.int16: ".complex32s", np.uint16: ".complex32u",
23-
np.float32: ".complex", np.complex64: ".complex"}
24-
FILTER = {np.int8: "Complex16 signed (*.complex16s *.cs8)", np.uint8: "Complex16 unsigned (*.complex16u *.cu8)",
25-
np.uint16: "Complex32 unsigned (*.complex32u *.cu16)", np.int16: "Complex32 signed (*.complex32s *.cs16)",
26-
np.float32: "Complex (*.complex)", np.complex64: "Complex (*.complex)"}
19+
SIGNAL_FILE_EXTENSIONS_BY_TYPE = {
20+
np.int8: ".complex16s",
21+
np.uint8: ".complex16u",
22+
np.int16: ".complex32s",
23+
np.uint16: ".complex32u",
24+
np.float32: ".complex",
25+
np.complex64: ".complex"
26+
}
27+
28+
SIGNAL_NAME_FILTERS_BY_TYPE = {
29+
np.int8: "Complex16 signed (*.complex16s *.cs8)",
30+
np.uint8: "Complex16 unsigned (*.complex16u *.cu8)",
31+
np.uint16: "Complex32 unsigned (*.complex32u *.cu16)",
32+
np.int16: "Complex32 signed (*.complex32s *.cs16)",
33+
np.float32: "Complex (*.complex)",
34+
np.complex64: "Complex (*.complex)"
35+
}
36+
37+
EVERYTHING_FILE_FILTER = "All Files (*)"
38+
39+
SIGNAL_NAME_FILTERS = list(sorted(set(SIGNAL_NAME_FILTERS_BY_TYPE.values())))
40+
41+
COMPRESSED_COMPLEX_FILE_FILTER = "Compressed Complex File (*.coco)"
42+
WAV_FILE_FILTER = "Waveform Audio File Format (*.wav *.wave)"
43+
PROTOCOL_FILE_FILTER = "Protocol (*.proto.xml *.proto)"
44+
BINARY_PROTOCOL_FILE_FILTER = "Binary Protocol (*.bin)"
45+
PLAIN_BITS_FILE_FILTER = "Plain Bits (*.txt)"
46+
FUZZING_FILE_FILTER = "Fuzzing Profile (*.fuzz.xml *.fuzz)"
47+
SIMULATOR_FILE_FILTER = "Simulator Profile (*.sim.xml *.sim)"
48+
TAR_FILE_FILTER = "Tar Archive (*.tar *.tar.gz *.tar.bz2)"
49+
ZIP_FILE_FILTER = "Zip Archive (*.zip)"
50+
51+
52+
def __get__name_filter_for_signals() -> str:
53+
return ";;".join([EVERYTHING_FILE_FILTER] + SIGNAL_NAME_FILTERS + [COMPRESSED_COMPLEX_FILE_FILTER, WAV_FILE_FILTER])
2754

2855

2956
def get_open_dialog(directory_mode=False, parent=None, name_filter="full") -> QFileDialog:
30-
fip = FileIconProvider()
3157
dialog = QFileDialog(parent=parent, directory=RECENT_PATH)
32-
dialog.setIconProvider(fip)
3358

3459
if directory_mode:
3560
dialog.setFileMode(QFileDialog.Directory)
@@ -38,98 +63,43 @@ def get_open_dialog(directory_mode=False, parent=None, name_filter="full") -> QF
3863
dialog.setFileMode(QFileDialog.ExistingFiles)
3964
dialog.setWindowTitle("Open Files")
4065
if name_filter == "full":
41-
name_filter = "All Files (*);;" \
42-
"Complex (*.complex);;" \
43-
"Complex16 unsigned (*.complex16u *.cu8);;" \
44-
"Complex16 signed (*.complex16s *.cs8);;" \
45-
"Complex32 unsigned (*.complex32u *.cu16);;" \
46-
"Complex32 signed (*.complex32s *.cs16);;" \
47-
"WAV (*.wav);;" \
48-
"Protocols (*.proto.xml *.proto);;" \
49-
"Binary Protocols (*.bin);;" \
50-
"Fuzzing Profiles (*.fuzz.xml *.fuzz);;" \
51-
"Simulator (*.sim.xml *.sim)" \
52-
"Plain Bits (*.txt);;" \
53-
"Tar Archives (*.tar *.tar.gz *.tar.bz2);;" \
54-
"Zip Archives (*.zip)"
66+
name_filter = __get__name_filter_for_signals() + ";;" \
67+
+ ";;".join([PROTOCOL_FILE_FILTER, BINARY_PROTOCOL_FILE_FILTER, PLAIN_BITS_FILE_FILTER,
68+
FUZZING_FILE_FILTER, SIMULATOR_FILE_FILTER, TAR_FILE_FILTER, ZIP_FILE_FILTER])
69+
elif name_filter == "signals_only":
70+
name_filter = __get__name_filter_for_signals()
5571
elif name_filter == "proto":
56-
name_filter = "Protocols (*.proto.xml *.proto);; Binary Protocols (*.bin)"
72+
name_filter = ";;".join([PROTOCOL_FILE_FILTER, BINARY_PROTOCOL_FILE_FILTER])
5773
elif name_filter == "fuzz":
58-
name_filter = "Fuzzprofiles (*.fuzz.xml *.fuzz)"
74+
name_filter = FUZZING_FILE_FILTER
5975
elif name_filter == "simulator":
60-
name_filter = "Simulator (*.sim.xml *.sim)"
76+
name_filter = SIMULATOR_FILE_FILTER
6177

6278
dialog.setNameFilter(name_filter)
6379

64-
dialog.setOptions(QFileDialog.DontResolveSymlinks)
65-
dialog.setViewMode(QFileDialog.Detail)
66-
6780
return dialog
6881

6982

70-
def uncompress_archives(file_names, temp_dir):
71-
"""
72-
Extract each archive from the list of filenames.
73-
Normal files stay untouched.
74-
Add all files to the Recent Files.
75-
:type file_names: list of str
76-
:type temp_dir: str
77-
:rtype: list of str
78-
"""
79-
result = []
80-
for filename in file_names:
81-
if filename.endswith(".tar") or filename.endswith(".tar.gz") or filename.endswith(".tar.bz2"):
82-
obj = tarfile.open(filename, "r")
83-
extracted_file_names = []
84-
for j, member in enumerate(obj.getmembers()):
85-
obj.extract(member, temp_dir)
86-
extracted_filename = os.path.join(temp_dir, obj.getnames()[j])
87-
extracted_file_names.append(extracted_filename)
88-
archives[extracted_filename] = filename
89-
result.extend(extracted_file_names[:])
90-
elif filename.endswith(".zip"):
91-
obj = zipfile.ZipFile(filename)
92-
extracted_file_names = []
93-
for j, info in enumerate(obj.infolist()):
94-
obj.extract(info, path=temp_dir)
95-
extracted_filename = os.path.join(temp_dir, obj.namelist()[j])
96-
extracted_file_names.append(extracted_filename)
97-
archives[extracted_filename] = filename
98-
result.extend(extracted_file_names[:])
99-
else:
100-
result.append(filename)
101-
102-
return result
103-
104-
105-
def get_save_file_name(initial_name: str, wav_only=False, caption="Save signal", selected_name_filter=None):
83+
def ask_save_file_name(initial_name: str, caption="Save signal", selected_name_filter=None):
10684
global RECENT_PATH
10785
if caption == "Save signal":
108-
name_filter = "Complex (*.complex);;" \
109-
"Complex16 unsigned (*.complex16u *.cu8);;" \
110-
"Complex16 signed (*.complex16s *.cs8);;" \
111-
"Complex32 unsigned (*.complex32u *.cu16);;" \
112-
"Complex32 signed (*.complex32s *.cs16);;" \
113-
"Complex compressed (*.coco);;" \
114-
"WAV (*.wav);;" \
115-
"All Files (*)"
116-
if wav_only:
117-
name_filter = "WAV (*.wav);;All Files (*)"
118-
elif caption == "Save fuzz profile":
119-
name_filter = "Fuzzing Profile (*.fuzz.xml *.fuzz);;All Files (*)"
86+
name_filter = __get__name_filter_for_signals()
87+
elif caption == "Save fuzzing profile":
88+
name_filter = FUZZING_FILE_FILTER
12089
elif caption == "Save encoding":
12190
name_filter = ""
12291
elif caption == "Save simulator profile":
123-
name_filter = "Simulator (*.sim.xml *.sim);;All Files (*)"
92+
name_filter = SIMULATOR_FILE_FILTER
12493
elif caption == "Export spectrogram":
12594
name_filter = "Frequency Time (*.ft);;Frequency Time Amplitude (*.fta)"
95+
elif caption == "Save protocol":
96+
name_filter = ";;".join([PROTOCOL_FILE_FILTER, BINARY_PROTOCOL_FILE_FILTER])
12697
else:
127-
name_filter = "Protocols (*.proto.xml *.proto);;Binary Protocol (*.bin);;All Files (*)"
98+
name_filter = EVERYTHING_FILE_FILTER
12899

129100
filename = None
130101
dialog = QFileDialog(directory=RECENT_PATH, caption=caption, filter=name_filter)
131102
dialog.setFileMode(QFileDialog.AnyFile)
132-
dialog.setViewMode(QFileDialog.Detail)
133103
dialog.setLabelText(QFileDialog.Accept, "Save")
134104
dialog.setAcceptMode(QFileDialog.AcceptSave)
135105

@@ -147,23 +117,23 @@ def get_save_file_name(initial_name: str, wav_only=False, caption="Save signal",
147117
return filename
148118

149119

150-
def save_data_dialog(signal_name: str, data, sample_rate=1e6, wav_only=False, parent=None) -> str:
120+
def ask_signal_file_name_and_save(signal_name: str, data, sample_rate=1e6, wav_only=False, parent=None) -> str:
151121
if wav_only:
152-
if not signal_name.endswith(".wav"):
122+
if not signal_name.endswith(".wav") and not signal_name.endswith(".wave"):
153123
signal_name += ".wav"
154-
name_filter = "WAV (*.wav)"
124+
selected_name_filter = WAV_FILE_FILTER
155125
else:
156-
if not any(signal_name.endswith(e) for e in FILTER.values()):
126+
if not any(signal_name.endswith(e) for e in SIGNAL_NAME_FILTERS_BY_TYPE.values()):
157127
try:
158-
dtype = next(d for d in EXT.keys() if d == data.dtype)
159-
signal_name += EXT[dtype]
160-
name_filter = FILTER[dtype]
128+
dtype = next(d for d in SIGNAL_FILE_EXTENSIONS_BY_TYPE.keys() if d == data.dtype)
129+
signal_name += SIGNAL_FILE_EXTENSIONS_BY_TYPE[dtype]
130+
selected_name_filter = SIGNAL_NAME_FILTERS_BY_TYPE[dtype]
161131
except StopIteration:
162-
name_filter = None
132+
selected_name_filter = None
163133
else:
164-
name_filter = None
134+
selected_name_filter = None
165135

166-
filename = get_save_file_name(signal_name, wav_only, selected_name_filter=name_filter)
136+
filename = ask_save_file_name(signal_name, selected_name_filter=selected_name_filter)
167137

168138
if filename:
169139
try:
@@ -232,6 +202,41 @@ def rewrite_tar(tar_name: str):
232202
shutil.rmtree(tempdir)
233203

234204

205+
def uncompress_archives(file_names, temp_dir):
206+
"""
207+
Extract each archive from the list of filenames.
208+
Normal files stay untouched.
209+
Add all files to the Recent Files.
210+
:type file_names: list of str
211+
:type temp_dir: str
212+
:rtype: list of str
213+
"""
214+
result = []
215+
for filename in file_names:
216+
if filename.endswith(".tar") or filename.endswith(".tar.gz") or filename.endswith(".tar.bz2"):
217+
obj = tarfile.open(filename, "r")
218+
extracted_file_names = []
219+
for j, member in enumerate(obj.getmembers()):
220+
obj.extract(member, temp_dir)
221+
extracted_filename = os.path.join(temp_dir, obj.getnames()[j])
222+
extracted_file_names.append(extracted_filename)
223+
archives[extracted_filename] = filename
224+
result.extend(extracted_file_names[:])
225+
elif filename.endswith(".zip"):
226+
obj = zipfile.ZipFile(filename)
227+
extracted_file_names = []
228+
for j, info in enumerate(obj.infolist()):
229+
obj.extract(info, path=temp_dir)
230+
extracted_filename = os.path.join(temp_dir, obj.namelist()[j])
231+
extracted_file_names.append(extracted_filename)
232+
archives[extracted_filename] = filename
233+
result.extend(extracted_file_names[:])
234+
else:
235+
result.append(filename)
236+
237+
return result
238+
239+
235240
def get_directory():
236241
directory = QFileDialog.getExistingDirectory(None, "Choose Directory", QDir.homePath(),
237242
QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)

0 commit comments

Comments
 (0)