Explorar o código

Milestone: Application works normally with base layer,
while using a layer class. Application is ready for new layers.

Billy Barrow %!s(int64=8) %!d(string=hai) anos
pai
achega
481de6cbc5
Modificáronse 3 ficheiros con 357 adicións e 77 borrados
  1. 101 0
      PF2/Layer.py
  2. 99 69
      PF2/__init__.py
  3. 157 8
      ui/PF2_Activity.glade

+ 101 - 0
PF2/Layer.py

@@ -0,0 +1,101 @@
+from gi.repository import Gtk, GLib
+
+class Layer:
+    def __init__(self, base, name, on_change):
+        self.mask = []
+        self.tools = []
+        self.tool_map = {}
+        self.name = name
+        self.enabled = True
+        self.selected_tool = 0
+        self.editable = not base
+        self.tool_box = Gtk.FlowBox()
+        self.tool_stack = Gtk.Stack()
+
+        self.layer_changed = on_change
+
+
+    def add_tool(self, tool):
+        self.tool_box.add(tool.tool_button)
+        self.tool_stack.add(tool.widget)
+        self.tool_map[tool.tool_button] = tool
+
+        tool.tool_button.connect("clicked", self.on_tool_button_clicked)
+        tool.connect_on_change(self.on_tool_change)
+
+        self.tools += [tool,]
+
+
+    def on_tool_change(self, tool, prop):
+        self.layer_changed(self)
+
+
+    def on_tool_button_clicked(self, sender):
+        if(sender.get_active()):
+            self.tool_stack.set_visible_child(self.tool_map[sender].widget)
+            for key in self.tool_map:
+                if(key != sender):
+                    key.set_active(False)
+
+        elif(self.tool_stack.get_visible_child() == self.tool_map[sender].widget):
+            sender.set_active(True)
+
+    def get_layer_dict(self):
+        layerDict = {}
+
+        # Get tool values
+        toolDict = {}
+        for tool in self.tools:
+            toolDict[tool.id] = tool.get_properties_as_dict()
+
+        layerDict["tools"] = toolDict
+        layerDict["name"] = self.name
+        layerDict["mask"] = self.mask
+        layerDict["enabled"] = self.enabled
+
+        return layerDict
+
+    def set_from_layer_dict(self, dict):
+        # Load Tool Data
+        for tool in self.tools:
+            if(tool.id in dict["tools"]):
+                GLib.idle_add(tool.load_properties, dict["tools"][tool.id])
+
+        # The base layer only has tools.
+        if(self.editable):
+            # Load Mask Vectors
+            self.mask = dict["mask"]
+
+            # Load Layer Name
+            self.name = dict["name"]
+
+            # Load Enabled State
+            self.enabled = dict["enabled"]
+
+
+    def render_layer(self, baseImage, image, callback=None):
+        # We are passed a base image (original)
+        # Make a copy of this to pass through the tools
+        layer = baseImage.copy()
+
+        # Base layer needs no mask processing
+        if(not self.editable):
+            ntools = len(self.tools)
+            count = 1
+            for tool in self.tools:
+                if (callback != None):
+                    # For showing progress
+                    callback(tool.name, ntools, count - 1)
+
+                # Call the tool's image processing function
+                layer = tool.on_update(layer)
+                count += 1
+
+        ## Here we would blend with the mask
+        image = layer
+
+        return image
+
+
+
+

+ 99 - 69
PF2/__init__.py

@@ -9,7 +9,7 @@ import numpy
 import getpass
 import os
 
-from PF2 import Histogram
+from PF2 import Histogram, Layer
 from PF2.Tools import BlackWhite
 from PF2.Tools import Colours
 from PF2.Tools import Contrast
@@ -49,7 +49,7 @@ class PF2(Activity.Activity):
             "scroll_window",
             "open_button",
             "original_toggle",
-            "tools",
+            "tool_box_stack",
             "tool_stack",
             "open_window",
             "open_header",
@@ -64,7 +64,11 @@ class PF2(Activity.Activity):
             "zoom_toggle",
             "zoom_reveal",
             "zoom",
-            "reset"
+            "reset",
+            "mask_draw_toggle",
+            "mask_erase_toggle",
+            "mask_brush_size",
+            "layers_list",
         ]
 
         for component in components:
@@ -74,26 +78,22 @@ class PF2(Activity.Activity):
 
         # Set up tools
         self.tools = [
-            Contrast.Contrast(),
-            Tonemap.Tonemap(),
-            Details.Details(),
-            Colours.Colours(),
-            HueEqualiser.HueEqualiser(),
-            BlackWhite.BlackWhite(),
+            Contrast.Contrast,
+            Tonemap.Tonemap,
+            Details.Details,
+            Colours.Colours,
+            HueEqualiser.HueEqualiser,
+            BlackWhite.BlackWhite
         ]
 
-        self.tool_map = {}
 
-        for tool in self.tools:
-            self.ui["tools"].add(tool.tool_button)
-            self.ui["tool_stack"].add(tool.widget)
-            self.tool_map[tool.tool_button] = tool
-            tool.tool_button.connect("clicked", self.on_tool_button_clicked)
-            tool.connect_on_change(self.on_tool_change)
+        # Setup layers
+        self.layers = []
+        self.base_layer = self.create_layer("base", True)
 
         # Set the first tool to active
-        self.tools[0].tool_button.set_active(True)
-        self.ui["tools"].show_all()
+        # self.tools[0].tool_button.set_active(True)
+        # self.ui["tools"].show_all()
 
         # Disable ui components by default
         self.ui["original_toggle"].set_sensitive(False)
@@ -143,21 +143,10 @@ class PF2(Activity.Activity):
         self.ui["redo"].connect("clicked", self.on_redo)
         self.ui["reset"].connect("clicked", self.on_reset)
 
-        self.ui["preview_eventbox"].connect('motion-notify-event', self.foo)
-
-    def foo(self, widget, event):
-        print(event.x, event.y)
-
-
-    def on_tool_button_clicked(self, sender):
-        if(sender.get_active()):
-            self.ui["tool_stack"].set_visible_child(self.tool_map[sender].widget)
-            for key in self.tool_map:
-                if(key != sender):
-                    key.set_active(False)
+        self.ui["preview_eventbox"].connect('motion-notify-event', self.preview_dragged)
+        self.ui["mask_draw_toggle"].connect("toggled", self.mask_draw_toggled)
+        self.ui["mask_erase_toggle"].connect("toggled", self.mask_erase_toggled)
 
-        elif(self.ui["tool_stack"].get_visible_child() == self.tool_map[sender].widget):
-            sender.set_active(True)
 
 
     def on_open_clicked(self, sender):
@@ -271,7 +260,7 @@ class PF2(Activity.Activity):
             self.ui["original_toggle"].set_active(False)
 
 
-    def on_tool_change(self, tool, property):
+    def on_layer_change(self, layer):
         if(self.has_loaded):
             if(not self.change_occurred):
                 self.change_occurred = True
@@ -426,14 +415,13 @@ class PF2(Activity.Activity):
     def run_stack(self, image, callback=None):
         if(not self.running_stack):
             self.running_stack = True
-            ntools = len(self.tools)
-            count = 1
-            for tool in self.tools:
-                if(callback != None):
-                    # For showing progress
-                    callback(tool.name, ntools, count-1)
-                image = tool.on_update(image)
-                count += 1
+
+            baseImage = image.copy()
+
+            for layer in self.layers:
+                print(layer)
+                image = layer.render_layer(baseImage, image, callback)
+
             self.running_stack = False
             return image
 
@@ -484,26 +472,22 @@ class PF2(Activity.Activity):
         print(path)
         f = open(path, "w")
 
-        toolDict = {}
-        for tool in self.tools:
-            toolDict[tool.id] = tool.get_properties_as_dict()
+        layerDict = {}
+        for layer in self.layers:
+            layerDict[layer.name] = layer.get_layer_dict()
 
         if(not self.undoing) and (self.has_loaded):
             if(len(self.undo_stack)-1 != self.undo_position):
                 self.undo_stack = self.undo_stack[:self.undo_position+1]
-            if(self.undo_stack[self.undo_position] != toolDict):
-                self.undo_stack += [toolDict,]
+            if(self.undo_stack[self.undo_position] != layerDict):
+                self.undo_stack += [layerDict,]
                 self.undo_position = len(self.undo_stack)-1
                 GLib.idle_add(self.update_undo_state)
 
         data = {
             "path":self.image_path,
             "format-revision":1,
-            "layers": {
-                "base":{
-                    "tools": toolDict
-                }
-            }
+            "layers": layerDict
         }
 
         f.write(str(data))
@@ -515,29 +499,34 @@ class PF2(Activity.Activity):
         if(os.path.exists(path)):
             f = open(path, 'r')
             sdata = f.read()
-            try:
+            if(True):
+            #try:
                 data = ast.literal_eval(sdata)
                 if(data["format-revision"] == 1):
-                    for tool in self.tools:
-                        if(tool.id in data["layers"]["base"]["tools"]):
-                            GLib.idle_add(tool.load_properties,data["layers"]["base"]["tools"][tool.id])
-
-                    self.undo_stack = [data["layers"]["base"]["tools"],]
+                    for layer in data["layers"]:
+                        if(layer == "base"):
+                            self.base_layer.set_from_layer_dict(data["layers"][layer])
+                        else:
+                            ilayer = self.create_layer(layer, False)
+                            ilayer.set_from_layer_dict(data["layers"][layer])
+
+                    self.undo_stack = [data["layers"],]
                     self.undo_position = 0
                     loadDefaults = False
-            except:
-                GLib.idle_add(self.show_message,"Unable to load previous edits…",
-                                                "The edit file for this photo is corrupted and could not be loaded.")
+            #except:
+            #    GLib.idle_add(self.show_message,"Unable to load previous edits…",
+            #                                    "The edit file for this photo is corrupted and could not be loaded.")
 
         if(loadDefaults):
-            for tool in self.tools:
-                GLib.idle_add(tool.reset)
+            for layer in self.layers:
+                for tool in layer.tools:
+                    GLib.idle_add(tool.reset)
 
-            toolDict = {}
-            for tool in self.tools:
-                toolDict[tool.id] = tool.get_properties_as_dict()
+            layerDict = {}
+            for layer in self.layers:
+                layerDict[layer.name] = layer.get_layer_dict()
 
-            self.undo_stack = [toolDict, ]
+            self.undo_stack = [layerDict, ]
             self.undo_position = 0
 
         GLib.idle_add(self.update_undo_state)
@@ -547,9 +536,13 @@ class PF2(Activity.Activity):
 
     def update_from_undo_stack(self, data):
         self.undoing = True
-        for tool in self.tools:
-            if (tool.id in data):
-                tool.load_properties(data[tool.id])
+        for layer in data:
+            if (layer == "base"):
+                self.base_layer.set_from_layer_dict(data[layer])
+            else:
+                ilayer = self.create_layer(layer, False)
+                ilayer.set_from_layer_dict(data[layer])
+
 
     def get_export_image(self, w, h):
         GLib.idle_add(self.on_export_started)
@@ -567,7 +560,44 @@ class PF2(Activity.Activity):
 
 
 
+    ## Layers Stuff ##
+
+    def preview_dragged(self, widget, event):
+        print(event.x, event.y)
+
+    def mask_draw_toggled(self, widget):
+        if(widget.get_active()):
+            self.ui["mask_erase_toggle"].set_active(False)
+
+    def mask_erase_toggled(self, widget):
+        if(widget.get_active()):
+            self.ui["mask_draw_toggle"].set_active(False)
+
+
+    def create_layer(self, layer_name, is_base):
+        layer = Layer.Layer(is_base, layer_name, self.on_layer_change)
+        for tool in self.tools:
+            tool_instance = tool()
+            layer.add_tool(tool_instance)
+
+        self.layers += [layer,]
+
+        self.create_layer_ui(layer)
+
+        return layer
 
+    def create_layer_ui(self, layer):
+        layer_box = Gtk.HBox()
+        layer_toggle = Gtk.CheckButton()
+        layer_toggle.set_sensitive(not layer.editable)
+        layer_toggle.set_active(layer.enabled)
+        layer_label = Gtk.Label()
+        layer_label.set_label(layer.name)
 
+        layer_box.add(layer_toggle)
+        layer_box.add(layer_label)
 
+        self.ui["layers_list"].add(layer_box)
 
+        self.ui["tool_box_stack"].add(layer.tool_box)
+        self.ui["tool_stack"].add(layer.tool_stack)

+ 157 - 8
ui/PF2_Activity.glade

@@ -94,6 +94,13 @@
       </object>
     </child>
   </object>
+  <object class="GtkAdjustment" id="PF2_mask_brush_size">
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="value">20</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <object class="GtkBox" id="PF2_main">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -308,6 +315,7 @@
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
                         <property name="transition_type">slide-up</property>
+                        <property name="reveal_child">True</property>
                         <child>
                           <object class="GtkBox">
                             <property name="visible">True</property>
@@ -340,7 +348,7 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <child>
-                                      <object class="GtkListBox">
+                                      <object class="GtkListBox" id="PF2_layers_list">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
                                       </object>
@@ -374,6 +382,147 @@
                         <property name="position">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkRevealer" id="PF2_layer_mask_reveal">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="transition_type">slide-up</property>
+                        <property name="reveal_child">True</property>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">&lt;b&gt;Layer Mask&lt;/b&gt;</property>
+                                <property name="use_markup">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkButtonBox">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="margin_left">8</property>
+                                    <property name="margin_right">8</property>
+                                    <property name="margin_top">8</property>
+                                    <property name="margin_bottom">8</property>
+                                    <property name="layout_style">expand</property>
+                                    <child>
+                                      <object class="GtkToggleButton" id="PF2_mask_draw_toggle">
+                                        <property name="label" translatable="yes">Draw</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">True</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkToggleButton" id="PF2_mask_erase_toggle">
+                                        <property name="label" translatable="yes">Erase</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">True</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkBox">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="margin_left">8</property>
+                                    <property name="margin_right">8</property>
+                                    <property name="margin_bottom">8</property>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label" translatable="yes">Brush Size</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkScale">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="hexpand">True</property>
+                                        <property name="adjustment">PF2_mask_brush_size</property>
+                                        <property name="round_digits">1</property>
+                                        <property name="value_pos">right</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkSeparator">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkBox">
                         <property name="visible">True</property>
@@ -395,13 +544,13 @@
                           </packing>
                         </child>
                         <child>
-                          <object class="GtkFlowBox" id="PF2_tools">
+                          <object class="GtkStack" id="PF2_tool_box_stack">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="halign">start</property>
-                            <property name="valign">start</property>
-                            <property name="orientation">vertical</property>
-                            <property name="selection_mode">none</property>
+                            <property name="transition_type">crossfade</property>
+                            <child>
+                              <placeholder/>
+                            </child>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -424,7 +573,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">True</property>
-                        <property name="position">2</property>
+                        <property name="position">3</property>
                       </packing>
                     </child>
                     <child>
@@ -456,7 +605,7 @@
                       <packing>
                         <property name="expand">True</property>
                         <property name="fill">True</property>
-                        <property name="position">3</property>
+                        <property name="position">4</property>
                       </packing>
                     </child>
                   </object>