# -*- coding: utf-8 -*-
# cython: profile=False

#  Copyright © 2015-2017  B. Clausius <barcc@gmx.de>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

# pylint: disable=C0326,W0614
# although this file is compiled with Python3 syntax, Cython needs at least division from __future__
from __future__ import print_function, division

# This line makes cython happy
global __name__, __package__    # pylint: disable=W0604
#px/__compiled = True
__compiled = False
#px/DEF OFFSCREEN = False
OFFSCREEN = False

import sys, os
from warnings import warn_explicit, warn

#px+cimport gl_[[GLVARIANT]] as gl

from pybiklib import config as config_
from pybiklib.settings import settings

#px/cimport [[_glarea_VARIANT]] as glarea
from . import glarea
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#px/cimport [[_qtui_VARIANT]] as qtui
from . import qtui
#pxd+from qt cimport *
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxd+from qtq cimport *
#pxm>IF_END
#px/cimport qt as Qt
from .debug_purepython import *

#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxh>#include "Python.h"
#pxm>IF_END
#pxh>#include <QtCore/QObject>
#pxh>#include <QtCore/QVariant>
#pxh>#include <QtGui/QImage>
#pxh>#include <QtGui/QOpenGLDebugLogger>
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxh>#include <QtGui/QPainter>
#pxh>#include <QtGui/QDesktopServices>
#pxh>#include <QtGui/QStandardItemModel>
#pxh>#include <QtWidgets/QOpenGLWidget>
#pxh>#include <QtWidgets/QMainWindow>
#pxh>#include <QtWidgets/QLineEdit>
#pxh>#include <QtWidgets/QLabel>
#pxh>#include <QtWidgets/QPushButton>
#pxh>#include <QtWidgets/QTreeView>
#pxh>#include <QtWidgets/QDialog>
#pxh>#include <QtWidgets/QListWidget>
#pxh>#include <QtWidgets/QListWidgetItem>
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxh>#include <QtQuick/QQuickItem>
#pxh>#include <QtQuick/QQuickView>
#pxm>IF_END
#pxh>
#pxc>#include "[[_qt_VARIANT]]_moc.h"
#pxc>

#pxd>cdef extern from "[[_qt_VARIANT]]_moc.h":#
#pxm>DINDENT

#px/from libc.stdio cimport printf
def printf(fmt, *args): print(fmt % args, end='')
#px/from libc.stdio cimport puts
puts = print

#px/cdef enum: #
if True:
    DEBUG_MSG = 0x0002
    DEBUG_MSGGL = 0x0004
    DEBUG_MSGEXT = 0x0020
    DEBUG_DRAW = 0x0040
    DEBUG_FPS = 0x0080
    DEBUG_VFPS = 0x0100
    DEBUG_QML = 0x0200
    DEBUG_GLDEBUG = 0x0400
    DEBUG_PHONE = 0x0800
    DEBUG_SIM = 0x1000
    DEBUG_NOCONTROLS = 0x2000
#px+cdef long debug
debug = 0
    
def set_debug_flags(module):
    global debug
    if module.DEBUG_MSG:   debug |= DEBUG_MSG
    if module.DEBUG_MSGGL: debug |= DEBUG_MSGGL
    if module.DEBUG_MSGEXT: debug |= DEBUG_MSGEXT
    if module.DEBUG_DRAW: debug |= DEBUG_DRAW
    if module.DEBUG_FPS: debug |= DEBUG_FPS
    if module.DEBUG_VFPS: debug |= DEBUG_VFPS
    if module.DEBUG_QML: debug |= DEBUG_QML
    if module.DEBUG_GLDEBUG: debug |= DEBUG_GLDEBUG
    if module.DEBUG_PHONE: debug |= DEBUG_PHONE
    if module.DEBUG_SIM: debug |= DEBUG_SIM
    if module.DEBUG_NOCONTROLS: debug |= DEBUG_NOCONTROLS
    

# Workaround to compile on xenial:
# error: calls to overloaded operators cannot appear in a constant-expression
# failed lines:
# #px+cdef enum:
#     #px/SAFE_MODIFIER_MASK = Qt.ShiftModifier | Qt.ControlModifier | Qt.KeypadModifier
#px+cdef enum:
    #px+SHIFTMODIFIER = Qt.ShiftModifier
    #px+CONTROLMODIFIER = Qt.ControlModifier
    #px+KEYPADMODIFIER = Qt.KeypadModifier
#px+cdef enum:
    #px/SAFE_MODIFIER_MASK = SHIFTMODIFIER | CONTROLMODIFIER | KEYPADMODIFIER
SAFE_MODIFIER_MASK = int(Qt.ShiftModifier | Qt.ControlModifier | Qt.KeypadModifier)
#pxm>IF '[[QTVARIANT]]' == 'qtq'
IGNORE_KEYS = [Qt.Key_Shift, Qt.Key_Control, Qt.Key_Meta, Qt.Key_Alt, Qt.Key_AltGr,
               Qt.Key_CapsLock, Qt.Key_NumLock, Qt.Key_ScrollLock]
#pxm>IF_END
class QtKeys:
    Key_Period = Qt.Key_Period
    Key_Escape = Qt.Key_Escape
    Key_Right = Qt.Key_Right
    Key_Left = Qt.Key_Left
    Key_Up = Qt.Key_Up
    Key_Down = Qt.Key_Down
    ShiftModifier = Qt.ShiftModifier
    ControlModifier = Qt.ControlModifier
    LeftButton = Qt.LeftButton
    RightButton = Qt.RightButton


#### Data ####

#px+ctypedef QObject *pQObject
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#px+ctypedef QQuickItem *pQQuickItem
#pxm>IF_END

#px/cdef struct RenderData:
class renderdata: pass
    #px+QOpenGLTexture* texture
    #px+int atlas_width
    #px+int atlas_height
    #px+QOpenGLFramebufferObject* pickbuffer
    #px+QOpenGLFramebufferObject* offscreenbuffer
    #px+QElapsedTimer *fps_monotonic_time
    #px+int fps_count
    
    #px+cbool initialized
    #px+Renderer *renderer
    #px+cbool atlas_changed
    #px+cbool pickxy_changed
    #px+QOpenGLDebugLogger *gldebuglogger
#px+cdef RenderData renderdata

#px+DEF maxproxy = 21

#px/cdef struct UIData:
class uidata:
    #px+cbool atlas_changed
    #px+int atlas_width
    #px+int atlas_height
    #px+cbool pick_requested
    
    #px+float grid_unit_px
    #px+int width
    #px+int height
    #px+int gl_y
    
    #px+QFileSystemWatcher* filesystemwatcher
    #px+QTimer *settings_timer
    #px+QSurfaceFormat default_format
    #px/QCursor *cursors[18]
    cursors = [None]*18
    #px+QImage offscreen_image
    
    #px+MainView *mainwindow
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px+int modelselect_currentpage
    #px+float speed
    #px+float animation_angle
    #px+float angle_max
    #px+QTimer *animate_timer
    #px+QStandardItemModel *treestore
    #px+MoveEdit *move_edit
    #px+QLabel *statuslabel
    #px+DrawingArea *drawingarea
    #px+PreferencesDialog *preferences
    #px+HelpDialog *help
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    #px+cbool model_pagevisible
    #px+int entered
    #px+QQmlEngine *engine
    #px+QObject *ctx
    #pxm>IF_END
#px+cdef UIData uidata

class PyData:
    filesystemwatcher_path = None
    filesystemwatcher_handlers = {}
    #px+singleshot_handler = None
    #px+singleshot_queue = []
    app = None
    modelselect = None
pydata = PyData()

#pxm-FUNC P
def init_module():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    qtui.init_module()
    #pxm>IF_END
    if debug & DEBUG_MSGEXT:
        print('init module:', __name__)
        print('  from package:', __package__)
        print('  compiled:', __compiled)
        #px+print('  GL-type: [[GLVARIANT]]')
        print('  OFFSCREEN:', OFFSCREEN)
        
    renderdata.renderer = NULL
    uidata.atlas_changed = False
    uidata.pick_requested = False
    uidata.filesystemwatcher = NULL
    uidata.width = 0
    uidata.height = 0
    uidata.gl_y = 0
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.help = NULL
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    uidata.model_pagevisible = False
    uidata.entered = False
    #pxm>IF_END
    

#### Helpers ####

#pxm-FUNC P
def q2str(qstr:str)->'':  return qstr
    #px+cdef QByteArray data = qstr.toUtf8()
    #px+return data.data()[:data.size()].decode('utf-8')
#pxm-FUNC P
def str2q(pstr)->str:  return pstr
    #px+data = pstr.encode('utf-8')
    #px+return fromUtf8(<char*>data, len(data))

#px+cdef QVariantList _list2q(value):
#px+    cdef QVariantList qlist
#px+    cdef QVariant qvalue
#px+    cdef int i
#px+    qlist.reserve(len(value))
#px+    for i in range(len(value)):
#px+        qvalue = tovariant(value[i])
#px+        qlist.append(qvalue)
#px+    return qlist
    
#pxm-FUNC P
def tovariant(value)->'QVariant':
    #px/cdef QVariant qvalue
    qvalue = list(value) if type(value) == tuple else value
    #px+if type(value) is bool:
    #px+    qvalue.setValue[cbool](value)
    #px+elif type(value) is int:
    #px+    qvalue.setValue[int](value)
    #px+elif type(value) is float:
    #px+    qvalue.setValue[double](value)
    #px+elif type(value) is str:
    #px+    qvalue.setValue[QString](str2q(value))
    #px+elif type(value) is list or type(value) is tuple:
    #px+    qvalue.setValue[QVariantList](_list2q(value))
    #px+else:
    #px+    print('value has wrong typ ' + repr(type(value)))
    return qvalue
    
#px+cdef _q2list(const QVariantList &ql):
#px+    return [fromvariant(ql.at(i)) for i in range(ql.size())]
#px+cdef _qs2list(const QStringList &ql):
#px+    return [q2str(ql[i]) for i in range(ql.size())]
#px+cdef _q2dict(const QVariantMap &qm):
#px+    cdef QStringList qkeys = qm.keys()
#px+    cdef QVariantList qvals = qm.values()
#px+    return {k:v for k,v in zip(_qs2list(qkeys), _q2list(qvals))}
    
#pxm-FUNC P
def fromvariant(qvalue:'const QVariant &')->'':  return qvalue
    #px+cdef const char* typename = qvalue.typeName()
    #px+if typename == NULL:
    #px+    return None
    #px+if typename == b'bool':
    #px+    return qvalue.value[cbool]()
    #px+elif typename == b'int':
    #px+    return qvalue.value[int]()
    #px+elif typename == b'double':
    #px+    return qvalue.value[double]()
    #px+elif typename == b'QString':
    #px+    return q2str(qvalue.value[QString]())
    #px+elif typename == b'void*':
    #px+    res = qvalue.value[int]()
    #px+    return None if res == 0 else '<void*>({})'.format(res)
    #px+elif qvalue.canConvert[QVariantMap]():
    #px+    return _q2dict(qvalue.value[QVariantMap]())
    #px+elif qvalue.canConvert[QVariantList]():
    #px+    return _q2list(qvalue.value[QVariantList]())
    #px+elif qvalue.canConvert[pQObject]():
    #px+    return 'QObject({})'.format(q2str(qvalue.value[pQObject]().objectName()))
    #px+else:
    #px+    warn(RuntimeWarning('unknown typename %s' % typename[:]))
    #px+    return None


#### render thread ####

#pxm-QD_CPPCLASS
class Renderer (QObject):
    #pxm-FUNC HD nogil
    def __init__(this): pass
        #pxh> : QObject() {}
    #pxm-FUNC HD nogil
    def connect_gldebuglogger(self, gldebuglogger:'QOpenGLDebugLogger *'):
    #pxh>{
        #pxh>QObject::connect(gldebuglogger, SIGNAL(messageLogged(const QOpenGLDebugMessage &)), this,
        #pxh-                   SLOT(on_messageLogged(const QOpenGLDebugMessage &)), Qt::DirectConnection);
        renderdata.gldebuglogger.messageLogged.connect(self.on_messageLogged)
    #pxh>}
    #pxh>
    #pxm-FUNC PH nogil
    def slot_on_beforeRendering(self):
        if not renderdata.initialized:
            glarea.gl_init()
            #px+IF OFFSCREEN:
            #px+    render_offscreen_init()
            renderdata.initialized = True
        #pxm>IF '[[QTVARIANT]]' == 'qtw'
        #px+IF OFFSCREEN:
        #px+    renderdata.offscreenbuffer.bind()
        #pxm>IF_END
        if renderdata.atlas_changed:
            render_update_atlas()
            renderdata.atlas_changed = False
        else:
            renderdata.texture.bind()
        glarea.gl_render()
        renderdata.texture.release()
        if debug & DEBUG_DRAW:
            glarea.gl_render_select_debug()
        if debug & DEBUG_FPS:
            render_fps()
        if renderdata.pickxy_changed:
            render_pick()
            renderdata.pickxy_changed = False
        #px+IF OFFSCREEN:
        #px+    this.offscreen_image(renderdata.offscreenbuffer.toImage())
    #pxm-FUNC PHD nogil
    def slot_on_messageLogged(self, msg:'const QOpenGLDebugMessage &'):
        #px/printf('GL%05x: %s\n', msg.id(), msg.message().toUtf8().data())
        printf('GL%05x: %s\n', msg.id(), msg.message())
    #pxh>
    #pxm:Q_SIGNAL
    picking_result = pyqtSignal(int)
    debug_fps = pyqtSignal(int)
    # only variant qtq:
    offscreen_image = pyqtSignal(QImage)
    #px.
    #pxd>void picking_result(int index) nogil
    #pxd>void debug_fps(int fps) nogil
    #pxd>void offscreen_image(QImage image) nogil
    #pxd>void on_beforeRendering() nogil
#pxm>CPPCLASS_END
        

#pxm-FUNC P nogil
def render_update_atlas():
    render_destroy_texture()
    #px/renderdata.texture = new QOpenGLTexture(Target2D)
    renderdata.texture = QOpenGLTexture(Target2D)
    renderdata.texture.setFormat(RGBA32F)
    renderdata.texture.setSize(renderdata.atlas_width, renderdata.atlas_height, 1)
    renderdata.texture.setMinMagFilters(Linear, Linear)
    renderdata.texture.allocateStorage()
    renderdata.texture.bind()
    glarea.gl_set_atlas_texture(renderdata.atlas_width, renderdata.atlas_height)
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
def gl_delete_atlas():
    if renderdata.texture is not NULL:
        renderdata.texture.destroy()
        renderdata.texture = NULL
#pxm>IF_END
        
#pxm-FUNC P nogil
def render_fps():
    #px+cdef int fps
    renderdata.fps_count += 1
    if not renderdata.fps_monotonic_time.hasExpired(1000):
        return
    fps = 1000 * renderdata.fps_count // renderdata.fps_monotonic_time.restart()
    renderdata.fps_count = 0
    #px/renderdata.renderer.debug_fps(fps)
    renderdata.renderer.debug_fps.emit(fps)
    
#pxm-FUNC P nogil
def render_pick():
    if renderdata.pickbuffer is NULL:
        #px/renderdata.pickbuffer = new QOpenGLFramebufferObject(1, 1, GL_TEXTURE_2D)
        renderdata.pickbuffer = QOpenGLFramebufferObject(1, 1, GL_TEXTURE_2D)
        renderdata.pickbuffer.setAttachment(Depth)
    renderdata.pickbuffer.bind()
    index = glarea.gl_pick_polygons()
    renderdata.pickbuffer.release()
    #px/renderdata.renderer.picking_result(index)
    renderdata.renderer.picking_result.emit(index)
    
#### beforeSynchronizing ####

#px/IF OFFSCREEN:
if True:
    #pxm-FUNC P nogil
    def render_offscreen_init():
        #px+cdef QOpenGLFramebufferObjectFormat format
        format.setSamples(16)
        #px/renderdata.offscreenbuffer = new QOpenGLFramebufferObject(uidata.width, uidata.height, format)
        renderdata.offscreenbuffer = QOpenGLFramebufferObject(uidata.width, uidata.height, format)
        renderdata.offscreenbuffer.setAttachment(Depth)
        #pxm>IF '[[QTVARIANT]]' == 'qtq'
        uidata.mainwindow.setRenderTarget(renderdata.offscreenbuffer)
        #pxm>IF_END
        #px+printf('offscreenbuffer samples=%d (%d)\n', renderdata.offscreenbuffer.format().samples(), format.samples())
    
#pxm-FUNC P nogil
def render_init():
    renderdata.texture = NULL
    renderdata.pickbuffer = NULL
    renderdata.fps_monotonic_time = NULL
    renderdata.fps_count = 0
    renderdata.atlas_width = 0
    renderdata.atlas_height = 0
    
    renderdata.initialized = False
    renderdata.atlas_changed = False
    renderdata.pickxy_changed = False
    #px/renderdata.renderer = new Renderer()
    renderdata.renderer = Renderer()
    renderdata.gldebuglogger = NULL
    
    glarea.sync_set_fixedshaders()
    if debug & DEBUG_GLDEBUG:
        render_init_gldebuglogger()
    if debug & DEBUG_FPS:
        #px/renderdata.fps_monotonic_time = new QElapsedTimer()
        renderdata.fps_monotonic_time = QElapsedTimer()
        renderdata.fps_count = 0
        renderdata.fps_monotonic_time.start()
    if debug & DEBUG_QML:
        #px/dump_info('Renderer', <QObject*>renderdata.renderer)
        dump_info('Renderer', renderdata.renderer)
    if debug & DEBUG_MSGGL:
        print_surface_info()
    
#pxm-FUNC P nogil
def render_destroy_texture():
    if renderdata.texture is not NULL:
        renderdata.texture.destroy()
        del renderdata.texture
        renderdata.texture = NULL
        
#pxm-FUNC P nogil
def render_destroy():
    render_destroy_texture()
    if renderdata.pickbuffer is not NULL:
        del renderdata.pickbuffer
        renderdata.pickbuffer = NULL
    if renderdata.fps_monotonic_time is not NULL:
        del renderdata.fps_monotonic_time
        renderdata.fps_monotonic_time = NULL
    if renderdata.gldebuglogger is not NULL:
        del renderdata.gldebuglogger
        renderdata.gldebuglogger = NULL
        
#pxm-FUNC P nogil
def render_init_gldebuglogger():
    #px+cdef OpenGLDebugMessageList messages
    #px+cdef QOpenGLDebugMessage message
    #px+cdef int i
    #px/renderdata.gldebuglogger = new QOpenGLDebugLogger(<QObject*>renderdata.renderer)
    renderdata.gldebuglogger = QOpenGLDebugLogger(renderdata.renderer)
    if not renderdata.gldebuglogger.initialize():
        puts('QOpenGLDebugLogger not successfully initialized')
        return
    renderdata.gldebuglogger.enableMessages()
    renderdata.renderer.connect_gldebuglogger(renderdata.gldebuglogger)
    renderdata.gldebuglogger.startLogging(SynchronousLogging)
    messages = renderdata.gldebuglogger.loggedMessages()
    #px/printf('QOpenGLDebugLogger successfully initialized and started (%d initial messages)\n', messages.size())
    printf('QOpenGLDebugLogger successfully initialized and started (%d initial messages)\n', len(messages))
    #px/for i in range(messages.size()):
    for message in messages:
        #px+message = messages.at(i)
        renderdata.renderer.on_messageLogged(message)
        
#pxm-FUNC P nogil
def _pfmt(name:'char *', f:int, rf:int, df:int):
    printf('  %s: %d', name, f)
    if f != rf:  printf(' (%d)', rf)
    if rf != df: printf(' [%d]', df)
    puts('')
#pxm-FUNC P nogil
def _pfmt2(name:'char *', f1:int, f2:int, rf1:int, rf2:int, df1:int, df2:int):
    printf('  %s: %d.%d', name, f1, f2)
    if f1 != rf1 or f2 != rf2:  printf(' (%d.%d)', rf1, rf2)
    if rf1 != df1 or rf2 != df2: printf(' [%d.%d]', df1, df2)
    puts('')
    
#pxm-FUNC P nogil
def print_surface_info():
    #px+cdef QOpenGLContext *glcontext
    glcontext = currentContext()
    glformat = glcontext.format()
    glrformat = defaultFormat() # requested format
    gldformat = uidata.default_format
    puts('Surface format (requested (), default []):')
    _pfmt('red', glformat.redBufferSize(), glrformat.redBufferSize(), gldformat.redBufferSize())
    _pfmt('green', glformat.greenBufferSize(), glrformat.greenBufferSize(),  gldformat.greenBufferSize())
    _pfmt('blue', glformat.blueBufferSize(), glrformat.blueBufferSize(), gldformat.blueBufferSize())
    _pfmt('alpha', glformat.alphaBufferSize(), glrformat.alphaBufferSize(), gldformat.alphaBufferSize())
    _pfmt('depth', glformat.depthBufferSize(), glrformat.depthBufferSize(), gldformat.depthBufferSize())
    _pfmt('options', glformat.options(), glrformat.options(), gldformat.options())
    _pfmt('profile', glformat.profile(), glrformat.profile(), gldformat.profile())
    _pfmt('renderableType', glformat.renderableType(), glrformat.renderableType(), gldformat.renderableType())
    _pfmt('samples', glformat.samples(), glrformat.samples(), gldformat.samples())
    _pfmt('stencil', glformat.stencilBufferSize(), glrformat.stencilBufferSize(), gldformat.stencilBufferSize())
    _pfmt('stereo', glformat.stereo(), glrformat.stereo(), gldformat.stereo())
    _pfmt('swapBehavior', glformat.swapBehavior(), glrformat.swapBehavior(), gldformat.swapBehavior())
    _pfmt('swapInterval', glformat.swapInterval(), glrformat.swapInterval(), gldformat.swapInterval())
    _pfmt2('version', glformat.majorVersion(), glformat.minorVersion(),
                     glrformat.majorVersion(), glrformat.minorVersion(),
                     gldformat.majorVersion(), gldformat.minorVersion())
    puts('OpenGL context:')
    #px+cdef int i
    i = openGLModuleType()
    if i == LibGL:
        puts('  module type: libGL')
    elif i == LibGLES:
        puts('  module type: LibGLES')
    else:
        printf('  unknown module type: %d\n', i)
    i = glcontext.isOpenGLES()
    if glcontext.isOpenGLES():
        puts('  isOpenGLES: True')
    else:
        puts('  isOpenGLES: False')
    if glcontext.hasExtension(QByteArray(b"GL_KHR_debug")):
        puts('  hasExtension GL_KHR_debug: True')
    else:
        puts('  hasExtension GL_KHR_debug: False')
    #XXX: not available in PyQt
    #puts('OpenGL functions:')
    #px+cdef QOpenGLFunctions *glFuncs = glcontext.functions()
    # needed for clang
    #px+cdef int features = glFuncs.openGLFeatures()
    #px+printf('  features: 0x%0x\n', features)
    

#### ui: communicate with render-thread  ####

def set_pick_requested(pick_requested):
    uidata.pick_requested = pick_requested
    
#pxm-FUNC P
def set_atlas_size(width:int, height:int)->'cpdef':
    uidata.atlas_changed = True
    uidata.atlas_width = width
    uidata.atlas_height = height
    
#pxm-FUNC P
def set_default_surface_format():
    #px/cdef QSurfaceFormat glformat
    glformat = QSurfaceFormat()
    #px+cdef int samples
    samples = settings['draw.samples']
    if samples > 0:
        glformat.setSamples(2**samples)
    if debug & DEBUG_VFPS:
        glformat.setSwapInterval(0)
    #px+IF '[[GLVARIANT]]' != 'ogl':
    #px+    glformat.setRenderableType(OpenGLES)
    if debug & DEBUG_GLDEBUG:
        glformat.setOption(DebugContext)
    uidata.default_format = defaultFormat()
    setDefaultFormat(glformat)
    
    
#### ui: communicate with qml ####

#pxm-QD_CPPCLASS
class SectionNameIndexItem (QObject):
    pass
    #pxm>Q_PROPERTY prop_section = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_name = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_index = 'int MEMBER'
    #pxh>
    #pxm-FUNC HD
    def __init__(section:str, name:str, index:int):  pass
    #pxh>{ m_section = section; m_name = name; m_index = index; }
#pxm>CPPCLASS_END

#pxm-QD_CPPCLASS
class TextKeyItem (QObject):
    pass
    #pxm>Q_PROPERTY prop_text = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_key = 'QString MEMBER NOTIFY'
    #pxh>
    #pxm-FUNC HD nogil
    def __init__(text:str, key:str):  pass
    #pxh>{ m_text = text; m_key = key; }
#pxm>CPPCLASS_END

#pxm-QD_CPPCLASS
class FacePrefsItem (QObject):
    pass
    #pxm>Q_PROPERTY prop_color = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_folder = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_image = 'QString MEMBER NOTIFY'
    #pxm>Q_PROPERTY prop_imagemode = 'QString MEMBER NOTIFY'
    #pxh>
    #pxm-FUNC HD nogil
    def __init__(color:str, folder:str, image:str, imagemode:str):  pass
    #pxh>{ m_color = color; m_folder = folder; m_image = image; m_imagemode = imagemode; }
#pxm>CPPCLASS_END

#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxm>QD_CPPCLASS class ContextProperty (QObject):
#px-
class ContextProperty (QObject, metaclass=MetaSlot):
    #pxd>pass
    #pxm-FUNC PH nogil
    def slot_inspect(self, msg:str, val:'const QVariant &'):
        #px/if val.canConvert[pQObject]():
        if val.canConvert(pQObject):
            #px/dump_info(msg.toUtf8().data(), val.value[pQObject]())
            dump_info(msg, val.value(QObject))
        else:
            with gil:
                if val.isValid():
                    print('{}: {}: {}'.format(q2str(msg), val.typeName()[:], fromvariant(val)))
                else:
                    print('{}: not valid: {}'.format(q2str(msg), fromvariant(val)))
    #pxm-FUNC PH nogil
    def slot_gu(self, pixel:float)->float:
        return uidata.grid_unit_px * pixel
    #pxm-FUNC PH with gil
    def slot_tr(self, string:str)->str:
        #px+try:
            return str2q(_(q2str(string)))
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_tr_tooltip(self, string:str)->str:
        #px+try:
            return str2q('') if debug & DEBUG_PHONE else str2q(_(q2str(string)))
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_config(self, attr:str)->str:
        #px+try:
            return str2q(getattr(config_, q2str(attr)))
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_config_tr(self, attr:str)->str:
        #px+try:
            return str2q(_(getattr(config_, q2str(attr))))
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_get_settings(self, key:str)->'QVariant':
        #px+try:
            return tovariant(settings[q2str(key)])
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_set_settings(self, key:str, value:'const QVariant &'):
        #px/try:
            assert not isinstance(value, QVariant)
            settings[q2str(key)] = fromvariant(value)
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_set_settings_arg(self, key:str, arg:str, value:'const QVariant &'):
        #px/try:
            assert not isinstance(value, QVariant)
            #px/if not arg.isEmpty():
            if arg:
                settings[q2str(key).format(q2str(arg))] = fromvariant(value)
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_set_settings_arg_url(self, key:str, arg:str, qurl:'const QUrl &'):
        #px/try:
            assert isinstance(qurl, QUrl)
            #px/if arg.isEmpty():  return
            if not arg:  return
            url = q2str(qurl.toString())
            if not url.startswith('file:///'):  return
            settings[q2str(key).format(q2str(arg))] = url[7:]
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_del_settings(self, key:str):
        #px+try:
            del settings[q2str(key)]
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH with gil
    def slot_del_settings_arg(self, key:str, arg:str):
        #px+try:
            #px/if not arg.isEmpty():
            if arg:
                del settings[q2str(key).format(q2str(arg))]
        #px+except:  sys.excepthook(*sys.exc_info())
    #pxm-FUNC PH nogil
    def slot_singlewin(self)->bool:
        return debug & DEBUG_PHONE
    #pxm-FUNC PH nogil
    def slot_absolute_pos(self, item:'QQuickItem *', x:'qreal', y:'qreal')->'QPointF':
        #TODO: can this be replaced by mapToItem?
        #      ctx.absolute_pos(item, x, y) =?= item.mapToItem(ctx.rootitem(), x, y)
        #px/cdef QPointF pos, parentpos
        pos = QPointF()
        if item is NULL:
            pos.setX(x)
            pos.setY(y)
        else:
            #px/parentpos = absolute_pos(item.parentItem(), x, y)
            parentpos = self.absolute_pos(item.parentItem(), x, y)
            pos.setX(parentpos.x() + item.x())
            pos.setY(parentpos.y() + item.y())
        return pos
    #pxm-FUNC PH nogil
    def slot_window_pos(self, item:'QQuickItem *')->'QPointF':
        #px+cdef QQuickWindow *window
        window = item.window()
        #px/cdef QPointF pos
        pos = QPointF()
        pos.setX(window.x())
        pos.setY(window.y())
        return pos
    #pxm-FUNC PH nogil const
    def slot_rootitem(self)->'QObject *':
        return uidata.mainwindow.contentItem()
    #pxm-FUNC PH with gil
    def slot_animation_step(self, angle:'qreal'):
        glarea.set_animation_next(angle)
        update_drawingarea()
    #pxm-FUNC PH with gil
    def slot_animation_stopped(self):
        ctx_set(b'animation_running', False)
        ctx_set(b'animation_angle', 0)
        pydata.app.on_animation_ending()
    #pxm-FUNC PH with gil
    def slot_plugin_activated(self, index:int):
        pydata.app.on_plugin_activated(index)
    #pxm-FUNC PH with gil
    def slot_editing_finished(self, code:str, pos:int):
        pydata.app.on_edit_finished(q2str(code), pos)
    #pxm-FUNC PH with gil
    def slot_action(self, string:str):
        getattr(pydata.app, 'on_action_%s_triggered' % q2str(string))()
        
    #pxm-FUNC PH with gil
    def slot_key_pressed(self, key:int, modifiers:int, fromdrawingarea:bool)->bool:
        return pydata.app.on_key_pressed(key, modifiers & SAFE_MODIFIER_MASK, fromdrawingarea)
    #pxm-FUNC PH with gil
    def slot_rotation_changed(self, dx:int, dy:int):
        pydata.app.on_camrotation_changed(dx, dy)
    #pxm-FUNC PH nogil
    def slot_drawingarea_width_changed(self, width:int):
        uidata.width = width
    #pxm-FUNC PH nogil
    def slot_drawingarea_height_changed(self, height:int):
        uidata.height = height
    #pxm-FUNC PH with gil
    def slot_drawingarea_mouse_entered(self, entered:int):
        uidata.entered = entered
        pydata.app.on_mouse_entered()
    #pxm-FUNC PH with gil
    def slot_drawingarea_mouse_pressed(self, modifiers:int, button:int, x:int, y:int, height:int):
        pydata.app.on_mouse_pressed(modifiers, button, x, y, height)
    #pxm-FUNC PH with gil
    def slot_drawingarea_mouse_released(self, x:int, y:int):
        pydata.app.on_mouse_released(x, y)
    #pxm-FUNC PH with gil
    def slot_drawingarea_mouse_moved(self, buttons:int, x:int, y:int, height:int, pixeldensity:'qreal'):
        pydata.app.on_mouse_moved(buttons, x, y, height, pixeldensity)
    #pxm-FUNC PH with gil
    def slot_drawingarea_zoom(self, delta:'qreal'):
        pydata.app.on_mouse_zoom(delta)
    #pxm-FUNC PH with gil
    def slot_drag_dropped_color(self, x:int, height_y:int, color:str):
        pydata.app.on_mouse_drop_color(x, height_y, q2str(color))
    #pxm-FUNC PH with gil
    def slot_drag_dropped_url(self, x:int, height_y:int, url:str):
        pydata.app.on_mouse_drop_url(x, height_y, q2str(url))
        
    #pxm-FUNC PH nogil
    def slot_set_transient(self, val:'const QVariant &'):
        #px+cdef pQObject obj
        #px/if val.canConvert[pQObject]():
        if val.canConvert(QObject):
            #px/obj = val.value[pQObject]()
            obj = val.value(QObject)
            if obj.isWindowType():
                #px/(<QWindow*>obj).setTransientParent(uidata.mainwindow)
                obj.setTransientParent(uidata.mainwindow)
                
    #pxm-FUNC PH with gil
    def slot_dialog_text(self, string:str)->str:
        return str2q(pydata.app.on_dialog_text(q2str(string)))
    #pxm-FUNC PH with gil
    def slot_model_start_selection(self):
        if pydata.modelselect is None:
            pydata.modelselect = pydata.app.on_action_selectmodel_triggered()
        uidata.model_pagevisible = True
    #pxm-FUNC PH nogil
    def slot_model_pagevisible(self)->bool:
        return uidata.model_pagevisible
    #pxm-FUNC PH with gil
    def slot_model_get_modeldata(self, pageindex:int)->'QVariant':
        items, index, title = pydata.modelselect.on_model_get_modeldata(pageindex)
        ctx_set(b'selectmodel_title', title)
        ctx_set(b'selectmodel_index', index)
        return create_stdmodel(items)
    #pxm-FUNC PH with gil
    def slot_model_selected(self, pageindex:int, index:int)->bool:
        mtype_size = pydata.modelselect.on_convert_to_mtype_size(pageindex, index)
        if mtype_size is None:
            return True
        pydata.app.on_load_other_game(*mtype_size)
        uidata.model_pagevisible = False
        return False
    #pxm-FUNC PH with gil
    def slot_model_back(self):
        if pydata.modelselect.on_model_back():
            return
        uidata.model_pagevisible = False
            
    #pxm-FUNC PH with gil
    def slot_movekey_item_changed(self):
        settings['draw.accels'] = ctx_get_stdmodel(b'movekey_model')
    #pxm-FUNC PH with gil
    def slot_movekey_item_added(self, index:int)->int:
        values = ctx_get_stdmodel(b'movekey_model')
        if 0 <= index < len(values):
            values.insert(index, ('', ''))
        else:
            index = len(values)
            values.append(('', ''))
        ctx_set_stdmodel(b'movekey_model', values)
        settings['draw.accels'] = values
        return index
    #pxm-FUNC PH with gil
    def slot_movekey_item_removed(self, index:int)->int:
        values = ctx_get_stdmodel(b'movekey_model')
        del values[index]
        ctx_set_stdmodel(b'movekey_model', values)
        settings['draw.accels'] = values
        return min(index, len(values)-1)
    #pxm-FUNC PH with gil
    def slot_movekey_items_reset(self):
        del settings['draw.accels']
        ctx_set_stdmodel(b'movekey_model', settings['draw.accels'])
    #pxm-FUNC PH with gil
    def slot_keytostring(self, key:int, modifiers:int)->str:
        if key in IGNORE_KEYS:
            #px/return QString()
            return ''
        modifiers &= SAFE_MODIFIER_MASK
        return QKeySequence(key|modifiers, 0,0,0).toString(PortableText)
    #pxm-FUNC PH with gil const
    def slot_facesmodelitem(self, qkey:str)->'QObject *':
        key = q2str(qkey)
        assert key
        color = settings['theme.faces',key,'color']
        folder, image = get_imagefile(key)
        imagemode = settings['theme.faces',key,'mode_nick']
        #px/return <QObject*>new FacePrefsItem(str2q(color), str2q(folder), str2q(image), str2q(imagemode))
        return QVariant({'color': color, 'folder': folder, 'image': image, 'imagemode': imagemode})
#pxh>
    #pxm:Q_SIGNAL
    prefupdateface = pyqtSignal()
    prefupdatebkgr = pyqtSignal()
    #px.
#pxh>
    #pxm:Q_PROPERTY
    prop_edittext = 'QString MEMBER NOTIFY'
    prop_editposition = 'QString MEMBER NOTIFY'
    prop_toolbarstate = 'QVariantList MEMBER NOTIFY'
    prop_sidepanemodel = 'QList<QObject*> MEMBER NOTIFY'
    prop_gl_y = 'int READ WRITE'
    prop_debugmsg = 'QString MEMBER NOTIFY'
    prop_status = 'QString MEMBER NOTIFY'
    prop_animation_running = 'bool MEMBER NOTIFY'
    prop_animation_speed = 'float READ NOTIFY'
    prop_animation_angle = 'float MEMBER NOTIFY'
    prop_animation_maxangle = 'float MEMBER NOTIFY'
    prop_messagebox_visible = 'bool MEMBER NOTIFY'
    prop_messagetext = 'QString MEMBER NOTIFY'
    prop_selectmodel_title = 'QString MEMBER'
    prop_selectmodel_index = 'int MEMBER'
    prop_preferences_visible = 'bool MEMBER NOTIFY'
    prop_preferences_tabindex = 'int MEMBER NOTIFY'
    prop_shadermodel = 'QList<QObject*> MEMBER NOTIFY'
    prop_samplemodel = 'QList<QObject*> MEMBER NOTIFY'
    prop_activesamples = 'int MEMBER NOTIFY'
    prop_sampleindex = 'int MEMBER'
    prop_movekey_model = 'QList<QObject*> MEMBER NOTIFY'
    prop_facesmodel = 'QList<QObject*> MEMBER NOTIFY'
    prop_imagemodel = 'QList<QObject*> MEMBER NOTIFY'
    prop_helpdialog_visible = 'bool MEMBER NOTIFY'
    prop_aboutdialog_visible = 'bool MEMBER NOTIFY'
    #px.
    #pxh>//property access functions
    #pxm-FUNC PH nogil
    def set_gl_y(self, y:'int'):
        uidata.gl_y = y
    #pxm-FUNC PH nogil const
    def get_gl_y(self)->'int':
        return uidata.gl_y
    #pxh>
    #pxm-FUNC PH with gil const
    def get_animation_speed(self)->float:
        return settings['draw.speed']
#pxm>CPPCLASS_END
#pxm>IF_END
    
    
#pxm>IF '[[QTVARIANT]]' == 'qtq'
class QmlError (Warning): pass
#pxm>IF_END

# also used in beforeSynchronizing
#pxm-FUNC P nogil
def dump_info(msg:'const char *', qtobj:'const QObject *'):
    #px+cdef const QMetaObject *mo
    #px+cdef QMetaProperty p
    #px+cdef QMetaMethod m
    #px+cdef int i
    
    mo = qtobj.metaObject()
    
    printf('==== %s\n', msg)
    #printf('Object-Name: %s\n', q2charp(qtobj.objectName()))
    printf('Class-Name: %s\n', mo.className())
    if 0 < mo.propertyCount():
        printf('Properties: %d (%d inherited)\n', mo.propertyCount(), mo.propertyOffset())
        for i in range(mo.propertyCount()):
            p = mo.property(i)
            printf('  %d %s %s\n', i, p.typeName(), p.name())
    if 0 < mo.methodCount():
        printf('Methods: %d (%d inherited)\n', mo.methodCount(), mo.methodOffset())
        for i in range(mo.methodCount()):
            m = mo.method(i)
            printf('  %d %s %s\n', i, m.typeName(), m.methodSignature().data())
            
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxm-FUNC P with gil
def print_warnings(errors:'const QmlErrorList &'):
    #px+cdef int i
    #px+cdef QQmlError error
    #px/for i in range(errors.size()):
    for error in errors:
        #px+error = errors.at(i)
        filename = q2str(error.url().toLocalFile())
        msg, line = q2str(error.description()), error.line()
        print('{2}:{3}:{4}: {0}'.format(msg, QmlError, filename, line, error.column()), file=sys.stderr)
        warn_explicit(msg, QmlError, filename, line, module=os.path.basename(filename))
#pxm>IF_END
        
def set_debug_text(text):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    qtui.set_debug_text(str2q(text))
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'debugmsg', text)
    #pxm>IF_END
    
def set_edit_moves(code, pos):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.move_edit.setText(str2q(code))
    uidata.move_edit.setCursorPosition(pos)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'edittext', code)
    ctx_set(b'editposition', pos)
    #pxm>IF_END
    
def set_status_text(text):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.statuslabel.setText(str2q(text))
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'status', text)
    #pxm>IF_END
    
show_message_text = None
#pxm-FUNC P with gil
def show_message_cb():
    global show_message_text
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px/cdef QMessageBox *dialog = new QMessageBox(uidata.mainwindow)
    dialog = QMessageBox(uidata.mainwindow)
    dialog.setWindowTitle(str2q(_(config_.APPNAME)))
    dialog.setText(str2q(show_message_text))
    dialog.setIcon(QMessageBox_Information)
    dialog.setStandardButtons(QMessageBox_Close)
    dialog.exec()
    #px+del dialog
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'messagetext', show_message_text)
    ctx_set(b'messagebox_visible', True)
    #pxm>IF_END
    show_message_text = None
def show_message(message):
    global show_message_text
    show_message_text = message
    #px/singleShot(100, show_message_cb)
    QTimer.singleShot(100, show_message_cb)
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-FUNC P
def _fill_treestore(parent:'QStandardItem *', tree):
    #px+cdef QStandardItem *item
    for transl, func, subtree in tree:
        #px/item = new QStandardItem(str2q(transl))
        item = QStandardItem(str2q(transl))
        if func is not None:
            item.setData(tovariant(func))
        parent.appendRow(item)
        _fill_treestore(item, subtree)
#pxm>IF_END
        
def fill_sidepane(plugin_data):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    # remove old plugins
    uidata.treestore.clear()
    qtui.clear_sidepane()
    #self.plugin_group_widgets.clear()
    # fill treestore with plugins
    #px+cdef QStandardItem *item
    for transl, func, subtree in plugin_data:
        #px/item = new QStandardItem(str2q(transl))
        item = QStandardItem(str2q(transl))
        if func is not None:
            item.setData(tovariant(func))
        uidata.treestore.appendRow(item)
        _fill_treestore(item, subtree)
    # create widgets in the sidepane to display the plugins
    #px+cdef QPushButton *button
    #px+cdef QTreeView *treeview
    for i, [transl, func, subtree] in enumerate(plugin_data):
        button = qtui.create_sidepane_button(str2q(transl))
        treeview = qtui.create_sidepane_treeview(uidata.treestore, i)
        uidata.mainwindow.connect_sidepane(button, treeview)
        qtui.set_active_plugin_group(-1)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    pydata.plugin_data = plugin_data
    #pxm>IF_END
        
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-FUNC P
def _hide_row(treeview:int, index:'const QModelIndex &')->bool:
    funcidx = fromvariant(index.data(Qt.UserRole + 1))
    if not pydata.app.on_plugin_test_idx(funcidx):
        return True
    hide_all = funcidx is None
    rows = uidata.treestore.rowCount(index)
    for r in range(rows):
        hide = _hide_row(treeview, uidata.treestore.index(r, 0, index))
        qtui.set_row_hidden(treeview, r, index, hide)
        hide_all = hide_all and hide
    return hide_all
#pxm>IF_END
def update_sidepane():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px+cdef QModelIndex index
    for i in range(qtui.get_plugin_group_count()):
        index = qtui.root_index(i)
        qtui.hide_row(i, _hide_row(i, index))
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    def filter_plugin_data(tree):
        for section, func, subtree in tree:
            assert func is None
            for transl1, func, subtree in subtree:
                funcidx1 = -1 if func is None else func
                if pydata.app.on_plugin_test_idx(funcidx1):
                    subptree = []
                    for transl2, func, subtree in subtree:
                        assert not subtree, subtree
                        funcidx = -1 if func is None else func
                        if pydata.app.on_plugin_test_idx(funcidx):
                            subptree.append((section, ' • ' + transl2, funcidx))
                    if subptree or funcidx1 >= 0:
                        yield section, transl1, funcidx1
                        for section, name, funcidx in subptree:
                            yield section, name, funcidx
    ctx_set_ssi_model(b'sidepanemodel', filter_plugin_data(pydata.plugin_data))
    #pxm>IF_END
        
def set_toolbar_state(state):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    qtui.set_toolbar_state(state[0], state[1], state[2], state[3], state[4])
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'toolbarstate', state)
    #pxm>IF_END

#pxm>IF '[[QTVARIANT]]' == 'qtq'
def ctx_set(propname, value):
    uidata.ctx.setProperty(propname, tovariant(value))
    
#pxm-FUNC P
def create_ssi_model(values)->'QVariant':
    #px/cdef QObjectList qlist
    items = []
    #qlist.reserve(len(values))
    for section, name, index, *unused in values:
        #px/qlist.append(<QObject*>new SectionNameIndexItem(str2q(section), str2q(name), index))
        items.append({'section': str2q(section), 'name': str2q(name), 'index': index})
    #px+cdef QVariant items
    #px+items.setValue[QObjectList](qlist)
    return items
    
def ctx_set_ssi_model(propname, values):
    uidata.ctx.setProperty(propname, create_ssi_model(values))
    
def ctx_get_stdmodel(propname):
    #px/cdef QVariantList qlist = uidata.ctx.property(propname).value[QVariantList]()
    qlist = uidata.ctx.property(propname)
    #px+cdef QObject *qitem
    items = []
    #px/for i in range(qlist.size()):
    for value in qlist:
        #px+qitem = qlist.at(i).value[pQObject]()
        #px/items.append((fromvariant(qitem.property(b'text')), fromvariant(qitem.property(b'key'))))
        items.append((value['text'], value['key']))
    return items
#pxm>IF_END
    
#pxm-FUNC P
def create_stdmodel(values)->'QVariant':
    #px/cdef QObjectList qlist
    items = []
    for text, key, *unused in values:
        #px/qlist.append(<QObject*>new TextKeyItem(str2q(text), str2q(key)))
        items.append({'text': text, 'key': key})
    #px+cdef QVariant items
    #px+items.setValue[QObjectList](qlist)
    return items
    
#pxm>IF '[[QTVARIANT]]' == 'qtq'
def ctx_set_stdmodel(propname, values):
    uidata.ctx.setProperty(propname, create_stdmodel(values))
    
def ctx_invoke(name):
    invokeMethod(uidata.ctx, name, Qt.DirectConnection)
#pxm>IF_END
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-QD_CPPCLASS
class MainView (QMainWindow):
    #pxm-FUNC HD nogil
    def __init__(): pass
        #pxh> : QMainWindow() {}
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxm-QD_CPPCLASS
class MainView (QQuickView):
    #pxm-FUNC HD nogil
    def __init__(engine:'QQmlEngine *', parent:'QQuickWindow *'): pass
        #pxh> : QQuickView(engine, parent) {}
#pxm>IF_END
    
    #pxm-FUNC CHD nogil
    def connect_renderer(self, renderer:'Renderer *'):
    #pxc>{
        #pxm>IF '[[QTVARIANT]]' == 'qtq'
        #pxc-QObject::connect(this, SIGNAL(beforeRendering()), renderer, SLOT(on_beforeRendering()), Qt::DirectConnection);
        self.beforeRendering.connect(renderer.on_beforeRendering)
        #pxm>IF_END
        #pxc-QObject::connect(renderer, SIGNAL(picking_result(int)), this, SLOT(_on_picking_result(int)), Qt::QueuedConnection);
        renderer.picking_result.connect(self._on_picking_result)
        #pxc-QObject::connect(renderer, SIGNAL(debug_fps(int)), this, SLOT(_on_debug_fps(int)), Qt::QueuedConnection);
        renderer.debug_fps.connect(self._on_debug_fps)
    #pxc>}
    
    #pxm-FUNC CHD nogil
    def connect_renderer_offscreen(self, renderer:'Renderer *'):
    #pxc>{
        #pxc>QObject::connect(renderer, SIGNAL(offscreen_image(QImage)),
        #pxc-                 this, SLOT(_on_offscreen_image(QImage)), Qt::QueuedConnection);
        pass
    #pxc>}
    
    #pxm-FUNC CHD nogil
    def connect_view(self):
    #pxc>{
        #pxc-connect(this, SIGNAL(beforeSynchronizing()), this, SLOT(on_beforeSynchronizing()), Qt::DirectConnection);
        self.beforeSynchronizing.connect(self.on_beforeSynchronizing, type=Qt.DirectConnection)
        #pxc-connect(this, SIGNAL(sceneGraphInvalidated()), this, SLOT(on_sceneGraphInvalidated()), Qt::DirectConnection);
        self.sceneGraphInvalidated.connect(self.on_sceneGraphInvalidated, type=Qt.DirectConnection)
    #pxc>}
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
    #pxm-FUNC CHD nogil
    def connect_sidepane(self, button:'QPushButton *', treeview:'QTreeView *'):
    #pxc-{
        obsc = lambda i: lambda: self._on_button_sidepane_clicked(i)
        #pxc-connect(button, SIGNAL(clicked()), this, SLOT(_on_button_sidepane_clicked()), Qt::DirectConnection);
        button.clicked.connect(obsc(i))
        #pxc-connect(treeview, SIGNAL(activated(const QModelIndex &)), this, SLOT(_on_treeview_activated(const QModelIndex &)), Qt::DirectConnection);
        treeview.activated.connect(self._on_treeview_activated)
    #pxc>}
#pxm>IF_END
    
#pxh>
    #pxm-FUNC PH with gil
    def slot__on_picking_result(self, index:int):
        pydata.app.on_picking_result(index)
        
    #pxm-FUNC PH nogil
    def slot__on_debug_fps(self, fps:int):
        #pxm>IF '[[QTVARIANT]]' == 'qtw'
        qtui.set_debug_text(QString_number(fps))
        #pxm>IF '[[QTVARIANT]]' == 'qtq'
        uidata.ctx.setProperty(b'debugmsg', QVariant(fps))
        #pxm>IF_END
        
    #pxm-FUNC PH nogil
    def slot__on_offscreen_image(self, image:'QImage'):
        uidata.offscreen_image = image
        
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #pxd>void on_beforeSynchronizing() nogil
    #pxm>IF_END
    #pxm-FUNC PH nogil
    def slot_on_beforeSynchronizing(this):
        if renderdata.renderer is NULL:
            render_init()
            this.connect_renderer(renderdata.renderer)
            #px+IF OFFSCREEN:
            #px+    this.connect_renderer_offscreen(renderdata.renderer)
        glarea.render_resize(uidata.gl_y, uidata.width, uidata.height)
        glarea.sync()
        
        if uidata.atlas_changed:
            renderdata.atlas_changed = True
            renderdata.atlas_width = uidata.atlas_width
            renderdata.atlas_height = uidata.atlas_height
            glarea.sync_set_atlas_data()
            uidata.atlas_changed = False
        if uidata.pick_requested:
            renderdata.pickxy_changed = True
            glarea.sync_set_pick_position()
            
    #pxm-FUNC PH nogil
    def slot_on_sceneGraphInvalidated(this):
        if renderdata.renderer is not NULL:
            render_destroy()
            del renderdata.renderer
            renderdata.renderer = NULL
            
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxh>
    #pxm-FUNC PH with gil
    def resizeEvent(this, event:'QResizeEvent *'):
        settings['window.size'] = event.size().width(), event.size().height()
        pos = qtui.splitter_pos()
        if pos > 0:  # ignore the first resize event where sizes==[0,0]
            settings['window.divider'] = pos
        #px/this.QWidget_resizeEvent(event)
        QWidget.resizeEvent(this, event)
    #pxm-FUNC PH with gil
    def closeEvent(this, event:'QCloseEvent *'):
        destroy_resources()
        try:
            pydata.app.on_closing()
        finally:
            #px/this.QWidget_closeEvent(event)
            QWidget.closeEvent(this, event)
    #pxm-FUNC PH with gil
    def keyPressEvent(this, event:'QKeyEvent *'):
        if not pydata.app.on_key_pressed(event.key(), int(event.modifiers()) & SAFE_MODIFIER_MASK, False):
            event.ignore()
            #px/this.QWidget_keyPressEvent(event)
            QWidget.keyPressEvent(this, event)
#pxh>
    #pxm:ACTION
    action_challenge = pydata
    action_new_solved = pydata
    action_preferences = pydata
    action_reset_rotation = pydata
    action_rewind = pydata
    action_previous = pydata
    action_stop = pydata
    action_play = pydata
    action_next = pydata
    action_forward = pydata
    action_mark_set = pydata
    action_mark_remove = pydata
    action_initial_state = pydata
    action_edit_cube = pydata
    #px.
    
    #pxm-FUNC PH with gil
    def slot_on_action_selectmodel_triggered(this):
        if pydata.modelselect is None:
            pydata.modelselect = pydata.app.on_action_selectmodel_triggered()
        uidata.modelselect_currentpage = 1
        items, index, title = pydata.modelselect.on_model_get_modeldata(1)
        qtui.fill_page(create_stdmodel(items), str2q(title))
        qtui.set_page(1)
    #pxm-FUNC PH with gil
    def slot_on_action_selectmodel_back_triggered(this):
        if pydata.modelselect.on_model_back():
            uidata.modelselect_currentpage -= 1
            items, index, title = pydata.modelselect.on_model_get_modeldata(uidata.modelselect_currentpage)
            qtui.fill_page(create_stdmodel(items), str2q(title))
            qtui.set_page(1)
        else:
            qtui.set_page(0)
    #pxm-FUNC PHD with gil
    def _listwidget_itemActivated(self, item:'QListWidgetItem *'):
        index = item.listWidget().currentRow()  #TODO: store index in item
        mtype_size = pydata.modelselect.on_convert_to_mtype_size(uidata.modelselect_currentpage, index)
        if mtype_size is None:
            uidata.modelselect_currentpage += 1
            items, index, title = pydata.modelselect.on_model_get_modeldata(uidata.modelselect_currentpage)
            qtui.fill_page(create_stdmodel(items), str2q(title))
        else:
            pydata.app.on_load_other_game(*mtype_size)
            qtui.set_page(0)
            while pydata.modelselect.on_model_back():
                pass
    #pxm-FUNC PH with gil
    def slot_on_listwidget_itemActivated(this, item:'QListWidgetItem *'):
        this._listwidget_itemActivated(item)
    #pxm-FUNC PH with gil
    def slot_on_listwidget_itemClicked(this, item:'QListWidgetItem *'):
        this._listwidget_itemActivated(item)
    #pxm-FUNC PH with gil
    def slot_on_action_quit_triggered(this):
        this.close()
    #pxm-FUNC PH with gil
    def slot_on_action_editbar_toggled(this, checked:bool):
        qtui.set_frame_editbar_visible(checked)
        settings['window.editbar'] = checked
    #pxm-FUNC PH with gil
    def slot_on_action_statusbar_toggled(this, checked:bool):
        qtui.set_statusbar_visible(checked)
        settings['window.statusbar'] = checked
    #pxm-FUNC PH nogil
    def slot_on_action_help_triggered(this):
        if uidata.help == NULL:
            help_dialog()
        help_dialog_show()
    #pxm-FUNC PH nogil
    def slot_on_action_info_triggered(this):
        about_dialog()
    #pxm-FUNC PH with gil
    def slot_on_splitter_splitterMoved(self, pos:int, index:int):
        if index == 1:
            settings['window.divider'] = pos
            qtui.splitter_update_minimumsize()
    #pxm-FUNC PH with gil
    def slot_on_button_edit_clear_clicked(this):
        uidata.move_edit.setText(str2q(''))
        pydata.app.on_edit_finished(q2str(uidata.move_edit.text()), uidata.move_edit.cursorPosition())
    #pxm-FUNC PH with gil
    def slot_on_button_edit_exec_clicked(this):
        pydata.app.on_edit_finished(q2str(uidata.move_edit.text()), uidata.move_edit.cursorPosition())
    #pxm-FUNC PH with gil
    def slot_on_action_jump_to_editbar_triggered(this):
        uidata.move_edit.setFocus()
        
    #pxm-FUNC PH with gil
    def slot__on_button_sidepane_clicked(this):
        qtui.set_active_plugin_group_by_obj(this.sender())
    #pxm-FUNC PH with gil
    def slot__on_treeview_activated(this, index:'const QModelIndex &'):
        func_idx = fromvariant(index.data(Qt.UserRole + 1))
        if func_idx is None:
            return
        pydata.app.on_plugin_activated(func_idx)
#pxm>IF_END
#pxm>CPPCLASS_END


#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-QD_CPPCLASS
class MoveEdit (QLineEdit):
    #pxd>pass
    #pxm-FUNC CHD
    def __init__(this):
        #pxc> : QLineEdit() {
        #pxc-connect(this, SIGNAL(returnPressed()), this, SLOT(on_returnpressed()), Qt::DirectConnection);
        this.returnPressed.connect(this.on_returnpressed, type=Qt.DirectConnection)
        #pxc>}
    #pxm-FUNC PH with gil
    def keyPressEvent(this, event:'QKeyEvent *'):
        if event.matches(MoveToNextWord):
            pos = pydata.app.on_edit_moves_nextword(q2str(this.text()), this.cursorPosition())
            this.setCursorPosition(pos)
        elif event.matches(MoveToPreviousWord):
            pos = pydata.app.on_edit_moves_prevword(q2str(this.text()), this.cursorPosition())
            this.setCursorPosition(pos)
        elif event.key() == Qt.Key_Right and event.modifiers() == Qt.AltModifier:
            pydata.app.on_edit_moves_swapnext(q2str(this.text()), this.cursorPosition())
        elif event.key() == Qt.Key_Left and event.modifiers() == Qt.AltModifier:
            pydata.app.on_edit_moves_swapprev(q2str(this.text()), this.cursorPosition())
        else:
            #px/this.QLineEdit_keyPressEvent(event)
            QLineEdit.keyPressEvent(self, event)
    #pxm-FUNC PH with gil const
    def slot_on_returnpressed(self):
        pydata.app.on_edit_finished(q2str(this.text()), this.cursorPosition())
#pxm>CPPCLASS_END
    
    
#pxm-QD_CPPCLASS
class DrawingArea (QOpenGLWidget):
    #pxm-FUNC HD nogil
    def __init__(): pass
        #pxh> : QOpenGLWidget() {}
    #pxm-FUNC PHD with gil
    def init(this):
        this.setAcceptDrops(True)
        this.setFocusPolicy(Qt.StrongFocus)
        if debug & DEBUG_NOCONTROLS:
            this.setMinimumSize(128, 128)  # for thumbnail generation
        else:
            this.setMinimumSize(300, 300)
        this.setMouseTracking(True)
    #pxm-FUNC PH nogil
    def paintGL(this):
        uidata.mainwindow.on_beforeSynchronizing()
        renderdata.renderer.on_beforeRendering()
    #pxm-FUNC PH with gil
    def resizeGL(this, width:int, height:int):
        uidata.width = width
        uidata.height = height
    #pxm-FUNC PH with gil
    def keyPressEvent(this, event:'QKeyEvent *'):
        if not pydata.app.on_key_pressed(event.key(), event.modifiers() & SAFE_MODIFIER_MASK, True):
            #px/this.QWidget_keyPressEvent(event)
            QWidget.keyPressEvent(this, event)
    #pxm-FUNC PH with gil
    def mousePressEvent(this, event:'QMouseEvent *'):
        pydata.app.on_mouse_pressed(event.modifiers(), event.button(), event.x(), event.y(), this.height())
    #pxm-FUNC PH with gil
    def mouseReleaseEvent(this, event:'QMouseEvent *'):
        pydata.app.on_mouse_released(event.x(), event.y())
    #pxm-FUNC PH with gil
    def mouseMoveEvent(this, event:'QMouseEvent *'):
        pydata.app.on_mouse_moved(True, event.x(), event.y(), this.height(), 4)
    #pxm-FUNC PH with gil
    def wheelEvent(this, event:'QWheelEvent *'):
        pydata.app.on_mouse_zoom(event.angleDelta().y() / 120)
    #pxm-FUNC PH with gil
    def dragEnterEvent(this, event:'QDragEnterEvent *'):
        #px+cdef const QMimeData *mime_data
        mime_data = event.mimeData()
        if debug & DEBUG_MSG:
            #px/print('drag enter:', _qs2list(mime_data.formats()))
            print('drag enter:', mime_data.formats())
        if mime_data.hasColor():
            event.acceptProposedAction()
        elif mime_data.hasUrls() and mime_data.urls().at(0).isLocalFile():
            event.acceptProposedAction()
    #pxm-FUNC PH with gil
    def dropEvent(this, event:'QDropEvent *'):
        #px+cdef const QMimeData *mime_data
        mime_data = event.mimeData()
        if mime_data.hasColor():
            pydata.app.on_mouse_drop_color(event.pos().x(), this.height() - event.pos().y(),
                                                q2str(mime_data.colorData().toString()))
        elif mime_data.hasUrls():
            pydata.app.on_mouse_drop_url(event.pos().x(), this.height() - event.pos().y(),
                                                q2str(mime_data.urls().at(0).toString()))
        else:
            if debug & DEBUG_MSG:
                #px/print('Skip mime type:', ' '.join(_qs2list(mime_data.formats())))
                print('Skip mime type:', ' '.join(mime_data.formats()))
#pxm>CPPCLASS_END


#pxm-QD_CPPCLASS
class PreferencesDialog (QDialog):
    #pxh>QStandardItemModel *liststore_movekeys;
    #pxd+QStandardItemModel *liststore_movekeys
    #pxh>bool liststore_blocked;
    #pxd+cbool liststore_blocked
    #pxh>QString current_facekey;
    #pxd+QString current_facekey
    #pxh>QString image_dirname;
    #pxd+QString image_dirname
    
    #pxm-FUNC HD nogil
    def __init__(parent:'QWidget *'): pass
        #pxh> : QDialog(parent), liststore_blocked(false) {}
    #pxm-FUNC PHD
    def init(this):
        speed_min, speed_max = settings['draw.speed_range']
        mirror_min, mirror_max = settings['draw.mirror_distance_range']
        #px/this.liststore_movekeys = new QStandardItemModel(<QObject*>this)
        this.liststore_movekeys = QStandardItemModel(this)
        qtui.setupUi_pref(this,
                settings['draw.speed'], speed_min, speed_max,
                mirror_min, mirror_max, this.liststore_movekeys)
#pxh>
    #pxm-FUNC PH with gil
    def slot_on_slider_animspeed_valueChanged(this, value:int):
        settings['draw.speed'] = value
    #pxm-FUNC PH with gil
    def slot_on_button_animspeed_reset_clicked(this):
        this.findChild(str2q('slider_animspeed')).setProperty(b'value', tovariant(settings['draw.speed_default']))
        del settings['draw.speed']
    #pxm-FUNC PH with gil
    def slot_on_combobox_shader_currentIndexChanged(this, value:int):
        settings['draw.shader'] = value
    #pxm-FUNC PH with gil
    def slot_on_button_shader_reset_clicked(this):
        this.findChild(str2q('combobox_shader')).setProperty(b'currentIndex', tovariant(settings['draw.shader_default']))
        del settings['draw.shader']
    #pxm-FUNC PH with gil
    def slot_on_combobox_samples_currentIndexChanged(this, value:int):
        settings['draw.samples'] = value
        #visible = (this.sample_buffers != 2**settings['draw.samples'])
        this.findChild(str2q('label_needs_restarted')).setProperty(b'visible', tovariant(True))
    #pxm-FUNC PH with gil
    def slot_on_button_antialiasing_reset_clicked(this):
        this.findChild(str2q('combobox_samples')).setProperty(b'currentIndex', tovariant(settings['draw.samples_default']))
        del settings['draw.samples']
    #pxm-FUNC PH with gil
    def slot_on_checkbox_mirror_faces_toggled(this, checked:bool):
        uidata.preferences.findChild(str2q('spinbox_mirror_faces')).setProperty(b'enabled', tovariant(checked))
        settings['draw.mirror_faces'] = checked
    #pxm-FUNC PH with gil
    def slot_on_spinbox_mirror_faces_valueChanged(this, value:'double'):
        settings['draw.mirror_distance'] = value
    #pxm-FUNC PH with gil
    def slot_on_button_mirror_faces_reset_clicked(this):
        this.findChild(str2q('checkbox_mirror_faces')).setProperty(b'checked', tovariant(settings['draw.mirror_faces_default']))
        del settings['draw.mirror_faces']
        this.findChild(str2q('spinbox_mirror_faces')).setProperty(b'value', tovariant(settings['draw.mirror_distance_default']))
        del settings['draw.mirror_distance']
    #pxm-FUNC PH with gil
    def slot_on_button_mousemode_quad_toggled(this, checked:bool):
        if checked:
            settings['draw.selection_nick'] = 'quadrant'
    #pxm-FUNC PH with gil
    def slot_on_button_mousemode_ext_toggled(this, checked:bool):
        if checked:
            settings['draw.selection_nick'] = 'simple'
    #pxm-FUNC PH with gil
    def slot_on_button_mousemode_gesture_toggled(this, checked:bool):
        if checked:
            settings['draw.selection_nick'] = 'gesture'
            
    #pxm-FUNC PHD
    def fill_move_key_list(this):
        this.liststore_blocked = True
        #px+cdef QStandardItem *item
        for i, (move, key) in enumerate(settings['draw.accels']):
            #px/item = new QStandardItem(str2q(move))
            item = QStandardItem(str2q(move))
            this.liststore_movekeys.setItem(i, 0, item)
            #px/item = new QStandardItem(str2q(key))
            item = QStandardItem(str2q(key))
            this.liststore_movekeys.setItem(i, 1, item)
        #px/item = new QStandardItem(str2q(_('Move')))
        item = QStandardItem(str2q(_('Move')))
        this.liststore_movekeys.setHorizontalHeaderItem(0, item)
        #px/item = new QStandardItem(str2q(_('Key')))
        item = QStandardItem(str2q(_('Key')))
        this.liststore_movekeys.setHorizontalHeaderItem(1, item)
        this.liststore_blocked = False
        
    #pxm-FUNC PHD
    def get_move_key_list(this)->object:
        move_keys = []
        row_count = this.liststore_movekeys.rowCount()
        for i in range(row_count):
            move = fromvariant(this.liststore_movekeys.item(i, 0).data(Qt.DisplayRole))
            key = fromvariant(this.liststore_movekeys.item(i, 1).data(Qt.DisplayRole))
            move_keys.append((move, key))
        return move_keys
        
    #pxm-FUNC PH with gil
    def slot_on_liststore_movekeys_itemChanged(this, unused_item:'QStandardItem *'):
        if this.liststore_blocked:  return
        settings['draw.accels'] = this.get_move_key_list()
        
    #pxm-FUNC PH with gil
    def slot_on_button_movekey_add_clicked(this):
        this.liststore_blocked = True
        qtui.add_movekey_row()
        this.liststore_blocked = False
        
    #pxm-FUNC PH with gil
    def slot_on_button_movekey_remove_clicked(this):
        qtui.remove_movekey_row()
        settings['draw.accels'] = this.get_move_key_list()
        
    #pxm-FUNC PH with gil
    def slot_on_button_movekey_reset_clicked(this):
        del settings['draw.accels']
        this.fill_move_key_list()
        
    #pxm-FUNC PH with gil
    def slot__on_listview_faces_currentRowChanged(this, current:'const QModelIndex &'):
        this.current_facekey = qtui.get_liststore_faces_facekey(current)
        pydata.app.on_dialog_change_current_face(q2str(this.current_facekey))
        
    #pxm-FUNC PH with gil
    def slot_on_button_color_clicked(this):
        #px+cdef QString color
        current_facekey = q2str(this.current_facekey)
        color = str2q(settings['theme.faces',current_facekey,'color'])
        color = qtui.color_dialog(this, color)
        if q2str(color):
            qtui.set_button_color(color)
            settings['theme.faces',current_facekey,'color'] = q2str(color)
            
    #pxm-FUNC PH with gil
    def slot_on_button_color_reset_clicked(this):
        current_facekey = q2str(this.current_facekey)
        del settings['theme.faces',current_facekey,'color']
        qtui.set_button_color(str2q(settings['theme.faces',current_facekey,'color']))
        
    #pxm-FUNC PH with gil
    def slot_on_combobox_image_activated(this, index:int):
        pydata.app.on_dialog_change_current_image(q2str(this.current_facekey), index)
        
    #pxm-FUNC PH with gil
    def slot_on_button_image_reset_clicked(this):
        pydata.app.on_dialog_reset_current_image(q2str(this.current_facekey))
        
    #pxm-FUNC PH with gil
    def slot_on_radiobutton_tiled_toggled(this, checked:bool):
        if checked:
            settings['theme.faces',q2str(this.current_facekey),'mode_nick'] = 'tiled'
    #pxm-FUNC PH with gil
    def slot_on_radiobutton_mosaic_toggled(this, checked:bool):
        if checked:
            settings['theme.faces',q2str(this.current_facekey),'mode_nick'] = 'mosaic'
            
    #pxm-FUNC PH with gil
    def slot_on_button_background_color_clicked(this):
        #px+cdef QString color
        color = str2q(settings['theme.bgcolor'])
        color = qtui.color_dialog(this, color)
        if q2str(color):
            qtui.set_button_background_color(color)
            settings['theme.bgcolor'] = q2str(color)
            
    #pxm-FUNC PH with gil
    def slot_on_button_background_color_reset_clicked(this):
        del settings['theme.bgcolor']
        qtui.set_button_background_color(str2q(settings['theme.bgcolor']))
#pxm>CPPCLASS_END

def set_combobox_current_image(index_icon, imagefile, index):
    #px/cdef QIcon icon
    icon = QIcon()
    if imagefile is not None:
        icon.addFile(str2q(imagefile))
    qtui.set_combobox_image(index_icon, icon, index)
    
def set_preferences_current_face_theme(color, imageindex_icon, imagefile, imageindex, imagemode):
    qtui.set_button_color(str2q(color))
    set_combobox_current_image(imageindex_icon, imagefile, imageindex)
    qtui.set_imagemode(imagemode)
    
def get_filedialog_imagefile():
    filename = q2str(getOpenFileName(uidata.preferences, str2q(_("Open Image")), uidata.preferences.image_dirname))
    if filename:
        uidata.preferences.image_dirname = str2q(os.path.dirname(filename))
    return filename
#pxm>IF_END
    
def preferences_dialog(shader_names, sample_buffers, facenames, stockicons):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px/uidata.preferences = new PreferencesDialog(uidata.mainwindow)
    uidata.preferences = PreferencesDialog(uidata.mainwindow)
    uidata.preferences.init()
    qtui.preferences_block_signals(True)
    
    # graphic tab
    for nick in settings['draw.shader_range']:
        qtui.combobox_add_shaderitem(str2q(shader_names.get(nick, nick)), str2q(nick))
    uidata.preferences.findChild(str2q('combobox_shader')).setProperty(b'currentIndex', tovariant(settings['draw.shader']))
    for text in settings['draw.samples_range']:
        qtui.combobox_add_samplesitem(str2q(_(text)), str2q(text))
    uidata.preferences.findChild(str2q('combobox_samples')).setProperty(b'currentIndex', tovariant(settings['draw.samples']))
    uidata.preferences.findChild(str2q('checkbox_mirror_faces')).setProperty(b'checked', tovariant(settings['draw.mirror_faces']))
    uidata.preferences.findChild(str2q('spinbox_mirror_faces')).setProperty(b'value', tovariant(settings['draw.mirror_distance']))
    
    # mouse tab
    selection_nick = settings['draw.selection_nick']
    if selection_nick == 'quadrant':
        uidata.preferences.findChild(str2q('button_mousemode_quad')).setProperty(b'checked', tovariant(True))
    elif selection_nick == 'simple':
        uidata.preferences.findChild(str2q('button_mousemode_ext')).setProperty(b'checked', tovariant(True))
    elif selection_nick == 'gesture':
        uidata.preferences.findChild(str2q('button_mousemode_gesture')).setProperty(b'checked', tovariant(True))
        
    # keys tab
    uidata.preferences.fill_move_key_list()
    
    # theme tab
    for facename, facekey in facenames:
        qtui.add_liststore_faces_row(str2q(facename), str2q(facekey))
    qtui.add_combobox_image_item(QIcon(), str2q(_('plain')), str2q(''))
    for filename in stockicons:
        qtui.add_combobox_image_item(get_icon(filename), str2q(''), str2q(filename))
    qtui.add_combobox_image_item(QIcon(), str2q(_('select …')), str2q('/'))
    uidata.preferences.image_dirname = str2q(get_pictures_folder())
    qtui.finalize_liststore_faces(uidata.preferences)
    
    qtui.set_button_background_color(str2q(settings['theme.bgcolor']))
    
    qtui.preferences_block_signals(False)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set_stdmodel(b'shadermodel', ([shader_names.get(nick, nick), nick] for nick in settings['draw.shader_range']))
    ctx_set(b'activesamples', sample_buffers)
    ctx_set(b'sampleindex', len(bin(sample_buffers)[3:]))
    ctx_set_stdmodel(b'samplemodel', ([_(text), text] for text in settings['draw.samples_range']))
    
    ctx_set_stdmodel(b'movekey_model', settings['draw.accels'])
    
    ctx_set_stdmodel(b'facesmodel', facenames)
    images = [[fn and os.path.join(config_.UI_DIR, 'images', fn), fn] for fn in stockicons]
    ctx_set_stdmodel(b'imagemodel', images)
    #pxm>IF_END
    
def show_preferences():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.preferences.show()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'preferences_visible', True)
    #pxm>IF_END
    

#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-QD_CPPCLASS
class HelpDialog (QDialog):
    #pxm-FUNC HD nogil
    def __init__(parent:'QWidget *'): pass
        #pxh> : QDialog(parent) {}
    #pxm-FUNC PHD
    def init(this, helptext:str):
        qtui.setupUi_help(this, helptext)
#pxm>CPPCLASS_END

#pxm-FUNC P with gil
def help_dialog():
    #px/uidata.help = new HelpDialog(uidata.mainwindow)
    uidata.help = HelpDialog(uidata.mainwindow)
    uidata.help.init(str2q(pydata.app.on_dialog_text('help_text')))
    
#pxm-FUNC P nogil
def help_dialog_show():
    uidata.help.show()
    

#pxm-QD_CPPCLASS
class AboutDialog (QDialog):
    #pxm-FUNC HD nogil
    def __init__(parent:'QWidget *'): pass
        #pxh> : QDialog(parent) {}
    #pxm-FUNC PHD
    def init(this):
        qtui.setupUi_about(this)
        
    #pxm-FUNC PH nogil
    def showEvent(self, unused_event:'QShowEvent *'):
        qtui.update_animation(True)
        
    #pxm-FUNC PH nogil
    def resizeEvent(self, unused_event:'QResizeEvent *'):
        qtui.update_animation(False)
        
    #pxm-FUNC PH nogil
    def eventFilter(self, unused_watched:'QObject *', event:'QEvent *')->bool:
        qtui.event_filter_stop_animation(event)
        return False
#pxh>
    #pxm-FUNC HD nogil
    def slot_on_text_translators_anchorClicked(this, link:'const QUrl &'): pass
        #pxh>{ QDesktopServices::openUrl(link); }
        
    #pxm-FUNC PH nogil
    def slot_on_tab_widget_currentChanged(self, index:int):
        qtui.tab_widget_currentChanged(index)
        
    #pxm-FUNC PH with gil
    def slot_on_text_license_short_anchorClicked(self, url:'const QUrl &'):
        qtui.show_full_license(q2str(url.toString()) == 'text:FULL_LICENSE_TEXT', url)
#pxm>CPPCLASS_END

#pxm-FUNC P with gil
def about_dialog():
    #px/cdef AboutDialog *about = new AboutDialog(uidata.mainwindow)
    about = AboutDialog(uidata.mainwindow)
    about.init()
    
    qtui.fill_header(str2q(config_.APPICON_FILE), str2q(_(config_.APPNAME)),
                     str2q(config_.VERSION), str2q(_(config_.SHORT_DESCRIPTION)))
    qtui.fill_about_tab(str2q(config_.COPYRIGHT),
                        str2q(pydata.app.on_dialog_text('about_website')),
                        str2q(pydata.app.on_dialog_text('about_translators')))
    qtui.fill_contribute_tab(str2q(pydata.app.on_dialog_text('about_contribute')))
    license_full = pydata.app.on_dialog_text('about_license_full')
    if license_full:
        qtui.fill_license_tab(str2q(pydata.app.on_dialog_text('about_license_short')),
                              False, str2q(license_full))
    else:
        qtui.fill_license_tab(str2q(pydata.app.on_dialog_text('about_license_short')),
                              True, str2q(pydata.app.on_dialog_text('about_license_notfound')))
    about.exec()
    about.deleteLater()
#pxm>IF_END


def initial_resize():
    uidata.width, uidata.height = settings['window.size']
    uidata.mainwindow.resize(uidata.width, uidata.height)
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px+cdef int s1=0, s2=0, divider_pos, divider_pos2
    divider_pos = settings['window.divider']
    uidata.mainwindow.show()
    #px/qtui.get_splitter_sizes(s1, s2)
    s1, s2 = qtui.get_splitter_sizes()
    divider_pos2 = s1 + s2 - divider_pos
    qtui.set_splitter_sizes(divider_pos, divider_pos2)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    uidata.mainwindow.show()
    #pxm>IF_END
    #px/singleShot(20, _on_init_finalize)
    QTimer.singleShot(20, _on_init_finalize)
    
def set_title(title):
    uidata.mainwindow.setTitle(str2q(title))
    
def load_ui(path):
    pass
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    #px+cdef QmlErrorList errors
    uidata.mainwindow.setSource(QUrl())
    uidata.engine.clearComponentCache()
    uidata.mainwindow.setSource(fromLocalFile(str2q(path)))
    errors = uidata.mainwindow.errors()
    #px/if errors.size() > 0:
    if len(errors) > 0:
        print_warnings(errors)
    #pxm>IF_END
        
def close_mainwindow():
    uidata.mainwindow.close()
    
def get_samples():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #TODO: uidata.drawingarea.format().samples() returns only 0 here
    return defaultFormat().samples()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    return uidata.mainwindow.format().samples()
    #pxm>IF_END
    
def update_drawingarea():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.drawingarea.update()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    uidata.mainwindow.update()
    #pxm>IF_END
    
def set_cursor(index):
    if index < 0:
        index += 18
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px/uidata.drawingarea.setCursor(uidata.cursors[index][0])
    uidata.drawingarea.setCursor(uidata.cursors[index])
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    if uidata.entered:
        #px/uidata.mainwindow.setCursor(uidata.cursors[index][0])
        uidata.mainwindow.setCursor(uidata.cursors[index])
    #pxm>IF_END
    
def unset_cursor():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.drawingarea.unsetCursor()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    if uidata.entered:
        uidata.mainwindow.unsetCursor()
    #pxm>IF_END
    
#pxm>IF '[[QTVARIANT]]' == 'qtq'
def set_mousegrab(grab):
    uidata.mainwindow.setMouseGrabEnabled(grab)
    #TODO: need setOverrideCursor, see qt-doc for QWindow::setCursor
    #if grab:
    #    app.setOverrideCursor(uidata.cursors[17])
    #else:
    #    app.restoreOverrideCursor()
#pxm>IF_END
    
def save_screenshot(filename):
    #px+IF OFFSCREEN:
        #px+cdef QImage image
        #px+image = uidata.offscreen_image
    #px/ELSE:
    if True:
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
        #px+cdef QPixmap image
        image = uidata.mainwindow.grab()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
        #px+cdef QImage image
        image = uidata.mainwindow.grabWindow()
    #pxm>IF_END
    if not (debug & DEBUG_SIM):
        image.save(str2q(filename), NULL, -1)
        
        
#### misc qt ####

def parse_color(name):
    #px/cdef QColor color
    color = QColor()
    color.setNamedColor(str2q(name))
    return color.red(), color.green(), color.blue()
    
def parse_color_f(name):
    #px/cdef QColor color
    color = QColor()
    color.setNamedColor(str2q(name))
    return color.redF(), color.greenF(), color.blueF()
    
def load_image_from_file(filename, maxsize):
    #px+cdef int width, height, scaled_width, scaled_height
    #px+cdef bytes data
    #px/cdef QImage image
    image = QImage()
    image.load(str2q(filename), NULL)
    if image.isNull():
        return None
    # scale the image to size 2^n for OpenGL
    width = image.width()
    height = image.height()
    if width < 1 or height < 1:
        return None
    scaled_width = maxsize
    while scaled_width > width:
        scaled_width //= 2
    scaled_height = maxsize
    while scaled_height > height:
        scaled_height //= 2
    image = image.scaled(scaled_width, scaled_height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
    image = image.convertToFormat(Format_RGBA8888, Qt.AutoColor)
    #px+data = image.bits()[:image.byteCount()]
    #px:
    data = image.bits()
    data.setsize(image.byteCount())
    data = bytes(data)
    #px.
    return image.width(), image.height(), data
        
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-FUNC P
def get_icon(filename)->'QIcon':
    #px+cdef QPixmap pixmap
    from ..theme import Theme
    image = Theme.textures.image_from_file(filename)
    if image is None:
        return QIcon()
    else:
        width, height, data = image
        pixmap = QPixmap(width, height)
        pixmap.convertFromImage(QImage(data, width, height, Format_RGBA8888))
        return QIcon(pixmap)
#pxm>IF_END
        
def keyval_from_name(keystr):
    keystr = '+'.join(('Num' if k == 'KP' else k) for k in keystr.split('+'))
    #px/cdef QKeySequence keyseq = fromString(str2q(keystr), PortableText)
    keyseq = fromString(keystr, PortableText)
    if keyseq.count() == 0:
        return 0
    return keyseq[0]
    
def get_pictures_folder():
    #px/cdef QStringList locations = standardLocations(PicturesLocation)
    locations = standardLocations(PicturesLocation)
    #px/if locations.size() <= 0:
    if not locations:
        locations = standardLocations(HomeLocation)
        #px/if locations.size() <= 0:
        if not locations:
            return ''
    return q2str(locations[0])
    
def get_imagefile(facekey):
    imagefile = settings['theme.faces',facekey,'image']
    currentfolder = get_pictures_folder()
    if imagefile.startswith('/'):
        currentfolder = os.path.dirname(imagefile)
    elif imagefile:
        imagefile = os.path.join(config_.UI_DIR, 'images', imagefile)
    return currentfolder, imagefile
    
def text_to_html(text):
    return q2str(fromPlainText(str2q(text)).toHtml())
    
#pxm-FUNC P with gil
def _on_filesystemwatcher_changed_1(qpath:str):
    if pydata.filesystemwatcher_path is not None:
        return
    pydata.filesystemwatcher_path = q2str(qpath)
    #px/singleShot(100, _on_filesystemwatcher_changed_2)
    QTimer.singleShot(100, _on_filesystemwatcher_changed_2)
#pxm-FUNC P with gil
def _on_filesystemwatcher_changed_2():
    path = pydata.filesystemwatcher_path
    pydata.filesystemwatcher_path = None
    pydata.filesystemwatcher_handlers[path]()
    if os.path.isfile(path):
        uidata.filesystemwatcher.addPath(str2q(path))
    
def create_filesystemwatcher(path, callback):
    #px/cdef QString qpath = str2q(path)
    qpath = path
    if uidata.filesystemwatcher is NULL:
        #px/uidata.filesystemwatcher = new QFileSystemWatcher(NULL)
        uidata.filesystemwatcher = QFileSystemWatcher(None)
        #px/connect(uidata.filesystemwatcher, &directoryChanged, _on_filesystemwatcher_changed_1)
        uidata.filesystemwatcher.directoryChanged.connect(_on_filesystemwatcher_changed_1)
        #px/connect(uidata.filesystemwatcher, &fileChanged, _on_filesystemwatcher_changed_1)
        uidata.filesystemwatcher.fileChanged.connect(_on_filesystemwatcher_changed_1)
    pydata.filesystemwatcher_handlers[path] = callback
    uidata.filesystemwatcher.addPath(qpath)
    
#XXX: avoid this function, only one active singleShot-handler
#px+cdef void _on_singleShot() with gil:
    #px+pydata.singleshot_handler()
    #px+if pydata.singleshot_queue:
        #px+msec, pydata.singleshot_handler = pydata.singleshot_queue.pop(0)
        #px+singleShot(msec, _on_singleShot)
    #px+else:
        #px+pydata.singleshot_handler = None
def timer_singleShot(msec, func):
    #px+if pydata.singleshot_handler is None:
        #px+pydata.singleshot_handler = func
        #px+singleShot(msec, _on_singleShot)
    #px+else:
        #px+pydata.singleshot_queue.append((msec, func))
    #px-
    QTimer.singleShot(msec, func)
    
#pxm-FUNC P with gil
def _on_settings_timer_timeout():
    pydata.app.on_settings_timer_timeout()
    
#pxm-FUNC P
def settings_timer_create():
    #px/uidata.settings_timer = new QTimer()
    uidata.settings_timer = QTimer()
    uidata.settings_timer.setInterval(5000)
    uidata.settings_timer.setSingleShot(True)
    #px/connect(uidata.settings_timer, &timeout, _on_settings_timer_timeout)
    uidata.settings_timer.timeout.connect(_on_settings_timer_timeout)
    
#pxm-FUNC P
def settings_timer_start()->'cpdef':
    if not uidata.settings_timer.isActive():
        uidata.settings_timer.start()
        
#pxm-FUNC P
def settings_timer_destroy():
    uidata.settings_timer.stop()
    uidata.settings_timer.deleteLater()
    uidata.settings_timer = NULL
    
def set_animation_speed():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.speed = settings['draw.speed']
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_invoke(b'animation_speed_changed')
    #pxm>IF_END
    
#pxm-FUNC P
def animation_init():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px/uidata.animate_timer = new QTimer()
    uidata.animate_timer = QTimer()
    #uidata.animate_timer.setInterval(5000)
    #px/connect(uidata.animate_timer, &timeout, _on_animate_timer_timeout)
    uidata.animate_timer.timeout.connect(_on_animate_timer_timeout)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'animation_running', False)
    #pxm>IF_END
    set_animation_speed()
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-FUNC P with gil
def _on_animate_timer_timeout():
    #px+cdef float increment
    increment = uidata.speed * 1e-02 * 20
    increment = min(increment, 45)
    uidata.animation_angle += increment
    glarea.set_animation_next(uidata.animation_angle)
    if uidata.animation_angle < uidata.angle_max:
        update_drawingarea()
    else:
        pydata.app.on_animation_ending()
#pxm>IF_END
    
#pxm-FUNC P
def animate_timer_start(msec, angle_max)->'cpdef':
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.animation_angle = 0.
    uidata.angle_max = angle_max
    if not uidata.animate_timer.isActive():
        uidata.animate_timer.start(msec)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'animation_maxangle', angle_max)
    ctx_set(b'animation_running', True)
    #pxm>IF_END
        
# only for demos
def animate_timer_step(angle_max):
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.angle_max = angle_max
    _on_animate_timer_timeout()
    #pxm>IF_END
    pass
    
def animate_timer_stop():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.animate_timer.stop()
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    ctx_set(b'animation_running', False)
    #pxm>IF_END
    
#pxm>IF '[[QTVARIANT]]' == 'qtw'
#pxm-FUNC P
def animate_timer_destroy():
    uidata.animate_timer.stop()
    uidata.animate_timer.deleteLater()
    uidata.animate_timer = NULL
#pxm>IF_END
    
#pxm-FUNC P
def load_cursors(filepattern):
    #px/cdef QImage images[17]
    images = [None]*17
    pos = []
    # Load 3 cursors from file (n - ne)
    for i, (x, y) in enumerate([(8, 0), (15, 0), (15, 0)]):
        filename = filepattern.format(i)
        images[i+1] = QImage(str2q(filename))
        pos.append((x, y))
    # 1 cursor (nnw)
    x, y = pos[1]
    images[0] = images[2].mirrored(True, False)
    pos.insert(0, (15-x, y))
    # 12 cursors (ene - nw)
    #px/cdef QTransform transform
    transform = QTransform()
    transform.rotate(90)
    for i in range(4, 16):
        x, y = pos[i-4]
        images[i] = images[i-4].transformed(transform)
        pos.append((15-y, x))
    images[16] = images[0]
    pos.append(pos[0])
    for i in range(16):
        x, y = pos[i+1]
        #px/uidata.cursors[i] = new QCursor(fromImage(images[i+1]), x, y)
        uidata.cursors[i] = QCursor(fromImage(images[i+1]), x, y)
    # cursor for center faces
    filename = filepattern.format('ccw')
    #px/uidata.cursors[16] = new QCursor(QPixmap(str2q(filename)), 7, 7)
    uidata.cursors[16] = QCursor(QPixmap(str2q(filename)), 7, 7)
    # background cursor
    #px/uidata.cursors[17] = new QCursor()
    uidata.cursors[17] = QCursor()
    uidata.cursors[17].setShape(Qt.CrossCursor)
    
#pxm-FUNC P
def destroy_cursors():
    for i in range(18):
        #px+del uidata.cursors[i]
        uidata.cursors[i] = NULL
        
#pxm-FUNC P
def destroy_resources():
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    uidata.drawingarea.makeCurrent()
    gl_delete_atlas()
    glarea.gl_exit()
    uidata.drawingarea.doneCurrent()
    uidata.drawingarea.setMouseTracking(False)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    settings['window.size'] = uidata.mainwindow.width(), uidata.mainwindow.height()
    #pxm>IF_END
    
def create_window(pyapp):
    pydata.app = pyapp
    init_module()
    # Set default format before any widget is created, so that everything uses the same format.
    # To test this use DEBUG_VFPS. The framerate should be then >60.
    set_default_surface_format()
    
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    #px/uidata.mainwindow = new MainView()
    uidata.mainwindow = MainView()
    #px/uidata.treestore = new QStandardItemModel()
    uidata.treestore = QStandardItemModel()
    #px/uidata.drawingarea = new DrawingArea()
    uidata.drawingarea = DrawingArea()
    
    qtui.setupUi_main(uidata.mainwindow,
                            QIcon(str2q(os.path.join(config_.UI_DIR, 'qt', 'images', 'new-random.png'))),
                            QIcon(str2q(os.path.join(config_.UI_DIR, 'qt', 'images', 'new-solved.png'))),
                            QIcon(str2q(os.path.join(config_.UI_DIR, 'qt', 'images', 'select-model.png'))),
                            QKeySequence(str2q(settings['action.edit_moves'])),
                            QKeySequence(str2q(settings['action.edit_cube'])),
                            settings['window.editbar'],
                            settings['window.statusbar'])
    uidata.mainwindow.setWindowIcon(QIcon(str2q(config_.APPICON_FILE)))
    qtui.set_shortcuts(QKeySequence(str2q(settings['action.selectmodel'])),
                       QKeySequence(str2q(settings['action.initial_state'])),
                       QKeySequence(str2q(settings['action.reset_rotation'])),
                       QKeySequence(str2q(settings['action.preferences'])),
                      )
    #px/uidata.move_edit = new MoveEdit()
    uidata.move_edit = MoveEdit()
    uidata.drawingarea.init()
    qtui.add_widgets(uidata.mainwindow, uidata.move_edit, uidata.drawingarea)
    #px/uidata.statuslabel = new QLabel()
    uidata.statuslabel = QLabel()
    uidata.mainwindow.statusBar().addWidget(uidata.statuslabel, 1)
    if debug & DEBUG_NOCONTROLS:
        qtui.hide_controls()
    
    #px+cdef QSizePolicy sizepolicy
    sizepolicy = QSizePolicy(Expanding, Expanding)
    sizepolicy.setHorizontalStretch(0)
    sizepolicy.setVerticalStretch(0)
    uidata.drawingarea.setSizePolicy(sizepolicy)
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    #px/uidata.engine = new QQmlEngine()
    uidata.engine = QQmlEngine()
    uidata.engine.setOutputWarningsToStandardError(False)
    #px/connect(uidata.engine, &warnings, print_warnings)
    uidata.engine.warnings.connect(print_warnings)
    #px/uidata.ctx = <QObject*>(new ContextProperty())
    uidata.ctx = ContextProperty()
    uidata.engine.rootContext().setContextProperty(str2q('ctx'), uidata.ctx)
    if debug & DEBUG_QML:
        dump_info('Context Property', uidata.ctx)
        
    #px/uidata.mainwindow = new MainView(uidata.engine, NULL)
    uidata.mainwindow = MainView(uidata.engine, NULL)
    uidata.mainwindow.setResizeMode(SizeRootObjectToView)
    uidata.mainwindow.setTitle(str2q(_(config_.APPNAME)))
    #XXX: Maybe set to false and implement the relates signal handlers to be nice to system resources
    uidata.mainwindow.setPersistentOpenGLContext(True)
    
    uidata.mainwindow.connect_view()
    uidata.mainwindow.setClearBeforeRendering(False)
    #pxm>IF_END
    animation_init()
    settings_timer_create()
    load_cursors(os.path.join(config_.UI_DIR, 'cursors', 'mouse_{}.png'))
    
def destroy_window():
    destroy_cursors()
    settings_timer_destroy()
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    animate_timer_destroy()
    #pxm>IF_END
    pydata.app = None
    
#pxm-FUNC P with gil
def _on_init_finalize():
    pydata.app.on_init_finalize()
    
#pxm>IF '[[QTVARIANT]]' == 'qtq'
#pxm-FUNC P with gil
def _on_closing():
    destroy_resources()
    pydata.app.on_closing()
    pydata.app = None
#pxm>IF_END
        
def app_post_create(gettext):
    #px/cdef QApplication *app = <QApplication*>QCoreApplication_instance()
    app = QCoreApplication.instance()
    
    # font
    #px+cdef QFont font
    try:
        uidata.grid_unit_px = float(os.environ['GRID_UNIT_PX']) / 8
    except (KeyError, ValueError):
        uidata.grid_unit_px = 1.
        fontpixelsize = 14.0
    else:
        # On the Ubuntu phone the default font is broken without Ubuntu.Components imported
        font = app.font()
        # Default in ubuntu-ui-toolkit: fontUnits = 14.0f (to small, use 18.0), DEFAULT_GRID_UNIT_PX = 8
        #TODO: use QWindow.devicePixelRatio
        fontpixelsize = 18.0 * uidata.grid_unit_px / app.devicePixelRatio()
        font.setFamily(str2q("Ubuntu"))
        font.setPixelSize(int(fontpixelsize))
        font.setWeight(50)
        app.setFont(font)
        
    global _
    _ = gettext
    #pxm>IF '[[QTVARIANT]]' == 'qtw'
    qtui.init_gettext(_)
    #pxm>IF_END
    
    #pxm>IF '[[QTVARIANT]]' == 'qtq'
    # variant 'qtw' uses closeEvent instead
    #px/connect(app, &aboutToQuit, app, &_on_closing, Qt.DirectConnection)
    app.aboutToQuit.connect(_on_closing, type=Qt.DirectConnection)
    #pxm>IF_END
    
    

