{"action":"created","issue":{"url":"https://api.github.com/repos/ralph-group/pymeasure/issues/109","repository_url":"https://api.github.com/repos/ralph-group/pymeasure","labels_url":"https://api.github.com/repos/ralph-group/pymeasure/issues/109/labels{/name}","comments_url":"https://api.github.com/repos/ralph-group/pymeasure/issues/109/comments","events_url":"https://api.github.com/repos/ralph-group/pymeasure/issues/109/events","html_url":"https://github.com/ralph-group/pymeasure/issues/109","id":252954726,"number":109,"title":"Forking/Pickling issue when worker.start() is called?!","user":{"login":"JuMaD","id":13823675,"avatar_url":"https://avatars1.githubusercontent.com/u/13823675?v=4","gravatar_id":"","url":"https://api.github.com/users/JuMaD","html_url":"https://github.com/JuMaD","followers_url":"https://api.github.com/users/JuMaD/followers","following_url":"https://api.github.com/users/JuMaD/following{/other_user}","gists_url":"https://api.github.com/users/JuMaD/gists{/gist_id}","starred_url":"https://api.github.com/users/JuMaD/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JuMaD/subscriptions","organizations_url":"https://api.github.com/users/JuMaD/orgs","repos_url":"https://api.github.com/users/JuMaD/repos","events_url":"https://api.github.com/users/JuMaD/events{/privacy}","received_events_url":"https://api.github.com/users/JuMaD/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2017-08-25T16:30:00Z","updated_at":"2017-08-28T11:30:07Z","closed_at":null,"body":"Using the ManagedWindow class, I can add experiments to the queue. However, once they should \"start\" (i.e. the next() method is used) the whole .py file gets interpreted again instead.\r\n\r\nIn the debugger I traced it down to a new thread being started when the worker is started. When Manager.next._worker.start() is called). Due to the error message below my best guess is, that it has something to do with the forking/pickling issue(s). Since I have no clue about multi-threading, I hope someone can help me fix this. \r\n\r\n![image](https://user-images.githubusercontent.com/13823675/29722901-df3fba50-89c2-11e7-9f97-4c32b699a961.png)\r\n"},"comment":{"url":"https://api.github.com/repos/ralph-group/pymeasure/issues/comments/325328771","html_url":"https://github.com/ralph-group/pymeasure/issues/109#issuecomment-325328771","issue_url":"https://api.github.com/repos/ralph-group/pymeasure/issues/109","id":325328771,"user":{"login":"JuMaD","id":13823675,"avatar_url":"https://avatars1.githubusercontent.com/u/13823675?v=4","gravatar_id":"","url":"https://api.github.com/users/JuMaD","html_url":"https://github.com/JuMaD","followers_url":"https://api.github.com/users/JuMaD/followers","following_url":"https://api.github.com/users/JuMaD/following{/other_user}","gists_url":"https://api.github.com/users/JuMaD/gists{/gist_id}","starred_url":"https://api.github.com/users/JuMaD/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JuMaD/subscriptions","organizations_url":"https://api.github.com/users/JuMaD/orgs","repos_url":"https://api.github.com/users/JuMaD/repos","events_url":"https://api.github.com/users/JuMaD/events{/privacy}","received_events_url":"https://api.github.com/users/JuMaD/received_events","type":"User","site_admin":false},"created_at":"2017-08-28T11:30:07Z","updated_at":"2017-08-28T11:30:07Z","body":"Ok then. Here's the (adjusted) example from the docs\r\n\r\n`\r\nprint('Running')\r\nclass RandomProcedure(Procedure):\r\n    iterations = IntegerParameter('Loop Iterations')\r\n    delay = FloatParameter('Delay Time', units='s', default=0.2)\r\n    seed = Parameter('Random Seed', default='12345')\r\n\r\n    DATA_COLUMNS = ['Iteration', 'Random Number']\r\n\r\n    def startup(self):\r\n        log.info(\"Setting the seed of the random number generator\")\r\n        random.seed(self.seed)\r\n\r\n    def execute(self):\r\n        log.info(\"Starting the loop of %d iterations\" % self.iterations)\r\n        for i in range(self.iterations):\r\n            data = {\r\n                'Iteration': i,\r\n                'Random Number': random.random()\r\n            }\r\n            self.emit('results', data)\r\n            log.debug(\"Emitting results: %s\" % data)\r\n            sleep(self.delay)\r\n            if self.should_stop():\r\n                log.warning(\"Caught the stop flag in the procedure\")\r\n                break\r\n\r\n\r\nclass MainWindow(ManagedWindow):\r\n    def __init__(self):\r\n        super(MainWindow, self).__init__(\r\n            procedure_class=RandomProcedure,\r\n            inputs=['iterations', 'delay', 'seed'],\r\n            displays=['iterations', 'delay', 'seed'],\r\n            x_axis='Iteration',\r\n            y_axis='Random Number'\r\n        )\r\n        self.setWindowTitle('GUI Example')\r\n\r\n    def queue(self):\r\n        filename = 'test.csv'\r\n\r\n        procedure = self.make_procedure()\r\n        results = Results(procedure, filename)\r\n        experiment = self.new_experiment(results)\r\n\r\n        self.manager.queue(experiment)`\r\n\r\nAnd my edited ManagedWindow class:\r\n`class ManagedWindow(QtGui.QMainWindow):\r\n    \"\"\" The ManagedWindow uses a Manager to control Workers in a Queue,\r\n    and provides a simple interface. The queue method must be overwritten\r\n    by a child class which is required to pass an Experiment containing the\r\n    Results and Procedure to self.manager.queue.\r\n    \"\"\"\r\n    EDITOR = 'gedit'\r\n\r\n    def __init__(self, procedure_class, inputs=(), displays=(), x_axis=None, y_axis=None,\r\n                 log_channel='', log_level=logging.INFO, parent=None):\r\n        super().__init__(parent)\r\n        app = QtCore.QCoreApplication.instance()\r\n        app.aboutToQuit.connect(self.quit)\r\n        self.procedure_class = procedure_class\r\n        self.inputs = inputs\r\n        self.displays = displays\r\n        self.log = logging.getLogger(log_channel)\r\n        self.log_level = log_level\r\n        log.setLevel(log_level)\r\n        self.log.setLevel(log_level)\r\n        self.x_axis, self.y_axis = x_axis, y_axis\r\n        self._setup_ui()\r\n        self._layout()\r\n\r\n\r\n    def _setup_ui(self):\r\n        self.list_widget = QListWidget()\r\n        self.list_widget.itemClicked.connect(self.instrument_clicked)\r\n        self.refresh_button = QtGui.QPushButton('Refresh', self)\r\n        self.refresh_button.setEnabled(True)\r\n        self.refresh_button.clicked.connect(self.refresh_instruments)\r\n        self.refresh_instruments()\r\n\r\n        self.log_widget = LogWidget()\r\n        self.log.addHandler(self.log_widget.handler)  # needs to be in Qt context?\r\n        log.info(\"ManagedWindow connected to logging\")\r\n        self.queue_button = QtGui.QPushButton('Queue', self)\r\n        self.queue_button.clicked.connect(self.queue)\r\n\r\n        self.abort_button = QtGui.QPushButton('Abort', self)\r\n        self.abort_button.setEnabled(False)\r\n        self.abort_button.clicked.connect(self.abort)\r\n\r\n        self.plot_widget = PlotWidget(self.procedure_class.DATA_COLUMNS, self.x_axis, self.y_axis)\r\n        self.plot = self.plot_widget.plot\r\n\r\n        self.browser_widget = BrowserWidget(\r\n            self.procedure_class,\r\n            self.displays,\r\n            [self.x_axis, self.y_axis],\r\n            parent=self\r\n        )\r\n        self.browser_widget.show_button.clicked.connect(self.show_experiments)\r\n        self.browser_widget.hide_button.clicked.connect(self.hide_experiments)\r\n        self.browser_widget.clear_button.clicked.connect(self.clear_experiments)\r\n        self.browser_widget.open_button.clicked.connect(self.open_experiment)\r\n        self.browser = self.browser_widget.browser\r\n\r\n        self.browser.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)\r\n        self.browser.customContextMenuRequested.connect(self.browser_item_menu)\r\n        self.browser.itemChanged.connect(self.browser_item_changed)\r\n\r\n        self.inputs = InputsWidget(\r\n            self.procedure_class,\r\n            self.inputs,\r\n            parent=self\r\n        )\r\n\r\n        self.manager = Manager(self.plot, self.browser, log_level=self.log_level, parent=self)\r\n        self.manager.abort_returned.connect(self.abort_returned)\r\n        self.manager.queued.connect(self.queued)\r\n        self.manager.running.connect(self.running)\r\n        self.manager.finished.connect(self.finished)\r\n        self.manager.log.connect(self.log.handle)\r\n\r\n    def _layout(self):\r\n        self.main = QtGui.QWidget(self)\r\n\r\n        inputs_dock = QtGui.QWidget(self)\r\n        inputs_vbox = QtGui.QVBoxLayout(self.main)\r\n\r\n        hbox = QtGui.QHBoxLayout()\r\n        hbox.setSpacing(10)\r\n        hbox.setContentsMargins(-1, 6, -1, 6)\r\n        hbox.addWidget(self.queue_button)\r\n        hbox.addWidget(self.abort_button)\r\n        hbox.addStretch()\r\n\r\n        inputs_vbox.addWidget(self.inputs)\r\n        inputs_vbox.addLayout(hbox)\r\n        inputs_vbox.addWidget(self.list_widget)\r\n        inputs_vbox.addWidget(self.refresh_button)\r\n        inputs_vbox.addStretch()\r\n        inputs_dock.setLayout(inputs_vbox)\r\n\r\n        dock = QtGui.QDockWidget('Input Parameters')\r\n        dock.setWidget(inputs_dock)\r\n        dock.setFeatures(QtGui.QDockWidget.NoDockWidgetFeatures)\r\n        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock)\r\n\r\n        tabs = QtGui.QTabWidget(self.main)\r\n        tabs.addTab(self.plot_widget, \"Results Graph\")\r\n        tabs.addTab(self.log_widget, \"Experiment Log\")\r\n\r\n        splitter = QtGui.QSplitter(QtCore.Qt.Vertical)\r\n        splitter.addWidget(tabs)\r\n        splitter.addWidget(self.browser_widget)\r\n        self.plot_widget.setMinimumSize(100, 200)\r\n\r\n        vbox = QtGui.QVBoxLayout(self.main)\r\n        vbox.setSpacing(0)\r\n        vbox.addWidget(splitter)\r\n\r\n        self.main.setLayout(vbox)\r\n        self.setCentralWidget(self.main)\r\n        self.main.show()\r\n        self.resize(1000, 800)\r\n\r\n    def quit(self, evt=None):\r\n        self.close()\r\n\r\n    def refresh_instruments(self):\r\n        if self.list_widget.count() == 0:\r\n            startup = True\r\n        else:\r\n            self.list_widget.clear()\r\n            startup = False\r\n        rm = visa.ResourceManager()\r\n        instrs = rm.list_resources()\r\n        for n, instr in enumerate(instrs):\r\n            # trying to catch errors in comunication\r\n            try:\r\n                res = rm.open_resource(instr)\r\n                # try to avoid errors from *idn?\r\n                try:\r\n                    # noinspection PyUnresolvedReferences\r\n                    idn = res.ask('*idn?')[:-1]\r\n                except visa.Error:\r\n                    idn = \"Not known\"\r\n                finally:\r\n                    res.close()\r\n                    self.list_widget.addItem(str(n)+\"-\"+str(instr)+\"-\"+str(idn))\r\n            except visa.VisaIOError as e:\r\n                print(n, \":\", instr, \":\", \"Visa IO Error: check connections\")\r\n                print(e)\r\n        rm.close()\r\n        if not startup:\r\n            QMessageBox.information(self, \"Refresh\", \"Instruments refreshed\")\r\n        log.info('Connected instruments refreshed.')\r\n\r\n    def instrument_clicked(self, item):\r\n        itemtext = item.text()\r\n        n, instr, idn = itemtext.split('-')\r\n        QMessageBox.information(self, \"Instrument Selection\",\r\n                                \"You clicked: \\nitem\\t\\t\" + n + \"\\nadress:\\t\\t\" + instr + \"\\nidn:\\t\\t\" + idn)  # TODO: Replace this with a function that adds the selected instrument(-adress) to the current procedure\r\n\r\n    def browser_item_changed(self, item, column):\r\n        if column == 0:\r\n            state = item.checkState(0)\r\n            experiment = self.manager.experiments.with_browser_item(item)\r\n            if state == 0:\r\n                self.plot.removeItem(experiment.curve)\r\n            else:\r\n                experiment.curve.x = self.plot_widget.plot_frame.x_axis\r\n                experiment.curve.y = self.plot_widget.plot_frame.y_axis\r\n                experiment.curve.update()\r\n                self.plot.addItem(experiment.curve)\r\n\r\n    def browser_item_menu(self, position):\r\n        item = self.browser.itemAt(position)\r\n\r\n        if item is not None:\r\n            experiment = self.manager.experiments.with_browser_item(item)\r\n\r\n            menu = QtGui.QMenu(self)\r\n\r\n            # Open\r\n            action_open = QtGui.QAction(menu)\r\n            action_open.setText(\"Open Data Externally\")\r\n            action_open.triggered.connect(\r\n                lambda: self.open_file_externally(experiment.results.data_filename))\r\n            menu.addAction(action_open)\r\n\r\n            # Change Color\r\n            action_change_color = QtGui.QAction(menu)\r\n            action_change_color.setText(\"Change Color\")\r\n            action_change_color.triggered.connect(\r\n                lambda: self.change_color(experiment))\r\n            menu.addAction(action_change_color)\r\n\r\n            # Remove\r\n            action_remove = QtGui.QAction(menu)\r\n            action_remove.setText(\"Remove Graph\")\r\n            if self.manager.is_running():\r\n                if self.manager.running_experiment() == experiment:  # Experiment running\r\n                    action_remove.setEnabled(False)\r\n            action_remove.triggered.connect(lambda: self.remove_experiment(experiment))\r\n            menu.addAction(action_remove)\r\n\r\n            # Use parameters\r\n            action_use = QtGui.QAction(menu)\r\n            action_use.setText(\"Use These Parameters\")\r\n            action_use.triggered.connect(\r\n                lambda: self.set_parameters(experiment.procedure.parameter_objects()))\r\n            menu.addAction(action_use)\r\n            menu.exec_(self.browser.viewport().mapToGlobal(position))\r\n\r\n    def remove_experiment(self, experiment):\r\n        reply = QtGui.QMessageBox.question(self, 'Remove Graph',\r\n                                           \"Are you sure you want to remove the graph?\",\r\n                                           QtGui.QMessageBox.Yes |\r\n                                           QtGui.QMessageBox.No, QtGui.QMessageBox.No)\r\n        if reply == QtGui.QMessageBox.Yes:\r\n            self.manager.remove(experiment)\r\n\r\n    def show_experiments(self):\r\n        root = self.browser.invisibleRootItem()\r\n        for i in range(root.childCount()):\r\n            item = root.child(i)\r\n            item.setCheckState(0, QtCore.Qt.Checked)\r\n\r\n    def hide_experiments(self):\r\n        root = self.browser.invisibleRootItem()\r\n        for i in range(root.childCount()):\r\n            item = root.child(i)\r\n            item.setCheckState(0, QtCore.Qt.Unchecked)\r\n\r\n    def clear_experiments(self):\r\n        self.manager.clear()\r\n\r\n    def open_experiment(self):\r\n        dialog = ResultsDialog(self.procedure_class.DATA_COLUMNS, self.x_axis, self.y_axis)\r\n        if dialog.exec_():\r\n            filenames = dialog.selectedFiles()\r\n            for filename in map(str, filenames):\r\n                if filename in self.manager.experiments:\r\n                    QtGui.QMessageBox.warning(self, \"Load Error\",\r\n                                              \"The file %s cannot be opened twice.\" % os.path.basename(\r\n                                                  filename))\r\n                elif filename == '':\r\n                    return\r\n                else:\r\n                    results = Results.load(filename)\r\n                    experiment = self.new_experiment(results)\r\n                    experiment.curve.update()\r\n                    experiment.browser_item.progressbar.setValue(100.)\r\n                    self.manager.load(experiment)\r\n                    log.info('Opened data file %s' % filename)\r\n\r\n    def change_color(self, experiment):\r\n        color = QtGui.QColorDialog.getColor(\r\n            initial=experiment.curve.opts['pen'].color(), parent=self)\r\n        if color.isValid():\r\n            pixelmap = QtGui.QPixmap(24, 24)\r\n            pixelmap.fill(color)\r\n            experiment.browser_item.setIcon(0, QtGui.QIcon(pixelmap))\r\n            experiment.curve.setPen(pg.mkPen(color=color, width=2))\r\n\r\n    def open_file_externally(self, filename):\r\n        # TODO: Make this function OS-agnostic\r\n        import subprocess\r\n        proc = subprocess.Popen([self.EDITOR, filename])\r\n\r\n    def make_procedure(self):\r\n        if not isinstance(self.inputs, InputsWidget):\r\n            raise Exception(\"ManagedWindow can not make a Procedure\"\r\n                            \" without a InputsWidget type\")\r\n        return self.inputs.get_procedure()\r\n\r\n    def new_curve(self, results, color=None, **kwargs):\r\n        if color is None:\r\n            color = pg.intColor(self.browser.topLevelItemCount() % 8)\r\n        return self.plot_widget.new_curve(results, color=color, **kwargs)\r\n\r\n    def new_experiment(self, results, curve=None):\r\n        if curve is None:\r\n            curve = self.new_curve(results)\r\n        browser_item = BrowserItem(results, curve)\r\n        return Experiment(results, curve, browser_item)\r\n\r\n    def set_parameters(self, parameters):\r\n        \"\"\" This method should be overwritten by the child class. The\r\n        parameters argument is a dictionary of Parameter objects.\r\n        The Parameters should overwrite the GUI values so that a user\r\n        can click \"Queue\" to capture the same parameters.\r\n        \"\"\"\r\n        if not isinstance(self.inputs, InputsWidget):\r\n            raise Exception(\"ManagedWindow can not set parameters\"\r\n                            \" without a InputsWidget\")\r\n        self.inputs.set_parameters(parameters)\r\n\r\n    def queue(self):\r\n        \"\"\" This method should be overwritten by the child class. The\r\n        self.manager.queue method should be passed an Experiment object\r\n        which contains the Results and Procedure to be run.\r\n        \"\"\"\r\n        raise Exception(\"ManagedWindow child class does not implement queue method\")\r\n\r\n    def abort(self):\r\n        self.abort_button.setEnabled(False)\r\n        self.abort_button.setText(\"Resume\")\r\n        self.abort_button.clicked.disconnect()\r\n        self.abort_button.clicked.connect(self.resume)\r\n        try:\r\n            self.manager.abort()\r\n        except:\r\n            log.error('Failed to abort experiment', exc_info=True)\r\n            self.abort_button.setText(\"Abort\")\r\n            self.abort_button.clicked.disconnect()\r\n            self.abort_button.clicked.connect(self.abort)\r\n\r\n    def resume(self):\r\n        self.abort_button.setText(\"Abort\")\r\n        self.abort_button.clicked.disconnect()\r\n        self.abort_button.clicked.connect(self.abort)\r\n        if self.manager.experiments.has_next():\r\n            self.manager.resume()\r\n        else:\r\n            self.abort_button.setEnabled(False)\r\n\r\n    def queued(self, experiment):\r\n        self.abort_button.setEnabled(True)\r\n        self.browser_widget.show_button.setEnabled(True)\r\n        self.browser_widget.hide_button.setEnabled(True)\r\n        self.browser_widget.clear_button.setEnabled(True)\r\n\r\n    def running(self, experiment):\r\n        self.browser_widget.clear_button.setEnabled(False)\r\n\r\n    def abort_returned(self, experiment):\r\n        if self.manager.experiments.has_next():\r\n            self.abort_button.setText(\"Resume\")\r\n            self.abort_button.setEnabled(True)\r\n        else:\r\n            self.browser_widget.clear_button.setEnabled(True)\r\n\r\n    def finished(self, experiment):\r\n        if not self.manager.experiments.has_next():\r\n            self.abort_button.setEnabled(False)\r\n            self.browser_widget.clear_button.setEnabled(True)\r\n`\r\n\r\nEverything else is the same as in 0.4.6. Starting the worker (i.e. in this example clicking \"queue\") leads to a re-interpretation of the whole code and a new window in a new thread instead of a running \"experiment\". What I mean by this is that even 'print('Running')' gets executed a second time."}}
{"actor":{"display_login":"JuMaD"}}