Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImageWidget in Roam API (with solution suggestion attached) #468

Open
HeatherHillers opened this issue Aug 3, 2020 · 0 comments
Open

ImageWidget in Roam API (with solution suggestion attached) #468

HeatherHillers opened this issue Aug 3, 2020 · 0 comments

Comments

@HeatherHillers
Copy link
Contributor

We do a lot of custom plugins and forms with roam. The user wants foto loading in our plugins/forms, and we want to use the exceptionally well made ImageWidget available for automatically generated featureforms. Unfortunately, it is not available in the api. It would be nice if it was. Especially since all the internal roam code will change with Roam 3.

I have made a wrapper for the roam ImageWidget that I have been able to use in my plugin, which I am attaching. I hope it will be helpful. If you do not want to incorporate the idea, maybe it has a place in the documentation. I took the code from the Roam 2.7.2 hotfix branch. I pulled the code that featureform.py uses to handle the image widget into a class.

The wrapper assumes that the widget is being added to a QStackedWidget, where the first page of the widget is taken (by the form or plugin layout). The parent widget is the widget containing the QStackedWidget, which is adding the image widget to itself.

The wrapper's implementation looks like this:

from ev_roam_image_widget_wrapper import ImageWidgetWrapper

image_filename = "my_default_image.jpg" # the original/default/saved filename
image_folder = "c:\my_path_to\the\image\directory" # might be roam's, or our own
parent = self # i am the form widget, and I will add the image widget to myself

def setup_image_widget(self):
   # create image widget and add it to my form
   self.image_wrapper = ImageWidgetWrapper(self.canvas,
                                          self.stackedWidget,
                                          image_filename,
                                          image_folder,
                                          parent)
   self.image_widget = self.image_wrapper.qwidget()
   row, column = (9, 3) 
   self.form_widget.layout().addWidget(self.image_widget, row, column) # add the image widget 
                                           to the right place in my form

def update_image_value(self):
        # get the current filename.  if the loaded image has changed, it will 
        # be saved to the disk at this point
        filename = self.image_wrapper.get_saved_image_value()  

        bk_util.set_value("foto", filename) # save the filename in our way 

The wrapper looks like this:

# wraps the roam image widget and its interactions with a stacked widget
# so that the widget can be used in the context of custom forms and plugins

import os
import inspect
import functools
from PyQt4.QtCore import pyqtSignal, QVariant
from PyQt4.QtGui import QPixmap
from roam.api import RoamEvents
from roam.api import utils as qgisutils
import roam

from roam.editorwidgets.imagewidget import ImageWidget
from roam.editorwidgets.uifiles.imagewidget import QMapImageWidget

FeatureSaveException = qgisutils.FeatureSaveException



class ImageWidgetWrapper(object):
    form_closed = pyqtSignal(int)

    def __init__(self, canvas, stack_widget, image_filename, image_folder, parent_widget=None):
        """
        :param canvas: the roam canvas object
        :param stack_widget: the QStackedWidget that the large widgets will be appended to
        :param image_filename:
        :param image_folder:
        :param parent_widget:
        """
        self.canvas = canvas
        self.stackedWidget = stack_widget
        self.widgetstack = []
        self.image_folder = image_folder
        self.image_filename = image_filename
        self.image_path = os.path.join(self.image_folder, self.image_filename)
        self.image_widget = QMapImageWidget(parent=parent_widget)
        self.image_wrapper = None

        layer = None
        label = None
        # The field is just used for information, not for saving data, so we just send in a dummy
        field = FakeField("foto")
        self.image_object = ImageWidget(self.image_widget, layer, label, field)
        self.image_object.initWidget(self.image_widget)
        self.image_object.largewidgetrequest.connect(self.add_widget)
        pic = QPixmap(self.image_path)
        self.image_widget.loadImage(pic, fromfile=True)


    def qwidget(self):
        # this returns the initialised QMapImageWidget object that can be added into a form
        return self.image_widget


    def add_widget(self, widgettype, lastvalue, callback, config, initconfig=None, cancel_callback=None):
        # from dataentrywidget, which recieves the featureform show_widget signal that
        # is emitted on reciept of largewidgetrequest from the editorwidget
        if not initconfig:
            initconfig = {}

        if inspect.isclass(widgettype):
            widget = widgettype.createwidget(config=initconfig)
            largewidgetwrapper = widgettype.for_widget(widget, None, None, None, None, map=self.canvas)
        else:
            # If the user passes in a instance we can just use that and trust they have set it up right.
            largewidgetwrapper = widgettype
            widget = largewidgetwrapper.widget

        largewidgetwrapper.finished.connect(callback)
        largewidgetwrapper.initWidget(widget)
        largewidgetwrapper.config = config

        if isinstance(lastvalue, QPixmap):
            lastvalue = QPixmap(lastvalue)

        largewidgetwrapper.setvalue(lastvalue)

        largewidgetwrapper.largewidgetrequest.connect(RoamEvents.show_widget.emit)
        largewidgetwrapper.finished.connect(functools.partial(self.cleanup, largewidgetwrapper))
        largewidgetwrapper.cancel.connect(functools.partial(self.cleanup, largewidgetwrapper))
        if cancel_callback:
            largewidgetwrapper.cancel.connect(cancel_callback)

        try:
            largewidgetwrapper.before_load()
            position = self.stackedWidget.addWidget(widget)
            self.stackedWidget.setCurrentIndex(position)
            self.widgetstack.append(largewidgetwrapper)
            largewidgetwrapper.after_load()
        except roam.editorwidgets.core.exceptions.RejectedException as rejected:
            RoamEvents.raisemessage("Program Error", rejected.message)
            self.cleanup(largewidgetwrapper)

    def cleanup(self, wrapper, *args):
        # Pop the widget off the current widget stack and kill it.
        try:
            index = self.widgetstack.index(wrapper)
            del self.widgetstack[index]
        except ValueError:
            pass
        index = self.stackedWidget.indexOf(wrapper.widget)
        if index == -1:
            return
        self.clearwidget(index)
        wrapper.deleteLater()
        wrapper.setParent(None)

    def clearwidget(self, position=0):
        widget = self.stackedWidget.widget(position)
        self.stackedWidget.removeWidget(widget)
        if widget:
            widget.deleteLater()
            widget.setParent(None)
        self.stackedWidget.setCurrentIndex(0)

    def get_saved_image_value(self):
        widget = self.image_object
        if widget.modified:
            self.image_filename = widget.get_filename()
            saved = widget.save(self.image_folder, self.image_filename)
            if not saved:
                raise FeatureSaveException("Image Error",
                                           "Could not save image to {}".format(self.image_folder))
        return self.image_filename


class FakeField(object):
    def __init__(self, name):
        self.field_name = name

    def name(self):
        return self.field_name
    def type(self):
        return QVariant.String
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant