예제 #1
0
class MSSViewWindow(QtWidgets.QMainWindow):
    """Derives QMainWindow to provide some common functionality to all
       MSUI view windows.
    """
    name = "Abstract MSS View Window"
    identifier = None

    viewCloses = QtCore.pyqtSignal(name="viewCloses")
    # views for mscolab
    viewClosesId = QtCore.Signal(int, name="viewClosesId")

    def __init__(self, parent=None, model=None, _id=None):
        super(MSSViewWindow, self).__init__(parent)

        # Object variables:
        self.waypoints_model = model  # pointer to the current flight track.

        # List that accommodates the dock window instances: Needs to be defined
        # in proper size in derived classes!
        self.docks = []

        # emit _id if not none
        logging.debug(_id)
        self._id = _id
        # Used to force close window without the dialog popping up
        self.force_close = False

    def handle_force_close(self):
        self.force_close = True
        self.close()

    def closeEvent(self, event):
        """
        if force_close is True then close window without dialog
        else ask user if he/she wants to close the window.

        Overloads QtGui.QMainWindow.closeEvent(). This method is called if
        Qt receives a window close request for our application window.
        """
        if self.force_close is True:
            event.accept()
            return

        ret = QtWidgets.QMessageBox.warning(
            self, self.tr("Mission Support System"),
            self.tr("Do you want to close this {}?".format(self.name)),
            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
            QtWidgets.QMessageBox.No)
        if ret == QtWidgets.QMessageBox.Yes:
            self.viewCloses.emit()
            if self._id is not None:
                self.viewClosesId.emit(self._id)
            logging.debug(self._id)
            event.accept()
        else:
            event.ignore()

    def setFlightTrackModel(self, model):
        """Set the QAbstractItemModel instance that the view displays.
        """
        self.waypoints_model = model

    def controlToBeCreated(self, index):
        """Check if the dock widget at index <index> exists. If yes, show
           the widget and return -1. Otherwise return <index-1>.
        """
        index -= 1
        if index >= 0 and self.docks[index] is not None:
            # The widget has already been created, but is not visible at
            # the moment.
            self.docks[index].show()
            self.docks[index].raise_()
            index = -1
        if hasattr(self, "cbTools"):
            self.cbTools.setCurrentIndex(0)
        return index

    def createDockWidget(self, index, title, widget):
        """Create a new dock widget. A pointer to the dock widget will be
           stored in self.docks[index]. The dock will have the title <title>
           and contain the Qt widget <widget>.
        """
        self.docks[index] = QtWidgets.QDockWidget(title, self)
        self.docks[index].setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        # setWidget transfers the widget's ownership to Qt -- no setParent()
        # call is necessary:
        self.docks[index].setWidget(widget)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.docks[index])

        # Check if another dock widget occupies the dock area. If yes,
        # tabbify the old and the new widget.
        for dock in self.docks:
            if dock and not dock == self.docks[index] and not dock.isFloating(
            ):
                self.tabifyDockWidget(dock, self.docks[index])
                break
        self.docks[index].show()
        self.docks[index].raise_()

    @abstractmethod
    def getView(self):
        """Return view object that tools can interact with.

        ABSTRACT method, needs to be implemented in derived classes.
        """
        return None

    def setIdentifier(self, identifier):
        self.identifier = identifier
예제 #2
0
class ConnectionManager(QtCore.QObject):

    signal_reload = QtCore.Signal(int, name="reload_wps")
    signal_message_receive = QtCore.Signal(str, name="message rcv")
    signal_message_reply_receive = QtCore.Signal(str, name="message reply")
    signal_message_edited = QtCore.Signal(str, name="message editted")
    signal_message_deleted = QtCore.Signal(str, name="message deleted")
    signal_new_permission = QtCore.Signal(int, int, name="new permission")
    signal_update_permission = QtCore.Signal(int,
                                             int,
                                             str,
                                             name="update permission")
    signal_revoke_permission = QtCore.Signal(int,
                                             int,
                                             name="revoke permission")
    signal_project_permissions_updated = QtCore.Signal(
        int, name="project permissions updated")
    signal_project_deleted = QtCore.Signal(int, name="project deleted")

    def __init__(self,
                 token,
                 user,
                 mscolab_server_url=mss_default.mscolab_server_url):
        super(ConnectionManager, self).__init__()
        self.token = token
        self.user = user
        self.mscolab_server_url = mscolab_server_url
        self.sio = socketio.Client(reconnection_attempts=5)
        self.sio.connect(self.mscolab_server_url)

        self.sio.on('file-changed', handler=self.handle_file_change)
        # on chat message recive
        self.sio.on('chat-message-client',
                    handler=self.handle_incoming_message)
        self.sio.on('chat-message-reply-client',
                    handler=self.handle_incoming_message_reply)
        # on message edit
        self.sio.on('edit-message-client', handler=self.handle_message_edited)
        # on message delete
        self.sio.on('delete-message-client',
                    handler=self.handle_message_deleted)
        # on new permission
        self.sio.on('new-permission', handler=self.handle_new_permission)
        # on update of permission
        self.sio.on('update-permission', handler=self.handle_update_permission)
        # on revoking project permission
        self.sio.on('revoke-permission', handler=self.handle_revoke_permission)
        # on updating project permissions in admin window
        self.sio.on('project-permissions-updated',
                    handler=self.handle_project_permissions_updated)
        # On Project Delete
        self.sio.on('project-deleted', handler=self.handle_project_deleted)

        self.sio.emit('start', {'token': token})

    def handle_update_permission(self, message):
        """
        signal update of permission affected
        """
        message = json.loads(message)
        p_id = int(message["p_id"])
        u_id = int(message["u_id"])
        access_level = message["access_level"]
        self.signal_update_permission.emit(p_id, u_id, access_level)

    def handle_new_permission(self, message):
        """
        signal updating of newly added permission
        """
        message = json.loads(message)
        p_id = int(message["p_id"])
        u_id = int(message["u_id"])
        self.signal_new_permission.emit(p_id, u_id)

    def handle_revoke_permission(self, message):
        """
        Signal update of revoked permission
        """
        message = json.loads(message)
        p_id = int(message["p_id"])
        u_id = int(message["u_id"])
        self.signal_revoke_permission.emit(p_id, u_id)

    def handle_project_permissions_updated(self, message):
        message = json.loads(message)
        u_id = int(message["u_id"])
        self.signal_project_permissions_updated.emit(u_id)

    def handle_incoming_message(self, message):
        # raise signal to render to view
        logging.debug(message)
        # emit signal
        self.signal_message_receive.emit(message)

    def handle_incoming_message_reply(self, message):
        self.signal_message_reply_receive.emit(message)

    def handle_message_edited(self, message):
        self.signal_message_edited.emit(message)

    def handle_message_deleted(self, message):
        self.signal_message_deleted.emit(message)

    def handle_file_change(self, message):
        message = json.loads(message)
        self.signal_reload.emit(message["p_id"])

    def handle_project_deleted(self, message):
        p_id = int(json.loads(message)["p_id"])
        self.signal_project_deleted.emit(p_id)

    def handle_new_room(self, p_id):
        logging.debug("adding user to new room")
        self.sio.emit('add-user-to-room', {"p_id": p_id, "token": self.token})

    def send_message(self, message_text, p_id, reply_id):
        logging.debug("sending message")
        self.sio.emit(
            'chat-message', {
                "p_id": p_id,
                "token": self.token,
                "message_text": message_text,
                "reply_id": reply_id
            })

    def edit_message(self, message_id, new_message_text, p_id):
        self.sio.emit(
            'edit-message', {
                "message_id": message_id,
                "new_message_text": new_message_text,
                "p_id": p_id,
                "token": self.token
            })

    def delete_message(self, message_id, p_id):
        self.sio.emit('delete-message', {
            'message_id': message_id,
            'p_id': p_id,
            'token': self.token
        })

    def save_file(self, token, p_id, content, comment=None):
        logging.debug("saving file")
        self.sio.emit(
            'file-save', {
                "p_id": p_id,
                "token": self.token,
                "content": content,
                "comment": comment
            })

    def disconnect(self):
        self.sio.disconnect()
예제 #3
0
class VPathInteractor(PathInteractor):
    """Subclass of PathInteractor that implements an interactively editable
       vertical profile of the flight track.
    """
    signal_get_vsec = QtCore.Signal(name="get_vsec")

    def __init__(self, ax, waypoints, redraw_xaxis=None, clear_figure=None, numintpoints=101):
        """Constructor passes a PathV instance its parent.

        Arguments:
        ax -- matplotlib.Axes object into which the path should be drawn.
        waypoints -- flighttrack.WaypointsModel instance.
        numintpoints -- number of intermediate interpolation points. The entire
                        flight track will be interpolated to this number of
                        points.
        redrawXAxis -- callback function to redraw the x-axis on path changes.
        """
        self.numintpoints = numintpoints
        self.redraw_xaxis = redraw_xaxis
        self.clear_figure = clear_figure
        super(VPathInteractor, self).__init__(
            ax=ax, waypoints=waypoints, mplpath=PathV([[0, 0]], numintpoints=numintpoints))

    def get_num_interpolation_points(self):
        return self.numintpoints

    def redraw_figure(self):
        """For the side view, changes in the horizontal position of a waypoint
           (including moved waypoints, new or deleted waypoints) make a complete
           redraw of the figure necessary.

           Calls the callback function 'redrawXAxis()'.
        """
        self.redraw_path()
        # emit signal to redraw map
        self.signal_get_vsec.emit()
        if self.clear_figure() is not None:
            self.clear_figure()

        if self.redraw_xaxis is not None:
            self.redraw_xaxis(self.path.ilats, self.path.ilons, self.path.itimes)
        self.ax.figure.canvas.draw()

    def button_release_delete_callback(self, event):
        """Called whenever a mouse button is released.
        """
        if not self.showverts or event.button != 1:
            return

        if self._ind is not None:
            if self.confirm_delete_waypoint(self._ind):
                # removeRows() will trigger a signal that will redraw the path.
                self.waypoints_model.removeRows(self._ind)
            self._ind = None

    def button_release_insert_callback(self, event):
        """Called whenever a mouse button is released.

        From the click event's coordinates, best_index is calculated as
        the index of a vertex whose x coordinate > clicked x coordinate.
        This is the position where the waypoint is to be inserted.

        'lat' and 'lon' are calculated as an average of each of the first waypoint
        in left and right neighbourhood of inserted waypoint.

        The coordinates are checked against "locations" defined in mss' config.

        A new waypoint with the coordinates, and name is inserted into the waypoints_model.
        """
        if not self.showverts or event.button != 1 or event.inaxes is None:
            return
        y = event.ydata
        wpm = self.waypoints_model
        flightlevel = float(pressure2flightlevel(y))
        [lat, lon], best_index = self.get_lat_lon(event)
        loc = find_location(lat, lon)  # skipped tolerance which uses appropriate_epsilon_km
        if loc is not None:
            (lat, lon), location = loc
        else:
            location = ""
        new_wp = ft.Waypoint(lat, lon, flightlevel, location=location)
        wpm.insertRows(best_index, rows=1, waypoints=[new_wp])
        self.redraw_figure()

        self._ind = None

    def get_lat_lon(self, event):
        x = event.xdata
        wpm = self.waypoints_model
        vertices = self.pathpatch.get_path().vertices
        vertices = np.ndarray.tolist(vertices)
        for index, vertex in enumerate(vertices):
            vertices[index].append(datetime.datetime(2012, 7, 1, 10, 30))
        best_index = 1
        # if x axis has increasing coordinates
        if vertices[-1][0] > vertices[0][0]:
            for index, vertex in enumerate(vertices):
                if x >= vertex[0]:
                    best_index = index + 1
        # if x axis has decreasing coordinates
        else:
            for index, vertex in enumerate(vertices):
                if x <= vertex[0]:
                    best_index = index + 1
        # number of subcoordinates is determined by difference in x coordinates
        number_of_intermediate_points = math.floor(vertices[best_index][0] - vertices[best_index - 1][0])
        intermediate_vertices_list = path_points([vertices[best_index - 1], vertices[best_index]],
                                                 number_of_intermediate_points)
        wp1Array = [wpm.waypoint_data(best_index - 1).lat, wpm.waypoint_data(best_index - 1).lon,
                    datetime.datetime(2012, 7, 1, 10, 30)]
        wp2Array = [wpm.waypoint_data(best_index).lat, wpm.waypoint_data(best_index).lon,
                    datetime.datetime(2012, 7, 1, 10, 30)]
        intermediate_waypoints_list = latlon_points(wp1Array, wp2Array,
                                                    number_of_intermediate_points, connection="greatcircle")

        # best_index1 is the best index among the intermediate coordinates to fit the hovered point
        # if x axis has increasing coordinates
        best_index1 = 1
        if vertices[-1][0] > vertices[0][0]:
            for index, vertex in enumerate(intermediate_vertices_list[0]):
                if x >= vertex:
                    best_index1 = index + 1
        # if x axis has decreasing coordinates
        else:
            for index, vertex in enumerate(intermediate_vertices_list[0]):
                if x <= vertex:
                    best_index1 = index + 1
        # depends if best_index1 or best_index1 - 1 on closeness to left or right neighbourhood
        return [intermediate_waypoints_list[0][best_index1 - 1],
                intermediate_waypoints_list[1][best_index1 - 1]], best_index

    def button_release_move_callback(self, event):
        """Called whenever a mouse button is released.
        """
        if not self.showverts or event.button != 1:
            return

        if self._ind is not None:
            # Submit the new pressure (the only value that can be edited
            # in the side view) to the data model.
            vertices = self.pathpatch.get_path().vertices
            pressure = vertices[self._ind][1]
            # http://doc.trolltech.com/4.3/qabstractitemmodel.html#createIndex
            qt_index = self.waypoints_model.createIndex(self._ind, ft.PRESSURE)
            # NOTE: QVariant cannot handle numpy.float64 types, hence convert
            # to float().
            self.waypoints_model.setData(qt_index, QtCore.QVariant(float(pressure / 100.)))

        self._ind = None

    def motion_notify_callback(self, event):
        """Called on mouse movement. Redraws the path if a vertex has been
           picked and is being dragged.

        In the side view, the horizontal position of a waypoint is locked.
        Hence, points can only be moved in the vertical direction (y position
        in this view).
        """
        if not self.showverts or self._ind is None or event.inaxes is None or event.button != 1:
            return
        vertices = self.pathpatch.get_path().vertices
        # Set the new y position of the vertex to event.ydata. Keep the
        # x coordinate.
        vertices[self._ind] = vertices[self._ind][0], event.ydata
        self.redraw_path(vertices)

    def qt_data_changed_listener(self, index1, index2):
        """Listens to dataChanged() signals emitted by the flight track
           data model. The side view can thus react to data changes
           induced by another view (table, top view).
        """
        # If the altitude of a point has changed, only the plotted flight
        # profile needs to be redrawn (redraw_path()). If the horizontal
        # position of a waypoint has changed, the entire figure needs to be
        # redrawn, as this affects the x-position of all points.
        self.pathpatch.get_path().update_from_WaypointsTableModel(self.waypoints_model)
        if index1.column() in [ft.FLIGHTLEVEL, ft.PRESSURE, ft.LOCATION]:
            self.redraw_path(self.pathpatch.get_path().vertices)
        elif index1.column() in [ft.LAT, ft.LON]:
            self.redraw_figure()
        elif index1.column() in [ft.TIME_UTC]:
            if self.redraw_xaxis is not None:
                self.redraw_xaxis(self.path.ilats, self.path.ilons, self.path.itimes)