Beispiel #1
0
def main():
    global app

    # sys.argv.extend(['-platform', 'eglfs'])

    # Qt Charts uses Qt Graphics View Framework for drawing, therefore QApplication must be used.
    app = QApplication(sys.argv)

    viewer = QQuickView()

    # The following are needed to make examples run without having to install the module
    # in desktop environments.
    extraImportPath = QGuiApplication.applicationDirPath()
    if sys.platform == 'win32':
        extraImportPath += "/../../../../qml"
    else:
        extraImportPath += "/../../../qml"

    viewer.engine().addImportPath(extraImportPath)
    viewer.engine().quit.connect(app.quit)

    viewer.setTitle("QML Oscilloscope")

    dataSource = datasource.DataSource(viewer)
    viewer.rootContext().setContextProperty("dataSource", dataSource)

    main_qml = path.dirname(__file__) + "/qml/qmloscilloscope/main.qml"
    viewer.setSource(QUrl(main_qml))
    viewer.setResizeMode(QQuickView.SizeRootObjectToView)
    viewer.setColor(QColor("#404040"))
    viewer.show()

    return app.exec_()
Beispiel #2
0
class View(object):
    def __init__(self, iface, chart):

        dir_path = os.path.dirname(os.path.realpath(__file__))
        qml = os.path.join(dir_path, "qml", "scatterplot.qml")
        self.view = QQuickView()
        self.view.setResizeMode(QQuickView.SizeRootObjectToView)
        self.view.rootContext().setContextProperty("pychart", chart)
        self.view.setColor(QColor("#000000"))
        self.view.setSource(QUrl.fromLocalFile(qml))

        self.container = QWidget.createWindowContainer(self.view)
        self.widget = QDockWidget()
        self.widget.setWidget(self.container)
        iface.addDockWidget(Qt.BottomDockWidgetArea, self.widget)

        self.read_settings()

    def read_settings(self, settings=None):
        if not settings:
            settings = Settings.Snapshot()

        self.view.setColor(QColor(settings.background_color))

    def show(self):
        self.widget.show()

    def hide(self):
        self.widget.hide()
Beispiel #3
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.setWindowTitle('Read OpenVSLAM map')

        self.mapInfo = MapInfo()

        qml_view = QQuickView()
        qml_view.rootContext().setContextProperty('mapInfo', self.mapInfo)
        qml_view.setSource(
            QUrl(
                os.path.join(os.path.dirname(os.path.realpath(__file__)),
                             'read_map.qml')))
        qml_view.setResizeMode(QQuickView.SizeRootObjectToView)
        self.qml_gui = qml_view.rootObject()

        qml_view_container = QWidget.createWindowContainer(qml_view, self)
        qml_view_container.setMinimumSize(800, 200)
        self.setCentralWidget(qml_view_container)

        self.mapInfo.filenameChanged.connect(self.read_data)
        self.mapInfo.visualiseCloud.connect(self.pptk)
        self.mapInfo.visualiseTrajectory.connect(self.pyplot)
        self.mapInfo.exportCloud.connect(self.export_cloud)

        self.data = None
        self.cloud = None
        self.trajectory = None
Beispiel #4
0
class CountdownApp(QObject):
    QMLFILE = 'main.qml'

    def __init__(self):
        super(QObject, self).__init__()
        self.app = QGuiApplication(sys.argv)
        self.view = QQuickView()
        self.view.setResizeMode(QQuickView.SizeRootObjectToView)
        #self.view.engine().quit.connect(self.app.quit)

        self.cds = CountdownList()
        self.cdt = []
        for name, targetDatetime in {
                "silvester": datetime(2018, 1, 1, 0, 0, 0),
                "geburtstag": datetime(2018, 3, 12, 0, 0, 0)
        }.items():
            cdobj = CountdownData()
            countdown = CountdownTimer(cdobj, targetDatetime, name)
            countdown.start()
            self.cds.append(cdobj)
            self.cdt.append(countdown)

        self.view.rootContext().setContextProperty('countdowns', self.cds)
        self.view.setSource(QUrl(self.QMLFILE))

        self.t = QTimer()
        self.t.timeout.connect(self.addCountdown)
        self.t.start(10000)

    def run(self):
        self.view.show()
        sys.exit(self.app.exec_())

    @pyqtSlot()
    def addCountdown(self):
        for name, targetDatetime in {
                "antrittsvorlesung": datetime(2018, 1, 19, 0, 0, 0)
        }.items():
            cdobj = CountdownData()
            countdown = CountdownTimer(cdobj, targetDatetime, name)
            countdown.start()
            self.cds.append(cdobj)
            self.cdt.append(countdown)
Beispiel #5
0
def main(arguments):
    app = QGuiApplication(sys.argv)
    view = QQuickView()
    f = QFile(':/default.txt')
    f.open(QIODevice.ReadOnly)
    model = TreeModel(f.readAll())
    f.close()

    rootContext = view.rootContext().setContextProperty('model', model)
    view.setSource(QUrl.fromLocalFile('TreeModel.qml'))
    view.show()
    sys.exit(app.exec_())
Beispiel #6
0
def run():
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    app = QGuiApplication(sys.argv)

    view = QQuickView()
    view.setTitle('Hot reloading demo')

    qml_engine = view.rootContext().engine()
    qml_engine.addImportPath(lib_dir_path)

    notifier = HotReloadNotifier(demo_dir_path, qml_engine, parent=app)
    view.rootContext().setContextProperty('hotReloadNotifier', notifier)

    qml_url = QUrl.fromLocalFile(os.path.join(demo_dir_path, 'Demo.qml'))
    view.setSource(qml_url)

    view.show()
    exit_code = app.exec_()

    # notifier.stop()  # seems like this is not needed
    sys.exit(exit_code)
Beispiel #7
0
class VVSQMLApp(QObject):
    QMLFILE = 'gui.qml'

    def __init__(self, connections):
        super(QObject, self).__init__()
        self.app = QGuiApplication(sys.argv)
        self.view = QQuickView()
        self.view.setResizeMode(QQuickView.SizeRootObjectToView)
        if settings['alwaysOnTop']:
            self.view.setFlags(Qt.WindowStaysOnTopHint)

        self.con = []
        for connection in connections:
            updaterThread = VVSConnectionUpdater(
                connection[0],
                connection[1],
                connection[2],
                updateDelay=settings['updateDelay'])
            updaterThread.start()
            self.con.append(updaterThread)
            #print(connection)
        #self.con = VVSConnectionUpdater('5006021', 'X60', 'Leonberg Bf')
        #self.con.start()

        #print(self.con)

        self.view.rootContext().setContextProperty('con', self.con)
        self.view.setSource(QUrl(self.QMLFILE))

        #Setup notifications
        VVSNotifier.setup(self.con)

    def run(self):
        if settings['fullscreen']:
            self.view.showFullScreen()
        else:
            self.view.show()
        sys.exit(self.app.exec_())
Beispiel #8
0
def main():
    """Main Function Entry."""
    app = QApplication(sys.argv)

    # Create a label and set its properties
    applable = QQuickView()
    applable.setSource(QUrl('basic.qml'))

    conn = SlotClass()
    context = applable.rootContext()
    context.setContextProperty("conn", conn)

    # Show the Label
    applable.show()
    # Execute the Application and Exit
    app.exec_()
    sys.exit()
Beispiel #9
0
def main():
    global VIEW
    global APP
    APP = QGuiApplication(sys.argv)
    VIEW = QQuickView()
    url = QUrl('main.qml')
    VIEW.setSource(url)


    submit = submitUserInput()

    context = VIEW.rootContext()
    context.setContextProperty("submit", submit)
    VIEW.show()


    sys.exit(APP.exec_())
Beispiel #10
0
class MainWindow(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)

        self._controller = Controller()

        self.view = QQuickView()

        full_path = os.path.realpath(__file__)
        folder = os.path.dirname(full_path)
        qml_file = os.path.join(folder, 'qml', 'App.qml')
        qml_qurl = QtCore.QUrl.fromLocalFile(qml_file)

        self.view.setSource(qml_qurl)

        # Add context properties to use this objects from qml
        rc = self.view.rootContext()
        rc.setContextProperty('controller', self._controller)

    def show(self):
        self.view.show()
Beispiel #11
0
    def initPlugins(self):
        # init QML plugin container
        if hasattr(self, 'stackedWidget'):
            del (self.stackedWidget)

        self.stackedWidget = QStackedWidget(self)
        self.plugins = []

        for pluginName in self.config.PLUGINS:
            view = QQuickView()
            view.setFlags(Qt.SubWindow)

            # add clientapi into the plugin context
            context = view.rootContext()

            context.setContextProperty('zulApi', self.zulApi)
            context.setContextProperty('GUI', self)

            # load plugin translations if i18n subfolder exists
            i18nDir = 'plugins/{}/i18n'.format(pluginName)
            if os.path.exists(i18nDir):
                translator = QtCore.QTranslator()
                fileName = 'send_'.format(QtCore.QLocale.system().name())
                #fileName = 'send_fr'
                translator.load(fileName, i18nDir)
                self.app.installTranslator(translator)

            # load QML file
            plugin_index_path = 'plugins/{}/index.qml'.format(pluginName)
            view.setSource(QUrl(plugin_index_path))

            plugin = view.rootObject()
            self.plugins.append(plugin)

            # call plugin init callback
            plugin.init()

            # add the plugin in the container
            container = QWidget.createWindowContainer(view, self)
            self.stackedWidget.addWidget(container)
Beispiel #12
0
def main():
    app = QGuiApplication(sys.argv)
    app.setApplicationName('InfiniteCopy')

    openDataBase()

    view = QQuickView()

    clipboardItemModel = ClipboardItemModel()
    clipboardItemModel.create()

    filterProxyModel = QSortFilterProxyModel()
    filterProxyModel.setSourceModel(clipboardItemModel)

    clipboard = Clipboard()
    clipboard.setFormats([
        mimeText,
        mimeHtml,
        mimePng,
        mimeSvg
        ])
    clipboard.changed.connect(clipboardItemModel.addItem)

    engine = view.engine()

    imageProvider = ClipboardItemModelImageProvider(clipboardItemModel)
    engine.addImageProvider("items", imageProvider)

    context = view.rootContext()
    context.setContextProperty('clipboardItemModel', clipboardItemModel)
    context.setContextProperty('clipboardItemModelFilterProxy', filterProxyModel)
    context.setContextProperty('clipboard', clipboard)

    view.setSource(QUrl.fromLocalFile('qml/MainWindow.qml'))
    view.setGeometry(100, 100, 400, 240)
    view.show()

    engine.quit.connect(QGuiApplication.quit)

    return app.exec_()
Beispiel #13
0
def createWindow():
    view = QQuickView()
    view.setSurfaceType(QSurface.OpenGLSurface)

    fmt = QSurfaceFormat()
    fmt.setAlphaBufferSize(8)
    fmt.setRenderableType(QSurfaceFormat.OpenGL)
    view.setFormat(fmt)

    color = QColor()
    color.setRedF(0.0)
    color.setGreenF(0.0)
    color.setBlueF(0.0)
    color.setAlphaF(0.0)
    view.setColor(color)

    view.setClearBeforeRendering(True)
    view.setFlags(Qt.FramelessWindowHint | Qt.ToolTip
                  | Qt.WindowStaysOnTopHint)

    context = view.rootContext()

    return (view, context)
Beispiel #14
0
 def setup_from_vispy_pyqt5(self, root_window: QMainWindow,
                            vispy_canvas: MyCanvas, quick_view: QQuickView):
     self.root_win = root_window
     self.vispy_canvas = vispy_canvas
     self.top_panel = quick_view.rootContext().setContextProperty(
         "visualizer_o", self)
Beispiel #15
0
    appLabel.setSource(QUrl('main.qml'))
    #appLabel.load(QUrl('main2.qml'))

    # Show the Label
    appLabel.show()

    # Create a QML engine.
    engine = QQmlEngine()

    # Initialize PhotoBoothEngine.
    pbengine = PhotoBoothEngine()
    pbengine.on_status.connect(appLabel.rootObject().status)
    pbengine.on_update_filter_preview.connect(
        appLabel.rootObject().updateImageFilterPreview)

    appLabel.rootContext().setContextProperty('pbengine', pbengine)

    # Create a component factory and load the QML script.
    print("Hello")
    component = QQmlComponent(appLabel.engine())
    component.loadUrl(QUrl('TextStatusFly.qml'))

    print("Hello2")
    asdf = component.create(appLabel.rootContext())

    print("Hello3")
    asdf.setParentItem(appLabel.rootObject())
    asdf.setParent(appLabel.rootObject())

    print("Hello4")
    #asdf.setProperty("targetX", 100)
Beispiel #16
0
class Example(QWidget):
    @pyqtSlot(QVariant)
    def sendImgToGallery(self, aword):
        print(aword)
        #o = self.view.findChild(QObject, 'textEdit')
        #print(o)
        print("kek")

    def getTagsString(self, tagsArray):
        tagsString = ""
        for tag in tagsArray:
            tagsString += tag+", "
        return tagsString

    def buttonClicked(self):
        file = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
        if (file):
            QMetaObject.invokeMethod(
                self.view.rootObject(), "clear")
            #QMetaObject.invokeMethod(
             #   self.view.rootObject(), "showBusyIndicator")
        self.startProcessing(file)

    def selectSortDirPath(self):
        file = str(QFileDialog.getExistingDirectory(self, "Select Directory"))
        if (file):
            self.sortDirPath = file
            QMetaObject.invokeMethod(self.view.rootObject(), "setSortDitPathTextField", Q_ARG(QVariant, file))
            
    def getAllImagePathFromDir(self, directoryPath):
        fileNameArray = []
        for (dirpath, dirnames, filenames) in walk(directoryPath):
            fileNameArray.extend(filenames)
            break
        for idx in range(len(fileNameArray)):
            fileNameArray[idx] = directoryPath+"/"+fileNameArray[idx]
        return fileNameArray

    def getImageInformationFromKhiena(self, fileNameArray):
        imgInfoArray = []
        print(fileNameArray)
        for idx in range(len(fileNameArray)):
            imgInfoArray.append(apiKhiena.imgSearch(fileNameArray[idx]))
            if (idx % 2 == 0):
                time.sleep(10)

        print("============")
        # print(imgInfoArray)
        return imgInfoArray

    def startProcessing(self, pathToDir):
        fileNameArray = self.getAllImagePathFromDir(pathToDir)
        fileImageInfoArray = self.getImageInformationFromKhiena(fileNameArray)
        imgCounter = 0
        for imageInfo in fileImageInfoArray:
            if (not imageInfo["source"]):
                continue
            imgInfo = imgUrlGetter.getImgInfoFromUrl(imageInfo["source"])
            completeSourceText = '<a href="' + \
                imageInfo["source"]+'"+>'+imageInfo["source"]+'</a>'
            print("img Path: "+imgInfo["imgLink"])
            stringTags = self.getTagsString(imgInfo["tags"])
            if (imageInfo["rating"]==0):
                stringTags += "safe, "
            elif  (imageInfo["rating"]==1):
                stringTags += "questionable, "
            elif  (imageInfo["rating"]==2):
                stringTags += "explicit, "
            
            print("RATING: ",imageInfo["rating"])

            stringTags += "artist:"+imageInfo["artist"]
            similarity = imageInfo["similarity"]
            colorRowRect = "#f2ca7d"
            if similarity < 90:
                colorRowRect = "#f04747"

            btnName = "sendBtnName"+str(imgCounter)
            value = {"similarity": similarity,
                     "source": completeSourceText,
                     "localImgPath": imageInfo["localImgPath"],
                     "remoteImgPath": imgInfo["imgLink"],
                     "tags": stringTags,
                     "colorRowRect": colorRowRect,
                     "sendBtnName":btnName}
            # print(value)
            imgCounter+=1
            QMetaObject.invokeMethod(
                self.view.rootObject(), "append", Q_ARG(QVariant, value))
            
            #print(btnName)
            #sendButton = self.view.findChild(QObject, "sendBtnName0")
            #print(sendButton)
            #sendButton.clicked.connect(self.sendImgToGallery)

        #QMetaObject.invokeMethod(
        #        self.view.rootObject(), "hideBusyIndicator")

    def on_qml_mouse_clicked(self):
        print('mouse clicked')

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        self.view = QQuickView()
        self.view.setSource(QUrl('mainWin.qml'))
        self.view.rootContext().setContextProperty("ex",self)
        self.sortDirPath = ""
        openFolderButton = self.view.findChild(QObject, "toolButton")
        openFolderButton.clicked.connect(self.buttonClicked)

        selectSortDirButton = self.view.findChild(QObject, "selectSortDirButton")
        selectSortDirButton.clicked.connect(self.selectSortDirPath)

        child = self.view.findChild(QObject, "limgListModel")

        imageInfo = {"idshnik": 4,
                     'similarity': 80.859408,
                     'title': "Dragons' Journeyby Selianth",
                     'source': 'https://furrynetwork.com/artwork/1382822',

                     'artist': 'selianth',
                     'rating': 0,
                     'localImgPath': '/home/ /драконы-неразр-тест/425808117_107442_11461217378126189089.jpg'}
        colorRowRect = "#f2ca7d"
        value1 = {'similarity': 97.144509,
                  'source': '<a href="https://furrynetwork.com/artwork/341709">https://furrynetwork.com/artwork/341709</a>',
                  'localImgPath': '/home/ /драконы-неразр-тест/246938919_136836_11381943016272256370.jpg',
                  'remoteImgPath': 'https://d3gz42uwgl1r1y.cloudfront.net/ko/kodar/submission/2016/02/1dac4b544380d5874a518047f24b4eb2.jpg',
                  'tags': "artist:Kodar, any, dragon, fantasy, western",
                  "colorRowRect": colorRowRect,
                  "sendBtnName":"sendBtn0"}

        value = {"similarity": imageInfo["similarity"],
                 "source": imageInfo["source"],
                 "localImgPath": imageInfo["localImgPath"],
                 }
        QMetaObject.invokeMethod(
            self.view.rootObject(), "append", Q_ARG(QVariant, value1))
        self.view.show()
            return animal.type()

        if role == self.SizeRole:
            return animal.size()

        return QVariant()

    def roleNames(self):
        return self._roles


if __name__ == '__main__':
    import sys

    app = QGuiApplication(sys.argv)

    model = AnimalModel()
    model.addAnimal(Animal("Wolf", "Medium"))
    model.addAnimal(Animal("Polar bear", "Large"))
    model.addAnimal(Animal("Quoll", "Small"))

    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView)
    ctxt = view.rootContext()
    ctxt.setContextProperty('myModel', model)

    view.setSource(QUrl('qrc:view.qml'))
    view.show()

    sys.exit(app.exec_())
Beispiel #18
0
class Selector(QWidget):
    def __init__(self, goal_context, config_path, experimentlogger):
        self.path = config_path
        super(Selector, self).__init__()
        self.ros_timer = QTimer()
        self.ros_timer.timeout.connect(self.ros_poller)
        self.setup_ui()

        self.AUTOEXECUTE = False
        self.ignore_move_events = False

        self.context = goal_context
        self.planner = None
        self.current_plan = None
        self.current_action_succeeded = False
        self.current_action_index = 0
        self.current_menu_index = 0
        self.task = None

        self.planner = planning.Planner(goal_context.init, self.path)
        self.ros_handler = None
        self.experiment_logger = experimentlogger

        self.init_menu()
        self.show_goal_ui()

    def ros_poller(self):
        #         print "checking for ROS triggered changes in ROS event queue"
        if self.ros_handler and self.ros_handler.event_queue:
            #            print "Processing events from ROS event queue. There are %d events" % len(self.ros_handler.event_queue)
            event = self.ros_handler.event_queue.pop(0)
            #            print "popped ", str(event[0])
            if "teleop" not in str(event[0]):
                event[0](
                    *event[1]
                )  # event is a pair (method,[params]) the asteriks unpacks parameters
            else:
                logger.debug(
                    "HEY, don't teleoperate while actions are running: %s",
                    str(event[0]))
#            print "executed ", str(event[0])

    def setup_ui(self):
        self.view = QQuickView()
        self.view.setResizeMode(QQuickView.SizeRootObjectToView)
        container = QWidget.createWindowContainer(self.view)
        container.setFocusPolicy(Qt.StrongFocus)
        layout = QVBoxLayout()
        layout.addWidget(container)
        self.setLayout(layout)

        ctx = self.view.rootContext()
        ctx.setContextProperty("selector", self)
        ctx.setContextProperty("C", ui_constants)

        self.set_menu([])
        self.set_plan([])
        self.set_current(None)
        self.view.setSource(QUrl(self.path + 'ui.qml'))

        self.resize(800, 800)
        self.move(300, 300)
        self.setWindowTitle('Goal Planner GUI')
        self.setWindowIcon(QIcon('images/icon.png'))

        reload = QShortcut(QKeySequence("Ctrl+R"), self)
        reload.activated.connect(self.reload_ui)

        quit = QShortcut(QKeySequence("Ctrl+C"), self)
        quit.activated.connect(self.quit)
        quit = QShortcut(QKeySequence("Esc"), self)
        quit.activated.connect(self.quit)

        self.ros_timer.start(10)
        self.showMaximized()

    def test_populate(self):
        l = ["give", "grasp", "drop", "open", "close"]
        self.set_menu(l)

    def set_current(self, goal):
        ctx = self.view.rootContext()
        ctx.setContextProperty("currentGoal", QVariant(goal))
        self.current = goal  # do not free the list while it's in the model

    def set_menu(self, elems):
        ctx = self.view.rootContext()
        ctx.setContextProperty("selectionsModel", QVariant(elems))
        self.elems = elems  # do not free the list while it's in the model

    def set_plan(self, elems):
        ctx = self.view.rootContext()
        ctx.setContextProperty("planModel", QVariant(elems))
        self.plans = elems  # do not free the list while it's in the model

    def activate_loading(self):
        compute_image_focus = self.view.rootObject().findChild(
            QObject, "compute_image_focus")
        compute_image_focus.turnOn()

    def deactivate_loading(self):
        compute_image_focus = self.view.rootObject().findChild(
            QObject, "compute_image_focus")
        compute_image_focus.turnOff()

    @pyqtSlot()
    def goal_menu(self):
        class create_goal_menu(QThread):
            def __init__(self, instance):
                QThread.__init__(self)
                self.instance = instance

            def __del__(self):
                self.wait()

            def run(self):
                if constants.SHOW_FUNCTION_GOALS:
                    self.instance.next_goals = goals.FunctionGoal.initial_goals(
                        self.instance.context
                    ) + goals.ActionGoal.initial_goals(self.instance.context)
                else:
                    self.instance.next_goals = goals.ActionGoal.initial_goals(
                        self.instance.context)

        #         logger2.debug("Next goals: %s", map(str, self.next_goals))
                self.instance.next_goals = [
                    g for g in self.instance.next_goals
                    if self.instance.filter_goal(g)
                ]
        """
        Shows the selection of options
        """
        self.experiment_logger.log_performance(
            "Goal Menu shows next goals ...")
        self.create_goal_menu_thread = create_goal_menu(self)
        self.create_goal_menu_thread.finished.connect(
            self.create_goal_menu_done)
        self.create_goal_menu_thread.start()
        self.activate_loading()

    def create_goal_menu_done(self):
        items = [GoalMenuItem(g, -1) for g in self.next_goals]
        items.append(BackMenuItem())
        self.current_goal = GOAL_MENU
        self.build_header(None)
        self.set_menu(items)
        self.experiment_logger.log_performance(
            "Goal Menu shows next goals done", True)
        self.create_goal_menu_thread = None
        self.deactivate_loading()

    @pyqtSlot()
    def action_menu(self):
        self.next_goals = goals.ExecActionGoal.initial_goals(self.context)

        items = [GoalMenuItem(g, -1) for g in self.next_goals]
        items.append(BackMenuItem())

        self.current_goal = ACTION_MENU
        self.build_header(None)
        self.set_menu(items)

    @pyqtSlot(int)
    def select_goal(self, index):
        #         logger2.debug("select goal")
        #         image = self.view.rootObject().findChild(QObject, "compute_image")
        #         image.turnOn()
        #        self.experiment_logger.log_performance("user chose goal #%s" %index)

        #traceback.print_stack()
        if self.next_goals:
            self.display_goal(self.next_goals[index])
        #self.experiment_logger.log_performance("goal #%s displayed" %index)
#         image.turnOff()

    @pyqtSlot(int)
    def select_action(self, index):
        if self.ignore_move_events:
            logger.debug(
                "aborting action execution because an action is already in progress"
            )
            return
        #print self.current_action_index, index
        if not (self.current_action_index == index
                or self.current_action_index + 1 == index):
            logger.debug("Execute previous actions first!!!")
            return
        if self.current_plan and index < len(self.current_plan):
            if self.current_plan[
                    index].status == plans.ActionStatusEnum.EXECUTED:
                logger.debug("The action was already executed")
                return

            plan_node = self.current_plan[index]

            plan_node.status = plans.ActionStatusEnum.IN_PROGRESS
            self.display_status_text("Action is in progress")
            self.ignore_move_events = True
            self.ros_handler.execute_on_robot(plan_node.action,
                                              plan_node.arguments)

            items = [
                PlanItem(pnode, self.context) for pnode in self.current_plan
            ]
            self.set_plan(items)
            planView = self.view.rootObject().findChild(QObject, "planView")
            planView.setProperty("currentIndex", index)
            self.current_action_index = index
            self.current_action_succeeded = False
            logger.debug("action selection of %s done, action executed" %
                         index)
#            self.show_plan_ui()

    def action_executed(self):
        success = self.current_action_succeeded
        planView = self.view.rootObject().findChild(QObject, "planView")
        index = self.current_action_index
        logger.debug("current action index %d was executed. success = %s" %
                     (index, success))
        if success:
            plan_node = self.current_plan[index]
            if not self.planner.execute_action(plan_node.action,
                                               plan_node.arguments):
                plan_node.status = plans.ActionStatusEnum.FAILED
                logger.fatal(
                    "\033[91mWarning warning warning, Planner found action to be infeasible... aborting\033[0m"
                )
                #assert False
            self.current_plan[index].status = plans.ActionStatusEnum.EXECUTED
            #logger.debug("will refresh context now")
            #self.refresh_context(self.planner.problem)
        else:
            self.current_plan[
                index].status = plans.ActionStatusEnum.UNSUCCESSFUL
        self.ignore_move_events = False
        logger.debug("select next action in plan GUI")
        planView = self.view.rootObject().findChild(QObject, "planView")
        items = [PlanItem(pnode, self.context) for pnode in self.current_plan]
        self.set_plan(items)
        if success and index + 1 < len(self.current_plan):
            planView.setProperty("currentIndex",
                                 index + 1)  # select next plan step
            if self.AUTOEXECUTE:
                logger.debug("Auto-executing next")
                pui = self.view.rootObject().findChild(QObject, "planner_ui")
                pui.teleop_select()
            else:
                logger.debug("autoexecute ist turned off")
                self.refresh_context(self.planner.problem)
        else:
            planView.setProperty("currentIndex", index)
#        self.show_plan_ui()

    def handle_world_update(self):
        world = self.view.rootObject().findChild(QObject, "world_image")
        world.turn_world()
        self.init_menu()
        self.show_goal_ui()
        self.planner.set_state(self.context.init)
        # print("World change happened, world turning image displayed. Now I check the plan")
        # print("goal= ",self.goal)
        # self.plan(goal)

    @pyqtSlot()
    def back(self):
        if self.ignore_move_events and self.current_plan[
                self.
                current_action_index].status == plans.ActionStatusEnum.IN_PROGRESS:
            #action is currently running -> abort it
            self.current_plan[
                self.
                current_action_index].status = plans.ActionStatusEnum.UNSUCCESSFUL
            planView = self.view.rootObject().findChild(QObject, "planView")
            items = [
                PlanItem(pnode, self.context) for pnode in self.current_plan
            ]
            self.set_plan(items)
            planView.setProperty("currentIndex", self.current_action_index)
            self.ros_handler.abort_current_action()
            self.ignore_move_events = False
            return
        if self.menu_stack:
            g = self.menu_stack.pop()
            self.current_goal = None
            if g == GOAL_MENU:
                self.goal_menu()
            elif g == ACTION_MENU:
                self.action_menu()
            else:
                self.display_goal(g)
        else:
            self.init_menu()
            self.show_goal_ui()

    @pyqtSlot()
    def choose(self):
        logger.debug("choose pressed")
        # self.menu_stack.pop()
        goal = self.current_goal
        if goal.is_unique():
            if isinstance(goal, goals.ExecActionGoal):
                self.execute_action_goal(goal)
            else:
                self.plan(goal)
            return

        if self.current_goal is not None:
            self.menu_stack.append(self.current_goal)
        current_index = goal.arg_index(goal.get_current_arg())
        logger.debug("index = %d", current_index)
        logger.debug(
            "\033[93m next from current goal----------\n----------\n--------\n-------"
        )
        self.next_goals = [
            g for g in self.current_goal.next_all()
            if not g.is_empty() and self.filter_goal(g)
        ]
        #self.next_goals = [g for g in self.current_goal.next() if not g.is_empty() and self.filter_goal(g)]
        logger.debug("\033[0m next_goals: %s", map(str, self.next_goals))
        items = [GoalMenuItem(g, current_index) for g in self.next_goals]

        if self.menu_stack:
            items.append(ChooseMenuItem())
            items.append(BackMenuItem())

        self.build_header(goal)
        self.set_menu(items)

    def reload_ui(self):
        #        print "reload_ui clear cache"
        self.view.engine().clearComponentCache()
        #        print "reload_ui set menu"
        self.set_menu([])
        #        print "reload_ui set plan"
        self.set_plan([])
        #        print "reload_ui set current"
        self.set_current(None)

        if self.mode == MENU_MODE_GOAL:
            logger.debug("reload_ui show goal")
            self.show_goal_ui()
        else:
            logger.debug("reload_ui show plan")
            self.show_plan_ui()
        #print "reload_ui updatself.menu_stack.append(self.current_goal)e"
        self.repaint()

    @pyqtSlot()
    def quit(self):
        #pass
        # self.hide()
        sys.exit(0)

#   @pyqtSlot('QString')

    def display_status_text(self, displaytext):
        #        print "about to display in statusbar >%s<" % displaytext
        bar = self.view.rootObject().findChild(QObject, "statusbar")
        #        QMetaObject.invokeMethod(bar, "display", Qt.DirectConnection, Q_ARG(QVariant, displaytext))
        #bar.display("%s" % displaytext)
        bar.display("")

    def action_help(self, action):
        help = ""
        if action == "go":
            help = "go [robot] [room], i.e., move the robot into a room"
        elif action == "approach":
            help = "approach [robot] [target base], i.e., move the robot to a target (furniture, flowerbed, humans) in the current room"
        elif action == "drop":
            help = "drop [robot] [object] [target], i.e., drop an object on a target (furniture, flowerbed, humans)"
        elif action == "arrange":
            help = "arrange [robot] [flower] [vase], i.e., arrange a flower in a vase"
        elif action == "open":
            help = "open [robot] [bottle], i.e., lets the robot open a bottle"
        elif action == "drink":
            help = "drink [human] [vessel] [content], i.e., the robot gives a drink in a vessel with the specified content to the human"
        elif action == "pour":
            help = "pour [from vessel] [to vessel] [content], i.e., the robot pours a liquid (3. parameter) from the first vessel into the second one"
        elif action == "give":
            help = "give [robot] [object] [human], i.e., the robot brings an object to the human"
        elif action == "grasp":
            help = "grasp [robot] [object], i.e., the robot grasps an object"
        elif action == "pick":
            help = "pick [robot] [flower] [vase], i.e., the robot picks a flower from a vase"
        else:
            help = "Unknown action " + action
        return help

    @pyqtSlot(int)
    def display_status(self, index):
        text = "Unknown Element with Index " + str(index)
        #         print "Displaying ", text
        #         print "Current goal -> ", self.current_goal
        #         print "Current menustack -> ", self.menu_stack
        #         print "current -> ", self.current
        #         print "Mode -> ", self.mode
        self.current_menu_index = index
        if self.mode == MENU_MODE_GOAL:
            if self.current_goal:
                #                 print "ui.py/display_status debug dump of current goal:"
                #                 print self.current_goal.__class__
                #                 if type(self.current_goal) is not int:
                #                     print "active = %s" % self.current_goal.arg_index
                #                     print "goal args= "
                #                     for arg in self.current_goal.args:
                #                         print str(arg)
                if (index < len(self.next_goals)):
                    next_goal = self.next_goals[index]
                    text = str(next_goal)
                    if isinstance(next_goal, goals.ActionGoal):
                        action = next_goal.action.name
                        help = self.action_help(action)
                        text = "Current: " + text + "\nHelp: " + help
                elif (index == len(self.next_goals)):
                    text = "BACK"
            else:
                text = rootMenu[index].arg[0].text
        else:
            if index < len(self.current_plan):
                text = str(self.current_plan[index])
            else:
                text = "Cannot display status of step %s in a plan of length %s" % (
                    index, len(self.current_plan))

#         if self.current_goal and isinstance(self.current_goal, goals.ActionGoal):
#             action = self.current_goal.action.name
#             help = self.action_help(action)
#             text = "Current: " + text + "\nHelp: " + help
        self.display_status_text(text)
        self.experiment_logger.log_navigation(index, text)

    def init_menu(self):
        #         logger2.debug("initializing menu")
        #         image = self.view.rootObject().findChild(QObject, "compute_image")
        #         image.turnOn()
        self.mode = MENU_MODE_GOAL
        self.menu_stack = []
        self.current_goal = None
        self.build_header(None)

        ctx = self.view.rootContext()
        # root = self.view.rootObject().findChild(QObject, "rootMenu")
        logger.debug("set context property to root menu %s", rootMenu)
        #         image.turnOff()
        ctx.setContextProperty("selectionsModel", rootMenu)
        self.set_menu(rootMenu)

    def show_goal_ui(self):
        self.mode = MENU_MODE_GOAL
        gui = self.view.rootObject().findChild(QObject, "goal_ui")
        pui = self.view.rootObject().findChild(QObject, "planner_ui")
        pui.setProperty("visible", False)
        gui.setProperty("visible", True)
        gui.setProperty("focus", True)

    def show_plan_ui(self):
        logger.debug("show plan ui in mode %s", self.mode)
        self.mode = MENU_MODE_PLAN
        gui = self.view.rootObject().findChild(QObject, "goal_ui")
        pui = self.view.rootObject().findChild(QObject, "planner_ui")
        gui.setProperty("visible", False)
        pui.setProperty("visible", True)
        pui.setProperty("focus", True)
        self.display_status(0)

    def add_image_provider(self, image_provider):
        self.image_provider = image_provider
        self.view.rootContext().engine().addImageProvider(
            'ros_image_provider', self.image_provider)
        image_provider.connect_gui(self)

    # def change_focuspoint_color_srv(self):
    # widget = self.view.rootObject().findChild(QObject, 'crosshair')

    # def switch_to_video_view(self):
    #     self.view.rootObject().switch_to_video_view()

    # def switch_to_main_view(self):
    #     self.view.rootObject().switch_to_main_view()

    def build_header(self, goal):
        if goal is None or goal.__class__ == goals.GoalSpec:
            self.set_current(None)
            return

        self.set_current(CurrentGoalItem(goal))

    def display_goal(self, goal):
        class display_goal_menu(QThread):
            def __init__(self, instance, goal):
                QThread.__init__(self)
                self.instance = instance
                self.goal = goal

            def __del__(self):
                self.wait()

            def run(self):
                if self.goal.is_unique():
                    if isinstance(self.goal, goals.ExecActionGoal):
                        self.instance.execute_action_goal(self.goal)
                    else:
                        self.instance.plan(self.goal, False)
                        self.instance.show_plan_flag = True
                    return

                self.instance.current_index = self.goal.arg_index(
                    self.goal.get_current_arg())
                self.instance.next_goals = [
                    g for g in self.instance.current_goal.next_flattened()
                    if not g.is_empty() and self.instance.filter_goal(g)
                ]

        logger.debug("will display goal %s", str(goal))
        self.experiment_logger.log_performance("ui.show_goal_ui display goal")

        #         #CalledGoals.Instance().call_goal(goal)
        #         if goal.is_unique():
        #             if isinstance(goal, goals.ExecActionGoal):
        #                 self.execute_action_goal(goal)
        #             else:
        #                 self.plan(goal)
        #             return
        #
        #         if self.current_goal is not None:
        #             self.menu_stack.append(self.current_goal)
        #
        #         self.current_goal = goal
        #         self.current_index = goal.arg_index(goal.get_current_arg())
        #tmp_goals = [(-CalledGoals.Instance().get_calls(g)*100+i, g) for i, g in enumerate(self.current_goal.next_flattened()) if not g.is_empty() and self.filter_goal(g)]
        #tmp_goals.sort()
        #self.next_goals = [g for _, g in tmp_goals]
        if self.current_goal is not None:
            self.menu_stack.append(self.current_goal)

        self.current_goal = goal
        self.show_plan_flag = False

        self.display_goal_menu_thread = display_goal_menu(self, goal)
        self.display_goal_menu_thread.finished.connect(
            self.display_goal_menu_done)
        self.display_goal_menu_thread.start()
        self.activate_loading()

#         self.next_goals = [g for g in self.current_goal.next_flattened() if not g.is_empty() and self.filter_goal(g)]

    def display_goal_menu_done(self):
        if self.show_plan_flag:
            self.plan_update_gui()
            if self.experiment_logger.autoperform:
                self.quit()

        items = [GoalMenuItem(g, self.current_index) for g in self.next_goals]
        if self.menu_stack:
            #items.append(ChooseMenuItem())
            items.append(BackMenuItem())

        self.build_header(self.current_goal)
        self.set_menu(items)
        self.experiment_logger.log_performance("ui.display_goal_menu_done",
                                               True)
        self.display_goal_menu_thread = None
        self.deactivate_loading()

    def execute_action_goal(self, action_goal):
        action = action_goal.action
        problem = self.context.problem

        #instantiate an action that matches the selected action and execute it
        valid_args = action_goal.get_matches()
        action_args = []
        for aarg in action_goal.action.args:
            if aarg in action_goal.used_arguments:
                i = action_goal.used_arguments.index(aarg)
                objects = set(tup[i] for tup in valid_args)
                # print self.args[i], map(str, objects)
            else:
                objects = list(problem.get_all_objects(aarg.type))
            action_args.append(objects)

        inst_function = action.get_inst_func(self.context.init)
        args = None
        for mapping in action.smart_instantiate(inst_function, action.args,
                                                action_args, problem):
            args = [mapping[a] for a in action.args]
            break

        self.planner.execute_action(action, args)
        #self.refresh_context(self.planner.problem)

        self.menu_stack = []
        self.action_menu()

    def refresh_context(self, problem, refs=None):
        if refs is None:  # use old references instead
            refs = self.context.refs
        self.context = goals.GoalContext(problem, refs)

    def plan_update_gui(self):
        items = [PlanItem(pnode, self.context) for pnode in self.current_plan]
        self.set_plan(items)
        self.show_plan_ui()

    def plan(self, goal, update_gui=True):
        self.menu_stack = []
        self.current_goal = None
        self.current_action_index = 0

        assert self.planner is not None
        self.planner.find_plan(goal)
        self.current_plan = self.planner.get_plan()

        if update_gui:
            self.plan_update_gui()

    def filter_goal(self, goal):
        if goal.is_universal():
            return False
        return True
Beispiel #19
0
if __name__ == '__main__':
    #===========================================================================
    # import os
    # import sys
    # app = QGuiApplication(sys.argv)
    # engine = QQmlApplicationEngine()
    # context = engine.rootContext()
    # game = Game()
    # context.setContextProperty('loginCh', game)
    # qml_filename = os.path.join('qml/main.qml')
    # engine.load(qml_filename)
    # sys.exit(app.exec_())
    #===========================================================================

    myApp = QApplication(sys.argv)
    # Create a label and set its properties
    appLabel = QQuickView()
    game = Game()
    user = User()
    appLabel.rootContext().setContextProperty('loginCh', game)
    appLabel.rootContext().setContextProperty('user', user)
    appLabel.setSource(QUrl('qml/Loginw.qml'))

    # Show the Label
    appLabel.show()

    # Execute the Application and Exit
    myApp.exec_()
    sys.exit()
Beispiel #20
0
    appLabel = QQuickView()
    appLabel.setSource(QUrl('main.qml'))
    #appLabel.load(QUrl('main2.qml'))

    # Show the Label
    appLabel.show()
    
    # Create a QML engine.
    engine = QQmlEngine()
    
    # Initialize PhotoBoothEngine.
    pbengine = PhotoBoothEngine()
    pbengine.on_status.connect(appLabel.rootObject().status)
    pbengine.on_update_filter_preview.connect(appLabel.rootObject().updateImageFilterPreview)
    
    appLabel.rootContext().setContextProperty('pbengine', pbengine)

    # Create a component factory and load the QML script.
    print("Hello")
    component = QQmlComponent(appLabel.engine())
    component.loadUrl(QUrl('TextStatusFly.qml'))
    
    print("Hello2")
    asdf = component.create(appLabel.rootContext())
    
    print("Hello3")
    asdf.setParentItem(appLabel.rootObject())
    asdf.setParent(appLabel.rootObject())
    
    print("Hello4")
    #asdf.setProperty("targetX", 100)
Beispiel #21
0
class Application(QApplication):
    """Main Nuxeo Drive application controlled by a system tray icon + menu"""

    icon = QIcon(str(find_icon("app_icon.svg")))
    icons: Dict[str, QIcon] = {}
    icon_state = None
    use_light_icons = None
    filters_dlg: Optional[FiltersDialog] = None
    _delegator: Optional["NotificationDelegator"] = None
    tray_icon: DriveSystrayIcon

    def __init__(self, manager: "Manager", *args: Any) -> None:
        super().__init__(list(*args))
        self.manager = manager

        self.osi = self.manager.osi
        self.setWindowIcon(self.icon)
        self.setApplicationName(APP_NAME)
        self._init_translator()
        self.setQuitOnLastWindowClosed(False)

        self.ask_for_metrics_approval()

        self._conflicts_modals: Dict[str, bool] = dict()
        self.current_notification: Optional[Notification] = None
        self.default_tooltip = APP_NAME

        font = QFont("Helvetica, Arial, sans-serif", 12)
        self.setFont(font)
        self.ratio = sqrt(QFontMetricsF(font).height() / 12)

        self.init_gui()

        self.manager.dropEngine.connect(self.dropped_engine)

        self.setup_systray()
        self.manager.reloadIconsSet.connect(self.load_icons_set)

        # Direct Edit
        self.manager.direct_edit.directEditConflict.connect(self._direct_edit_conflict)
        self.manager.direct_edit.directEditError.connect(self._direct_edit_error)

        # Check if actions is required, separate method so it can be overridden
        self.init_checks()

        # Setup notification center for macOS
        if MAC:
            self._setup_notification_center()

        # Application update
        self.manager.updater.appUpdated.connect(self.quit)
        self.manager.updater.serverIncompatible.connect(self._server_incompatible)
        self.manager.updater.wrongChannel.connect(self._wrong_channel)

        # Display release notes on new version
        if self.manager.old_version != self.manager.version:
            self.show_release_notes(self.manager.version)

        # Listen for nxdrive:// sent by a new instance
        self.init_nxdrive_listener()

        # Connect this slot last so the other slots connected
        # to self.aboutToQuit can run beforehand.
        self.aboutToQuit.connect(self.manager.stop)

    @if_frozen
    def add_qml_import_path(self, view: QQuickView) -> None:
        """
        Manually set the path to the QML folder to fix errors with unicode paths.
        This is needed only on Windows when packaged with Nuitka.
        """
        if Options.freezer != "nuitka":
            return

        qml_dir = Options.res_dir.parent / "PyQt5" / "Qt" / "qml"
        log.debug(f"Setting QML import path for {view} to {qml_dir!r}")
        view.engine().addImportPath(str(qml_dir))

    def init_gui(self) -> None:

        self.api = QMLDriveApi(self)
        self.conflicts_model = FileModel()
        self.errors_model = FileModel()
        self.engine_model = EngineModel(self)
        self.action_model = ActionModel()
        self.file_model = FileModel()
        self.ignoreds_model = FileModel()
        self.language_model = LanguageModel()

        self.add_engines(list(self.manager._engines.values()))
        self.engine_model.statusChanged.connect(self.update_status)
        self.language_model.addLanguages(Translator.languages())

        flags = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint

        if WINDOWS:
            self.conflicts_window = QQuickView()
            self.add_qml_import_path(self.conflicts_window)
            self.conflicts_window.setMinimumWidth(550)
            self.conflicts_window.setMinimumHeight(600)
            self.settings_window = QQuickView()
            self.add_qml_import_path(self.settings_window)
            self.settings_window.setMinimumWidth(640)
            self.settings_window.setMinimumHeight(520)
            self.systray_window = SystrayWindow()
            self.add_qml_import_path(self.systray_window)

            self._fill_qml_context(self.conflicts_window.rootContext())
            self._fill_qml_context(self.settings_window.rootContext())
            self._fill_qml_context(self.systray_window.rootContext())
            self.systray_window.rootContext().setContextProperty(
                "systrayWindow", self.systray_window
            )

            self.conflicts_window.setSource(
                QUrl.fromLocalFile(str(find_resource("qml", "Conflicts.qml")))
            )
            self.settings_window.setSource(
                QUrl.fromLocalFile(str(find_resource("qml", "Settings.qml")))
            )
            self.systray_window.setSource(
                QUrl.fromLocalFile(str(find_resource("qml", "Systray.qml")))
            )
            flags |= Qt.Popup
        else:
            self.app_engine = QQmlApplicationEngine()
            self._fill_qml_context(self.app_engine.rootContext())
            self.app_engine.load(
                QUrl.fromLocalFile(str(find_resource("qml", "Main.qml")))
            )
            root = self.app_engine.rootObjects()[0]
            self.conflicts_window = root.findChild(QQuickWindow, "conflictsWindow")
            self.settings_window = root.findChild(QQuickWindow, "settingsWindow")
            self.systray_window = root.findChild(SystrayWindow, "systrayWindow")
            if LINUX:
                flags |= Qt.Drawer

        self.systray_window.setFlags(flags)

        self.manager.newEngine.connect(self.add_engines)
        self.manager.initEngine.connect(self.add_engines)
        self.manager.dropEngine.connect(self.remove_engine)
        self._window_root(self.conflicts_window).changed.connect(self.refresh_conflicts)
        self._window_root(self.systray_window).appUpdate.connect(self.api.app_update)
        self._window_root(self.systray_window).getLastFiles.connect(self.get_last_files)
        self.api.setMessage.connect(self._window_root(self.settings_window).setMessage)

        if self.manager.get_engines():
            current_uid = self.engine_model.engines_uid[0]
            self.get_last_files(current_uid)
            self.update_status(self.manager._engines[current_uid])

        self.manager.updater.updateAvailable.connect(
            self._window_root(self.systray_window).updateAvailable
        )
        self.manager.updater.updateProgress.connect(
            self._window_root(self.systray_window).updateProgress
        )

    @pyqtSlot(Action)
    def action_started(self, action: Action) -> None:
        self.refresh_actions()

    @pyqtSlot(Action)
    def action_progressing(self, action: Action) -> None:
        self.action_model.set_progress(action.export())

    @pyqtSlot(Action)
    def action_done(self, action: Action) -> None:
        self.refresh_actions()

    def add_engines(self, engines: Union[Engine, List[Engine]]) -> None:
        if not engines:
            return

        engines = engines if isinstance(engines, list) else [engines]
        for engine in engines:
            self.engine_model.addEngine(engine.uid)

    def remove_engine(self, uid: str) -> None:
        self.engine_model.removeEngine(uid)

    def _fill_qml_context(self, context: QQmlContext) -> None:
        """ Fill the context of a QML element with the necessary resources. """
        context.setContextProperty("ConflictsModel", self.conflicts_model)
        context.setContextProperty("ErrorsModel", self.errors_model)
        context.setContextProperty("EngineModel", self.engine_model)
        context.setContextProperty("ActionModel", self.action_model)
        context.setContextProperty("FileModel", self.file_model)
        context.setContextProperty("IgnoredsModel", self.ignoreds_model)
        context.setContextProperty("languageModel", self.language_model)
        context.setContextProperty("api", self.api)
        context.setContextProperty("application", self)
        context.setContextProperty("currentLanguage", self.current_language())
        context.setContextProperty("manager", self.manager)
        context.setContextProperty("osi", self.osi)
        context.setContextProperty("updater", self.manager.updater)
        context.setContextProperty("ratio", self.ratio)
        context.setContextProperty("update_check_delay", Options.update_check_delay)
        context.setContextProperty("isFrozen", Options.is_frozen)
        context.setContextProperty("WINDOWS", WINDOWS)
        context.setContextProperty("tl", Translator._singleton)
        context.setContextProperty(
            "nuxeoVersionText", f"{APP_NAME} {self.manager.version}"
        )
        metrics = self.manager.get_metrics()
        versions = (
            f'Python {metrics["python_version"]}, '
            f'Qt {metrics["qt_version"]}, '
            f'SIP {metrics["sip_version"]}'
        )
        if Options.system_wide:
            versions += " [admin]"
        context.setContextProperty("modulesVersionText", versions)

        colors = {
            "darkBlue": "#1F28BF",
            "nuxeoBlue": "#0066FF",
            "lightBlue": "#00ADED",
            "teal": "#73D2CF",
            "purple": "#8400FF",
            "red": "#C02828",
            "orange": "#FF9E00",
            "darkGray": "#495055",
            "mediumGray": "#7F8284",
            "lightGray": "#BCBFBF",
            "lighterGray": "#F5F5F5",
        }

        for name, value in colors.items():
            context.setContextProperty(name, value)

    def _window_root(self, window):
        if WINDOWS:
            return window.rootObject()
        return window

    def translate(self, message: str, values: List[Any] = None) -> str:
        return Translator.get(message, values)

    def _show_window(self, window: QWindow) -> None:
        window.show()
        window.raise_()
        window.requestActivate()

    def _init_translator(self) -> None:
        locale = Options.force_locale or Options.locale
        Translator(find_resource("i18n"), self.manager.get_config("locale", locale))
        # Make sure that a language change changes external values like
        # the text in the contextual menu
        Translator.on_change(self._handle_language_change)
        # Trigger it now
        self.osi.register_contextual_menu()
        self.installTranslator(Translator._singleton)

    @pyqtSlot(str, Path, str)
    def _direct_edit_conflict(self, filename: str, ref: Path, digest: str) -> None:
        log.debug(f"Entering _direct_edit_conflict for {filename!r} / {ref!r}")
        try:
            if filename in self._conflicts_modals:
                log.debug(f"Filename already in _conflicts_modals: {filename!r}")
                return
            log.debug(f"Putting filename in _conflicts_modals: {filename!r}")
            self._conflicts_modals[filename] = True

            msg = QMessageBox()
            msg.setInformativeText(
                Translator.get("DIRECT_EDIT_CONFLICT_MESSAGE", [short_name(filename)])
            )
            overwrite = msg.addButton(
                Translator.get("DIRECT_EDIT_CONFLICT_OVERWRITE"), QMessageBox.AcceptRole
            )
            msg.addButton(Translator.get("CANCEL"), QMessageBox.RejectRole)
            msg.setIcon(QMessageBox.Warning)
            msg.exec_()
            if msg.clickedButton() == overwrite:
                self.manager.direct_edit.force_update(ref, digest)
            del self._conflicts_modals[filename]
        except:
            log.exception(
                f"Error while displaying Direct Edit conflict modal dialog for {filename!r}"
            )

    @pyqtSlot(str, list)
    def _direct_edit_error(self, message: str, values: List[str]) -> None:
        """ Display a simple Direct Edit error message. """
        msg_text = self.translate(message, values)
        log.warning(f"DirectEdit error message: '{msg_text}', values={values}")
        msg = QMessageBox()
        msg.setWindowTitle(f"Direct Edit - {APP_NAME}")
        msg.setWindowIcon(self.icon)
        msg.setIcon(QMessageBox.Warning)
        msg.setTextFormat(Qt.RichText)
        msg.setText(msg_text)
        msg.exec_()

    @pyqtSlot()
    def _root_deleted(self) -> None:
        engine = self.sender()
        log.info(f"Root has been deleted for engine: {engine.uid}")

        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setWindowIcon(self.icon)
        msg.setText(Translator.get("DRIVE_ROOT_DELETED", [engine.local_folder]))
        recreate = msg.addButton(
            Translator.get("DRIVE_ROOT_RECREATE"), QMessageBox.AcceptRole
        )
        disconnect = msg.addButton(
            Translator.get("DRIVE_ROOT_DISCONNECT"), QMessageBox.RejectRole
        )

        msg.exec_()
        res = msg.clickedButton()
        if res == disconnect:
            self.manager.unbind_engine(engine.uid)
        elif res == recreate:
            engine.reinit()
            engine.start()

    @pyqtSlot()
    def _no_space_left(self) -> None:
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowIcon(self.icon)
        msg.setText(Translator.get("NO_SPACE_LEFT_ON_DEVICE"))
        msg.addButton(Translator.get("OK"), QMessageBox.AcceptRole)
        msg.exec_()

    @pyqtSlot(Path)
    def _root_moved(self, new_path: Path) -> None:
        engine = self.sender()
        log.info(f"Root has been moved for engine: {engine.uid} to {new_path!r}")
        info = [engine.local_folder, str(new_path)]

        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setWindowIcon(self.icon)
        msg.setText(Translator.get("DRIVE_ROOT_MOVED", info))
        move = msg.addButton(
            Translator.get("DRIVE_ROOT_UPDATE"), QMessageBox.AcceptRole
        )
        recreate = msg.addButton(
            Translator.get("DRIVE_ROOT_RECREATE"), QMessageBox.AcceptRole
        )
        disconnect = msg.addButton(
            Translator.get("DRIVE_ROOT_DISCONNECT"), QMessageBox.RejectRole
        )
        msg.exec_()
        res = msg.clickedButton()

        if res == disconnect:
            self.manager.unbind_engine(engine.uid)
        elif res == recreate:
            engine.reinit()
            engine.start()
        elif res == move:
            engine.set_local_folder(new_path)
            engine.start()

    def confirm_deletion(self, path: Path) -> DelAction:
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Question)
        msg.setWindowIcon(self.icon)

        cb = QCheckBox(Translator.get("DONT_ASK_AGAIN"))
        msg.setCheckBox(cb)

        mode = self.manager.get_deletion_behavior()
        unsync = None
        if mode is DelAction.DEL_SERVER:
            descr = "DELETION_BEHAVIOR_CONFIRM_DELETE"
            confirm_text = "DELETE_FOR_EVERYONE"
            unsync = msg.addButton(
                Translator.get("JUST_UNSYNC"), QMessageBox.RejectRole
            )
        elif mode is DelAction.UNSYNC:
            descr = "DELETION_BEHAVIOR_CONFIRM_UNSYNC"
            confirm_text = "UNSYNC"

        msg.setText(
            Translator.get(descr, [str(path), Translator.get("SELECT_SYNC_FOLDERS")])
        )
        msg.addButton(Translator.get("CANCEL"), QMessageBox.RejectRole)
        confirm = msg.addButton(Translator.get(confirm_text), QMessageBox.AcceptRole)
        msg.exec_()

        res = msg.clickedButton()
        if cb.isChecked():
            self.manager._dao.store_bool("show_deletion_prompt", False)

        if res == confirm:
            return mode
        if res == unsync:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Question)
            msg.setWindowIcon(self.icon)
            msg.setText(Translator.get("DELETION_BEHAVIOR_SWITCH"))
            msg.addButton(Translator.get("NO"), QMessageBox.RejectRole)
            confirm = msg.addButton(Translator.get("YES"), QMessageBox.AcceptRole)
            msg.exec_()
            res = msg.clickedButton()
            if res == confirm:
                self.manager.set_deletion_behavior(DelAction.UNSYNC)
            return DelAction.UNSYNC
        return DelAction.ROLLBACK

    @pyqtSlot(Path)
    def _doc_deleted(self, path: Path) -> None:
        engine: Engine = self.sender()
        mode = self.confirm_deletion(path)

        if mode is DelAction.ROLLBACK:
            # Re-sync the document
            engine.rollback_delete(path)
        else:
            # Delete or filter out the document
            engine.delete_doc(path, mode)

    @pyqtSlot(Path, Path)
    def _file_already_exists(self, oldpath: Path, newpath: Path) -> None:
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setWindowIcon(self.icon)
        msg.setText(Translator.get("FILE_ALREADY_EXISTS", values=[str(oldpath)]))
        replace = msg.addButton(Translator.get("REPLACE"), QMessageBox.AcceptRole)
        msg.addButton(Translator.get("CANCEL"), QMessageBox.RejectRole)
        msg.exec_()
        if msg.clickedButton() == replace:
            oldpath.unlink()
            normalize_event_filename(newpath)
        else:
            newpath.unlink()

    @pyqtSlot(object)
    def dropped_engine(self, engine: "Engine") -> None:
        # Update icon in case the engine dropped was syncing
        self.change_systray_icon()

    @pyqtSlot()
    def change_systray_icon(self) -> None:
        # Update status has the precedence over other ones
        if self.manager.updater.status not in (
            UPDATE_STATUS_UNAVAILABLE_SITE,
            UPDATE_STATUS_UP_TO_DATE,
        ):
            self.set_icon_state("update")
            return

        syncing = conflict = False
        engines = self.manager.get_engines()
        invalid_credentials = paused = offline = True

        for engine in engines.values():
            syncing |= engine.is_syncing()
            invalid_credentials &= engine.has_invalid_credentials()
            paused &= engine.is_paused()
            offline &= engine.is_offline()
            conflict |= bool(engine.get_conflicts())

        if offline:
            new_state = "error"
            Action(Translator.get("OFFLINE"))
        elif invalid_credentials:
            new_state = "error"
            Action(Translator.get("AUTH_EXPIRED"))
        elif not engines:
            new_state = "disabled"
            Action.finish_action()
        elif paused:
            new_state = "paused"
            Action.finish_action()
        elif syncing:
            new_state = "syncing"
        elif conflict:
            new_state = "conflict"
        else:
            new_state = "idle"
            Action.finish_action()

        self.set_icon_state(new_state)

    def refresh_conflicts(self, uid: str) -> None:
        """ Update the content of the conflicts/errors window. """
        self.conflicts_model.empty()
        self.errors_model.empty()
        self.ignoreds_model.empty()

        self.conflicts_model.addFiles(self.api.get_conflicts(uid))
        self.errors_model.addFiles(self.api.get_errors(uid))
        self.ignoreds_model.addFiles(self.api.get_unsynchronizeds(uid))

    @pyqtSlot()
    def show_conflicts_resolution(self, engine: "Engine") -> None:
        """ Display the conflicts/errors window. """
        self.refresh_conflicts(engine.uid)
        self._window_root(self.conflicts_window).setEngine.emit(engine.uid)
        self.conflicts_window.show()
        self.conflicts_window.requestActivate()

    @pyqtSlot()
    def show_settings(self, section: str = "General") -> None:
        sections = {"General": 0, "Accounts": 1, "About": 2}
        self._window_root(self.settings_window).setSection.emit(sections[section])
        self.settings_window.show()
        self.settings_window.requestActivate()

    @pyqtSlot()
    def show_systray(self) -> None:
        icon = self.tray_icon.geometry()

        if not icon or icon.isEmpty():
            # On Ubuntu it's likely we can't retrieve the geometry.
            # We're simply displaying the systray in the top right corner.
            screen = self.desktop().screenGeometry()
            pos_x = screen.right() - self.systray_window.width() - 20
            pos_y = 30
        else:
            dpi_ratio = self.primaryScreen().devicePixelRatio() if WINDOWS else 1
            pos_x = max(
                0, (icon.x() + icon.width()) / dpi_ratio - self.systray_window.width()
            )
            pos_y = icon.y() / dpi_ratio - self.systray_window.height()
            if pos_y < 0:
                pos_y = (icon.y() + icon.height()) / dpi_ratio

        self.systray_window.setX(pos_x)
        self.systray_window.setY(pos_y)

        self.systray_window.show()
        self.systray_window.raise_()

    @pyqtSlot()
    def hide_systray(self):
        self.systray_window.hide()

    @pyqtSlot()
    def open_help(self) -> None:
        self.manager.open_help()

    @pyqtSlot()
    def destroyed_filters_dialog(self) -> None:
        self.filters_dlg = None

    @pyqtSlot()
    def show_filters(self, engine: "Engine") -> None:
        if self.filters_dlg:
            self.filters_dlg.close()
            self.filters_dlg = None

        self.filters_dlg = FiltersDialog(self, engine)
        self.filters_dlg.destroyed.connect(self.destroyed_filters_dialog)

        # Close the settings window at the same time of the filters one
        if hasattr(self, "close_settings_too"):
            self.filters_dlg.destroyed.connect(self.settings_window.close)
            delattr(self, "close_settings_too")

        self.filters_dlg.show()
        self._show_window(self.settings_window)

    @pyqtSlot(str, object)
    def _open_authentication_dialog(
        self, url: str, callback_params: Dict[str, str]
    ) -> None:
        self.api._callback_params = callback_params
        if Options.is_frozen:
            """
            Authenticate through the browser.

            This authentication requires the server's Nuxeo Drive addon to include
            NXP-25519. Instead of opening the server's login page in a WebKit view
            through the app, it opens in the browser and retrieves the login token
            by opening an nxdrive:// URL.
            """
            self.manager.open_local_file(url)
        else:
            self._web_auth_not_frozen(url)

    def _web_auth_not_frozen(self, url: str) -> None:
        """
        Open a dialog box to fill the credentials.
        Then a request will be done using the Python client to
        get a token.

        This is used when the application is not frozen as there is no custom
        protocol handler in this case.
        """

        from PyQt5.QtWidgets import QLineEdit
        from nuxeo.client import Nuxeo

        dialog = QDialog()
        dialog.setWindowTitle(self.translate("WEB_AUTHENTICATION_WINDOW_TITLE"))
        dialog.setWindowIcon(self.icon)
        dialog.resize(250, 100)

        layout = QVBoxLayout()

        username = QLineEdit("Administrator", parent=dialog)
        password = QLineEdit("Administrator", parent=dialog)
        password.setEchoMode(QLineEdit.Password)
        layout.addWidget(username)
        layout.addWidget(password)

        def auth() -> None:
            """Retrieve a token and create the account."""
            user = str(username.text())
            pwd = str(password.text())
            nuxeo = Nuxeo(
                host=url,
                auth=(user, pwd),
                proxies=self.manager.proxy.settings(url=url),
                verify=Options.ca_bundle or not Options.ssl_no_verify,
            )
            try:
                token = nuxeo.client.request_auth_token(
                    device_id=self.manager.device_id,
                    app_name=APP_NAME,
                    permission=TOKEN_PERMISSION,
                    device=get_device(),
                )
            except Exception as exc:
                log.error(f"Connection error: {exc}")
                token = ""
            finally:
                del nuxeo

            # Check we have a token and not a HTML response
            if "\n" in token:
                token = ""

            self.api.handle_token(token, user)
            dialog.close()

        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
        buttons.accepted.connect(auth)
        buttons.rejected.connect(dialog.close)
        layout.addWidget(buttons)

        dialog.setLayout(layout)
        dialog.exec_()

    @pyqtSlot(object)
    def _connect_engine(self, engine: "Engine") -> None:
        engine.syncStarted.connect(self.change_systray_icon)
        engine.syncCompleted.connect(self.change_systray_icon)
        engine.invalidAuthentication.connect(self.change_systray_icon)
        engine.syncSuspended.connect(self.change_systray_icon)
        engine.syncResumed.connect(self.change_systray_icon)
        engine.offline.connect(self.change_systray_icon)
        engine.online.connect(self.change_systray_icon)
        engine.rootDeleted.connect(self._root_deleted)
        engine.rootMoved.connect(self._root_moved)
        engine.docDeleted.connect(self._doc_deleted)
        engine.fileAlreadyExists.connect(self._file_already_exists)
        engine.noSpaceLeftOnDevice.connect(self._no_space_left)
        self.change_systray_icon()

    def init_checks(self) -> None:
        for engine in self.manager.get_engines().values():
            self._connect_engine(engine)

        self.manager.newEngine.connect(self._connect_engine)
        self.manager.notification_service.newNotification.connect(
            self._new_notification
        )
        self.manager.notification_service.triggerNotification.connect(
            self._handle_notification_action
        )
        self.manager.updater.updateAvailable.connect(self._update_notification)
        self.manager.updater.noSpaceLeftOnDevice.connect(self._no_space_left)

        if not self.manager.get_engines():
            self.show_settings()
        else:
            for engine in self.manager.get_engines().values():
                # Prompt for settings if needed
                if engine.has_invalid_credentials():
                    self.show_settings("Accounts")  # f"Account_{engine.uid}"
                    break

        self.manager.start()

    @pyqtSlot()
    @if_frozen
    def _update_notification(self) -> None:
        self.change_systray_icon()

        # Display a notification
        status, version = self.manager.updater.status, self.manager.updater.version

        msg = ("AUTOUPDATE_UPGRADE", "AUTOUPDATE_DOWNGRADE")[
            status == UPDATE_STATUS_INCOMPATIBLE_SERVER
        ]
        description = Translator.get(msg, [version])
        flags = Notification.FLAG_BUBBLE | Notification.FLAG_UNIQUE
        if LINUX:
            description += " " + Translator.get("AUTOUPDATE_MANUAL")
            flags |= Notification.FLAG_SYSTRAY

        log.warning(description)
        notification = Notification(
            uuid="AutoUpdate",
            flags=flags,
            title=Translator.get("NOTIF_UPDATE_TITLE", [version]),
            description=description,
        )
        self.manager.notification_service.send_notification(notification)

    @pyqtSlot()
    def _server_incompatible(self) -> None:
        version = self.manager.version
        downgrade_version = self.manager.updater.version or ""
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowIcon(self.icon)
        msg.setText(Translator.get("SERVER_INCOMPATIBLE", [version, downgrade_version]))
        if downgrade_version:
            msg.addButton(
                Translator.get("CONTINUE_USING", [version]), QMessageBox.RejectRole
            )
            downgrade = msg.addButton(
                Translator.get("DOWNGRADE_TO", [downgrade_version]),
                QMessageBox.AcceptRole,
            )
        else:
            msg.addButton(Translator.get("CONTINUE"), QMessageBox.RejectRole)
        msg.exec_()

        res = msg.clickedButton()
        if downgrade_version and res == downgrade:
            self.manager.updater.update(downgrade_version)

    @pyqtSlot()
    def _wrong_channel(self) -> None:
        if self.manager.prompted_wrong_channel:
            log.debug(
                "Not prompting for wrong channel, already showed it since startup"
            )
            return
        self.manager.prompted_wrong_channel = True

        version = self.manager.version
        downgrade_version = self.manager.updater.version or ""
        version_channel = self.manager.updater.get_version_channel(version)
        current_channel = self.manager.get_update_channel()
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowIcon(self.icon)
        msg.setText(
            Translator.get("WRONG_CHANNEL", [version, version_channel, current_channel])
        )
        switch_channel = msg.addButton(
            Translator.get("USE_CHANNEL", [version_channel]), QMessageBox.AcceptRole
        )
        downgrade = msg.addButton(
            Translator.get("DOWNGRADE_TO", [downgrade_version]), QMessageBox.AcceptRole
        )
        msg.exec_()

        res = msg.clickedButton()
        if downgrade_version and res == downgrade:
            self.manager.updater.update(downgrade_version)
        elif res == switch_channel:
            self.manager.set_update_channel(version_channel)

    @pyqtSlot()
    def message_clicked(self) -> None:
        if self.current_notification:
            self.manager.notification_service.trigger_notification(
                self.current_notification.uid
            )

    def _setup_notification_center(self) -> None:
        if not self._delegator:
            self._delegator = NotificationDelegator.alloc().init()
            if self._delegator:
                self._delegator._manager = self.manager
        setup_delegator(self._delegator)

    @pyqtSlot(object)
    def _new_notification(self, notif: Notification) -> None:
        if not notif.is_bubble():
            return

        if self._delegator is not None:
            # Use notification center
            from ..osi.darwin.pyNotificationCenter import notify

            user_info = {"uuid": notif.uid} if notif.uid else None

            return notify(notif.title, "", notif.description, user_info=user_info)

        icon = QSystemTrayIcon.Information
        if notif.level == Notification.LEVEL_WARNING:
            icon = QSystemTrayIcon.Warning
        elif notif.level == Notification.LEVEL_ERROR:
            icon = QSystemTrayIcon.Critical

        self.current_notification = notif
        self.tray_icon.showMessage(notif.title, notif.description, icon, 10000)

    @pyqtSlot(str, str)
    def _handle_notification_action(self, action: str, engine_uid: str) -> None:
        func = getattr(self.api, action, None)
        if not func:
            log.error(f"Action {action}() is not defined in {self.api}")
            return
        func(engine_uid)

    def set_icon_state(self, state: str, force: bool = False) -> bool:
        """
        Execute systray icon change operations triggered by state change.

        The synchronization thread can update the state info but cannot
        directly call QtGui widget methods. This should be executed by the main
        thread event loop, hence the delegation to this method that is
        triggered by a signal to allow for message passing between the 2
        threads.

        Return True of the icon has changed state or if force is True.
        """

        if not force and self.icon_state == state:
            # Nothing to update
            return False

        self.tray_icon.setToolTip(self.get_tooltip())
        self.tray_icon.setIcon(self.icons[state])
        self.icon_state = state
        return True

    def get_tooltip(self) -> str:
        actions = Action.get_actions()
        if not actions:
            return self.default_tooltip

        # Display only the first action for now
        for action in actions.values():
            if action and action.type and not action.type.startswith("_"):
                break
        else:
            return self.default_tooltip

        return f"{self.default_tooltip} - {action!r}"

    @if_frozen
    def show_release_notes(self, version: str) -> None:
        """ Display release notes of a given version. """

        channel = self.manager.get_update_channel()
        log.info(f"Showing release notes, version={version!r} channel={channel}")

        # For now, we do care about beta only
        if channel != "beta":
            return

        url = (
            "https://api.github.com/repos/nuxeo/nuxeo-drive"
            f"/releases/tags/release-{version}"
        )

        if channel != "release":
            version += f" {channel}"

        try:
            # No need for the `verify` kwarg here as GitHub will never use a bad certificate.
            with requests.get(url) as resp:
                data = resp.json()
                html = markdown(data["body"])
        except Exception:
            log.warning(f"[{version}] Release notes retrieval error")
            return

        dialog = QDialog()
        dialog.setWindowTitle(f"{APP_NAME} {version} - Release notes")
        dialog.setWindowIcon(self.icon)

        dialog.resize(600, 400)

        notes = QTextEdit()
        notes.setStyleSheet("background-color: #eee; border: none;")
        notes.setReadOnly(True)
        notes.setHtml(html)

        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Ok)
        buttons.clicked.connect(dialog.accept)

        layout = QVBoxLayout()
        layout.addWidget(notes)
        layout.addWidget(buttons)
        dialog.setLayout(layout)
        dialog.exec_()

    def accept_unofficial_ssl_cert(self, hostname: str) -> bool:
        """Ask the user to bypass the SSL certificate verification."""
        from ..utils import get_certificate_details

        def signature(sig: str) -> str:
            """
            Format the certificate signature.

                >>> signature("0F4019D1E6C52EF9A3A929B6D5613816")
                0f:40:19:d1:e6:c5:2e:f9:a3:a9:29:b6:d5:61:38:16

            """
            from textwrap import wrap

            return str.lower(":".join(wrap(sig, 2)))

        cert = get_certificate_details(hostname=hostname)
        if not cert:
            return False

        subject = [
            f"<li>{details[0][0]}: {details[0][1]}</li>"
            for details in sorted(cert["subject"])
        ]
        issuer = [
            f"<li>{details[0][0]}: {details[0][1]}</li>"
            for details in sorted(cert["issuer"])
        ]
        urls = [
            f"<li><a href='{details}'>{details}</a></li>"
            for details in cert["caIssuers"]
        ]
        sig = f"<code><small>{signature(cert['serialNumber'])}</small></code>"
        message = f"""
<h2>{Translator.get("SSL_CANNOT_CONNECT", [hostname])}</h2>
<p style="color:red">{Translator.get("SSL_HOSTNAME_ERROR")}</p>

<h2>{Translator.get("SSL_CERTIFICATE")}</h2>
<ul>
    {"".join(subject)}
    <li style="margin-top: 10px;">{Translator.get("SSL_SERIAL_NUMBER")} {sig}</li>
    <li style="margin-top: 10px;">{Translator.get("SSL_DATE_FROM")} {cert["notBefore"]}</li>
    <li>{Translator.get("SSL_DATE_EXPIRATION")} {cert["notAfter"]}</li>
</ul>

<h2>{Translator.get("SSL_ISSUER")}</h2>
<ul style="list-style-type:square;">{"".join(issuer)}</ul>

<h2>{Translator.get("URL")}</h2>
<ul>{"".join(urls)}</ul>
"""

        dialog = QDialog()
        dialog.setWindowTitle(Translator.get("SSL_UNTRUSTED_CERT_TITLE"))
        dialog.setWindowIcon(self.icon)
        dialog.resize(600, 650)

        notes = QTextEdit()
        notes.setReadOnly(True)
        notes.setHtml(message)

        continue_with_bad_ssl_cert = False

        def accept() -> None:
            nonlocal continue_with_bad_ssl_cert
            continue_with_bad_ssl_cert = True
            dialog.accept()

        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.button(QDialogButtonBox.Ok).setEnabled(False)
        buttons.accepted.connect(accept)
        buttons.rejected.connect(dialog.close)

        def bypass_triggered(state: int) -> None:
            """Enable the OK button only when the checkbox is checked."""
            buttons.button(QDialogButtonBox.Ok).setEnabled(bool(state))

        bypass = QCheckBox(Translator.get("SSL_TRUST_ANYWAY"))
        bypass.stateChanged.connect(bypass_triggered)

        layout = QVBoxLayout()
        layout.addWidget(notes)
        layout.addWidget(bypass)
        layout.addWidget(buttons)
        dialog.setLayout(layout)
        dialog.exec_()

        return continue_with_bad_ssl_cert

    def show_metadata(self, path: Path) -> None:
        self.manager.ctx_edit_metadata(path)

    @pyqtSlot(bool)
    def load_icons_set(self, use_light_icons: bool = False) -> None:
        """Load a given icons set (either the default one "dark", or the light one)."""
        if self.use_light_icons is use_light_icons:
            return

        suffix = ("", "_light")[use_light_icons]
        mask = str(find_icon("active.svg"))  # Icon mask for macOS
        for state in {
            "conflict",
            "disabled",
            "error",
            "idle",
            "notification",
            "paused",
            "syncing",
            "update",
        }:
            icon = QIcon()
            icon.addFile(str(find_icon(f"{state}{suffix}.svg")))
            if MAC:
                icon.addFile(mask, mode=QIcon.Selected)
            self.icons[state] = icon

        self.use_light_icons = use_light_icons
        self.manager.set_config("light_icons", use_light_icons)

        # Reload the current showed icon
        if self.icon_state:
            self.set_icon_state(self.icon_state, force=True)

    def initial_icons_set(self) -> bool:
        """
        Try to guess the most appropriate icons set at start.
        The user will still have the possibility to change that in Settings.
        """
        use_light_icons = self.manager.get_config("light_icons", default=None)

        if use_light_icons is None:
            # Default value for GNU/Linux, macOS ans Windows 7
            use_light_icons = False

            if WINDOWS:
                win_ver = sys.getwindowsversion()
                version = (win_ver.major, win_ver.minor)
                if version > (6, 1):  # Windows 7
                    # Windows 8+ has a dark them by default
                    use_light_icons = True
        else:
            # The value stored in DTB as a string '0' or '1', convert to boolean
            use_light_icons = bool(int(use_light_icons))

        return use_light_icons

    def setup_systray(self) -> None:
        """Setup the icon system tray and its associated menu."""
        self.load_icons_set(use_light_icons=self.initial_icons_set())

        self.tray_icon = DriveSystrayIcon(self)
        if not self.tray_icon.isSystemTrayAvailable():
            log.critical("There is no system tray available!")
        else:
            self.tray_icon.setToolTip(APP_NAME)
            self.set_icon_state("disabled")
            self.tray_icon.show()

    def _handle_language_change(self) -> None:
        self.manager.set_config("locale", Translator.locale())
        if not MAC:
            self.tray_icon.setContextMenu(self.tray_icon.get_context_menu())
        self.osi.register_contextual_menu()

    def event(self, event: QEvent) -> bool:
        """ Handle URL scheme events under macOS. """
        url = getattr(event, "url", None)
        if not url:
            # This is not an event for us!
            return super().event(event)

        final_url = unquote(event.url().toString())
        try:
            return self._handle_nxdrive_url(final_url)
        except:
            log.exception(f"Error handling URL event {final_url!r}")
            return False

    def _show_msgbox_restart_needed(self) -> None:
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Information)
        msg.setText(Translator.get("RESTART_NEEDED_MSG", values=[APP_NAME]))
        msg.setWindowTitle(APP_NAME)
        msg.addButton(Translator.get("OK"), QMessageBox.AcceptRole)
        msg.exec_()

    def _handle_nxdrive_url(self, url: str) -> bool:
        """ Handle an nxdrive protocol URL. """

        info = parse_protocol_url(url)
        if not info:
            return False

        cmd = info["command"]
        path = normalized_path(info.get("filepath", ""))
        manager = self.manager

        log.info(f"Event URL={url}, info={info!r}")

        # Event fired by a context menu item
        func = {
            "access-online": manager.ctx_access_online,
            "copy-share-link": manager.ctx_copy_share_link,
            "edit-metadata": manager.ctx_edit_metadata,
        }.get(cmd, None)
        if func:
            func(path)
        elif "edit" in cmd:
            if self.manager.restart_needed:
                self._show_msgbox_restart_needed()
                return False

            manager.direct_edit.edit(
                info["server_url"],
                info["doc_id"],
                user=info["user"],
                download_url=info["download_url"],
            )
        elif cmd == "token":
            self.api.handle_token(info["token"], info["username"])
        else:
            log.warning(f"Unknown event URL={url}, info={info!r}")
            return False
        return True

    def init_nxdrive_listener(self) -> None:
        """
        Set up a QLocalServer to listen to nxdrive protocol calls.

        On Windows, when an nxdrive:// URL is opened, it creates a new
        instance of Nuxeo Drive. As we want the already running instance to
        receive this call (particularly during the login process), we set
        up a QLocalServer in that instance to listen to the new ones who will
        send their data.
        The Qt implementation of QLocalSocket on Windows makes use of named
        pipes. We just need to connect a handler to the newConnection signal
        to process the URLs.
        """
        named_pipe = f"{BUNDLE_IDENTIFIER}.protocol.{os.getpid()}"
        server = QLocalServer()
        server.setSocketOptions(QLocalServer.WorldAccessOption)
        server.newConnection.connect(self._handle_connection)
        try:
            server.listen(named_pipe)
            log.info(f"Listening for nxdrive:// calls on {server.fullServerName()}")
        except:
            log.info(
                f"Unable to start local server on {named_pipe}: {server.errorString()}"
            )

        self._nxdrive_listener = server
        self.aboutToQuit.connect(self._nxdrive_listener.close)

    def _handle_connection(self) -> None:
        """ Retrieve the connection with other instances and handle the incoming data. """

        con: QLocalSocket = None
        try:
            con = self._nxdrive_listener.nextPendingConnection()
            log.info("Receiving socket connection for nxdrive protocol handling")
            if not con or not con.waitForConnected():
                log.error(f"Unable to open server socket: {con.errorString()}")
                return

            if con.waitForReadyRead():
                payload = con.readAll()
                url = force_decode(payload.data())
                self._handle_nxdrive_url(url)

            con.disconnectFromServer()
            if con.state() == QLocalSocket.ConnectedState:
                con.waitForDisconnected()
        finally:
            del con
        log.info("Successfully closed server socket")

    def update_status(self, engine: "Engine") -> None:
        """
        Update the systray status for synchronization,
        conflicts/errors and software updates.
        """
        sync_state = error_state = update_state = ""

        update_state = self.manager.updater.status
        self.refresh_conflicts(engine.uid)

        # Check synchronization state
        if self.manager.restart_needed:
            sync_state = "restart"
        elif engine.is_paused():
            sync_state = "suspended"
        elif engine.is_syncing():
            sync_state = "syncing"

        # Check error state
        if engine.has_invalid_credentials():
            error_state = "auth_expired"
        elif self.conflicts_model.count:
            error_state = "conflicted"
        elif self.errors_model.count:
            error_state = "error"

        self._window_root(self.systray_window).setStatus.emit(
            sync_state, error_state, update_state
        )

    @pyqtSlot()
    def refresh_actions(self) -> None:
        actions = self.api.get_actions()
        if actions != self.action_model.actions:
            self.action_model.set_actions(actions)
        self.action_model.fileChanged.emit()

    @pyqtSlot(str)
    def get_last_files(self, uid: str) -> None:
        files = self.api.get_last_files(uid, 10, "")
        if files != self.file_model.files:
            self.file_model.empty()
            self.file_model.addFiles(files)
        self.file_model.fileChanged.emit()

    def current_language(self) -> Optional[str]:
        lang = Translator.locale()
        for tag, name in self.language_model.languages:
            if tag == lang:
                return name
        return None

    def show_metrics_acceptance(self) -> None:
        """ Display a "friendly" dialog box to ask user for metrics approval. """

        tr = Translator.get

        dialog = QDialog()
        dialog.setWindowTitle(tr("SHARE_METRICS_TITLE", [APP_NAME]))
        dialog.setWindowIcon(self.icon)
        dialog.setStyleSheet("background-color: #ffffff;")
        layout = QVBoxLayout()

        info = QLabel(tr("SHARE_METRICS_MSG", [COMPANY]))
        info.setTextFormat(Qt.RichText)
        info.setWordWrap(True)
        layout.addWidget(info)

        def analytics_choice(state) -> None:
            Options.use_analytics = bool(state)

        def errors_choice(state) -> None:
            Options.use_sentry = bool(state)

        # Checkboxes
        em_analytics = QCheckBox(tr("SHARE_METRICS_ERROR_REPORTING"))
        em_analytics.setChecked(True)
        em_analytics.stateChanged.connect(errors_choice)
        layout.addWidget(em_analytics)

        cb_analytics = QCheckBox(tr("SHARE_METRICS_ANALYTICS"))
        cb_analytics.stateChanged.connect(analytics_choice)
        layout.addWidget(cb_analytics)

        # Buttons
        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Apply)
        buttons.clicked.connect(dialog.close)
        layout.addWidget(buttons)
        dialog.setLayout(layout)
        dialog.resize(400, 200)
        dialog.show()
        dialog.exec_()

        states = []
        if Options.use_analytics:
            states.append("analytics")
        if Options.use_sentry:
            states.append("sentry")

        (Options.nxdrive_home / "metrics.state").write_text("\n".join(states))

    def ask_for_metrics_approval(self) -> None:
        """Should we setup and use Sentry and/or Google Analytics?"""

        # Check the user choice first
        Options.nxdrive_home.mkdir(parents=True, exist_ok=True)

        STATE_FILE = Options.nxdrive_home / "metrics.state"
        if STATE_FILE.is_file():
            lines = STATE_FILE.read_text().splitlines()
            Options.use_sentry = "sentry" in lines
            Options.use_analytics = "analytics" in lines
            # Abort now, the user already decided to use Sentry or not
            return

        # The user did not choose yet, display a message box
        self.show_metrics_acceptance()
Beispiel #22
0
#
#     win = engine.rootObjects()[0]
#     win.show()
#     sys.exit(app.exec_())

if __name__ == '__main__':

    _file = r"choice_cnx.csv"
    _qml = r'main.qml'

    # Prints QML errors
    def handleStatusChange(status):
        if status == QQuickView.Error:
            errors = view.errors()
            if errors:
                print(errors[0].description())

    myApp = QApplication(sys.argv)
    qmlRegisterType(TestModel, 'TestModel', 1, 0, 'TestModel')

    view = QQuickView()
    view.rootContext().setContextProperty('fromPython', "text de la nasa")
    view.statusChanged.connect(handleStatusChange)

    view.setSource(QUrl(_qml))

    try:
        sys.exit(myApp.exec_())
    except:
        print("Exiting")
        self.cur_alt = int(self.abs_alt) + int_alt
        self.newAlt.emit(self.cur_alt)

    def signal_new_batt_stat(self):
        print("new batt_stat is: " + str(self.batt_stat))
        pct = self.batt_stat / 15.0 * 100
        pct_str = "{0:.2f}".format(pct)
        rem_time = pct / 100 * 5
        time_str = "{0:.2f}".format(rem_time)
        self.newBattStat.emit(pct_str, time_str)

    def signal_state_changed(self, state):
        print("state changed to: " + state + "\n")
        self.stateChanged.emit(state)


app = QApplication([])
view = QQuickView()
view.setWidth(1200)
view.setHeight(720)
view.setTitle('Telemetry Tracker')
view.setResizeMode(QQuickView.SizeRootObjectToView)
url = QUrl('interface.qml')
gui = Gui(sys.argv[1])
gui.connect_signals()
view.rootContext().setContextProperty('gui', gui)
view.setSource(url)
view.show()
qml_window = view.rootObject()
app.exec_()
Beispiel #24
0
 def loadMainView(self):
     view = QQuickView()
     view.rootContext().setContextProperty("base", self)
     view.setSource(QUrl("main.qml"))
     self.mainWindow = self
     r = view.rootObject()
Beispiel #25
0
def main():
    try:
        app = QGuiApplication(sys.argv)

        app.setApplicationName("斗鱼客户端发版")
        app.setWindowIcon(QIcon("./image/app_icon.ico"))
        app.processEvents()
        view = QQuickView()

        context = view.rootContext()

        ############ object define #########
        ###LOG
        log = LOG()
        context.setContextProperty("_log", log)

        current = os.getcwd()
        ###ConfigManager
        config_manager = ConfigManager(current + "\\config\\config.json", view)
        context.setContextProperty("_config_manager", config_manager)

        ###TemplateParser
        template_parser = TemplateParser(current + "\\config\\template.json",
                                         view)
        context.setContextProperty("_template_parser", template_parser)

        ###VersionManager
        version_manager = VersionManager(current + "\\config\\version.json",
                                         view)
        context.setContextProperty("_version_manager", version_manager)

        ###UpdateManager
        update_manager = UpdateManager(current + "\\config\\update.json", view)
        context.setContextProperty("_update_manager", update_manager)

        ###DownloadUnziper
        downloader = DownloadUnziper(view)
        context.setContextProperty("_downloader", downloader)

        ###IniProducer
        ini_producer = IniProducer(view)
        context.setContextProperty("_ini_producer", ini_producer)

        ###ReleaseUpdater
        release_updater = ReleaseUpdater(view)
        context.setContextProperty("_release_updater", release_updater)

        ###InstallPackager
        install_packager = InstallPackager(view)
        context.setContextProperty("_install_packager", install_packager)

        #UpdateRater
        update_rater = UpdateRater(view)
        context.setContextProperty("_update_rater", update_rater)

        ############ signals connect slots #########
        config_manager.print.connect(log.print)
        config_manager.finalPathChanged.connect(release_updater.onFinalPath)

        template_parser.print.connect(log.print)
        template_parser.updateChanged.connect(ini_producer.onUpdateChanged)
        template_parser.versionAbstractCountChanged.connect(
            version_manager.onVersionAbstractCount)
        template_parser.versionAbstractChanged.connect(
            ini_producer.onEnableVersionAbstract)
        template_parser.updateInfoChanged.connect(
            ini_producer.onUpdateInfoPrefix)
        template_parser.deletedSuffixCascadeChanged.connect(
            release_updater.onDeletedSuffixAscade)
        template_parser.deletedSuffixChanged.connect(
            release_updater.onDeletedSuffix)
        template_parser.deletedFileCascadeChanged.connect(
            release_updater.onDeletedFileAscade)
        template_parser.deletedFileChanged.connect(
            release_updater.onDeletedFile)
        template_parser.deletedFolderChanged.connect(
            release_updater.onDeletedFolder)
        template_parser.deletedWorkplaceSuffixChanged.connect(
            release_updater.onDeletedWorkplaceSuffix)
        template_parser.deletedWorkplaceFileChanged.connect(
            release_updater.onDeletedWorkplaceFile)
        template_parser.srcPathChanged.connect(release_updater.onSrcPath)
        template_parser.destPathChanged.connect(release_updater.onDestPath)
        template_parser.neededFilesChanged.connect(
            release_updater.onNeededFiles)
        template_parser.filePackageChanged.connect(
            release_updater.onFilePackage)
        template_parser.resetPrgConfigChanged.connect(
            release_updater.onResetPrgConfig)
        template_parser.peSignedChanged.connect(
            release_updater.onPeSignedChanged)

        template_parser.deletedSuffixCascadeChanged.connect(
            install_packager.onDeletedSuffixAscade)
        template_parser.deletedSuffixChanged.connect(
            install_packager.onDeletedSuffix)
        template_parser.deletedFileCascadeChanged.connect(
            install_packager.onDeletedFileAscade)
        template_parser.deletedPackageFileChanged.connect(
            install_packager.onDeletedFile)
        template_parser.deletedFolderChanged.connect(
            install_packager.onDeletedFolder)
        template_parser.peSignedChanged.connect(
            install_packager.onPeSignedChanged)

        downloader.print.connect(log.print)

        release_updater.print.connect(log.print, type=Qt.QueuedConnection)
        release_updater.versionChanged.connect(
            config_manager.onLastVersionChanged)
        release_updater.minHideVersionChanged.connect(
            config_manager.onMinHideVersionChanged)
        release_updater.addItem.connect(update_manager.onAddItem)

        version_manager.print.connect(log.print)
        version_manager.lastNVersionAbstract.connect(
            ini_producer.onLastNVersionAbstract)

        ini_producer.print.connect(log.print)
        ini_producer.addItem.connect(version_manager.addItem)

        install_packager.print.connect(log.print)

        update_manager.print.connect(log.print)
        update_manager.dataChanged.connect(update_rater.onUpdateVersionData)

        update_rater.print.connect(log.print)

        context.setContextProperty("_main_window", view)

        view.setSource(QUrl('./qml/main.qml'))
        app.aboutToQuit.connect(app.quit)
        root = view.rootObject()
        root.finalPathChanged.connect(config_manager.onFinalPath)
        root.finalPathChanged.connect(release_updater.onFinalPath)

        view.show()

        ret = template_parser.Parse()

        #解析config.json
        ret = config_manager.Parse()

        #解析version.json
        ret = version_manager.Parse()

        #解析update.json
        ret = update_manager.Parse()

        #update_rater.OpenExcel("D:\\learn\\release\\PC客户端启动版本更新率数据.xlsx")

        app.exec_()
    except Exception as e:
        print("main except: " + str(e))
Beispiel #26
0
                        parents.append(
                            parents[-1].child(parents[-1].childCount() - 1))
                        indentations.append(position)

                else:
                    while position < indentations[-1] and len(parents) > 0:
                        parents.pop()
                        indentations.pop()

                # Append a new item to the current parent's list of children.
                parents[-1].appendChild(TreeItem(columnData, parents[-1]))

            number += 1


if __name__ == '__main__':
    import sys

    app = QGuiApplication(sys.argv)
    view = QQuickView()

    f = QFile(':/default.txt')
    f.open(QIODevice.ReadOnly)
    model = TreeModel(f.readAll())
    f.close()

    root_context = view.rootContext().setContextProperty('model', model)
    view.setSource(QUrl.fromLocalFile('simpletreemodel.qml'))
    view.show()
    sys.exit(app.exec_())
Beispiel #27
0
class P2CQMLApplication(QGuiApplication):
    def __init__(self, list_of_str):
        super().__init__(list_of_str)
        self._current_category = None
        self._current_torrent = None
        self._current_torrent_info = None
        self._status_timer = QTimer(self)
        self._movies_thread = None
        self._search_thread = None

    def run_view(self):
        self._view = QQuickView()
        self._view.engine().addImportPath("qml")
        self._rctx = self._view.rootContext()
        self._view.setResizeMode(QQuickView.SizeRootObjectToView)

        # set context variables
        self.categories = []
        self._rctx.setContextProperty("categoriesModel", self.categories)
        self.tiles = []
        self.torrents = []
        self._rctx.setContextProperty("moviesModel", self.tiles)
        self._set_loading(False)
        self._view.setSource(QUrl('qrc:/qml.qml'))
        self._view.showFullScreen()
#        self._view.show()

    def connect_daemon(self, daemon: P2CDaemon):
        self._daemon = daemon
        self._set_categories()
        self._connect_signals()

    def play(self, movie: Movie):
        self._set_movie_status("Ready to play!")
        self._set_media(movie)
        self._daemon.play(movie)
        self._set_additional_media_info()

    def buffer(self, movie: Movie):
        seconds = self._current_torrent.get_seconds_to_buffer()
        info = "just started"
        if seconds:
            if seconds < 15:
                info = "just a moment"
            else:
                # rount to minutes
                minutes = int(seconds / 60) + 1
                if minutes == 1:
                    info = "1 minute"
                else:
                    info = "{} minutes".format(minutes)
        self._set_movie_status("Buffering... ({})".format(info))
        self._daemon.buffer(movie)
        self._set_additional_media_info()

    def wait_for_metadata(self):
        self._set_movie_status("Getting metadata...")
        if self._current_torrent:
            self._set_additional_media_info(self._current_torrent.name)

    def select_movie(self, torrent: Torrent) -> Movie:
        movies = torrent.get_movies()
        if len(movies) == 0:
            return
        # TODO: show dialog with movie selecting instead of doing it automatically
        return max(movies, key=lambda x: x.size)

    def update_status(self):
        torrent = self._current_torrent
        if torrent:
            if(torrent.has_torrent_info()):
                movie = self.select_movie(torrent)
                if not movie:
                    self._set_movie_status("No movie in this torrent. Please, select another.")
                    return

                torrent.download_file(movie.path)
                self._set_duration(movie)
                if not self._daemon.is_playing(movie):
                    if(movie.can_play()):
#                        print(movie.get_subtitles())
                        self.play(movie)
                    else:
                        self.buffer(movie)

                ### DEBUG INFO
                text = "s: %s, num p: %s, rate: %s kbs, p_rate: %s kbs" % (
                    torrent.get_status()['state'],
                    torrent.get_status()['num_peers'],
                    int(torrent.get_status()['download_rate'] / 1024),
                    int(torrent.get_status()['download_payload_rate'] / 1024),
                    )
                self._view.rootObject().setProperty("debugText", text)
                ### END DEBUG INFO

            else:
                self.wait_for_metadata()
        else:
            self.wait_for_metadata()

    def on_category_clicked(self, index):
        # clear list
        self._set_torrents([], loading=True)

        category = self._daemon.get_categories()[index]
        self._current_category = category

        if self._current_category:
            self._search_thread = None
            self._movies_thread = SetMoviesThread(self._current_category)
            self._movies_thread.start()
            self._movies_thread.got_movies.connect(self._threaded_set_torrents)


    def on_movie_clicked(self, index):
        self._view.rootObject().setProperty("isMovieScene", True)

        torrent_ui = self.torrents[index]
        self._current_torrent = self._daemon.get_torrent(torrent_ui)
        self._current_torrent_info = torrent_ui
        self.update_status()

    def on_search(self, query):
        if len(query) < 3:
            return
            # clear list
        self._set_torrents([], loading=True)

        self._movies_thread = None
        self._search_thread = SearchThread(query, self._daemon.search)
        self._search_thread.start()
        self._search_thread.got_movies.connect(self._threaded_set_torrents)

    def on_exit(self):
        self.quit()

    def _connect_signals(self):
        self._view.rootObject().categoryClicked.connect(
            self.on_category_clicked)
        self._view.rootObject().movieClicked.connect(self.on_movie_clicked)
        self._view.rootObject().movieClicked.connect(self.on_movie_clicked)
        self._view.rootObject().searchQuery.connect(self.on_search)
        self._view.rootObject().exitAction.connect(self.on_exit)
        self._status_timer.timeout.connect(self.update_status)
        self._status_timer.start(500)

    def _set_movie_status(self, text):
        self._rctx.setContextProperty("movieStatus", text)

    def _set_media(self, movie: Movie):
        file_name = movie.get_target_path()
        self._rctx.setContextProperty("movieSource",
            QUrl.fromLocalFile(file_name))

    def _set_additional_media_info(self, title=None):
        self._rctx.setContextProperty("title",
            title or self._current_torrent_info.title or self._current_torrent_info.label)
        self._rctx.setContextProperty("poster",
            self._current_torrent_info.poster if self._current_torrent_info  and self._current_torrent_info.poster else '')

    def _set_categories(self):
        data = []
        for category in self._daemon.get_categories():
            data.append(Tile(category.label, category.service.name))
        self._rctx.setContextProperty("categoriesModel", data)
        self.categories = data

    def _threaded_set_torrents(self, data, thread):
        # if latest action
        if thread == self._movies_thread or thread == self._search_thread:
            self._set_torrents(data)

    def _set_torrents(self, data, loading=False):
        # only existing tiles
        for (tile, torrent_info) in zip(self.tiles, data[:len(self.tiles)]):
            if torrent_info.title:
                tile.name = torrent_info.title
                tile.source = torrent_info.label
            else:
                tile.name = torrent_info.label
                tile.source = None
            tile.poster = torrent_info.poster
            tile.description = torrent_info.description

        if len(data) != len(self.tiles):
            for torrent_info in data[len(self.tiles):]:
                if torrent_info.title:
                    tile = Tile(torrent_info.title, torrent_info.label,
                        torrent_info.poster, torrent_info.description)
                else:
                    tile = Tile(torrent_info.label, None, torrent_info.poster,
                        torrent_info.description)
                self.tiles.append(tile)

            self._rctx.setContextProperty("moviesModel", self.tiles)
        self.torrents = data
        self._set_loading(loading)

    def _set_loading(self, loading):
        self._rctx.setContextProperty("loadingMask", loading)

    def _set_duration(self, movie:Movie):
        tdelta = movie.get_movie_duration()
        if tdelta:
            self._view.rootObject().setProperty("movieDuration",
                tdelta.seconds * 1000)
Beispiel #28
0
    try:
        from dbus.mainloop.glib import DBusGMainLoop
        dbus_loop = DBusGMainLoop(set_as_default=True)
        bus = dbus.SessionBus()
        session = bus.get_object('org.qoverview.config',
                                 '/org/qoverview/config')
        config = dbus.Interface(session, 'org.qoverview.config.iface')
    except dbus.DBusException as e:
        print(e)
        print('Unable to connect to config-server via DBUS')
        sys.exit(1)

    print('KDE Frameworks:', 'Yes' if KDE_FRAMEWORKS else 'No')

    qmlview = QQuickView(QUrl('/usr/lib/qoverview/ui.qml'))

    qmlview.setResizeMode(qmlview.SizeRootObjectToView)

    root = qmlview.rootObject()
    context = qmlview.rootContext()

    interface = PythonQMLInterface(view=qmlview, uid=uid, app=app)
    context.setContextProperty('Python', interface)

    qmlview.setTitle(interface.uid)
    print(interface.uid)

    qmlview.showFullScreen()

    app.exec_()
Beispiel #29
0
class P2CQMLApplication(QGuiApplication):
    def __init__(self, list_of_str):
        super().__init__(list_of_str)
        self._current_category = None
        self._current_torrent = None
        self._current_torrent_info = None
        self._status_timer = QTimer(self)
        self._movies_thread = None
        self._search_thread = None

    def run_view(self):
        self._view = QQuickView()
        self._view.engine().addImportPath("qml")
        self._rctx = self._view.rootContext()
        self._view.setResizeMode(QQuickView.SizeRootObjectToView)

        # set context variables
        self.categories = []
        self._rctx.setContextProperty("categoriesModel", self.categories)
        self.tiles = []
        self.torrents = []
        self._rctx.setContextProperty("moviesModel", self.tiles)
        self._set_loading(False)
        self._view.setSource(QUrl('qrc:/qml.qml'))
        self._view.showFullScreen()
#        self._view.show()

    def connect_daemon(self, daemon: P2CDaemon):
        self._daemon = daemon
        self._set_categories()
        self._connect_signals()

    def play(self, movie: Movie):
        self._set_movie_status("Ready to play!")
        self._set_media(movie)
        self._daemon.play(movie)
        self._set_additional_media_info()

    def buffer(self, movie: Movie):
        seconds = self._current_torrent.get_seconds_to_buffer()
        info = "just started"
        if seconds:
            if seconds < 15:
                info = "just a moment"
            else:
                # rount to minutes
                minutes = int(seconds / 60) + 1
                if minutes == 1:
                    info = "1 minute"
                else:
                    info = "{} minutes".format(minutes)
        self._set_movie_status("Buffering... ({})".format(info))
        self._daemon.buffer(movie)
        self._set_additional_media_info()

    def wait_for_metadata(self):
        self._set_movie_status("Getting metadata...")
        if self._current_torrent:
            self._set_additional_media_info(self._current_torrent.name)

    def select_movie(self, torrent: Torrent) -> Movie:
        movies = torrent.get_movies()
        if len(movies) == 0:
            return
        # TODO: show dialog with movie selecting instead of doing it automatically
        return max(movies, key=lambda x: x.size)

    def update_status(self):
        torrent = self._current_torrent
        if torrent:
            if (torrent.has_torrent_info()):
                movie = self.select_movie(torrent)
                if not movie:
                    self._set_movie_status(
                        "No movie in this torrent. Please, select another.")
                    return

                torrent.download_file(movie.path)
                self._set_duration(movie)
                if not self._daemon.is_playing(movie):
                    if (movie.can_play()):
                        #                        print(movie.get_subtitles())
                        self.play(movie)
                    else:
                        self.buffer(movie)

                ### DEBUG INFO
                text = "s: %s, num p: %s, rate: %s kbs, p_rate: %s kbs" % (
                    torrent.get_status()['state'],
                    torrent.get_status()['num_peers'],
                    int(torrent.get_status()['download_rate'] / 1024),
                    int(torrent.get_status()['download_payload_rate'] / 1024),
                )
                self._view.rootObject().setProperty("debugText", text)
                ### END DEBUG INFO

            else:
                self.wait_for_metadata()
        else:
            self.wait_for_metadata()

    def on_category_clicked(self, index):
        # clear list
        self._set_torrents([], loading=True)

        category = self._daemon.get_categories()[index]
        self._current_category = category

        if self._current_category:
            self._search_thread = None
            self._movies_thread = SetMoviesThread(self._current_category)
            self._movies_thread.start()
            self._movies_thread.got_movies.connect(self._threaded_set_torrents)

    def on_movie_clicked(self, index):
        self._view.rootObject().setProperty("isMovieScene", True)

        torrent_ui = self.torrents[index]
        self._current_torrent = self._daemon.get_torrent(torrent_ui)
        self._current_torrent_info = torrent_ui
        self.update_status()

    def on_search(self, query):
        if len(query) < 3:
            return
            # clear list
        self._set_torrents([], loading=True)

        self._movies_thread = None
        self._search_thread = SearchThread(query, self._daemon.search)
        self._search_thread.start()
        self._search_thread.got_movies.connect(self._threaded_set_torrents)

    def on_exit(self):
        self.quit()

    def _connect_signals(self):
        self._view.rootObject().categoryClicked.connect(
            self.on_category_clicked)
        self._view.rootObject().movieClicked.connect(self.on_movie_clicked)
        self._view.rootObject().movieClicked.connect(self.on_movie_clicked)
        self._view.rootObject().searchQuery.connect(self.on_search)
        self._view.rootObject().exitAction.connect(self.on_exit)
        self._status_timer.timeout.connect(self.update_status)
        self._status_timer.start(500)

    def _set_movie_status(self, text):
        self._rctx.setContextProperty("movieStatus", text)

    def _set_media(self, movie: Movie):
        file_name = movie.get_target_path()
        self._rctx.setContextProperty("movieSource",
                                      QUrl.fromLocalFile(file_name))

    def _set_additional_media_info(self, title=None):
        self._rctx.setContextProperty(
            "title", title or self._current_torrent_info.title
            or self._current_torrent_info.label)
        self._rctx.setContextProperty(
            "poster",
            self._current_torrent_info.poster if self._current_torrent_info
            and self._current_torrent_info.poster else '')

    def _set_categories(self):
        data = []
        for category in self._daemon.get_categories():
            data.append(Tile(category.label, category.service.name))
        self._rctx.setContextProperty("categoriesModel", data)
        self.categories = data

    def _threaded_set_torrents(self, data, thread):
        # if latest action
        if thread == self._movies_thread or thread == self._search_thread:
            self._set_torrents(data)

    def _set_torrents(self, data, loading=False):
        # only existing tiles
        for (tile, torrent_info) in zip(self.tiles, data[:len(self.tiles)]):
            if torrent_info.title:
                tile.name = torrent_info.title
                tile.source = torrent_info.label
            else:
                tile.name = torrent_info.label
                tile.source = None
            tile.poster = torrent_info.poster
            tile.description = torrent_info.description

        if len(data) != len(self.tiles):
            for torrent_info in data[len(self.tiles):]:
                if torrent_info.title:
                    tile = Tile(torrent_info.title, torrent_info.label,
                                torrent_info.poster, torrent_info.description)
                else:
                    tile = Tile(torrent_info.label, None, torrent_info.poster,
                                torrent_info.description)
                self.tiles.append(tile)

            self._rctx.setContextProperty("moviesModel", self.tiles)
        self.torrents = data
        self._set_loading(loading)

    def _set_loading(self, loading):
        self._rctx.setContextProperty("loadingMask", loading)

    def _set_duration(self, movie: Movie):
        tdelta = movie.get_movie_duration()
        if tdelta:
            self._view.rootObject().setProperty("movieDuration",
                                                tdelta.seconds * 1000)
Beispiel #30
0
if __name__ == "__main__":

    global hector
    hector = Hector(config)
    readChannelConfiguration()

    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QGuiApplication(sys.argv)
    global view
    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    global screenMgr
    screenMgr = ScreenManager()
    view.rootContext().setContextProperty("screenManager", screenMgr)

    menuModel = MenuModel()
    for idx, drink in enumerate(available_drinks):
        menuModel.items.append({
            'menuId': '%d' % idx,
            'name': drink["name"] + ("\n\n‰" if alcoholic(drink) else ""),
            'colorCode': drink["color"]})
    view.rootContext().setContextProperty("menuModel", menuModel)

    menuMgr = MenuManager()
    menuMgr.setAction(drinkRequested)
    view.rootContext().setContextProperty("menuManager", menuMgr)

    dispensingModel = DispensingModel(12)
    for idx, ingr in enumerate(channelList):
Beispiel #31
0
    subscription = lc.subscribe("SIMULATOR", my_handler)


class WorkThread(QThread):
    # 定义一个信号
    trigger = pyqtSignal(str)

    def __int__(self):
        # 初始化函数,默认
        super(WorkThread, self).__init__()

    def run(self):
        # 等待5秒后,给触发信号,并传递test
        # time.sleep(2)
        self.trigger.emit("ok")
        HandleWork.lc.handle()


if __name__ == "__main__":
    path = 'main.qml'
    app = QGuiApplication([])
    viewer = QQuickView()
    con = MainPanel()
    context = viewer.rootContext()
    context.setContextProperty("con", con)
    viewer.engine().quit.connect(app.quit)
    viewer.setSource(QUrl(path))
    rootObject = viewer.rootObject()
    viewer.show()
    app.exec_()
Beispiel #32
0
class PhotoBoothGUI(QObject):
    def run(self):
        # Create main app
        self.myApp = QApplication(sys.argv)
        # Create a label and set its properties
        self.appLabel = QQuickView()
        #self.appLabel.setSource(QUrl('loading.qml'))

        # Show the Label
        self.appLabel.show()

        # Initialize PhotoBoothEngine.
        self.pbengine = PhotoBoothEngine()

        self.pbengine.on_change_url.connect(self.update_url_signal)
        self.pbengine.on_connect_signal.connect(self.connect_signal)

        self.pbengine.change_qml(0)
        self.pbengine.connect_state(0)

        print("UPDATE")
        #self.pbengine.on_status.connect(self.appLabel.rootObject().status)
        #self.pbengine.on_update_filter_preview.connect(self.appLabel.rootObject().updateImageFilterPreview)

        self.appLabel.rootContext().setContextProperty('pbengine',
                                                       self.pbengine)

        self.setup_text_status_fly_component()

        self.pbengine.start_state_thread(0)

        # Execute the Application and Exit
        self.myApp.exec_()
        sys.exit()

    def setup_text_status_fly_component(self):
        # Create a component factory and load the QML script.
        print("Hello")
        self.component = QQmlComponent(self.appLabel.engine())
        self.component.loadUrl(QUrl('qml/TextStatusFly.qml'))

        print("Hello2")
        self.statuswidget = self.component.create(self.appLabel.rootContext())

        print("Hello3")
        self.statuswidget.setParentItem(self.appLabel.rootObject())
        self.statuswidget.setParent(self.appLabel.rootObject())

        print("Hello4")
        #statuswidget.setProperty("targetX", 100)
        self.statuswidget.setProperty("objectName", "textStatusBar")

        print("Hello5")
        self.appLabel.rootContext().setContextProperty('textStatusBar',
                                                       self.statuswidget)

        self.statuswidget.setProperty("parentSet", True)

    def update_url_signal(self, url):
        print(" ** Updating URL: %s" % url)

        #self.pbengine.on_change_url.disconnect()
        #self.pbengine.on_connect_signal.disconnect()

        self.appLabel.rootContext().setContextProperty('textStatusBar', None)
        self.appLabel.setSource(QUrl())
        self.appLabel.engine().clearComponentCache()
        self.appLabel.setSource(QUrl(url))
        self.setup_text_status_fly_component()
        self.appLabel.show()

        # Reconnect
        #self.pbengine.on_change_url.connect(self.update_url_signal)
        #self.pbengine.on_connect_signal.connect(self.connect_signal)

    def connect_signal(self, signal, target):
        print(" ** Binding signal %s to target %s!" % (str(signal), target))
        print(" ** (getattr(self.appLabel, target) = %s)" %
              (str(getattr(self.appLabel.rootObject(), target))))
        signal.connect(getattr(self.appLabel.rootObject(), target))
Beispiel #33
0
from PyQt5.QtGui import QGuiApplication

if __name__ == '__main__':

    #get our data
    url = "http://country.io/names.json"
    response = urllib.request.urlopen(url)
    data = json.loads(response.read().decode('utf-8'))

    #Format and sort the data
    data_list = list(data.values())
    data_list.sort()

    #Set up the application window
    app = QGuiApplication(sys.argv)
    view = QQuickView()
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    #Expose the list to the Qml code
    my_model = QStringListModel()
    my_model.setStringList(data_list)
    view.rootContext().setContextProperty("myModel", my_model)

    #Load the QML file
    qml_file = os.path.join(os.path.dirname(__file__), "test.ui.qml")
    view.setSource(QUrl.fromLocalFile(os.path.abspath(qml_file)))

    #Show the window
    if view.status() == QQuickView.Error:
        sys.exit(-1)
    view.show()
Beispiel #34
0
        

def sniff_data(qroot):
    cap = pyshark.FileCapture('D:\\4.pcapng')
    for p in cap:
        if 'MODBUS' in p:
            if p['IP'].src == '192.168.0.103':
                packet_windturbine_1(p, qroot)
        else:
            continue

if __name__ == '__main__':
    
    path = 'main.qml'   
    app = QGuiApplication([])
    
    view = QQuickView()
    
    view.engine().quit.connect(app.quit)   
    view.setSource(QUrl(path))             
    view.show()

    context = view.rootContext()
    rootobj = view.rootObject()

    sniff_thread = Thread(target=sniff_data, args=(rootobj,))
    sniff_thread.setDaemon(True)
    sniff_thread.start()

    app.exec_()                         
Beispiel #35
0
qmlRegisterType(DataBlock, 'WeatherCategory', 1, 0, 'DataBlock')
qmlRegisterType(Geocoder, 'WeatherCategory', 1, 0, 'Geocoder')

component = QQmlComponent(engine)
component.loadUrl(QUrl('WeatherDash.qml'))

# Create the QML user interface.  Auto creates its own engine
view = QQuickView()

engine2 = view.engine
# Does not run
#engine2.quit.connect(app.quit)

#view.setSource(QUrl('PyTest.qml'))
# To Satisfy cool-retro-term needs
view.rootContext().setContextProperty('devicePixelRatio', app.devicePixelRatio())

view.setSource(QUrl('WeatherDash.qml'))
#view.setResizeMode(QDeclarativeView.SizeRootObjectToView)
view.setGeometry(100, 100, 750, 480)
# ala https://pythonspot.com/pyqt5-colors/
view.setColor(QColor(0, 30, 0))

view.show()

is_full_screen = False

# technique lifted from https://stackoverflow.com/questions/19131084/pyqt5-qml-signal-to-python-slot
# and augmented from https://stackoverflow.com/questions/30586983/how-to-close-pyqt5-program-from-qml
# could refine with https://stackoverflow.com/questions/24111717/how-to-bind-buttons-in-qt-quick-to-python-pyqt-5
# not 100% ideal, but adequate and interesting
Beispiel #36
0
class Application(QApplication):
    """Main Nuxeo Drive application controlled by a system tray icon + menu"""

    tray_icon = None
    icon_state = None

    def __init__(self, manager: "Manager", *args: Any) -> None:
        super().__init__(list(*args))
        self.manager = manager

        self.dialogs = dict()
        self.osi = self.manager.osi
        self.setWindowIcon(QIcon(self.get_window_icon()))
        self.setApplicationName(manager.app_name)
        self._init_translator()
        self.setQuitOnLastWindowClosed(False)
        self._delegator = None

        self.filters_dlg = None
        self._conflicts_modals = dict()
        self.current_notification = None
        self.default_tooltip = self.manager.app_name

        font = QFont("Helvetica, Arial, sans-serif", 12)
        self.setFont(font)
        self.ratio = sqrt(QFontMetricsF(font).height() / 12)

        self.init_gui()

        self.aboutToQuit.connect(self.manager.stop)
        self.manager.dropEngine.connect(self.dropped_engine)

        # This is a windowless application mostly using the system tray
        self.setQuitOnLastWindowClosed(False)

        self.setup_systray()

        # Direct Edit
        self.manager.direct_edit.directEditConflict.connect(
            self._direct_edit_conflict)
        self.manager.direct_edit.directEditError.connect(
            self._direct_edit_error)

        # Check if actions is required, separate method so it can be overridden
        self.init_checks()

        # Setup notification center for macOS
        if MAC:
            self._setup_notification_center()

        # Application update
        self.manager.updater.appUpdated.connect(self.quit)

        # Display release notes on new version
        if self.manager.old_version != self.manager.version:
            self.show_release_notes(self.manager.version)

        # Listen for nxdrive:// sent by a new instance
        self.init_nxdrive_listener()

    def init_gui(self) -> None:

        self.api = QMLDriveApi(self)
        self.conflicts_model = FileModel()
        self.errors_model = FileModel()
        self.engine_model = EngineModel()
        self.file_model = FileModel()
        self.ignoreds_model = FileModel()
        self.language_model = LanguageModel()

        self.add_engines(list(self.manager._engines.values()))
        self.engine_model.statusChanged.connect(self.update_status)
        self.language_model.addLanguages(Translator.languages())

        flags = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint

        if WINDOWS:
            self.conflicts_window = QQuickView()
            self.settings_window = QQuickView()
            self.systray_window = SystrayWindow()

            self._fill_qml_context(self.conflicts_window.rootContext())
            self._fill_qml_context(self.settings_window.rootContext())
            self._fill_qml_context(self.systray_window.rootContext())
            self.systray_window.rootContext().setContextProperty(
                "systrayWindow", self.systray_window)

            self.conflicts_window.setSource(
                QUrl.fromLocalFile(find_resource("qml", "Conflicts.qml")))
            self.settings_window.setSource(
                QUrl.fromLocalFile(find_resource("qml", "Settings.qml")))
            self.systray_window.setSource(
                QUrl.fromLocalFile(find_resource("qml", "Systray.qml")))
            flags |= Qt.Popup
        else:
            self.app_engine = QQmlApplicationEngine()
            self._fill_qml_context(self.app_engine.rootContext())
            self.app_engine.load(
                QUrl.fromLocalFile(find_resource("qml", "Main.qml")))
            root = self.app_engine.rootObjects()[0]
            self.conflicts_window = root.findChild(QQuickWindow,
                                                   "conflictsWindow")
            self.settings_window = root.findChild(QQuickWindow,
                                                  "settingsWindow")
            self.systray_window = root.findChild(SystrayWindow,
                                                 "systrayWindow")
            if LINUX:
                flags |= Qt.Drawer

        self.systray_window.setFlags(flags)

        self.manager.newEngine.connect(self.add_engines)
        self.manager.initEngine.connect(self.add_engines)
        self.manager.dropEngine.connect(self.remove_engine)
        self._window_root(self.conflicts_window).changed.connect(
            self.refresh_conflicts)
        self._window_root(self.systray_window).appUpdate.connect(
            self.api.app_update)
        self._window_root(self.systray_window).getLastFiles.connect(
            self.get_last_files)
        self.api.setMessage.connect(
            self._window_root(self.settings_window).setMessage)

        if self.manager.get_engines():
            current_uid = self.engine_model.engines_uid[0]
            self.get_last_files(current_uid)
            self.update_status(self.engine_model.engines[current_uid])

        self.manager.updater.updateAvailable.connect(
            self._window_root(self.systray_window).updateAvailable)
        self.manager.updater.updateProgress.connect(
            self._window_root(self.systray_window).updateProgress)

    def add_engines(self, engines: Union["Engine", List["Engine"]]) -> None:
        if not engines:
            return

        engines = engines if isinstance(engines, list) else [engines]
        for engine in engines:
            self.engine_model.addEngine(engine)

    def remove_engine(self, uid: str) -> None:
        self.engine_model.removeEngine(uid)

    def _fill_qml_context(self, context: "QQmlContext") -> None:
        """ Fill the context of a QML element with the necessary resources. """
        context.setContextProperty("ConflictsModel", self.conflicts_model)
        context.setContextProperty("ErrorsModel", self.errors_model)
        context.setContextProperty("EngineModel", self.engine_model)
        context.setContextProperty("FileModel", self.file_model)
        context.setContextProperty("IgnoredsModel", self.ignoreds_model)
        context.setContextProperty("languageModel", self.language_model)
        context.setContextProperty("api", self.api)
        context.setContextProperty("application", self)
        context.setContextProperty("currentLanguage", self.current_language())
        context.setContextProperty("manager", self.manager)
        context.setContextProperty("updater", self.manager.updater)
        context.setContextProperty("ratio", self.ratio)
        context.setContextProperty("isFrozen", Options.is_frozen)
        context.setContextProperty("tl", Translator._singleton)
        context.setContextProperty(
            "nuxeoVersionText",
            f"{self.manager.app_name} {self.manager.version}")
        metrics = self.manager.get_metrics()
        context.setContextProperty(
            "modulesVersionText",
            (f'Python {metrics["python_version"]}, '
             f'Qt {metrics["qt_version"]}, '
             f'SIP {metrics["sip_version"]}'),
        )

        colors = {
            "darkBlue": "#1F28BF",
            "nuxeoBlue": "#0066FF",
            "lightBlue": "#00ADED",
            "teal": "#73D2CF",
            "purple": "#8400FF",
            "red": "#C02828",
            "orange": "#FF9E00",
            "darkGray": "#495055",
            "mediumGray": "#7F8284",
            "lightGray": "#BCBFBF",
            "lighterGray": "#F5F5F5",
        }

        for name, value in colors.items():
            context.setContextProperty(name, value)

    def _window_root(self, window):
        if WINDOWS:
            return window.rootObject()
        return window

    def translate(self, message: str, values: dict = None) -> str:
        return Translator.get(message, values)

    def _show_window(self, window: "QWindow") -> None:
        window.show()
        window.raise_()

    def _destroy_dialog(self) -> None:
        sender = self.sender()
        name = sender.objectName()
        self.dialogs.pop(name, None)

    def _create_unique_dialog(self, name: str, dialog: QDialog) -> None:
        dialog.setObjectName(name)
        dialog.destroyed.connect(self._destroy_dialog)
        self.dialogs[name] = dialog

    def _init_translator(self) -> None:
        locale = Options.force_locale or Options.locale
        Translator(
            self.manager,
            find_resource("i18n"),
            self.manager.get_config("locale", locale),
        )
        self.installTranslator(Translator._singleton)

    @staticmethod
    def get_window_icon() -> str:
        return find_icon("app_icon.svg")

    @pyqtSlot(str, str, str)
    def _direct_edit_conflict(self, filename: str, ref: str,
                              digest: str) -> None:
        log.trace("Entering _direct_edit_conflict for %r / %r", filename, ref)
        try:
            if filename in self._conflicts_modals:
                log.trace("Filename already in _conflicts_modals: %r",
                          filename)
                return
            log.trace("Putting filename in _conflicts_modals: %r", filename)
            self._conflicts_modals[filename] = True

            msg = QMessageBox()
            msg.setInformativeText(
                Translator.get("DIRECT_EDIT_CONFLICT_MESSAGE",
                               [short_name(filename)]))
            overwrite = msg.addButton(
                Translator.get("DIRECT_EDIT_CONFLICT_OVERWRITE"),
                QMessageBox.AcceptRole)
            msg.addButton(Translator.get("CANCEL"), QMessageBox.RejectRole)
            msg.setIcon(QMessageBox.Warning)
            if msg.clickedButton() == overwrite:
                self.manager.direct_edit.force_update(ref, digest)
            del self._conflicts_modals[filename]
        except:
            log.exception(
                "Error while displaying Direct Edit conflict modal dialog for %r",
                filename,
            )

    @pyqtSlot(str, list)
    def _direct_edit_error(self, message: str, values: List[str]) -> None:
        """ Display a simple Direct Edit error message. """

        msg = QMessageBox()
        msg.setWindowTitle("Direct Edit - {self.manager.app_name}")
        msg.setWindowIcon(QIcon(self.get_window_icon()))
        msg.setIcon(QMessageBox.Warning)
        msg.setTextFormat(Qt.RichText)
        msg.setText(self.translate(message, values))
        msg.exec_()

    @pyqtSlot()
    def _root_deleted(self) -> None:
        engine = self.sender()
        log.debug("Root has been deleted for engine: %s", engine.uid)

        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setWindowIcon(QIcon(self.get_window_icon()))
        msg.setText(Translator.get("DRIVE_ROOT_DELETED",
                                   [engine.local_folder]))
        recreate = msg.addButton(Translator.get("DRIVE_ROOT_RECREATE"),
                                 QMessageBox.AcceptRole)
        disconnect = msg.addButton(Translator.get("DRIVE_ROOT_DISCONNECT"),
                                   QMessageBox.RejectRole)

        msg.exec_()
        res = msg.clickedButton()
        if res == disconnect:
            self.manager.unbind_engine(engine.uid)
        elif res == recreate:
            engine.reinit()
            engine.start()

    @pyqtSlot()
    def _no_space_left(self) -> None:
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowIcon(QIcon(self.get_window_icon()))
        msg.setText(Translator.get("NO_SPACE_LEFT_ON_DEVICE"))
        msg.addButton(Translator.get("OK"), QMessageBox.AcceptRole)
        msg.exec_()

    @pyqtSlot(str)
    def _root_moved(self, new_path: str) -> None:
        engine = self.sender()
        log.debug("Root has been moved for engine: %s to %r", engine.uid,
                  new_path)
        info = [engine.local_folder, new_path]

        msg = QMessageBox()
        msg.setIcon(QMessageBox.Critical)
        msg.setWindowIcon(QIcon(self.get_window_icon()))
        msg.setText(Translator.get("DRIVE_ROOT_MOVED", info))
        move = msg.addButton(Translator.get("DRIVE_ROOT_UPDATE"),
                             QMessageBox.AcceptRole)
        recreate = msg.addButton(Translator.get("DRIVE_ROOT_RECREATE"),
                                 QMessageBox.AcceptRole)
        disconnect = msg.addButton(Translator.get("DRIVE_ROOT_DISCONNECT"),
                                   QMessageBox.RejectRole)
        msg.exec_()
        res = msg.clickedButton()

        if res == disconnect:
            self.manager.unbind_engine(engine.uid)
        elif res == recreate:
            engine.reinit()
            engine.start()
        elif res == move:
            engine.set_local_folder(new_path)
            engine.start()

    @pyqtSlot(object)
    def dropped_engine(self, engine: "Engine") -> None:
        # Update icon in case the engine dropped was syncing
        self.change_systray_icon()

    @pyqtSlot()
    def change_systray_icon(self) -> None:
        # Update status has the precedence over other ones
        if self.manager.updater.status not in (
                UPDATE_STATUS_UNAVAILABLE_SITE,
                UPDATE_STATUS_UP_TO_DATE,
        ):
            self.set_icon_state("update")
            return

        syncing = conflict = False
        engines = self.manager.get_engines()
        invalid_credentials = paused = offline = True

        for engine in engines.values():
            syncing |= engine.is_syncing()
            invalid_credentials &= engine.has_invalid_credentials()
            paused &= engine.is_paused()
            offline &= engine.is_offline()
            conflict |= bool(engine.get_conflicts())

        if offline:
            new_state = "error"
            Action(Translator.get("OFFLINE"))
        elif invalid_credentials:
            new_state = "error"
            Action(Translator.get("AUTH_EXPIRED"))
        elif not engines:
            new_state = "disabled"
            Action.finish_action()
        elif paused:
            new_state = "paused"
            Action.finish_action()
        elif syncing:
            new_state = "syncing"
        elif conflict:
            new_state = "conflict"
        else:
            new_state = "idle"
            Action.finish_action()

        self.set_icon_state(new_state)

    def refresh_conflicts(self, uid: str) -> None:
        """ Update the content of the conflicts/errors window. """
        self.conflicts_model.empty()
        self.errors_model.empty()
        self.ignoreds_model.empty()

        self.conflicts_model.addFiles(self.api.get_conflicts(uid))
        self.errors_model.addFiles(self.api.get_errors(uid))
        self.ignoreds_model.addFiles(self.api.get_unsynchronizeds(uid))

    @pyqtSlot()
    def show_conflicts_resolution(self, engine: "Engine") -> None:
        """ Display the conflicts/errors window. """
        self.refresh_conflicts(engine.uid)
        self._window_root(self.conflicts_window).setEngine.emit(engine.uid)
        self.conflicts_window.show()
        self.conflicts_window.requestActivate()

    @pyqtSlot()
    def show_settings(self, section: str = "General") -> None:
        sections = {"General": 0, "Accounts": 1, "About": 2}
        self._window_root(self.settings_window).setSection.emit(
            sections[section])
        self.settings_window.show()
        self.settings_window.requestActivate()

    @pyqtSlot()
    def show_systray(self) -> None:
        icon = self.tray_icon.geometry()

        if not icon or icon.isEmpty():
            # On Ubuntu it's likely we can't retrieve the geometry.
            # We're simply displaying the systray in the top right corner.
            screen = self.desktop().screenGeometry()
            pos_x = screen.right() - self.systray_window.width() - 20
            pos_y = 30
        else:
            pos_x = max(0,
                        icon.x() + icon.width() - self.systray_window.width())
            pos_y = icon.y() - self.systray_window.height()
            if pos_y < 0:
                pos_y = icon.y() + icon.height()

        self.systray_window.setX(pos_x)
        self.systray_window.setY(pos_y)

        self.systray_window.show()
        self.systray_window.raise_()

    @pyqtSlot()
    def hide_systray(self):
        self.systray_window.hide()

    @pyqtSlot()
    def open_help(self) -> None:
        self.manager.open_help()

    @pyqtSlot()
    def destroyed_filters_dialog(self) -> None:
        self.filters_dlg = None

    @pyqtSlot()
    def show_filters(self, engine: "Engine") -> None:
        if self.filters_dlg:
            self.filters_dlg.close()
            self.filters_dlg = None

        from ..gui.folders_dialog import FiltersDialog

        self.filters_dlg = FiltersDialog(self, engine)
        self.filters_dlg.destroyed.connect(self.destroyed_filters_dialog)
        self.filters_dlg.show()

    def show_file_status(self) -> None:
        from ..gui.status_dialog import StatusDialog

        for _, engine in self.manager.get_engines().items():
            self.status = StatusDialog(engine.get_dao())
            self.status.show()
            break

    def show_activities(self) -> None:
        return
        # TODO: Create activities window
        self.activities.show()

    @pyqtSlot(str, object)
    def _open_authentication_dialog(self, url: str,
                                    callback_params: Dict[str, str]) -> None:
        self.api._callback_params = callback_params
        if Options.is_frozen:
            """
            Authenticate through the browser.

            This authentication requires the server's Nuxeo Drive addon to include
            NXP-25519. Instead of opening the server's login page in a WebKit view
            through the app, it opens in the browser and retrieves the login token
            by opening an nxdrive:// URL.
            """
            self.manager.open_local_file(url)
        else:
            self._web_auth_not_frozen(url)

    def _web_auth_not_frozen(self, url: str) -> None:
        """
        Open a dialog box to fill the credentials.
        Then a request will be done using the Python client to
        get a token.

        This is used when the application is not frozen as there is no custom
        protocol handler in this case.
        """

        from PyQt5.QtWidgets import QLineEdit
        from nuxeo.client import Nuxeo

        dialog = QDialog()
        dialog.setWindowTitle(
            self.translate("WEB_AUTHENTICATION_WINDOW_TITLE"))
        dialog.setWindowIcon(QIcon(self.get_window_icon()))
        dialog.resize(250, 100)

        layout = QVBoxLayout()

        username = QLineEdit("Administrator", parent=dialog)
        password = QLineEdit("Administrator", parent=dialog)
        password.setEchoMode(QLineEdit.Password)
        layout.addWidget(username)
        layout.addWidget(password)

        def auth() -> None:
            """Retrieve a token and create the account."""
            user = str(username.text())
            pwd = str(password.text())
            nuxeo = Nuxeo(host=url, auth=(user, pwd))
            try:
                token = nuxeo.client.request_auth_token(
                    device_id=self.manager.device_id,
                    app_name=APP_NAME,
                    permission=TOKEN_PERMISSION,
                    device=get_device(),
                )
            except Exception as exc:
                log.error(f"Connection error: {exc}")
                token = ""
            finally:
                del nuxeo
            self.api.handle_token(token, user)
            dialog.close()

        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Cancel
                                   | QDialogButtonBox.Ok)
        buttons.accepted.connect(auth)
        buttons.rejected.connect(dialog.close)
        layout.addWidget(buttons)

        dialog.setLayout(layout)
        dialog.exec_()

    @pyqtSlot(object)
    def _connect_engine(self, engine: "Engine") -> None:
        engine.syncStarted.connect(self.change_systray_icon)
        engine.syncCompleted.connect(self.change_systray_icon)
        engine.invalidAuthentication.connect(self.change_systray_icon)
        engine.syncSuspended.connect(self.change_systray_icon)
        engine.syncResumed.connect(self.change_systray_icon)
        engine.offline.connect(self.change_systray_icon)
        engine.online.connect(self.change_systray_icon)
        engine.rootDeleted.connect(self._root_deleted)
        engine.rootMoved.connect(self._root_moved)
        engine.noSpaceLeftOnDevice.connect(self._no_space_left)
        self.change_systray_icon()

    @pyqtSlot()
    def _debug_toggle_invalid_credentials(self) -> None:
        sender = self.sender()
        engine = sender.data()
        engine.set_invalid_credentials(not engine.has_invalid_credentials(),
                                       reason="debug")

    @pyqtSlot()
    def _debug_show_file_status(self) -> None:
        from ..gui.status_dialog import StatusDialog

        sender = self.sender()
        engine = sender.data()
        self.status_dialog = StatusDialog(engine.get_dao())
        self.status_dialog.show()

    def _create_debug_engine_menu(self, engine: "Engine",
                                  parent: QMenu) -> QMenu:
        menu = QMenu(parent)
        action = QAction(Translator.get("DEBUG_INVALID_CREDENTIALS"), menu)
        action.setCheckable(True)
        action.setChecked(engine.has_invalid_credentials())
        action.setData(engine)
        action.triggered.connect(self._debug_toggle_invalid_credentials)
        menu.addAction(action)
        action = QAction(Translator.get("DEBUG_FILE_STATUS"), menu)
        action.setData(engine)
        action.triggered.connect(self._debug_show_file_status)
        menu.addAction(action)
        return menu

    def create_debug_menu(self, menu: QMenu) -> None:
        menu.addAction(Translator.get("DEBUG_WINDOW"), self.show_debug_window)
        for engine in self.manager.get_engines().values():
            action = QAction(engine.name, menu)
            action.setMenu(self._create_debug_engine_menu(engine, menu))
            action.setData(engine)
            menu.addAction(action)

    @pyqtSlot()
    def show_debug_window(self) -> None:
        return
        debug = self.dialogs.get("debug")
        # TODO: if not debug: Create debug window
        self._show_window(debug)

    def init_checks(self) -> None:
        if Options.debug:
            self.show_debug_window()

        for _, engine in self.manager.get_engines().items():
            self._connect_engine(engine)

        self.manager.newEngine.connect(self._connect_engine)
        self.manager.notification_service.newNotification.connect(
            self._new_notification)
        self.manager.updater.updateAvailable.connect(self._update_notification)

        if not self.manager.get_engines():
            self.show_settings()
        else:
            for engine in self.manager.get_engines().values():
                # Prompt for settings if needed
                if engine.has_invalid_credentials():
                    self.show_settings("Accounts_" + engine.uid)
                    break

        self.manager.start()

    @pyqtSlot()
    @if_frozen
    def _update_notification(self) -> None:
        self.change_systray_icon()

        # Display a notification
        status, version = self.manager.updater.status, self.manager.updater.version

        msg = (
            "AUTOUPDATE_UPGRADE",
            "AUTOUPDATE_DOWNGRADE")[status == UPDATE_STATUS_DOWNGRADE_NEEDED]
        description = Translator.get(msg, [version])
        flags = Notification.FLAG_BUBBLE | Notification.FLAG_UNIQUE
        if LINUX:
            description += " " + Translator.get("AUTOUPDATE_MANUAL")
            flags |= Notification.FLAG_SYSTRAY

        log.warning(description)
        notification = Notification(
            uuid="AutoUpdate",
            flags=flags,
            title=Translator.get("NOTIF_UPDATE_TITLE", [version]),
            description=description,
        )
        self.manager.notification_service.send_notification(notification)

    @pyqtSlot()
    def message_clicked(self) -> None:
        if self.current_notification:
            self.manager.notification_service.trigger_notification(
                self.current_notification.uid)

    def _setup_notification_center(self) -> None:
        from ..osi.darwin.pyNotificationCenter import (
            setup_delegator,
            NotificationDelegator,
        )

        if not self._delegator:
            self._delegator = NotificationDelegator.alloc().init()
            self._delegator._manager = self.manager
        setup_delegator(self._delegator)

    @pyqtSlot(object)
    def _new_notification(self, notif: Notification) -> None:
        if not notif.is_bubble():
            return

        if self._delegator is not None:
            # Use notification center
            from ..osi.darwin.pyNotificationCenter import notify

            return notify(notif.title,
                          None,
                          notif.description,
                          user_info={"uuid": notif.uid})

        icon = QSystemTrayIcon.Information
        if notif.level == Notification.LEVEL_WARNING:
            icon = QSystemTrayIcon.Warning
        elif notif.level == Notification.LEVEL_ERROR:
            icon = QSystemTrayIcon.Critical

        self.current_notification = notif
        self.tray_icon.showMessage(notif.title, notif.description, icon, 10000)

    def set_icon_state(self, state: str) -> bool:
        """
        Execute systray icon change operations triggered by state change.

        The synchronization thread can update the state info but cannot
        directly call QtGui widget methods. This should be executed by the main
        thread event loop, hence the delegation to this method that is
        triggered by a signal to allow for message passing between the 2
        threads.

        Return True of the icon has changed state.
        """

        if self.icon_state == state:
            # Nothing to update
            return False

        self.tray_icon.setToolTip(self.get_tooltip())
        self.tray_icon.setIcon(self.icons[state])
        self.icon_state = state
        return True

    def get_tooltip(self) -> str:
        actions = Action.get_actions()
        if not actions:
            return self.default_tooltip

        # Display only the first action for now
        for action in actions.values():
            if action and action.type and not action.type.startswith("_"):
                break
        else:
            return self.default_tooltip

        if isinstance(action, FileAction):
            if action.get_percent() is not None:
                return "%s - %s - %s - %d%%" % (
                    self.default_tooltip,
                    action.type,
                    action.filename,
                    action.get_percent(),
                )
            return "%s - %s - %s" % (self.default_tooltip, action.type,
                                     action.filename)
        elif action.get_percent() is not None:
            return "%s - %s - %d%%" % (
                self.default_tooltip,
                action.type,
                action.get_percent(),
            )

        return "%s - %s" % (self.default_tooltip, action.type)

    @if_frozen
    def show_release_notes(self, version: str) -> None:
        """ Display release notes of a given version. """

        beta = self.manager.get_beta_channel()
        log.debug("Showing release notes, version=%r beta=%r", version, beta)

        # For now, we do care about beta only
        if not beta:
            return

        url = ("https://api.github.com/repos/nuxeo/nuxeo-drive"
               "/releases/tags/release-" + version)

        if beta:
            version += " beta"

        try:
            content = requests.get(url)
        except requests.HTTPError as exc:
            if exc.response.status_code == 404:
                log.error("[%s] Release does not exist", version)
            else:
                log.exception(
                    "[%s] Network error while fetching release notes", version)
            return
        except:
            log.exception("[%s] Unknown error while fetching release notes",
                          version)
            return

        try:
            data = content.json()
        except ValueError:
            log.exception("[%s] Invalid release notes", version)
            return
        finally:
            del content

        try:
            html = markdown(data["body"])
        except KeyError:
            log.error("[%s] Release notes is missing its body", version)
            return
        except (UnicodeDecodeError, ValueError):
            log.exception("[%s] Release notes conversion error", version)
            return

        dialog = QDialog()
        dialog.setWindowTitle(
            f"{self.manager.app_name} {version} - Release notes")
        dialog.setWindowIcon(QIcon(self.get_window_icon()))

        dialog.resize(600, 400)

        notes = QTextEdit()
        notes.setStyleSheet("background-color: #eee; border: none;")
        notes.setReadOnly(True)
        notes.setHtml(html)

        buttons = QDialogButtonBox()
        buttons.setStandardButtons(QDialogButtonBox.Ok)
        buttons.clicked.connect(dialog.accept)

        layout = QVBoxLayout()
        layout.addWidget(notes)
        layout.addWidget(buttons)
        dialog.setLayout(layout)
        dialog.exec_()

    def show_metadata(self, file_path: str) -> None:
        self.manager.ctx_edit_metadata(file_path)

    def setup_systray(self) -> None:
        icons = {}
        for state in {
                "conflict",
                "disabled",
                "error",
                "idle",
                "notification",
                "paused",
                "syncing",
                "update",
        }:
            name = "{}{}.svg".format(state, "_light" if WINDOWS else "")
            icon = QIcon()
            icon.addFile(find_icon(name))
            if MAC:
                icon.addFile(find_icon("active.svg"), mode=QIcon.Selected)
            icons[state] = icon
        setattr(self, "icons", icons)

        self.tray_icon = DriveSystrayIcon(self)
        if not self.tray_icon.isSystemTrayAvailable():
            log.critical("There is no system tray available!")
        else:
            self.tray_icon.setToolTip(self.manager.app_name)
            self.set_icon_state("disabled")
            self.tray_icon.show()

    def event(self, event: QEvent) -> bool:
        """ Handle URL scheme events under macOS. """

        url = getattr(event, "url", None)
        if not url:
            # This is not an event for us!
            return super().event(event)
        try:
            final_url = unquote(event.url().toString())
            return self._handle_nxdrive_url(final_url)
        except:
            log.exception("Error handling URL event %r", url)
            return False

    def _handle_nxdrive_url(self, url: str) -> bool:
        """ Handle an nxdrive protocol URL. """

        info = parse_protocol_url(url)
        if not info:
            return False

        cmd = info["command"]
        path = info.get("filepath", None)
        manager = self.manager

        log.debug("Event URL=%s, info=%r", url, info)

        # Event fired by a context menu item
        func = {
            "access-online": manager.ctx_access_online,
            "copy-share-link": manager.ctx_copy_share_link,
            "edit-metadata": manager.ctx_edit_metadata,
        }.get(cmd, None)
        if func:
            func(path)
        elif "edit" in cmd:
            manager.direct_edit.edit(
                info["server_url"],
                info["doc_id"],
                user=info["user"],
                download_url=info["download_url"],
            )
        elif cmd == "trigger-watch":
            for engine in manager._engine_definitions:
                manager.osi.watch_folder(engine.local_folder)
        elif cmd == "token":
            self.api.handle_token(info["token"], info["username"])
        else:
            log.warning("Unknown event URL=%r, info=%r", url, info)
            return False
        return True

    @if_frozen
    def init_nxdrive_listener(self) -> None:
        """
        Set up a QLocalServer to listen to nxdrive protocol calls.

        On Windows, when an nxdrive:// URL is opened, it creates a new
        instance of Nuxeo Drive. As we want the already running instance to
        receive this call (particularly during the login process), we set
        up a QLocalServer in that instance to listen to the new ones who will
        send their data.
        The Qt implementation of QLocalSocket on Windows makes use of named
        pipes. We just need to connect a handler to the newConnection signal
        to process the URLs.
        """

        self._nxdrive_listener = QLocalServer()
        self._nxdrive_listener.newConnection.connect(self._handle_connection)
        self._nxdrive_listener.listen("com.nuxeo.drive.protocol")
        self.aboutToQuit.connect(self._nxdrive_listener.close)

    @if_frozen
    def _handle_connection(self) -> None:
        """ Retrieve the connection with other instances and handle the incoming data. """

        con: QLocalSocket = self._nxdrive_listener.nextPendingConnection()
        log.debug("Receiving socket connection for nxdrive protocol handling")
        if not con or not con.waitForConnected():
            log.error(f"Unable to open server socket: {con.errorString()}")
            return

        if con.waitForReadyRead():
            payload = con.readAll()
            url = force_decode(payload.data())
            self._handle_nxdrive_url(url)

        con.disconnectFromServer()
        con.waitForDisconnected()
        del con
        log.debug("Successfully closed server socket")

        # Bring settings window to front
        self.settings_window.show()

    def update_status(self, engine: "Engine") -> None:
        """
        Update the systray status for synchronization,
        conflicts/errors and software updates.
        """
        sync_state = error_state = update_state = ""

        update_state = self.manager.updater.status
        self.refresh_conflicts(engine.uid)

        # Check synchronization state
        if engine.is_paused():
            sync_state = "suspended"
        elif engine.is_syncing():
            sync_state = "syncing"

        # Check error state
        if engine.has_invalid_credentials():
            error_state = "auth_expired"
        elif self.conflicts_model.count:
            error_state = "conflicted"
        elif self.errors_model.count:
            error_state = "error"

        self._window_root(self.systray_window).setStatus.emit(
            sync_state, error_state, update_state)

    @pyqtSlot(str)
    def get_last_files(self, uid: str) -> None:
        files = self.api.get_last_files(uid, 10, "", None)
        self.file_model.empty()
        self.file_model.addFiles(files)

    def current_language(self) -> Optional[str]:
        lang = Translator.locale()
        for tag, name in self.language_model.languages:
            if tag == lang:
                return name
        return None
Beispiel #37
0
from PyQt5.QtQml import QQmlContext, qmlRegisterType
from PyQt5.QtQuick import QQuickView
from PyQt5.QtCore import QUrl
from context import Context
from PyQt5.QtQml import QQmlListProperty

from plateau import Plateau
from case import Case
from qplateau import QPlateau
from qcase import QCase

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    
    myApp = Context()
 
    view = QQuickView()
    view.resize(800, 800)
    view.setResizeMode(QQuickView.SizeRootObjectToView)

    qmlRegisterType(QPlateau, 'temp', 1, 0, 'QPlateau')
    qmlRegisterType(QCase, 'temp0', 1, 0, 'QCase')
        
    ctx = view.rootContext()
    myApp.setContext( ctx )
            
    view.setSource(QUrl("qml/main.qml"))
    view.show()
    
    sys.exit(app.exec_())