forked from gnuradio/gnuradio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
executable file
·318 lines (275 loc) · 12.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
#!/usr/bin/env python3
# Copyright 2009-2020 Free Software Foundation, Inc.
# This file is part of GNU Radio
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
import argparse
import gettext
import locale
import logging
import logging.handlers
import os
import platform
import sys
VERSION_AND_DISCLAIMER_TEMPLATE = """\
GNU Radio Companion (%s) -
This program is part of GNU Radio.
GRC comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it.
"""
LOG_LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
# Load GNU Radio
# Do this globally so it is available for both run_gtk() and run_qt()
try:
from gnuradio import gr
except ImportError as ex:
# Throw a new exception with more information
print("Cannot find GNU Radio! (Have you sourced the environment file?)", file=sys.stderr)
# If this is a background session (not launched through a script), show a Tkinter error dialog.
# Tkinter should already be installed default with Python, so this shouldn't add new dependencies
if not sys.stdin.isatty():
import tkinter
from tkinter import messagebox
# Hide the main window
root = tkinter.Tk()
root.withdraw()
# Show the error dialog
# TODO: Have a more helpful dialog here. Maybe a link to the wiki pages?
messagebox.showerror("Cannot find GNU Radio", "Cannot find GNU Radio!")
# Throw the new exception
raise Exception("Cannot find GNU Radio!") from None
# Enable Logging
# Do this globally so it is available for both run_gtk() and run_qt()
# TODO: Advanced logging - https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles
# Note: All other modules need to use the 'grc.<module>' convention
log = logging.getLogger('grc')
# Set the root log name
# Since other files are in the 'grc' module, they automatically get a child logger when using:
# log = logging.getLogger(__name__)
# This log level should be set to DEBUG so the logger itself catches everything.
# The StreamHandler level can be set independently to choose what messages are sent to the console.
# The default console logging should be WARNING
log.setLevel(logging.DEBUG)
def run_gtk(args, log):
''' Runs the GTK version of GNU Radio Companion '''
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Gtk
# Delay importing until the logging is setup
from .gui.Platform import Platform
from .gui.Application import Application
# The platform is loaded differently between QT and GTK, so this is required both places
log.debug("Loading platform")
platform = Platform(
version=gr.version(),
version_parts=(gr.major_version(), gr.api_version(),
gr.minor_version()),
prefs=gr.prefs(),
install_prefix=gr.prefix()
)
platform.build_library()
log.debug("Loading application")
app = Application(args.flow_graphs, platform)
log.debug("Running")
sys.exit(app.run())
def run_qt(args, log):
''' Runs the Qt version of GNU Radio Companion '''
import platform
import locale
import gettext
from .gui_qt import grc
from .gui_qt import helpers
from .gui_qt import properties
# Delay importing until the logging is setup
from .gui_qt.Platform import Platform
''' Global Settings/Constants '''
# Initialize a class with all of the default settings and properties
# TODO: Move argv to separate argument parsing class that overrides default properties?
# TODO: Split settings/constants into separate classes rather than a single properites class?
settings = properties.Properties(sys.argv)
''' Translation Support '''
# Try to get the current locale. Always add English
lc, encoding = locale.getdefaultlocale()
if lc:
languages = [lc]
languages += settings.DEFAULT_LANGUAGE
log.debug("Using locale - %s" % str(languages))
# Still run even if the english translation isn't found
language = gettext.translation(settings.APP_NAME, settings.path.LANGUAGE, languages=languages,
fallback=True)
if type(language) == gettext.NullTranslations:
log.error("Unable to find any translation")
log.error("Default English translation missing")
else:
log.info("Using translation - %s" % language.info()["language"])
# Still need to install null translation to let the system handle calls to _()
language.install()
''' OS Platform '''
# Figure out system specific properties and setup defaults.
# Some properties can be overridden by preferences
# Get the current OS
if platform.system() == "Linux":
log.debug("Detected Linux")
settings.system.OS = "Linux"
# Determine if Unity is running....
try:
# current_desktop = os.environ['DESKTOP_SESSION']
current_desktop = os.environ['XDG_CURRENT_DESKTOP']
log.debug("Desktop Session - %s" % current_desktop)
if current_desktop == "Unity":
log.debug("Detected GRC is running under unity")
# Use the native menubar rather than leaving it in the window
settings.window.NATIVE_MENUBAR = True
except:
log.warning("Unable to determine the Linux desktop system")
elif platform.system() == "Darwin":
log.debug("Detected Mac OS X")
settings.system.OS = "OS X"
# Setup Mac specific QT elements
settings.window.NATIVE_MENUBAR = True
elif platform.system() == "Windows":
log.warning("Detected Windows")
settings.system.OS = "Windows"
else:
log.warning("Unknown operating system")
''' Preferences '''
# TODO: Move earlier? Need to load user preferences and override the default properties/settings
# The platform is loaded differently between QT and GTK, so this is required both places
log.debug("Loading platform")
# TODO: Might be beneficial to rename Platform to avoid confusion with the builtin Python module
# Possible names: internal, model?
model = Platform(
version=gr.version(),
version_parts=(gr.major_version(), gr.api_version(), gr.minor_version()),
prefs=gr.prefs(),
install_prefix=gr.prefix()
)
model.build_library()
# Launch GRC
app = grc.Application(settings, model, args.flow_graphs)
sys.exit(app.run())
def get_config_file_path(config_file: str = "grc.conf") -> str:
oldpath = os.path.join(os.path.expanduser("~/.gnuradio"), config_file)
try:
from gnuradio.gr import paths
newpath = os.path.join(paths.userconf(), config_file)
if os.path.exists(newpath):
return newpath
if os.path.exists(oldpath):
log.warn(f"Found specification for config path '{newpath}', but file does not exist. " +
f"Old default config file path '{oldpath}' exists; using that. " +
"Please consider moving configuration to new location.")
return oldpath
# Default to the correct path if both are configured.
# neither old, nor new path exist: create new path, return that
os.makedirs(newpath, exist_ok=True)
return newpath
except ImportError:
log.warn("Could not retrieve GNU Radio configuration directory from GNU Radio. Trying defaults.")
xdgconf = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config"))
xdgcand = os.path.join(xdgconf, config_file)
if os.path.exists(xdgcand):
return xdgcand
if os.path.exists(oldpath):
log.warn(f"Using legacy config path '{oldpath}'. Please consider moving configuration " +
f"files to '{newpath}'.")
return oldpath
# neither old, nor new path exist: create new path, return that
os.makedirs(xdgcand, exist_ok=True)
return xdgcand
def get_state_directory() -> str:
oldpath = os.path.expanduser("~/.gnuradio")
try:
from gnuradio.gr import paths
newpath = paths.persistent()
if os.path.exists(newpath):
return newpath
if os.path.exists(oldpath):
log.warn(f"Found specification for persistent state path '{newpath}', but file does not exist. " +
f"Old default persistent state path '{oldpath}' exists; using that. " +
"Please consider moving state to new location.")
return oldpath
# Default to the correct path if both are configured.
# neither old, nor new path exist: create new path, return that
os.makedirs(newpath, exist_ok=True)
return newpath
except (ImportError, NameError):
log.warn("Could not retrieve GNU Radio persistent state directory from GNU Radio. Trying defaults.")
xdgstate = os.getenv("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
xdgcand = os.path.join(xdgstate, "gnuradio")
if os.path.exists(xdgcand):
return xdgcand
if os.path.exists(oldpath):
log.warn(f"Using legacy state path '{oldpath}'. Please consider moving state " +
f"files to '{newpath}'.")
return oldpath
# neither old, nor new path exist: create new path, return that
os.makedirs(xdgcand, exist_ok=True)
return xdgcand
def main():
grc_version_from_config = ""
grc_qt_config_file = get_config_file_path('grc_qt.conf')
if os.path.isfile(grc_qt_config_file):
try:
from qtpy.QtCore import QSettings
qsettings = QSettings(grc_qt_config_file, QSettings.IniFormat)
grc_version_from_config = qsettings.value('grc/default_grc', "", type=str)
except Exception as e:
log.warning("main.py could not read grc_qt.conf")
log.warning(e)
# Argument parsing
parser = argparse.ArgumentParser(
description=VERSION_AND_DISCLAIMER_TEMPLATE % gr.version())
parser.add_argument('flow_graphs', nargs='*')
# Custom Configurations
# TODO: parser.add_argument('--config')
# Logging support
parser.add_argument('--log', choices=['debug', 'info', 'warning', 'error', 'critical'], default='info')
# TODO: parser.add_argument('--log-output')
# Graphics framework (QT or GTK)
gui_group = parser.add_argument_group('Framework')
gui_group_exclusive = gui_group.add_mutually_exclusive_group()
gui_group_exclusive.add_argument("--qt", dest='framework', action='store_const', const='qt',
help="GNU Radio Companion (QT)")
gui_group_exclusive.add_argument("--gtk", dest='framework', action='store_const', const='gtk',
help="GNU Radio Companion (GTK)")
# Default options if not already set with add_argument()
args = parser.parse_args()
# Print the startup message
py_version = sys.version.split()[0]
log.info("Starting GNU Radio Companion {} (Python {})".format(gr.version(), py_version))
# File logging
log_file = os.path.join(get_state_directory(), "grc.log")
try:
fileHandler = logging.FileHandler(log_file)
file_msg_format = '%(asctime)s [%(levelname)s] %(message)s'
if args.log == 'debug':
file_msg_format += ' (%(name)s:%(lineno)s)'
fileHandler.setLevel(logging.DEBUG)
log.info(f'Logging to {log_file} (DEBUG and higher)')
else:
fileHandler.setLevel(logging.INFO)
log.info(f'Logging to {log_file} (INFO and higher)')
file_formatter = logging.Formatter(file_msg_format)
fileHandler.setFormatter(file_formatter)
log.addHandler(fileHandler)
except (PermissionError, FileNotFoundError) as e:
log.error(f'Cannot write to {log_file} - {e}')
# GUI Framework
if args.framework == 'qt':
run_qt(args, log)
elif args.framework == 'gtk':
run_gtk(args, log)
else: # args.framework == None
if grc_version_from_config == 'grc_qt':
run_qt(args, log)
else:
run_gtk(args, log)