3
# Pybik -- A 3 dimensional magic cube game.
4
# Copyright © 2009, 2011-2012 B. Clausius <barcc@gmx.de>
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
# Original filename: main.c
21
# Original copyright and license: 1998, 2003--2004 John Darrington, GPL3+
23
from __future__ import print_function, division, unicode_literals
26
from collections import namedtuple
28
from . import debug, config
33
def format_opts(opts):
34
opts = ((o + o.strip('--')[0].upper() if o.endswith('=') else o) for o in opts)
35
return ' ' + ', '.join(opts)
37
def format_help(text, indent=0):
39
width = int(os.environ['COLUMNS']) - 2
40
except (KeyError, ValueError):
44
lines = text.split('\n')
47
words = line.split(' ')
51
elif len(res + word) + 1 <= width:
52
res = ' '.join((res, word))
57
return '\n'.ljust(indent+1).join(split(text))
59
def print_usage(unused_value, unused_opts):
60
print('Usage:', sys.argv[0], '[options]')
61
for args, unused_func, text in arg_info:
64
print(format_help(text))
66
opts = format_opts(args)
71
text = format_help(text, 20)
75
def print_version(*unused_args):
76
print(config.APPNAME, config.VERSION)
78
print(config.COPYRIGHT.replace('©', '(C)')) #XXX: help2man cannot handle unicode
80
print(config.wrap(config.LICENSE_INFO))
82
print(config.wrap(config.LICENSE_NOT_FOUND))
85
debug_level_names = [__a[6:].lower() for __a in debug.__all__ if __a.startswith('DEBUG_')]
88
(None, None, config.LONG_DESCRIPTION.replace('ő', 'o')), #XXX: help2man cannot handle unicode
89
(None, None, 'Pybik can be fully controlled and configured via the graphical user interface.'
90
' The options listed here are intended primarily for testing and debugging.'),
91
(None, None, 'Options:'),
92
(['-h', '--help'], print_usage,
93
'Show help message and exit'),
94
(['--version'], print_version,
95
'Show version number and exit'),
96
(['--config-file='], lambda value, opts: opts.setdefault('config-file', value),
97
'Specify the configuration file to use instead of the default location'),
98
(['--defaultconfig'], lambda value, opts: opts.setdefault('defaultconfig', True),
99
'Print default settings to stdout and exit'),
100
(['--pyside'], lambda value, opts: opts.setdefault('pyside', True),
101
'Use PySide, cannot combined with --pyqt4'),
102
(['--pyqt4'], lambda value, opts: opts.setdefault('pyqt4', True),
103
'Use PyQt4, cannot be combined with --pyside,'
104
' is neither --pyside nor --pyqt4 is given, the application'
105
' first imports PySide and if that fails it imports PyQt4'),
106
(['--pure-python'], lambda value, opts: opts.setdefault('pure-python', True),
107
'Use python module for rendering (very slow)'),
108
(['--debug='], lambda value, opts: opts.setdefault('debug', value),
109
'Enable debug output, D is a comma-separated list of debug flags:\n{0}'
110
.format(' '.join(debug_level_names))),
111
(None, None, 'Qt-Options, for a full list refer to the Qt-Reference,'
112
' but not all options make sense for this application:'),
114
'sets the application GUI style. Possible values are'
115
' motif, windows, and platinum.'),
116
(['-widgetcount'], None,
117
'prints debug message at the end about number of widgets left'
118
' undestroyed and maximum number of widgets existed at the same time'),
120
"sets the application's layout direction to left-to-right"),
121
(['-graphicssystem G'], None,
122
'sets the backend to be used for on-screen widgets and pixmaps.'
123
' Available options are raster and opengl.'),
126
def parse_args(args):
127
arg_functs = {o: f for opts, f, h in arg_info for o in opts or [] if f is not None}
132
index = arg.index('=')
136
arg, value = arg[:index+1], arg[index+1:]
138
func = arg_functs[arg]
146
print('You cannot use both --pyside and --pyqt4')
156
debug_flags = opts.get('debug', None)
157
if debug_flags is not None:
158
debug_flags = [f for f in debug_flags.split(',') if f]
159
for d in debug_flags:
160
if d not in debug_level_names:
161
print('unknown debug option:', d)
164
Opts = namedtuple('Opts', 'debug_flags pythonqt pure_python ui_args config_file defaultconfig')
165
return Opts(debug_flags, pythonqt, opts.get('pure-python', False), ui_args,
166
opts.get('config-file', config.USER_SETTINGS_FILE), opts.get('defaultconfig', False))
168
def import_ext(pythonqt, pure_python):
169
if pythonqt != 'pyqt4':
170
debug.debug('Importing PySide')
172
import PySide, PySide.QtCore, PySide.QtGui, PySide.QtOpenGL
173
except ImportError as e:
174
debug.debug(' Failed')
175
if pythonqt == 'pyside':
176
print('PySide not found.', e)
179
if pythonqt == 'pyqt4':
180
debug.debug('Importing PyQt4')
183
sip.setapi('QString', 2)
184
sip.setapi('QVariant', 2)
188
import PyQt4.QtOpenGL
189
except ImportError as e:
190
debug.debug(' Failed')
191
print('Python bindings for Qt4 not found.', e)
193
sys.modules[b'PySide'] = PyQt4
194
sys.modules[b'PySide.QtCore'] = PyQt4.QtCore
195
sys.modules[b'PySide.QtGui'] = PyQt4.QtGui
196
sys.modules[b'PySide.QtOpenGL'] = PyQt4.QtOpenGL
197
# Monkey patch QtCore
198
# pylint: disable=E1101
199
PyQt4.QtCore.Signal = PyQt4.QtCore.pyqtSignal
200
PyQt4.QtCore.Slot = PyQt4.QtCore.pyqtSlot
201
PyQt4.QtCore.Qt.CursorShape.CrossCursor = PyQt4.QtCore.Qt.CrossCursor
202
PyQt4.QtCore.Qt.TransformationMode.SmoothTransformation = \
203
PyQt4.QtCore.Qt.SmoothTransformation
204
# Monkey patch QtOpenGL
205
PyQt4.QtOpenGL.QGLContext.BindOption.LinearFilteringBindOption = \
206
PyQt4.QtOpenGL.QGLContext.LinearFilteringBindOption
207
PyQt4.QtOpenGL.QGLContext.BindOption.MipmapBindOption = PyQt4.QtOpenGL.QGLContext.MipmapBindOption
208
# pylint: enable=E1101
211
import PySide, PySide.QtCore, PySide.QtGui, PySide.QtOpenGL # pylint: disable=W0404
212
debug.debug('Qt modules:', PySide.__name__, PySide.QtCore.__name__,
213
PySide.QtGui.__name__, PySide.QtOpenGL.__name__)
218
sys.modules[b'pybiklib.gldraw'] = _gldraw
219
sys.modules[b'_gldraw'] = _gldraw
220
except ImportError as e:
222
print('Unable to import module _gldraw, using slow fallback.')
227
sys.modules[b'pybiklib.glarea'] = _glarea
228
except ImportError as e:
229
del sys.modules[b'pybiklib.gldraw']
230
del sys.modules[b'_gldraw']
232
print('Unable to import module _glarea, using slow fallback.')
235
from OpenGL import GL as unused_GL
236
except ImportError as e:
237
print('Python bindings for OpenGL not found.', e)
241
def run_app(root_dir, args, config_file):
243
from PySide.QtCore import QLocale, QTranslator, QTimer
244
from PySide.QtGui import QApplication, QPalette, QColor
246
# initialize QApplication
247
app = QApplication(args)
248
args = app.arguments()[1:]
250
print('Unknown arguments:', ' '.join(args))
252
app.setOrganizationName(config.PACKAGE)
253
app.setApplicationName(config.APPNAME)
254
app.setApplicationVersion(config.VERSION)
256
# Workaround for whatsThis-Text (white text on light background)
257
palette = app.palette()
258
colorfg = palette.color(QPalette.Active, QPalette.ToolTipText)
259
colorbg = palette.color(QPalette.Active, QPalette.ToolTipBase)
260
valuefg = colorfg.value()
261
valuebg = (valuefg + 255) // 2 if valuefg < 128 else valuefg // 2
262
colorbg = QColor.fromHsv(colorbg.hue(), colorbg.saturation(), valuebg)
263
palette.setColor(QPalette.Active, QPalette.ToolTipBase, colorbg)
264
app.setPalette(palette)
266
# initialize translation
267
language = QLocale.system().name()
268
# standard Qt translation, used for e.g. standard buttons and shortcut names
269
translator = QTranslator()
270
translator.load('qt_' + language, config.QT_LOCALE_DIR)
271
app.installTranslator(translator)
272
# the rest of the app use gettext for translation
273
if root_dir == sys.prefix:
274
# normal installation
277
# different root, e.g. /usr/local, source directory
278
LOCALEDIR = config.LOCALE_DIR
279
t = gettext.translation(config.PACKAGE, LOCALEDIR, languages=[language], fallback=True)
280
t.install(unicode=True, names=['ngettext'])
282
# initialize settings
283
from .settings import settings
285
settings.load(config_file)
286
except EnvironmentError as e:
287
error_message = N_('An error occurred while reading the settings:\n'
288
'{error_message}').format(error_message=e)
289
debug.debug(error_message)
291
QTimer.singleShot(0, lambda: window.error_dialog(_(error_message)))
294
# The application module can be imported only if a QApplication object is created
295
from .application import MainWindow
296
window = MainWindow()
298
# run the application
302
def run(root_dir=None):
303
opts = parse_args(sys.argv)
305
if opts.debug_flags is not None:
306
print(config.PACKAGE, config.VERSION)
307
debug.set_flags(opts.debug_flags)
308
print('debug flags:', *opts.debug_flags)
309
debug.debug('Qt args:', opts.ui_args)
311
if opts.defaultconfig:
312
from .settings import settings
314
settings.keystore.dump(sys.stdout, all=True)
317
import_ext(opts.pythonqt, opts.pure_python)
319
run_app(root_dir, opts.ui_args, opts.config_file)