Skip to content

Commit

Permalink
Merge pull request #757 from iceblu3710/BGImage_Kivy
Browse files Browse the repository at this point in the history
Background image on working canvas
  • Loading branch information
MaslowCommunityGardenRobot committed Aug 21, 2018
2 parents 7841f94 + fc7ac95 commit 486b6f0
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 7 deletions.
6 changes: 6 additions & 0 deletions DataStructures/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class Data(EventDispatcher):
gcodeShift = ObjectProperty([0.0,0.0]) #the amount that the gcode has been shifted
logger = Logger() #the module which records the machines behavior to review later

# Background image stuff, persist but not saved
backgroundFile = None
backgroundTexture = None
backgroundManualReg = []
backgroundRedraw = BooleanProperty(False)

'''
Flags
'''
Expand Down
17 changes: 17 additions & 0 deletions Settings/maslowSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,23 @@
"key": "distPerRotRightChainTolerance",
"firmwareKey": 41
}
],
"Background Settings":
[
{
"type": "string",
"title": "Background File or Directory",
"desc": "Current background file",
"key": "backgroundFile",
"default": ""
},
{
"type": "list",
"title": "Manual Registration",
"desc": "Relative corner coords for image correction",
"key": "manualReg",
"default": []
},
]
}

Expand Down
140 changes: 140 additions & 0 deletions UIElements/backgroundMenu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import os
from kivy.uix.gridlayout import GridLayout
from UIElements.fileBrowser import FileBrowser
from kivy.uix.popup import Popup
from DataStructures.makesmithInitFuncs import MakesmithInitFuncs
from UIElements.backgroundPickDlg import BackgroundPickDlg
from kivy.core.image import Image as CoreImage
from PIL import Image as PILImage
from io import BytesIO
import json

graphicsExtensions = (".jpg", ".png", ".jp2",".webp",".pbm",".ppm",".pgm")


class BackgroundMenu(GridLayout, MakesmithInitFuncs):

def __init__(self, data, **kwargs):
super(BackgroundMenu, self).__init__(**kwargs)
self.data = data

def updateAlignmentInConfig(self):
self.data.config.set('Background Settings', 'manualReg',
self.data.backgroundManualReg)
self.data.config.set('Background Settings', 'backgroundfile',
str(self.data.backgroundFile))
self.data.config.write()

def openBackground(self):
'''
Open The Pop-up To Load A File
Creates a new pop-up which can be used to open a file.
'''
# Starting path is either where the last opened file was
# or the users home directory
if not os.path.isdir(self.data.backgroundFile):
startingPath = os.path.dirname(self.data.backgroundFile)
else:
# Don't go up a dir if the "backgroundFile" is a directory!
startingPath = self.data.backgroundFile
if startingPath is "":
startingPath = os.path.expanduser('~')
# We want to filter to show only files that ground control can open
validFileTypes = graphicsExtensions
validFileTypes = ['*{0}'.format(fileType) for fileType in validFileTypes]

content = FileBrowser(select_string='Select',
favorites=[(startingPath, 'Last Location')],
path=startingPath,
filters=validFileTypes, dirselect=False)
content.bind(on_success=self.load, on_canceled=self.dismiss_popup,
on_submit=self.load)
self._popup = Popup(title="Select a file...", content=content,
size_hint=(0.9, 0.9))
self._popup.open()

def reloadBackground(self):
self.processBackground()
self.close()

def processBackground(self):
if self.data.backgroundFile == "" or os.path.isdir(
self.data.backgroundFile):
self.data.backgroundTexture = None
self.data.backgroundManualReg = []
self.updateAlignmentInConfig()
self.data.backgroundRedraw = True
return
else:
img = PILImage.open(self.data.backgroundFile)
img.thumbnail((1920, 1080), PILImage.ANTIALIAS)
img = img.transpose(PILImage.FLIP_TOP_BOTTOM)
imgBytes = BytesIO()
img.save(imgBytes, format="png")
imgBytes.seek(0)
texture = CoreImage(imgBytes, ext="png").texture
self.data.backgroundTexture = texture
if self.data.backgroundManualReg:
# Already have centers to use; just go on to warp_image
self.warp_image()
else:
# Start the manual alignment process
self.realignBackground()

def realignBackground(self):
content = BackgroundPickDlg(self.data)
content.setUpData(self.data)
content.close = self.close_PickDlg
self._popup = Popup(title="Background PointPicker", content=content,
size_hint=(0.9, 0.9))
self._popup.open()

def close_PickDlg(self, instance):
if instance.accepted:
# Update manual image registration marks
self.data.backgroundManualReg = instance.tex_coords
# Save the data from the popup
self.updateAlignmentInConfig()
self.warp_image()
self.dismiss_popup()
self.close()

def warp_image(self):
self.data.backgroundRedraw = True

def clear_background(self):
'''
Clear background
'''
self.data.backgroundFile = ""
self.processBackground()
self.close()

def load(self, instance):
'''
Load A Background Image File from the file dialog
Takes in a file path (from the pop-up filepicker
or directory, if no file was picked) and processes it.
'''
if len(instance.selection) == 1:
filename = instance.selection[0]
else:
# User pressed Submit without picking a file
filename = instance.path
# Save the file in the config...
self.data.backgroundFile = filename
# close the open file popup
self.dismiss_popup()
# new image loaded so clear manual alignment centers
self.data.backgroundManualReg = []
# process it
self.processBackground()
# Close the menu, going back to the main page.
self.close()

def dismiss_popup(self, *args):
'''
Close The File Picker (cancel was pressed instead of OK).
'''
self._popup.dismiss()
pass
88 changes: 88 additions & 0 deletions UIElements/backgroundPickDlg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from kivy.uix.gridlayout import GridLayout
from kivy.properties import StringProperty, ListProperty, ObjectProperty
from DataStructures.makesmithInitFuncs import MakesmithInitFuncs
from kivy.vector import Vector


class BackgroundPickDlg(GridLayout, MakesmithInitFuncs):
instructionText = StringProperty("Drag bounding box to frame edges")
texture = ObjectProperty(None)
tex_coords = ListProperty([0, 0, 1, 0, 1, 1, 0, 1])
tex_selection = ListProperty([0, 0, 100, 0, 100, 100, 0, 100])

def __init__(self, data, **kwargs):
super(BackgroundPickDlg, self).__init__(**kwargs)
self.data = data
# Load the texture
self.texture = self.data.backgroundTexture
# Window is not set up yet, wait for size.update
self.imWidg.bind(size=self.update)
self.update()

def update(self, *args):
if self.imWidg.size[0] is not 100 and self.imWidg.size[1] is not 100:
# Widget is stable, update the textures bounds
self.w, self.h = self.imWidg.size
self.coeff_size = [self.w, self.h, self.w, self.h,
self.w, self.h, self.w, self.h]
# Place selection box
self.tex_selection = [self.w * 0.2, self.h * 0.3,
self.w * 0.9, self.h * 0.3,
self.w * 0.9, self.h * 0.9,
self.w * 0.2, self.h * 0.9]
# Why??!!...
self.resize_texture()
self.reset_image()
else:
pass # Wait for widget to resize properly

def reset_image(self):
self.tex_coords = [0, 0, 1, 0, 1, 1, 0, 1]

def resize_texture(self):
coeffs = []
padOffx, padOffy = self.imWidg.pos
i = 0
for (p, s) in zip(self.coeff_size, self.tex_selection):
if i % 2: # y coord
if p is not 0:
coeffs.append(float(s - padOffy) / float(p))
else:
coeffs.append(0.0 - padOffy)
else: # x coord
if p is not 0:
coeffs.append(float(s - padOffx) / float(p))
else:
coeffs.append(0.0 - padOffx)
i += 1
self.tex_coords = coeffs

def accept_texture(self):
self.accepted = True
self.close(self)

def on_touch_down(self, touch):
for i in range(4):
x, y = self.tex_selection[i*2:i*2+2]
if Vector(x - touch.x, y - touch.y).length() < 10:
touch.grab(self)
touch.ud['tex'] = i
return True

return super(BackgroundPickDlg, self).on_touch_down(touch)

def on_touch_move(self, touch):
if touch.grab_current is not self:
return super(BackgroundPickDlg, self).on_touch_move(touch)

tex = touch.ud.get('tex')

if tex is not None:
self.tex_selection[tex * 2] = touch.x
self.tex_selection[tex * 2 + 1] = touch.y

def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
else:
return super(BackgroundPickDlg, self).on_touch_up(touch)
12 changes: 11 additions & 1 deletion UIElements/gcodeCanvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from kivy.core.window import Window
from UIElements.modernMenu import ModernMenu
from kivy.metrics import dp
from kivy.graphics.texture import Texture
from kivy.graphics import Rectangle

import re
import math
Expand Down Expand Up @@ -53,7 +55,8 @@ def initialize(self):
Window.bind(on_resize = self.centerCanvas)

self.data.bind(gcode = self.updateGcode)
self.data.bind(gcodeShift = self.reloadGcode)
self.data.bind(backgroundRedraw = self.updateGcode)
self.data.bind(gcodeShift = self.reloadGcode) #No need to reload if the origin is changed, just clear and redraw
self.data.bind(gcodeFile = self.centerCanvasAndReloadGcode)

global_variables._keyboard = Window.request_keyboard(self._keyboard_closed, self)
Expand Down Expand Up @@ -215,6 +218,12 @@ def drawWorkspace(self, *args):
Line(points = (-width/2,0,width/2,0), dash_offset = 5, group='workspace')
Line(points = (0, -height/2,0,height/2), dash_offset = 5, group='workspace')

texture = self.data.backgroundTexture
if texture is not None:
Rectangle(texture=texture, pos=(-width/2, -height/2),
size=(width, height),
tex_coords=self.data.backgroundManualReg)

def drawLine(self,gCodeLine,command):
'''
Expand Down Expand Up @@ -565,6 +574,7 @@ def updateGcode(self, *args):
'''

#reset variables
self.data.backgroundRedraw = False
self.xPosition = self.data.gcodeShift[0]*self.canvasScaleFactor
self.yPosition = self.data.gcodeShift[1]*self.canvasScaleFactor
self.zPosition = 0
Expand Down
24 changes: 19 additions & 5 deletions UIElements/screenControls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from DataStructures.makesmithInitFuncs import MakesmithInitFuncs
from UIElements.buttonTemplate import ButtonTemplate
from kivy.app import App
from UIElements.backgroundMenu import BackgroundMenu


class ScreenControls(FloatLayout, MakesmithInitFuncs):
Expand All @@ -15,10 +16,12 @@ def setButtonAppearance(self):
Called on creation to set up links to button background textures
'''
self.actionsBtn.btnBackground = self.data.iconPath + 'Generic.png'
self.actionsBtn.textColor = self.data.fontColor
self.settingsBtn.btnBackground = self.data.iconPath + 'Generic.png'
self.settingsBtn.textColor = self.data.fontColor
self.actionsBtn.btnBackground = self.data.iconPath + 'Generic.png'
self.actionsBtn.textColor = self.data.fontColor
self.settingsBtn.btnBackground = self.data.iconPath + 'Generic.png'
self.settingsBtn.textColor = self.data.fontColor
self.backgroundBtn.btnBackground = self.data.iconPath + 'Generic.png'
self.backgroundBtn.textColor = self.data.fontColor

def openSettings(self):
'''
Expand Down Expand Up @@ -52,4 +55,15 @@ def close_actions(self):
'''
Close pop-up
'''
self._popup.dismiss()
self._popup.dismiss()

def open_background(self):
'''
Open A Pop-up To Manage the Canvas Background
'''
content = BackgroundMenu(self.data)
content.setUpData(self.data)
content.close = self.close_actions
self._popup = Popup(title="Background Picture", content=content,
size_hint=(0.5, 0.5))
self._popup.open()
Loading

0 comments on commit 486b6f0

Please sign in to comment.