Always open console on Windows (but hide it)

Otherwise program crashes when trying to communicate with subprocess
(e.g. soapy_power) if started in pythonw.exe, bacause console is missing.
This commit is contained in:
Michal Krenek (Mikos) 2017-03-30 14:27:33 +02:00
parent e9391aedc0
commit 9d21d49157
11 changed files with 247 additions and 37 deletions

View File

@ -2,4 +2,3 @@ include LICENSE
include README.rst
include qspectrumanalyzer.desktop
include qspectrumanalyzer.png
include qspectrumanalyzer.svg

View File

@ -2,7 +2,7 @@
pkgname=qspectrumanalyzer
pkgver=2.1.0
pkgrel=1
pkgdesc="Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, rx_power, rtl_power, hackrf_sweep and other backends)"
pkgdesc="Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, rtl_power, hackrf_sweep, rx_power and other backends)"
arch=('any')
url="https://github.com/xmikos/qspectrumanalyzer"
license=('GPL3')

View File

@ -2,7 +2,7 @@ QSpectrumAnalyzer
=================
Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power,
rx_power, rtl_power, hackrf_sweep and other backends)
rtl_power, hackrf_sweep, rx_power and other backends)
Screenshots
-----------

View File

@ -1,5 +0,0 @@
#!/usr/bin/env python3
from qspectrumanalyzer.__main__ import main
if __name__ == '__main__':
main()

31
qspectrumanalyzer/__main__.py Executable file → Normal file
View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
import sys, signal, time
import sys, os, signal, time, argparse
from Qt import QtCore, QtGui, QtWidgets, __binding__
@ -17,6 +17,8 @@ from qspectrumanalyzer.ui_qspectrumanalyzer_persistence import Ui_QSpectrumAnaly
from qspectrumanalyzer.ui_qspectrumanalyzer_colors import Ui_QSpectrumAnalyzerColors
from qspectrumanalyzer.ui_qspectrumanalyzer import Ui_QSpectrumAnalyzerMainWindow
debug = False
# Allow CTRL+C and/or SIGTERM to kill us (PyQt blocks it otherwise)
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
@ -240,6 +242,10 @@ class QSpectrumAnalyzerMainWindow(QtWidgets.QMainWindow, Ui_QSpectrumAnalyzerMai
super().__init__(parent)
self.setupUi(self)
# Set window icon
icon_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "qspectrumanalyzer.svg")
self.setWindowIcon(QtGui.QIcon(icon_path))
# Create plot widgets and update UI
self.spectrumPlotWidget = SpectrumPlotWidget(self.mainPlotLayout)
self.waterfallPlotWidget = WaterfallPlotWidget(self.waterfallPlotLayout, self.histogramPlotLayout)
@ -612,7 +618,28 @@ class QSpectrumAnalyzerMainWindow(QtWidgets.QMainWindow, Ui_QSpectrumAnalyzerMai
def main():
app = QtWidgets.QApplication(sys.argv)
global debug
# Parse command line arguments
parser= argparse.ArgumentParser(
prog="qspectrumanalyzer",
description="Spectrum analyzer for multiple SDR platforms",
)
parser.add_argument("--debug", action="store_true",
help="detailed debugging messages")
parser.add_argument("--version", action="version",
version="%(prog)s {}".format(__version__))
args, unparsed_args = parser.parse_known_args()
debug = args.debug
# Hide console window on Windows
if sys.platform == 'win32' and not debug:
from qspectrumanalyzer import windows
if windows.is_attached_console_visible():
windows.set_attached_console_visible(False)
# Start PyQt application
app = QtWidgets.QApplication(sys.argv[:1] + unparsed_args)
app.setOrganizationName("QSpectrumAnalyzer")
app.setOrganizationDomain("qspectrumanalyzer.eutopia.cz")
app.setApplicationName("QSpectrumAnalyzer")

View File

@ -82,10 +82,11 @@ class BasePowerThread(QtCore.QThread):
"""Terminate power process"""
with self._shutdown_lock:
if self.process:
try:
self.process.terminate()
except ProcessLookupError:
pass
if self.process.poll() is None:
try:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None

View File

@ -140,13 +140,14 @@ class PowerThread(BasePowerThread):
"""Stop soapy_power process"""
with self._shutdown_lock:
if self.process:
try:
if sys.platform == 'win32':
self.process.send_signal(signal.CTRL_BREAK_EVENT)
else:
self.process.terminate()
except ProcessLookupError:
pass
if self.process.poll() is None:
try:
if sys.platform == 'win32':
self.process.send_signal(signal.CTRL_BREAK_EVENT)
else:
self.process.terminate()
except ProcessLookupError:
pass
self.process.wait()
self.process = None

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
id="svg4290"
version="1.1"
inkscape:version="0.91 r13725"
viewBox="0 0 32 32"
inkscape:export-filename="qspectrumanalyzer.png"
inkscape:export-xdpi="135"
inkscape:export-ydpi="135"
sodipodi:docname="qspectrumanalyzer.svg">
<defs
id="defs4292" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6568543"
inkscape:cx="0.51171273"
inkscape:cy="17.429759"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:snap-intersection-paths="true"
inkscape:snap-bbox="false"
inkscape:snap-nodes="true"
inkscape:object-nodes="true"
inkscape:snap-global="false"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="-4"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid4298" />
</sodipodi:namedview>
<metadata
id="metadata4295">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#000000;stroke:#666666;stroke-opacity:1"
id="rect4300"
width="30"
height="30"
x="1"
y="1"
rx="3"
ry="3" />
<path
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 16,1 0,30"
id="path4302"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 31,16 1,16"
id="path4302-5"
inkscape:connector-curvature="0" />
<use
x="0"
y="0"
xlink:href="#path4302"
id="use4369"
transform="translate(-7.5,0)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#path4302"
id="use4371"
transform="translate(7.5,0)"
width="100%"
height="100%" />
<path
inkscape:connector-curvature="0"
id="path4373"
d="M 31,8.5 1,8.5"
style="fill:none;fill-rule:evenodd;stroke:#666666;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<use
x="0"
y="0"
xlink:href="#path4302-5"
id="use4375"
transform="translate(0,7.5)"
width="100%"
height="100%" />
<path
style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1.0625,25.25 c 0,0 2.5545099,5.432144 4.3470654,0.903939 0.6415968,-1.620748 1.1843693,-10.505075 2.3294368,-10.451145 1.124605,0.05297 1.2928473,9.79388 3.0231028,11.399454 0.836834,0.735662 2.019611,-2.789575 2.96484,-2.691821 1.178304,0.121857 2.125956,3.657855 3.181223,2.383073 2.07617,-2.508052 1.527752,-21.6078848 2.588897,-21.5746877 1.200441,0.037555 1.333727,19.4989257 3.765313,21.7522057 1.533625,1.421166 1.791478,-2.106359 3.115303,-2.461012 0.660576,-0.176968 2.034012,3.420657 2.702589,3.52241 0.772278,0.117536 1.624099,-1.153766 2.01348,-0.813666"
id="path4394"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscssssssc" />
<rect
style="fill:none;stroke:#666666;stroke-opacity:1"
id="rect4300-8"
width="30"
height="30"
x="1"
y="1"
rx="3"
ry="3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
"""Windows-specific utilities"""
from ctypes import windll
# --- Window control ---
SW_SHOW = 5 # activate and display
SW_SHOWNA = 8 # show without activation
SW_HIDE = 0
GetConsoleWindow = windll.kernel32.GetConsoleWindow
ShowWindow = windll.user32.ShowWindow
IsWindowVisible = windll.user32.IsWindowVisible
# Handle to console window associated with current Python
# interpreter procss, 0 if there is no window
console_window_handle = GetConsoleWindow()
def set_attached_console_visible(state):
"""Show/hide system console window attached to current process.
Return it's previous state.
Availability: Windows"""
flag = {True: SW_SHOW, False: SW_HIDE}
return bool(ShowWindow(console_window_handle, flag[state]))
def is_attached_console_visible():
"""Return True if attached console window is visible"""
return IsWindowVisible(console_window_handle)
def set_windows_appusermodelid():
"""Make sure correct icon is used on Windows 7 taskbar"""
try:
return windll.shell32.SetCurrentProcessExplicitAppUserModelID("spyder.Spyder")
except AttributeError:
return "SetCurrentProcessExplicitAppUserModelID not found"
# [ ] the console state asks for a storage container
# [ ] reopen console on exit - better die open than become a zombie

View File

@ -27,7 +27,7 @@ try:
setup_entry_points = {
"console_scripts": [
Executable('QSpectrumAnalyzer=qspectrumanalyzer.__main__:main',
console=False, icon_file='qspectrumanalyzer.ico'),
console=True, icon_file='qspectrumanalyzer.ico'),
Executable('soapy_power=soapypower.__main__:main',
console=True),
],
@ -39,7 +39,8 @@ except ImportError:
setup(
name="QSpectrumAnalyzer",
version=__version__,
description="Spectrum analyzer for multiple SDR platforms (PyQtGraph based GUI for soapy_power, rx_power, rtl_power, hackrf_sweep and other backends)",
description=("Spectrum analyzer for multiple SDR platforms "
"(PyQtGraph based GUI for soapy_power, rtl_power, hackrf_sweep, rx_power and other backends)"),
long_description=open('README.rst').read(),
author="Michal Krenek (Mikos)",
author_email="m.krenek@gmail.com",
@ -48,19 +49,20 @@ setup(
packages=["qspectrumanalyzer", "qspectrumanalyzer.backends"],
package_data={
"qspectrumanalyzer": [
"qspectrumanalyzer.svg",
"*.ui",
"languages/*.qm",
"languages/*.ts"
]
"languages/*.ts",
],
},
data_files=[
("share/applications", ["qspectrumanalyzer.desktop"]),
("share/pixmaps", ["qspectrumanalyzer.png"])
("share/pixmaps", ["qspectrumanalyzer.png"]),
],
install_requires=[
"soapy_power>=1.5.0",
"soapy_power>=1.6.0",
"pyqtgraph>=0.10.0",
"Qt.py"
"Qt.py",
],
classifiers=[
"Development Status :: 4 - Beta",
@ -75,17 +77,26 @@ setup(
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Topic :: Communications :: Ham Radio",
"Topic :: Scientific/Engineering :: Visualization"
"Topic :: Scientific/Engineering :: Visualization",
],
options={
'build_qt': {
'packages': ['qspectrumanalyzer'],
'languages': ['cs'],
'replacement_bindings': 'Qt'
'replacement_bindings': 'Qt',
},
'build_exe': {
'datas': [
('qspectrumanalyzer/qspectrumanalyzer.svg', 'qspectrumanalyzer'),
('qspectrumanalyzer/*.ui', 'qspectrumanalyzer'),
('qspectrumanalyzer/languages/*.ts', 'qspectrumanalyzer/languages'),
('qspectrumanalyzer/languages/*.qm', 'qspectrumanalyzer/languages'),
('README.rst', '.'),
('LICENSE', '.'),
],
},
'build_exe': {},
'bdist_msi': {
'upgrade_code': '30740ef4-84e7-4e67-8e4a-12b53492c387',
'upgrade_code': '{30740EF4-84E7-4E67-8E4A-12B53492C387}',
'shortcuts': [
'ProgramMenuFolder\\QSpectrumAnalyzer=QSpectrumAnalyzer',
],

View File

@ -1,5 +0,0 @@
#!/usr/bin/env python3
from soapypower.__main__ import main
if __name__ == '__main__':
main()