Merge pull request #280 from GreatFruitOmsk/macosx

Better support for Mac OS X
master
daid 2012-12-06 04:51:36 -08:00
commit c4b3c26be1
24 changed files with 1628 additions and 1411 deletions

3
.gitignore vendored
View File

@ -3,7 +3,7 @@
*.pyc *.pyc
*.zip *.zip
*.exe *.exe
osx64-Cura-* darwin-Cura-*
win32-Cura-* win32-Cura-*
linux-Cura-* linux-Cura-*
Printrun Printrun
@ -18,3 +18,4 @@ printrun.bat
cura.bat cura.bat
object-mirror.png object-mirror.png
object.png object.png
*darwin.dmg

View File

@ -9,12 +9,13 @@ The slicing code is the same as Skeinforge. But the UI has been revamped to be..
""" """
from __future__ import absolute_import from __future__ import absolute_import
import __init__
import sys import sys
import platform import warnings
from optparse import OptionParser from optparse import OptionParser
import wx._core
from util import profile from util import profile
__author__ = 'Daid' __author__ = 'Daid'
@ -36,6 +37,7 @@ Reece.Arnott <http://forums.reprap.org/profile.php?12,152>
Wade <http://forums.reprap.org/profile.php?12,489> Wade <http://forums.reprap.org/profile.php?12,489>
Xsainnz <http://forums.reprap.org/profile.php?12,563> Xsainnz <http://forums.reprap.org/profile.php?12,563>
Zach Hoeken <http://blog.zachhoeken.com/> Zach Hoeken <http://blog.zachhoeken.com/>
Ilya Kulakov (kulakov.ilya@gmail.com)
Organizations: Organizations:
Ultimaker <http://www.ultimaker.com> Ultimaker <http://www.ultimaker.com>
@ -43,45 +45,66 @@ Art of Illusion <http://www.artofillusion.org/>"""
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
class CuraApp(wx.App):
def MacOpenFile(self, path):
try:
pass
except Exception as e:
warnings.warn("File at {p} cannot be read: {e}".format(p=path, e=str(e)))
def main(): def main():
parser = OptionParser(usage="usage: %prog [options] <filename>.stl") parser = OptionParser(usage="usage: %prog [options] <filename>.stl")
parser.add_option("-i", "--ini", action="store", type="string", dest="profileini", help="Load settings from a profile ini file") parser.add_option("-i", "--ini", action="store", type="string", dest="profileini",
parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner", help="Open the project planner") help="Load settings from a profile ini file")
parser.add_option("-F", "--flat", action="store_true", dest="openflatslicer", help="Open the 2D SVG slicer (unfinished)") parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner",
parser.add_option("-r", "--print", action="store", type="string", dest="printfile", help="Open the printing interface, instead of the normal cura interface.") help="Open the project planner")
parser.add_option("-p", "--profile", action="store", type="string", dest="profile", help="Internal option, do not use!") parser.add_option("-F", "--flat", action="store_true", dest="openflatslicer",
parser.add_option("-s", "--slice", action="store_true", dest="slice", help="Slice the given files instead of opening them in Cura") help="Open the 2D SVG slicer (unfinished)")
parser.add_option("-r", "--print", action="store", type="string", dest="printfile",
help="Open the printing interface, instead of the normal cura interface.")
parser.add_option("-p", "--profile", action="store", type="string", dest="profile",
help="Internal option, do not use!")
parser.add_option("-s", "--slice", action="store_true", dest="slice",
help="Slice the given files instead of opening them in Cura")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
if options.profile != None:
if options.profile is not None:
profile.loadGlobalProfileFromString(options.profile) profile.loadGlobalProfileFromString(options.profile)
if options.profileini != None: if options.profileini is not None:
profile.loadGlobalProfile(options.profileini) profile.loadGlobalProfile(options.profileini)
if options.openprojectplanner != None:
if options.openprojectplanner is not None:
from gui import projectPlanner from gui import projectPlanner
projectPlanner.main() projectPlanner.main()
return elif options.openflatslicer is not None:
if options.openflatslicer != None:
from gui import flatSlicerWindow from gui import flatSlicerWindow
flatSlicerWindow.main() flatSlicerWindow.main()
return elif options.printfile is not None:
if options.printfile != None:
from gui import printWindow from gui import printWindow
printWindow.startPrintInterface(options.printfile) printWindow.startPrintInterface(options.printfile)
return elif options.slice is not None:
if options.slice != None:
from util import sliceRun from util import sliceRun
sliceRun.runSlice(args) sliceRun.runSlice(args)
else: else:
if len(args) > 0: if len(args) > 0:
profile.putPreference('lastFile', ';'.join(args)) profile.putPreference('lastFile', ';'.join(args))
from gui import splashScreen
splashScreen.showSplash(mainWindowRunCallback)
def mainWindowRunCallback(splash): from gui import splashScreen
from gui import mainWindow
mainWindow.main(splash) def mainWindowRunCallback(splash):
from gui import mainWindow
if splash is not None:
splash.Show(False)
mainWindow.main()
app = CuraApp(False)
# Apple discurage usage of splash screens on a mac.
if sys.platform.startswith('darwin'):
mainWindowRunCallback(None)
else:
splashScreen.splashScreen(mainWindowRunCallback)
app.MainLoop()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1,7 +1,11 @@
# coding=utf-8
from __future__ import absolute_import from __future__ import absolute_import
import __init__
import wx, os, platform, types, webbrowser, threading, time, re import webbrowser
import threading
import time
import wx
import wx.wizard import wx.wizard
from gui import firmwareInstall from gui import firmwareInstall
@ -9,30 +13,36 @@ from gui import toolbarUtil
from gui import printWindow from gui import printWindow
from util import machineCom from util import machineCom
from util import profile from util import profile
from util.resources import getPathForImage
class InfoBox(wx.Panel): class InfoBox(wx.Panel):
def __init__(self, parent): def __init__(self, parent):
super(InfoBox, self).__init__(parent) super(InfoBox, self).__init__(parent)
self.SetBackgroundColour('#FFFF80') self.SetBackgroundColour('#FFFF80')
self.sizer = wx.GridBagSizer(5, 5) self.sizer = wx.GridBagSizer(5, 5)
self.SetSizer(self.sizer) self.SetSizer(self.sizer)
self.attentionBitmap = toolbarUtil.getBitmapImage('attention.png') self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png'))
self.errorBitmap = toolbarUtil.getBitmapImage('error.png') self.errorBitmap = wx.Bitmap(getPathForImage('error.png'))
self.readyBitmap = toolbarUtil.getBitmapImage('ready.png') self.readyBitmap = wx.Bitmap(getPathForImage('ready.png'))
self.busyBitmap = [toolbarUtil.getBitmapImage('busy-0.png'), toolbarUtil.getBitmapImage('busy-1.png'), toolbarUtil.getBitmapImage('busy-2.png'), toolbarUtil.getBitmapImage('busy-3.png')] self.busyBitmap = [
wx.Bitmap(getPathForImage('busy-0.png')),
wx.Bitmap(getPathForImage('busy-1.png')),
wx.Bitmap(getPathForImage('busy-2.png')),
wx.Bitmap(getPathForImage('busy-3.png'))
]
self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1)) self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1))
self.text = wx.StaticText(self, -1, '') self.text = wx.StaticText(self, -1, '')
self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT) self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT)
self.sizer.Add(self.bitmap, pos=(0,0), flag=wx.ALL, border=5) self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5)
self.sizer.Add(self.text, pos=(0,1), flag=wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL, border=5) self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5)
self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5) self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
self.sizer.AddGrowableCol(1) self.sizer.AddGrowableCol(1)
self.extraInfoButton.Show(False) self.extraInfoButton.Show(False)
self.extraInfoUrl = '' self.extraInfoUrl = ''
self.busyState = None self.busyState = None
self.timer = wx.Timer(self) self.timer = wx.Timer(self)
@ -54,18 +64,21 @@ class InfoBox(wx.Panel):
self.Layout() self.Layout()
self.SetErrorIndicator() self.SetErrorIndicator()
self.Refresh() self.Refresh()
def SetAttention(self, info): def SetAttention(self, info):
self.SetBackgroundColour('#FFFF80') self.SetBackgroundColour('#FFFF80')
self.text.SetLabel(info) self.text.SetLabel(info)
self.extraInfoButton.Show(False) self.extraInfoButton.Show(False)
self.SetAttentionIndicator() self.SetAttentionIndicator()
self.Refresh() self.Refresh()
def SetBusyIndicator(self): def SetBusyIndicator(self):
self.busyState = 0 self.busyState = 0
self.bitmap.SetBitmap(self.busyBitmap[self.busyState]) self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
def doExtraInfo(self, e):
webbrowser.open(self.extraInfoUrl)
def doBusyUpdate(self, e): def doBusyUpdate(self, e):
if self.busyState == None: if self.busyState == None:
return return
@ -73,22 +86,20 @@ class InfoBox(wx.Panel):
if self.busyState >= len(self.busyBitmap): if self.busyState >= len(self.busyBitmap):
self.busyState = 0 self.busyState = 0
self.bitmap.SetBitmap(self.busyBitmap[self.busyState]) self.bitmap.SetBitmap(self.busyBitmap[self.busyState])
def doExtraInfo(self, e):
webbrowser.open(self.extraInfoUrl)
def SetReadyIndicator(self): def SetReadyIndicator(self):
self.busyState = None self.busyState = None
self.bitmap.SetBitmap(self.readyBitmap) self.bitmap.SetBitmap(self.readyBitmap)
def SetErrorIndicator(self): def SetErrorIndicator(self):
self.busyState = None self.busyState = None
self.bitmap.SetBitmap(self.errorBitmap) self.bitmap.SetBitmap(self.errorBitmap)
def SetAttentionIndicator(self): def SetAttentionIndicator(self):
self.busyState = None self.busyState = None
self.bitmap.SetBitmap(self.attentionBitmap) self.bitmap.SetBitmap(self.attentionBitmap)
class InfoPage(wx.wizard.WizardPageSimple): class InfoPage(wx.wizard.WizardPageSimple):
def __init__(self, parent, title): def __init__(self, parent, title):
wx.wizard.WizardPageSimple.__init__(self, parent) wx.wizard.WizardPageSimple.__init__(self, parent)
@ -99,52 +110,52 @@ class InfoPage(wx.wizard.WizardPageSimple):
title = wx.StaticText(self, -1, title) title = wx.StaticText(self, -1, title)
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
sizer.Add(title, pos=(0, 0), span=(1,2), flag=wx.ALIGN_CENTRE|wx.ALL) sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL)
sizer.Add(wx.StaticLine(self, -1), pos=(1,0), span=(1,2), flag=wx.EXPAND|wx.ALL) sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
sizer.AddGrowableCol(1) sizer.AddGrowableCol(1)
self.rowNr = 2 self.rowNr = 2
def AddText(self,info): def AddText(self, info):
text = wx.StaticText(self, -1, info) text = wx.StaticText(self, -1, info)
self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT) self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
self.rowNr += 1 self.rowNr += 1
return text return text
def AddSeperator(self): def AddSeperator(self):
self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1,2), flag=wx.EXPAND|wx.ALL) self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
self.rowNr += 1 self.rowNr += 1
def AddHiddenSeperator(self): def AddHiddenSeperator(self):
self.AddText('') self.AddText('')
def AddInfoBox(self): def AddInfoBox(self):
infoBox = InfoBox(self) infoBox = InfoBox(self)
self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT|wx.EXPAND) self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND)
self.rowNr += 1 self.rowNr += 1
return infoBox return infoBox
def AddRadioButton(self, label, style = 0): def AddRadioButton(self, label, style=0):
radio = wx.RadioButton(self, -1, label, style=style) radio = wx.RadioButton(self, -1, label, style=style)
self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1,2), flag=wx.EXPAND|wx.ALL) self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL)
self.rowNr += 1 self.rowNr += 1
return radio return radio
def AddCheckbox(self, label, checked = False): def AddCheckbox(self, label, checked=False):
check = wx.CheckBox(self, -1) check = wx.CheckBox(self, -1)
text = wx.StaticText(self, -1, label) text = wx.StaticText(self, -1, label)
check.SetValue(checked) check.SetValue(checked)
self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT|wx.RIGHT) self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1,2), flag=wx.ALL) self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL)
self.rowNr += 1 self.rowNr += 1
return check return check
def AddButton(self, label): def AddButton(self, label):
button = wx.Button(self, -1, label) button = wx.Button(self, -1, label)
self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT) self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
self.rowNr += 1 self.rowNr += 1
return button return button
def AddDualButton(self, label1, label2): def AddDualButton(self, label1, label2):
button1 = wx.Button(self, -1, label1) button1 = wx.Button(self, -1, label1)
self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT) self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT)
@ -152,49 +163,50 @@ class InfoPage(wx.wizard.WizardPageSimple):
self.GetSizer().Add(button2, pos=(self.rowNr, 1)) self.GetSizer().Add(button2, pos=(self.rowNr, 1))
self.rowNr += 1 self.rowNr += 1
return button1, button2 return button1, button2
def AddTextCtrl(self, value): def AddTextCtrl(self, value):
ret = wx.TextCtrl(self, -1, value) ret = wx.TextCtrl(self, -1, value)
self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT) self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT)
self.rowNr += 1 self.rowNr += 1
return ret return ret
def AddLabelTextCtrl(self, info, value): def AddLabelTextCtrl(self, info, value):
text = wx.StaticText(self, -1, info) text = wx.StaticText(self, -1, info)
ret = wx.TextCtrl(self, -1, value) ret = wx.TextCtrl(self, -1, value)
self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT) self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1,1), flag=wx.LEFT) self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
self.rowNr += 1 self.rowNr += 1
return ret return ret
def AddTextCtrlButton(self, value, buttonText): def AddTextCtrlButton(self, value, buttonText):
text = wx.TextCtrl(self, -1, value) text = wx.TextCtrl(self, -1, value)
button = wx.Button(self, -1, buttonText) button = wx.Button(self, -1, buttonText)
self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT) self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT)
self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1,1), flag=wx.LEFT) self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT)
self.rowNr += 1 self.rowNr += 1
return text, button return text, button
def AddBitmap(self, bitmap): def AddBitmap(self, bitmap):
bitmap = wx.StaticBitmap(self, -1, bitmap) bitmap = wx.StaticBitmap(self, -1, bitmap)
self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT) self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT)
self.rowNr += 1 self.rowNr += 1
return bitmap return bitmap
def AddCheckmark(self, label, bitmap): def AddCheckmark(self, label, bitmap):
check = wx.StaticBitmap(self, -1, bitmap) check = wx.StaticBitmap(self, -1, bitmap)
text = wx.StaticText(self, -1, label) text = wx.StaticText(self, -1, label)
self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT|wx.RIGHT) self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT)
self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1,1), flag=wx.ALL) self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL)
self.rowNr += 1 self.rowNr += 1
return check return check
def AllowNext(self): def AllowNext(self):
return True return True
def StoreData(self): def StoreData(self):
pass pass
class FirstInfoPage(InfoPage): class FirstInfoPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(FirstInfoPage, self).__init__(parent, "First time run wizard") super(FirstInfoPage, self).__init__(parent, "First time run wizard")
@ -204,13 +216,16 @@ class FirstInfoPage(InfoPage):
self.AddText('* Configure Cura for your machine') self.AddText('* Configure Cura for your machine')
self.AddText('* Upgrade your firmware') self.AddText('* Upgrade your firmware')
self.AddText('* Check if your machine is working safely') self.AddText('* Check if your machine is working safely')
#self.AddText('* Calibrate your machine') #self.AddText('* Calibrate your machine')
#self.AddText('* Do your first print') #self.AddText('* Do your first print')
class RepRapInfoPage(InfoPage): class RepRapInfoPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(RepRapInfoPage, self).__init__(parent, "RepRap information") super(RepRapInfoPage, self).__init__(parent, "RepRap information")
self.AddText('RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.') self.AddText(
'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.')
self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.') self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.')
self.AddSeperator() self.AddSeperator()
self.AddText('You will have to manually install Marlin or Sprinter firmware.') self.AddText('You will have to manually install Marlin or Sprinter firmware.')
@ -229,6 +244,7 @@ class RepRapInfoPage(InfoPage):
profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2) profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2)
profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue())) profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue()))
class MachineSelectPage(InfoPage): class MachineSelectPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(MachineSelectPage, self).__init__(parent, "Select your machine") super(MachineSelectPage, self).__init__(parent, "Select your machine")
@ -239,13 +255,13 @@ class MachineSelectPage(InfoPage):
self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect) self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect)
self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)") self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)")
self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect) self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect)
def OnUltimakerSelect(self, e): def OnUltimakerSelect(self, e):
wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage) wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage)
def OnOtherSelect(self, e): def OnOtherSelect(self, e):
wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage) wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage)
def StoreData(self): def StoreData(self):
if self.UltimakerRadio.GetValue(): if self.UltimakerRadio.GetValue():
profile.putPreference('machine_width', '205') profile.putPreference('machine_width', '205')
@ -262,6 +278,7 @@ class MachineSelectPage(InfoPage):
profile.putProfileSetting('nozzle_size', '0.5') profile.putProfileSetting('nozzle_size', '0.5')
profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2) profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2)
class SelectParts(InfoPage): class SelectParts(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(SelectParts, self).__init__(parent, "Select upgraded parts you have") super(SelectParts, self).__init__(parent, "Select upgraded parts you have")
@ -283,14 +300,18 @@ class SelectParts(InfoPage):
if getPreference('ultimaker_extruder_upgrade') == 'True': if getPreference('ultimaker_extruder_upgrade') == 'True':
putProfileSetting('retraction_enable', 'True') putProfileSetting('retraction_enable', 'True')
class FirmwareUpgradePage(InfoPage): class FirmwareUpgradePage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware") super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware")
self.AddText('Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.') self.AddText(
'Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.')
self.AddHiddenSeperator() self.AddHiddenSeperator()
self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.') self.AddText(
'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.')
self.AddHiddenSeperator() self.AddHiddenSeperator()
self.AddText('Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.') self.AddText(
'Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.')
upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade') upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade')
upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick) upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick)
skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick) skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick)
@ -300,36 +321,38 @@ class FirmwareUpgradePage(InfoPage):
self.AddText('* Have other changes in the firmware') self.AddText('* Have other changes in the firmware')
button = self.AddButton('Goto this page for a custom firmware') button = self.AddButton('Goto this page for a custom firmware')
button.Bind(wx.EVT_BUTTON, self.OnUrlClick) button.Bind(wx.EVT_BUTTON, self.OnUrlClick)
def AllowNext(self): def AllowNext(self):
return False return False
def OnUpgradeClick(self, e): def OnUpgradeClick(self, e):
if firmwareInstall.InstallFirmware(): if firmwareInstall.InstallFirmware():
self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
def OnSkipClick(self, e): def OnSkipClick(self, e):
self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
def OnUrlClick(self, e): def OnUrlClick(self, e):
webbrowser.open('http://daid.mine.nu/~daid/marlin_build/') webbrowser.open('http://daid.mine.nu/~daid/marlin_build/')
class UltimakerCheckupPage(InfoPage): class UltimakerCheckupPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup") super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup")
self.checkBitmap = toolbarUtil.getBitmapImage('checkmark.png') self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png'))
self.crossBitmap = toolbarUtil.getBitmapImage('cross.png') self.crossBitmap = wx.Bitmap(getPathForImage('cross.png'))
self.unknownBitmap = toolbarUtil.getBitmapImage('question.png') self.unknownBitmap = wx.Bitmap(getPathForImage('question.png'))
self.endStopNoneBitmap = toolbarUtil.getBitmapImage('endstop_none.png') self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png'))
self.endStopXMinBitmap = toolbarUtil.getBitmapImage('endstop_xmin.png') self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png'))
self.endStopXMaxBitmap = toolbarUtil.getBitmapImage('endstop_xmax.png') self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png'))
self.endStopYMinBitmap = toolbarUtil.getBitmapImage('endstop_ymin.png') self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png'))
self.endStopYMaxBitmap = toolbarUtil.getBitmapImage('endstop_ymax.png') self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png'))
self.endStopZMinBitmap = toolbarUtil.getBitmapImage('endstop_zmin.png') self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png'))
self.endStopZMaxBitmap = toolbarUtil.getBitmapImage('endstop_zmax.png') self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png'))
self.AddText('It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional.') self.AddText(
'It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional.')
b1, b2 = self.AddDualButton('Run checks', 'Skip checks') b1, b2 = self.AddDualButton('Run checks', 'Skip checks')
b1.Bind(wx.EVT_BUTTON, self.OnCheckClick) b1.Bind(wx.EVT_BUTTON, self.OnCheckClick)
b2.Bind(wx.EVT_BUTTON, self.OnSkipClick) b2.Bind(wx.EVT_BUTTON, self.OnSkipClick)
@ -352,21 +375,21 @@ class UltimakerCheckupPage(InfoPage):
self.yMaxStop = False self.yMaxStop = False
self.zMinStop = False self.zMinStop = False
self.zMaxStop = False self.zMaxStop = False
self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton) self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton)
def __del__(self): def __del__(self):
if self.comm != None: if self.comm != None:
self.comm.close() self.comm.close()
def AllowNext(self): def AllowNext(self):
self.endstopBitmap.Show(False) self.endstopBitmap.Show(False)
return False return False
def OnSkipClick(self, e): def OnSkipClick(self, e):
self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() self.GetParent().FindWindowById(wx.ID_FORWARD).Enable()
def OnCheckClick(self, e = None): def OnCheckClick(self, e=None):
self.errorLogButton.Show(False) self.errorLogButton.Show(False)
if self.comm != None: if self.comm != None:
self.comm.close() self.comm.close()
@ -381,7 +404,7 @@ class UltimakerCheckupPage(InfoPage):
self.stopState.SetBitmap(self.unknownBitmap) self.stopState.SetBitmap(self.unknownBitmap)
self.checkupState = 0 self.checkupState = 0
self.comm = machineCom.MachineCom(callbackObject=self) self.comm = machineCom.MachineCom(callbackObject=self)
def OnErrorLog(self, e): def OnErrorLog(self, e):
printWindow.LogWindow('\n'.join(self.comm.getLog())) printWindow.LogWindow('\n'.join(self.comm.getLog()))
@ -447,7 +470,7 @@ class UltimakerCheckupPage(InfoPage):
wx.CallAfter(self.Layout) wx.CallAfter(self.Layout)
else: else:
wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString())) wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString()))
def mcMessage(self, message): def mcMessage(self, message):
if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message: if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message:
for data in message.split(' '): for data in message.split(' '):
@ -466,7 +489,7 @@ class UltimakerCheckupPage(InfoPage):
if tag == 'z_max': if tag == 'z_max':
self.zMaxStop = (value == 'H') self.zMaxStop = (value == 'H')
self.comm.sendCommand('M119') self.comm.sendCommand('M119')
if self.checkupState == 3: if self.checkupState == 3:
if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop: if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop:
self.checkupState = 4 self.checkupState = 4
@ -509,14 +532,15 @@ class UltimakerCheckupPage(InfoPage):
def mcProgress(self, lineNr): def mcProgress(self, lineNr):
pass pass
def mcZChange(self, newZ): def mcZChange(self, newZ):
pass pass
class UltimakerCalibrationPage(InfoPage): class UltimakerCalibrationPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration") super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration")
self.AddText("Your Ultimaker requires some calibration.") self.AddText("Your Ultimaker requires some calibration.")
self.AddText("This calibration is needed for a proper extrusion amount.") self.AddText("This calibration is needed for a proper extrusion amount.")
self.AddSeperator() self.AddSeperator()
@ -528,19 +552,21 @@ class UltimakerCalibrationPage(InfoPage):
self.AddSeperator() self.AddSeperator()
self.AddText("First we need the diameter of your filament:") self.AddText("First we need the diameter of your filament:")
self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter')) self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter'))
self.AddText("If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.") self.AddText(
"If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.")
self.AddText("Note: This value can be changed later at any time.") self.AddText("Note: This value can be changed later at any time.")
def StoreData(self): def StoreData(self):
profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue()) profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue())
class UltimakerCalibrateStepsPerEPage(InfoPage): class UltimakerCalibrateStepsPerEPage(InfoPage):
def __init__(self, parent): def __init__(self, parent):
super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration") super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration")
if profile.getPreference('steps_per_e') == '0': if profile.getPreference('steps_per_e') == '0':
profile.putPreference('steps_per_e', '865.888') profile.putPreference('steps_per_e', '865.888')
self.AddText("Calibrating the Steps Per E requires some manual actions.") self.AddText("Calibrating the Steps Per E requires some manual actions.")
self.AddText("First remove any filament from your machine.") self.AddText("First remove any filament from your machine.")
self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.") self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.")
@ -552,20 +578,21 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e')) self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e'))
self.AddText("You can repeat these steps to get better calibration.") self.AddText("You can repeat these steps to get better calibration.")
self.AddSeperator() self.AddSeperator()
self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:") self.AddText(
"If you still have filament in your printer which needs\nheat to remove, press the heat up button below:")
self.heatButton = self.AddButton("Heatup for filament removal") self.heatButton = self.AddButton("Heatup for filament removal")
self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick) self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick)
self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick) self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick)
self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick) self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick)
def OnSaveLengthClick(self, e): def OnSaveLengthClick(self, e):
currentEValue = float(self.stepsPerEInput.GetValue()) currentEValue = float(self.stepsPerEInput.GetValue())
realExtrudeLength = float(self.lengthInput.GetValue()) realExtrudeLength = float(self.lengthInput.GetValue())
newEValue = currentEValue * 100 / realExtrudeLength newEValue = currentEValue * 100 / realExtrudeLength
self.stepsPerEInput.SetValue(str(newEValue)) self.stepsPerEInput.SetValue(str(newEValue))
self.lengthInput.SetValue("100") self.lengthInput.SetValue("100")
def OnExtrudeClick(self, e): def OnExtrudeClick(self, e):
threading.Thread(target=self.OnExtrudeRun).start() threading.Thread(target=self.OnExtrudeRun).start()
@ -575,7 +602,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
currentEValue = float(self.stepsPerEInput.GetValue()) currentEValue = float(self.stepsPerEInput.GetValue())
self.comm = machineCom.MachineCom() self.comm = machineCom.MachineCom()
if not self.comm.isOpen(): if not self.comm.isOpen():
wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION) wx.MessageBox(
"Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
'Printer error', wx.OK | wx.ICON_INFORMATION)
self.heatButton.Enable(True) self.heatButton.Enable(True)
self.extrudeButton.Enable(True) self.extrudeButton.Enable(True)
return return
@ -585,9 +614,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
return return
if 'start' in line: if 'start' in line:
break break
#Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
time.sleep(3) time.sleep(3)
self.sendGCommand('M302') #Disable cold extrusion protection self.sendGCommand('M302') #Disable cold extrusion protection
self.sendGCommand("M92 E%f" % (currentEValue)) self.sendGCommand("M92 E%f" % (currentEValue))
self.sendGCommand("G92 E0") self.sendGCommand("G92 E0")
@ -599,13 +628,15 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
def OnHeatClick(self, e): def OnHeatClick(self, e):
threading.Thread(target=self.OnHeatRun).start() threading.Thread(target=self.OnHeatRun).start()
def OnHeatRun(self): def OnHeatRun(self):
self.heatButton.Enable(False) self.heatButton.Enable(False)
self.extrudeButton.Enable(False) self.extrudeButton.Enable(False)
self.comm = machineCom.MachineCom() self.comm = machineCom.MachineCom()
if not self.comm.isOpen(): if not self.comm.isOpen():
wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION) wx.MessageBox(
"Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable",
'Printer error', wx.OK | wx.ICON_INFORMATION)
self.heatButton.Enable(True) self.heatButton.Enable(True)
self.extrudeButton.Enable(True) self.extrudeButton.Enable(True)
return return
@ -617,17 +648,19 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
return return
if 'start' in line: if 'start' in line:
break break
#Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found.
time.sleep(3) time.sleep(3)
self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out. self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out.
wx.MessageBox('Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)', 'Machine heatup', wx.OK | wx.ICON_INFORMATION) wx.MessageBox(
'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)',
'Machine heatup', wx.OK | wx.ICON_INFORMATION)
self.sendGCommand('M104 S0') self.sendGCommand('M104 S0')
time.sleep(1) time.sleep(1)
self.comm.close() self.comm.close()
self.heatButton.Enable(True) self.heatButton.Enable(True)
self.extrudeButton.Enable(True) self.extrudeButton.Enable(True)
def sendGCommand(self, cmd): def sendGCommand(self, cmd):
self.comm.sendCommand(cmd) #Disable cold extrusion protection self.comm.sendCommand(cmd) #Disable cold extrusion protection
while True: while True:
@ -636,14 +669,15 @@ class UltimakerCalibrateStepsPerEPage(InfoPage):
return return
if line.startswith('ok'): if line.startswith('ok'):
break break
def StoreData(self): def StoreData(self):
profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue()) profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue())
class configWizard(wx.wizard.Wizard): class configWizard(wx.wizard.Wizard):
def __init__(self): def __init__(self):
super(configWizard, self).__init__(None, -1, "Configuration Wizard") super(configWizard, self).__init__(None, -1, "Configuration Wizard")
self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged) self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged)
self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging)
@ -662,10 +696,10 @@ class configWizard(wx.wizard.Wizard):
wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage) wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage)
#wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage) #wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage)
#wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage) #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage)
self.FitToPage(self.firstInfoPage) self.FitToPage(self.firstInfoPage)
self.GetPageAreaSizer().Add(self.firstInfoPage) self.GetPageAreaSizer().Add(self.firstInfoPage)
self.RunWizard(self.firstInfoPage) self.RunWizard(self.firstInfoPage)
self.Destroy() self.Destroy()
@ -674,7 +708,7 @@ class configWizard(wx.wizard.Wizard):
def OnPageChanged(self, e): def OnPageChanged(self, e):
if e.GetPage().AllowNext(): if e.GetPage().AllowNext():
self.FindWindowById(wx.ID_FORWARD).Enable() self.FindWindowById(wx.ID_FORWARD).Enable()
else: else:
self.FindWindowById(wx.ID_FORWARD).Disable() self.FindWindowById(wx.ID_FORWARD).Disable()
self.FindWindowById(wx.ID_BACKWARD).Disable() self.FindWindowById(wx.ID_BACKWARD).Disable()

View File

@ -25,7 +25,7 @@ from util import version
from util import sliceRun from util import sliceRun
from util import meshLoader from util import meshLoader
def main(splash): def main():
#app = wx.App(False) #app = wx.App(False)
if profile.getPreference('machine_type') == 'unknown': if profile.getPreference('machine_type') == 'unknown':
if platform.system() == "Darwin": if platform.system() == "Darwin":
@ -39,7 +39,6 @@ def main(splash):
for filename in glob.glob(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'example', '*.*'))): for filename in glob.glob(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'example', '*.*'))):
shutil.copy(filename, os.path.join(os.path.dirname(exampleFile), os.path.basename(filename))) shutil.copy(filename, os.path.join(os.path.dirname(exampleFile), os.path.basename(filename)))
profile.putPreference('lastFile', exampleFile) profile.putPreference('lastFile', exampleFile)
splash.Show(False)
configWizard.configWizard() configWizard.configWizard()
if profile.getPreference('startMode') == 'Simple': if profile.getPreference('startMode') == 'Simple':
simpleMode.simpleModeWindow() simpleMode.simpleModeWindow()
@ -53,12 +52,12 @@ class mainWindow(configBase.configWindowBase):
super(mainWindow, self).__init__(title='Cura - ' + version.getVersion()) super(mainWindow, self).__init__(title='Cura - ' + version.getVersion())
extruderCount = int(profile.getPreference('extruder_amount')) extruderCount = int(profile.getPreference('extruder_amount'))
wx.EVT_CLOSE(self, self.OnClose) wx.EVT_CLOSE(self, self.OnClose)
#self.SetIcon(icon.getMainIcon()) #self.SetIcon(icon.getMainIcon())
self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions())) self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
menubar = wx.MenuBar() menubar = wx.MenuBar()
fileMenu = wx.Menu() fileMenu = wx.Menu()
i = fileMenu.Append(-1, 'Load model file...\tCTRL+L') i = fileMenu.Append(-1, 'Load model file...\tCTRL+L')
@ -97,7 +96,7 @@ class mainWindow(configBase.configWindowBase):
# i = toolsMenu.Append(-1, 'Open SVG (2D) slicer...') # i = toolsMenu.Append(-1, 'Open SVG (2D) slicer...')
# self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i) # self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i)
menubar.Append(toolsMenu, 'Tools') menubar.Append(toolsMenu, 'Tools')
expertMenu = wx.Menu() expertMenu = wx.Menu()
i = expertMenu.Append(-1, 'Open expert settings...') i = expertMenu.Append(-1, 'Open expert settings...')
self.Bind(wx.EVT_MENU, self.OnExpertOpen, i) self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
@ -111,7 +110,7 @@ class mainWindow(configBase.configWindowBase):
i = expertMenu.Append(-1, 'ReRun first run wizard...') i = expertMenu.Append(-1, 'ReRun first run wizard...')
self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i) self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
menubar.Append(expertMenu, 'Expert') menubar.Append(expertMenu, 'Expert')
helpMenu = wx.Menu() helpMenu = wx.Menu()
i = helpMenu.Append(-1, 'Online documentation...') i = helpMenu.Append(-1, 'Online documentation...')
self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i) self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
@ -119,7 +118,7 @@ class mainWindow(configBase.configWindowBase):
self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i) self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
menubar.Append(helpMenu, 'Help') menubar.Append(helpMenu, 'Help')
self.SetMenuBar(menubar) self.SetMenuBar(menubar)
if profile.getPreference('lastFile') != '': if profile.getPreference('lastFile') != '':
self.filelist = profile.getPreference('lastFile').split(';') self.filelist = profile.getPreference('lastFile').split(';')
self.SetTitle('Cura - %s - %s' % (version.getVersion(), self.filelist[-1])) self.SetTitle('Cura - %s - %s' % (version.getVersion(), self.filelist[-1]))
@ -132,9 +131,9 @@ class mainWindow(configBase.configWindowBase):
#Main tabs #Main tabs
nb = wx.Notebook(self) nb = wx.Notebook(self)
(left, right) = self.CreateConfigTab(nb, 'Print config') (left, right) = self.CreateConfigTab(nb, 'Print config')
configBase.TitleRow(left, "Quality") configBase.TitleRow(left, "Quality")
c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.') c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.')
validators.validFloat(c, 0.0001) validators.validFloat(c, 0.0001)
@ -143,19 +142,19 @@ class mainWindow(configBase.configWindowBase):
validators.validFloat(c, 0.0001) validators.validFloat(c, 0.0001)
validators.wallThicknessValidator(c) validators.wallThicknessValidator(c)
c = configBase.SettingRow(left, "Enable retraction", 'retraction_enable', False, 'Retract the filament when the nozzle is moving over a none-printed area. Details about the retraction can be configured in the advanced tab.') c = configBase.SettingRow(left, "Enable retraction", 'retraction_enable', False, 'Retract the filament when the nozzle is moving over a none-printed area. Details about the retraction can be configured in the advanced tab.')
configBase.TitleRow(left, "Fill") configBase.TitleRow(left, "Fill")
c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.') c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.')
validators.validFloat(c, 0.0) validators.validFloat(c, 0.0)
c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough') c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough')
validators.validFloat(c, 0.0, 100.0) validators.validFloat(c, 0.0, 100.0)
configBase.TitleRow(right, "Speed && Temperature") configBase.TitleRow(right, "Speed && Temperature")
c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.') c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.')
validators.validFloat(c, 1.0) validators.validFloat(c, 1.0)
validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s") validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s")
validators.printSpeedValidator(c) validators.printSpeedValidator(c)
#configBase.TitleRow(right, "Temperature") #configBase.TitleRow(right, "Temperature")
c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself') c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself')
validators.validFloat(c, 0.0, 340.0) validators.validFloat(c, 0.0, 340.0)
@ -163,7 +162,7 @@ class mainWindow(configBase.configWindowBase):
if profile.getPreference('has_heated_bed') == 'True': if profile.getPreference('has_heated_bed') == 'True':
c = configBase.SettingRow(right, "Bed temperature", 'print_bed_temperature', '0', 'Temperature used for the heated printer bed. Set at 0 to pre-heat yourself') c = configBase.SettingRow(right, "Bed temperature", 'print_bed_temperature', '0', 'Temperature used for the heated printer bed. Set at 0 to pre-heat yourself')
validators.validFloat(c, 0.0, 340.0) validators.validFloat(c, 0.0, 340.0)
configBase.TitleRow(right, "Support structure") configBase.TitleRow(right, "Support structure")
c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere'], 'Type of support structure build.\n"Exterior only" is the most commonly used support setting.\n\nNone does not do any support.\nExterior only only creates support where the support structure will touch the build platform.\nEverywhere creates support even on the insides of the model.') c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere'], 'Type of support structure build.\n"Exterior only" is the most commonly used support setting.\n\nNone does not do any support.\nExterior only only creates support where the support structure will touch the build platform.\nEverywhere creates support even on the insides of the model.')
c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the expert settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.') c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the expert settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.')
@ -176,9 +175,9 @@ class mainWindow(configBase.configWindowBase):
validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.") validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.")
c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS') c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS')
validators.validFloat(c, 0.5, 1.5) validators.validFloat(c, 0.5, 1.5)
(left, right) = self.CreateConfigTab(nb, 'Advanced config') (left, right) = self.CreateConfigTab(nb, 'Advanced config')
configBase.TitleRow(left, "Machine size") configBase.TitleRow(left, "Machine size")
c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.') c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.')
validators.validFloat(c, 0.1, 10.0) validators.validFloat(c, 0.1, 10.0)
@ -275,7 +274,7 @@ class mainWindow(configBase.configWindowBase):
self.updateProfileToControls() self.updateProfileToControls()
self.SetBackgroundColour(nb.GetBackgroundColour()) self.SetBackgroundColour(nb.GetBackgroundColour())
self.Fit() self.Fit()
if wx.Display().GetClientArea().GetWidth() < self.GetSize().GetWidth(): if wx.Display().GetClientArea().GetWidth() < self.GetSize().GetWidth():
f = self.GetSize().GetWidth() - wx.Display().GetClientArea().GetWidth() f = self.GetSize().GetWidth() - wx.Display().GetClientArea().GetWidth()
@ -285,7 +284,7 @@ class mainWindow(configBase.configWindowBase):
self.SetMinSize(self.GetSize()) self.SetMinSize(self.GetSize())
self.Centre() self.Centre()
self.Show(True) self.Show(True)
def OnLoadProfile(self, e): def OnLoadProfile(self, e):
dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard("ini files (*.ini)|*.ini") dlg.SetWildcard("ini files (*.ini)|*.ini")
@ -311,7 +310,7 @@ class mainWindow(configBase.configWindowBase):
else: else:
wx.MessageBox('No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer.', 'Profile load error', wx.OK | wx.ICON_INFORMATION) wx.MessageBox('No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer.', 'Profile load error', wx.OK | wx.ICON_INFORMATION)
dlg.Destroy() dlg.Destroy()
def OnSaveProfile(self, e): def OnSaveProfile(self, e):
dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE) dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
dlg.SetWildcard("ini files (*.ini)|*.ini") dlg.SetWildcard("ini files (*.ini)|*.ini")
@ -319,7 +318,7 @@ class mainWindow(configBase.configWindowBase):
profileFile = dlg.GetPath() profileFile = dlg.GetPath()
profile.saveGlobalProfile(profileFile) profile.saveGlobalProfile(profileFile)
dlg.Destroy() dlg.Destroy()
def OnResetProfile(self, e): def OnResetProfile(self, e):
dlg = wx.MessageDialog(self, 'This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?', 'Profile reset', wx.YES_NO | wx.ICON_QUESTION) dlg = wx.MessageDialog(self, 'This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?', 'Profile reset', wx.YES_NO | wx.ICON_QUESTION)
result = dlg.ShowModal() == wx.ID_YES result = dlg.ShowModal() == wx.ID_YES
@ -327,22 +326,22 @@ class mainWindow(configBase.configWindowBase):
if result: if result:
profile.resetGlobalProfile() profile.resetGlobalProfile()
self.updateProfileToControls() self.updateProfileToControls()
def OnBatchRun(self, e): def OnBatchRun(self, e):
br = batchRun.batchRunWindow(self) br = batchRun.batchRunWindow(self)
br.Centre() br.Centre()
br.Show(True) br.Show(True)
def OnPreferences(self, e): def OnPreferences(self, e):
prefDialog = preferencesDialog.preferencesDialog(self) prefDialog = preferencesDialog.preferencesDialog(self)
prefDialog.Centre() prefDialog.Centre()
prefDialog.Show(True) prefDialog.Show(True)
def OnSimpleSwitch(self, e): def OnSimpleSwitch(self, e):
profile.putPreference('startMode', 'Simple') profile.putPreference('startMode', 'Simple')
simpleMode.simpleModeWindow() simpleMode.simpleModeWindow()
self.Close() self.Close()
def OnDefaultMarlinFirmware(self, e): def OnDefaultMarlinFirmware(self, e):
firmwareInstall.InstallFirmware() firmwareInstall.InstallFirmware()
@ -382,7 +381,7 @@ class mainWindow(configBase.configWindowBase):
if filelist[-1] == False: if filelist[-1] == False:
return return
self._loadModels(filelist) self._loadModels(filelist)
def _loadModels(self, filelist): def _loadModels(self, filelist):
self.filelist = filelist self.filelist = filelist
self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion()) self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion())
@ -395,7 +394,7 @@ class mainWindow(configBase.configWindowBase):
def OnLoadModel(self, e): def OnLoadModel(self, e):
self._showModelLoadDialog(1) self._showModelLoadDialog(1)
def OnLoadModel2(self, e): def OnLoadModel2(self, e):
self._showModelLoadDialog(2) self._showModelLoadDialog(2)
@ -404,7 +403,7 @@ class mainWindow(configBase.configWindowBase):
def OnLoadModel4(self, e): def OnLoadModel4(self, e):
self._showModelLoadDialog(4) self._showModelLoadDialog(4)
def OnSlice(self, e): def OnSlice(self, e):
if len(self.filelist) < 1: if len(self.filelist) < 1:
wx.MessageBox('You need to load a file before you can prepare it.', 'Print error', wx.OK | wx.ICON_INFORMATION) wx.MessageBox('You need to load a file before you can prepare it.', 'Print error', wx.OK | wx.ICON_INFORMATION)
@ -418,7 +417,7 @@ class mainWindow(configBase.configWindowBase):
if newSize.GetWidth() < wx.GetDisplaySize()[0]: if newSize.GetWidth() < wx.GetDisplaySize()[0]:
self.SetSize(newSize) self.SetSize(newSize)
self.progressPanelList.append(spp) self.progressPanelList.append(spp)
def OnPrint(self, e): def OnPrint(self, e):
if len(self.filelist) < 1: if len(self.filelist) < 1:
wx.MessageBox('You need to load a file and prepare it before you can print.', 'Print error', wx.OK | wx.ICON_INFORMATION) wx.MessageBox('You need to load a file and prepare it before you can print.', 'Print error', wx.OK | wx.ICON_INFORMATION)
@ -432,7 +431,7 @@ class mainWindow(configBase.configWindowBase):
ecw = expertConfig.expertConfigWindow() ecw = expertConfig.expertConfigWindow()
ecw.Centre() ecw.Centre()
ecw.Show(True) ecw.Show(True)
def OnProjectPlanner(self, e): def OnProjectPlanner(self, e):
pp = projectPlanner.projectPlanner() pp = projectPlanner.projectPlanner()
pp.Centre() pp.Centre()
@ -461,7 +460,7 @@ class mainWindow(configBase.configWindowBase):
def OnQuit(self, e): def OnQuit(self, e):
self.Close() self.Close()
def OnClose(self, e): def OnClose(self, e):
profile.saveGlobalProfile(profile.getDefaultProfilePath()) profile.saveGlobalProfile(profile.getDefaultProfilePath())
self.Destroy() self.Destroy()

View File

@ -1,14 +1,20 @@
import math, time, os # coding=utf-8
from __future__ import absolute_import
import math
from util import meshLoader from util import meshLoader
from util import util3d from util import util3d
from util import profile from util import profile
from util.resources import getPathForMesh
try: try:
import OpenGL import OpenGL
OpenGL.ERROR_CHECKING = False OpenGL.ERROR_CHECKING = False
from OpenGL.GLU import * from OpenGL.GLU import *
from OpenGL.GL import * from OpenGL.GL import *
hasOpenGLlibs = True hasOpenGLlibs = True
except: except:
print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/" print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
@ -19,8 +25,8 @@ def InitGL(window, view3D, zoom):
glMatrixMode(GL_MODELVIEW) glMatrixMode(GL_MODELVIEW)
glLoadIdentity() glLoadIdentity()
size = window.GetSize() size = window.GetSize()
glViewport(0,0, size.GetWidth(), size.GetHeight()) glViewport(0, 0, size.GetWidth(), size.GetHeight())
glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0]) glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0]) glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
@ -53,23 +59,23 @@ def DrawMachine(machineSize):
if profile.getPreference('machine_type') == 'ultimaker': if profile.getPreference('machine_type') == 'ultimaker':
glPushMatrix() glPushMatrix()
glEnable(GL_LIGHTING) glEnable(GL_LIGHTING)
glTranslate(100,200,-5) glTranslate(100, 200, -5)
glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8,0.8,0.8]) glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5,0.5,0.5]) glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
glEnable(GL_BLEND) glEnable(GL_BLEND)
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR) glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
global platformMesh global platformMesh
if platformMesh == None: if platformMesh == None:
platformMesh = meshLoader.loadMesh(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", 'ultimaker_platform.stl'))) platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
platformMesh.setRotateMirror(0, False, False, False, False, False) platformMesh.setRotateMirror(0, False, False, False, False, False)
DrawMesh(platformMesh) DrawMesh(platformMesh)
glPopMatrix() glPopMatrix()
glDisable(GL_LIGHTING) glDisable(GL_LIGHTING)
if False: if False:
glColor3f(0.7,0.7,0.7) glColor3f(0.7, 0.7, 0.7)
glLineWidth(2) glLineWidth(2)
glBegin(GL_LINES) glBegin(GL_LINES)
for i in xrange(0, int(machineSize.x), 10): for i in xrange(0, int(machineSize.x), 10):
@ -79,13 +85,13 @@ def DrawMachine(machineSize):
glVertex3f(0, i, 0) glVertex3f(0, i, 0)
glVertex3f(machineSize.x, i, 0) glVertex3f(machineSize.x, i, 0)
glEnd() glEnd()
glEnable(GL_LINE_SMOOTH) glEnable(GL_LINE_SMOOTH)
glEnable(GL_BLEND) glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glColor3f(0.0,0.0,0.0) glColor3f(0.0, 0.0, 0.0)
glLineWidth(4) glLineWidth(4)
glBegin(GL_LINE_LOOP) glBegin(GL_LINE_LOOP)
glVertex3f(0, 0, 0) glVertex3f(0, 0, 0)
@ -93,7 +99,7 @@ def DrawMachine(machineSize):
glVertex3f(machineSize.x, machineSize.y, 0) glVertex3f(machineSize.x, machineSize.y, 0)
glVertex3f(0, machineSize.y, 0) glVertex3f(0, machineSize.y, 0)
glEnd() glEnd()
glLineWidth(2) glLineWidth(2)
glBegin(GL_LINE_LOOP) glBegin(GL_LINE_LOOP)
glVertex3f(0, 0, machineSize.z) glVertex3f(0, 0, machineSize.z)
@ -115,47 +121,47 @@ def DrawMachine(machineSize):
glDisable(GL_CULL_FACE) glDisable(GL_CULL_FACE)
glEnable(GL_BLEND) glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glColor4ub(5,171,231,127) glColor4ub(5, 171, 231, 127)
glBegin(GL_QUADS) glBegin(GL_QUADS)
for x in xrange(0, int(machineSize.x), 20): for x in xrange(0, int(machineSize.x), 20):
for y in xrange(0, int(machineSize.y), 20): for y in xrange(0, int(machineSize.y), 20):
glVertex3f(x, y, -0.01) glVertex3f(x, y, -0.01)
glVertex3f(min(x+10, machineSize.x), y, -0.01) glVertex3f(min(x + 10, machineSize.x), y, -0.01)
glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01)
glVertex3f(x, min(y+10, machineSize.y), -0.01) glVertex3f(x, min(y + 10, machineSize.y), -0.01)
for x in xrange(10, int(machineSize.x), 20): for x in xrange(10, int(machineSize.x), 20):
for y in xrange(10, int(machineSize.y), 20): for y in xrange(10, int(machineSize.y), 20):
glVertex3f(x, y, -0.01) glVertex3f(x, y, -0.01)
glVertex3f(min(x+10, machineSize.x), y, -0.01) glVertex3f(min(x + 10, machineSize.x), y, -0.01)
glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01)
glVertex3f(x, min(y+10, machineSize.y), -0.01) glVertex3f(x, min(y + 10, machineSize.y), -0.01)
glEnd() glEnd()
glColor4ub(5*8/10,171*8/10,231*8/10,128) glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
glBegin(GL_QUADS) glBegin(GL_QUADS)
for x in xrange(10, int(machineSize.x), 20): for x in xrange(10, int(machineSize.x), 20):
for y in xrange(0, int(machineSize.y), 20): for y in xrange(0, int(machineSize.y), 20):
glVertex3f(x, y, -0.01) glVertex3f(x, y, -0.01)
glVertex3f(min(x+10, machineSize.x), y, -0.01) glVertex3f(min(x + 10, machineSize.x), y, -0.01)
glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01)
glVertex3f(x, min(y+10, machineSize.y), -0.01) glVertex3f(x, min(y + 10, machineSize.y), -0.01)
for x in xrange(0, int(machineSize.x), 20): for x in xrange(0, int(machineSize.x), 20):
for y in xrange(10, int(machineSize.y), 20): for y in xrange(10, int(machineSize.y), 20):
glVertex3f(x, y, -0.01) glVertex3f(x, y, -0.01)
glVertex3f(min(x+10, machineSize.x), y, -0.01) glVertex3f(min(x + 10, machineSize.x), y, -0.01)
glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01)
glVertex3f(x, min(y+10, machineSize.y), -0.01) glVertex3f(x, min(y + 10, machineSize.y), -0.01)
glEnd() glEnd()
glEnable(GL_CULL_FACE) glEnable(GL_CULL_FACE)
glColor4ub(5,171,231,64) glColor4ub(5, 171, 231, 64)
glBegin(GL_QUADS) glBegin(GL_QUADS)
glVertex3f(0, 0, machineSize.z) glVertex3f(0, 0, machineSize.z)
glVertex3f(0, machineSize.y, machineSize.z) glVertex3f(0, machineSize.y, machineSize.z)
glVertex3f(machineSize.x, machineSize.y, machineSize.z) glVertex3f(machineSize.x, machineSize.y, machineSize.z)
glVertex3f(machineSize.x, 0, machineSize.z) glVertex3f(machineSize.x, 0, machineSize.z)
glEnd() glEnd()
glColor4ub(5,171,231,96) glColor4ub(5, 171, 231, 96)
glBegin(GL_QUADS) glBegin(GL_QUADS)
glVertex3f(0, 0, 0) glVertex3f(0, 0, 0)
glVertex3f(0, 0, machineSize.z) glVertex3f(0, 0, machineSize.z)
@ -168,7 +174,7 @@ def DrawMachine(machineSize):
glVertex3f(machineSize.x, machineSize.y, machineSize.z) glVertex3f(machineSize.x, machineSize.y, machineSize.z)
glEnd() glEnd()
glColor4ub(5,171,231,128) glColor4ub(5, 171, 231, 128)
glBegin(GL_QUADS) glBegin(GL_QUADS)
glVertex3f(0, 0, machineSize.z) glVertex3f(0, 0, machineSize.z)
glVertex3f(0, 0, 0) glVertex3f(0, 0, 0)
@ -182,72 +188,73 @@ def DrawMachine(machineSize):
glEnd() glEnd()
glDisable(GL_BLEND) glDisable(GL_BLEND)
glPushMatrix() glPushMatrix()
glTranslate(5,5,2) glTranslate(5, 5, 2)
glLineWidth(2) glLineWidth(2)
glColor3f(0.5,0,0) glColor3f(0.5, 0, 0)
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(0,0,0) glVertex3f(0, 0, 0)
glVertex3f(20,0,0) glVertex3f(20, 0, 0)
glEnd() glEnd()
glColor3f(0,0.5,0) glColor3f(0, 0.5, 0)
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(0,0,0) glVertex3f(0, 0, 0)
glVertex3f(0,20,0) glVertex3f(0, 20, 0)
glEnd() glEnd()
glColor3f(0,0,0.5) glColor3f(0, 0, 0.5)
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(0,0,0) glVertex3f(0, 0, 0)
glVertex3f(0,0,20) glVertex3f(0, 0, 20)
glEnd() glEnd()
glDisable(GL_DEPTH_TEST) glDisable(GL_DEPTH_TEST)
#X #X
glColor3f(1,0,0) glColor3f(1, 0, 0)
glPushMatrix() glPushMatrix()
glTranslate(23,0,0) glTranslate(23, 0, 0)
noZ = ResetMatrixRotationAndScale() noZ = ResetMatrixRotationAndScale()
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(-0.8,1,0) glVertex3f(-0.8, 1, 0)
glVertex3f(0.8,-1,0) glVertex3f(0.8, -1, 0)
glVertex3f(0.8,1,0) glVertex3f(0.8, 1, 0)
glVertex3f(-0.8,-1,0) glVertex3f(-0.8, -1, 0)
glEnd() glEnd()
glPopMatrix() glPopMatrix()
#Y #Y
glColor3f(0,1,0) glColor3f(0, 1, 0)
glPushMatrix() glPushMatrix()
glTranslate(0,23,0) glTranslate(0, 23, 0)
ResetMatrixRotationAndScale() ResetMatrixRotationAndScale()
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(-0.8, 1,0) glVertex3f(-0.8, 1, 0)
glVertex3f( 0.0, 0,0) glVertex3f(0.0, 0, 0)
glVertex3f( 0.8, 1,0) glVertex3f(0.8, 1, 0)
glVertex3f(-0.8,-1,0) glVertex3f(-0.8, -1, 0)
glEnd() glEnd()
glPopMatrix() glPopMatrix()
#Z #Z
if not noZ: if not noZ:
glColor3f(0,0,1) glColor3f(0, 0, 1)
glPushMatrix() glPushMatrix()
glTranslate(0,0,23) glTranslate(0, 0, 23)
ResetMatrixRotationAndScale() ResetMatrixRotationAndScale()
glBegin(GL_LINES) glBegin(GL_LINES)
glVertex3f(-0.8, 1,0) glVertex3f(-0.8, 1, 0)
glVertex3f( 0.8, 1,0) glVertex3f(0.8, 1, 0)
glVertex3f( 0.8, 1,0) glVertex3f(0.8, 1, 0)
glVertex3f(-0.8,-1,0) glVertex3f(-0.8, -1, 0)
glVertex3f(-0.8,-1,0) glVertex3f(-0.8, -1, 0)
glVertex3f( 0.8,-1,0) glVertex3f(0.8, -1, 0)
glEnd() glEnd()
glPopMatrix() glPopMatrix()
glPopMatrix() glPopMatrix()
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST)
def ResetMatrixRotationAndScale(): def ResetMatrixRotationAndScale():
matrix = glGetFloatv(GL_MODELVIEW_MATRIX) matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
noZ = False noZ = False
@ -263,7 +270,7 @@ def ResetMatrixRotationAndScale():
matrix[0][2] = 0.0 matrix[0][2] = 0.0
matrix[1][2] = 0.0 matrix[1][2] = 0.0
matrix[2][2] = 1.0 matrix[2][2] = 1.0
if matrix[3][2] != 0.0: if matrix[3][2] != 0.0:
matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100) matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100) matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
@ -274,10 +281,11 @@ def ResetMatrixRotationAndScale():
matrix[2][2] = scale2D matrix[2][2] = scale2D
matrix[3][2] = -100 matrix[3][2] = -100
noZ = True noZ = True
glLoadMatrixf(matrix) glLoadMatrixf(matrix)
return noZ return noZ
def DrawBox(vMin, vMax): def DrawBox(vMin, vMax):
glBegin(GL_LINE_LOOP) glBegin(GL_LINE_LOOP)
glVertex3f(vMin[0], vMin[1], vMin[2]) glVertex3f(vMin[0], vMin[1], vMin[2])
@ -303,6 +311,7 @@ def DrawBox(vMin, vMax):
glVertex3f(vMin[0], vMax[1], vMax[2]) glVertex3f(vMin[0], vMax[1], vMax[2])
glEnd() glEnd()
def DrawMeshOutline(mesh): def DrawMeshOutline(mesh):
glEnable(GL_CULL_FACE) glEnable(GL_CULL_FACE)
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
@ -314,9 +323,10 @@ def DrawMeshOutline(mesh):
glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount) glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
glPolygonMode(GL_BACK, GL_FILL) glPolygonMode(GL_BACK, GL_FILL)
glCullFace(GL_BACK) glCullFace(GL_BACK)
glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_VERTEX_ARRAY)
def DrawMesh(mesh): def DrawMesh(mesh):
glEnable(GL_CULL_FACE) glEnable(GL_CULL_FACE)
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
@ -325,79 +335,81 @@ def DrawMesh(mesh):
glNormalPointer(GL_FLOAT, 0, mesh.normal) glNormalPointer(GL_FLOAT, 0, mesh.normal)
#Odd, drawing in batchs is a LOT faster then drawing it all at once. #Odd, drawing in batchs is a LOT faster then drawing it all at once.
batchSize = 999 #Warning, batchSize needs to be dividable by 3 batchSize = 999 #Warning, batchSize needs to be dividable by 3
extraStartPos = int(mesh.vertexCount / batchSize) * batchSize extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
extraCount = mesh.vertexCount - extraStartPos extraCount = mesh.vertexCount - extraStartPos
glCullFace(GL_BACK) glCullFace(GL_BACK)
for i in xrange(0, int(mesh.vertexCount / batchSize)): for i in xrange(0, int(mesh.vertexCount / batchSize)):
glDrawArrays(GL_TRIANGLES, i*batchSize, batchSize) glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount) glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
glCullFace(GL_FRONT) glCullFace(GL_FRONT)
glNormalPointer(GL_FLOAT, 0, mesh.invNormal) glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
for i in xrange(0, int(mesh.vertexCount / batchSize)): for i in xrange(0, int(mesh.vertexCount / batchSize)):
glDrawArrays(GL_TRIANGLES, i*batchSize, batchSize) glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
extraStartPos = int(mesh.vertexCount / batchSize) * batchSize extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
extraCount = mesh.vertexCount - extraStartPos extraCount = mesh.vertexCount - extraStartPos
glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount) glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
glCullFace(GL_BACK) glCullFace(GL_BACK)
glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
def DrawMeshSteep(mesh, angle): def DrawMeshSteep(mesh, angle):
cosAngle = math.sin(angle / 180.0 * math.pi) cosAngle = math.sin(angle / 180.0 * math.pi)
glDisable(GL_LIGHTING) glDisable(GL_LIGHTING)
glDepthFunc(GL_EQUAL) glDepthFunc(GL_EQUAL)
for i in xrange(0, int(mesh.vertexCount), 3): for i in xrange(0, int(mesh.vertexCount), 3):
if mesh.normal[i][2] < -0.999999: if mesh.normal[i][2] < -0.999999:
if mesh.vertexes[i+0][2] > 0.01: if mesh.vertexes[i + 0][2] > 0.01:
glColor3f(0.5,0,0) glColor3f(0.5, 0, 0)
glBegin(GL_TRIANGLES) glBegin(GL_TRIANGLES)
glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
glEnd() glEnd()
elif mesh.normal[i][2] < -cosAngle: elif mesh.normal[i][2] < -cosAngle:
glColor3f(-mesh.normal[i][2],0,0) glColor3f(-mesh.normal[i][2], 0, 0)
glBegin(GL_TRIANGLES) glBegin(GL_TRIANGLES)
glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
glEnd() glEnd()
elif mesh.normal[i][2] > 0.999999: elif mesh.normal[i][2] > 0.999999:
if mesh.vertexes[i+0][2] > 0.01: if mesh.vertexes[i + 0][2] > 0.01:
glColor3f(0.5,0,0) glColor3f(0.5, 0, 0)
glBegin(GL_TRIANGLES) glBegin(GL_TRIANGLES)
glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
glEnd() glEnd()
elif mesh.normal[i][2] > cosAngle: elif mesh.normal[i][2] > cosAngle:
glColor3f(mesh.normal[i][2],0,0) glColor3f(mesh.normal[i][2], 0, 0)
glBegin(GL_TRIANGLES) glBegin(GL_TRIANGLES)
glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
glEnd() glEnd()
glDepthFunc(GL_LESS) glDepthFunc(GL_LESS)
def DrawGCodeLayer(layer): def DrawGCodeLayer(layer):
filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
filamentArea = math.pi * filamentRadius * filamentRadius filamentArea = math.pi * filamentRadius * filamentRadius
lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
fillCycle = 0 fillCycle = 0
fillColorCycle = [[0.5,0.5,0.0],[0.0,0.5,0.5],[0.5,0.0,0.5]] fillColorCycle = [[0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]]
moveColor = [0,0,1] moveColor = [0, 0, 1]
retractColor = [1,0,0.5] retractColor = [1, 0, 0.5]
supportColor = [0,1,1] supportColor = [0, 1, 1]
extrudeColor = [1,0,0] extrudeColor = [1, 0, 0]
innerWallColor = [0,1,0] innerWallColor = [0, 1, 0]
skirtColor = [0,0.5,0.5] skirtColor = [0, 0.5, 0.5]
prevPathWasRetract = False prevPathWasRetract = False
glDisable(GL_CULL_FACE) glDisable(GL_CULL_FACE)
for path in layer: for path in layer:
if path.type == 'move': if path.type == 'move':
@ -420,13 +432,13 @@ def DrawGCodeLayer(layer):
else: else:
c = extrudeColor c = extrudeColor
if path.type == 'retract': if path.type == 'retract':
c = [0,1,1] c = [0, 1, 1]
if path.type == 'extrude': if path.type == 'extrude':
drawLength = 0.0 drawLength = 0.0
prevNormal = None prevNormal = None
for i in xrange(0, len(path.list)-1): for i in xrange(0, len(path.list) - 1):
v0 = path.list[i] v0 = path.list[i]
v1 = path.list[i+1] v1 = path.list[i + 1]
# Calculate line width from ePerDistance (needs layer thickness and filament diameter) # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
dist = (v0 - v1).vsize() dist = (v0 - v1).vsize()
@ -435,7 +447,7 @@ def DrawGCodeLayer(layer):
lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
drawLength += (v0 - v1).vsize() drawLength += (v0 - v1).vsize()
normal = (v0 - v1).cross(util3d.Vector3(0,0,1)) normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
normal.normalize() normal.normalize()
vv2 = v0 + normal * lineWidth vv2 = v0 + normal * lineWidth
@ -461,13 +473,13 @@ def DrawGCodeLayer(layer):
glVertex3f(vv4.x, vv4.y, vv4.z - zOffset) glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset) glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
glVertex3f(v0.x, v0.y, v0.z - zOffset) glVertex3f(v0.x, v0.y, v0.z - zOffset)
glVertex3f(vv0.x, vv0.y, vv0.z - zOffset) glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
glVertex3f(vv5.x, vv5.y, vv5.z - zOffset) glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset) glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
glVertex3f(v0.x, v0.y, v0.z - zOffset) glVertex3f(v0.x, v0.y, v0.z - zOffset)
glEnd() glEnd()
prevNormal = normal prevNormal = normal
prevVv1 = vv1 prevVv1 = vv1
prevVv3 = vv3 prevVv3 = vv3

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +1,40 @@
import sys, os # coding=utf-8
#We only need the core here, which speeds up the import. As we want to show the splashscreen ASAP. from __future__ import absolute_import
import wx._core
import wx._core #We only need the core here, which speeds up the import. As we want to show the splashscreen ASAP.
from util.resources import getPathForImage
def getBitmapImage(filename):
#The frozen executable has the script files in a zip, so we need to exit another level to get to our images.
if hasattr(sys, 'frozen'):
return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../images", filename)))
else:
return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", filename)))
class splashScreen(wx.SplashScreen): class splashScreen(wx.SplashScreen):
def __init__(self, callback): def __init__(self, callback):
self.callback = callback self.callback = callback
bitmap = getBitmapImage("splash.png") bitmap = wx.Bitmap(getPathForImage('splash.png'))
super(splashScreen, self).__init__(bitmap, wx.SPLASH_CENTRE_ON_SCREEN, 0, None) super(splashScreen, self).__init__(bitmap, wx.SPLASH_CENTRE_ON_SCREEN, 0, None)
wx.CallAfter(self.DoCallback) wx.CallAfter(self.DoCallback)
def DoCallback(self): def DoCallback(self):
self.callback(self) self.callback(self)
self.Destroy() self.Destroy()
def showSplash(callback): def showSplash(callback):
app = wx.App(False) from Cura.cura import CuraApp
app = CuraApp(False)
splashScreen(callback) splashScreen(callback)
app.MainLoop() app.MainLoop()
def testCallback(splashscreen): def testCallback(splashscreen):
print "Callback!" print "Callback!"
import time import time
time.sleep(2) time.sleep(2)
print "!Callback" print "!Callback"
def main(): def main():
showSplash(testCallback) showSplash(testCallback)
if __name__ == u'__main__': if __name__ == u'__main__':
main() main()

View File

@ -1,28 +1,23 @@
# coding=utf-8
from __future__ import absolute_import
from __future__ import division from __future__ import division
import os, sys
import wx import wx
from wx.lib import buttons from wx.lib import buttons
from util import profile from util import profile
from util.resources import getPathForImage
####################################################### #######################################################
# toolbarUtil contains help classes and functions for # toolbarUtil contains help classes and functions for
# toolbar buttons. # toolbar buttons.
####################################################### #######################################################
def getBitmapImage(filename):
#The frozen executable has the script files in a zip, so we need to exit another level to get to our images.
if hasattr(sys, 'frozen'):
return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../images", filename)))
else:
return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", filename)))
class Toolbar(wx.ToolBar): class Toolbar(wx.ToolBar):
def __init__(self, parent): def __init__(self, parent):
super(Toolbar, self).__init__(parent, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER) super(Toolbar, self).__init__(parent, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
self.SetToolBitmapSize( ( 21, 21 ) ) self.SetToolBitmapSize(( 21, 21 ))
if not hasattr(parent, 'popup'): if not hasattr(parent, 'popup'):
# Create popup window # Create popup window
@ -30,14 +25,14 @@ class Toolbar(wx.ToolBar):
parent.popup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK)) parent.popup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
parent.popup.text = wx.StaticText(parent.popup, -1, '') parent.popup.text = wx.StaticText(parent.popup, -1, '')
parent.popup.sizer = wx.BoxSizer() parent.popup.sizer = wx.BoxSizer()
parent.popup.sizer.Add(parent.popup.text, flag=wx.EXPAND|wx.ALL, border=1) parent.popup.sizer.Add(parent.popup.text, flag=wx.EXPAND | wx.ALL, border=1)
parent.popup.SetSizer(parent.popup.sizer) parent.popup.SetSizer(parent.popup.sizer)
parent.popup.owner = None parent.popup.owner = None
def OnPopupDisplay(self, e): def OnPopupDisplay(self, e):
self.UpdatePopup(e.GetEventObject()) self.UpdatePopup(e.GetEventObject())
self.GetParent().popup.Show(True) self.GetParent().popup.Show(True)
def OnPopupHide(self, e): def OnPopupHide(self, e):
if self.GetParent().popup.owner == e.GetEventObject(): if self.GetParent().popup.owner == e.GetEventObject():
self.GetParent().popup.Show(False) self.GetParent().popup.Show(False)
@ -50,13 +45,14 @@ class Toolbar(wx.ToolBar):
popup.Fit(); popup.Fit();
x, y = control.ClientToScreenXY(0, 0) x, y = control.ClientToScreenXY(0, 0)
sx, sy = control.GetSizeTuple() sx, sy = control.GetSizeTuple()
popup.SetPosition((x, y+sy)) popup.SetPosition((x, y + sy))
class ToggleButton(buttons.GenBitmapToggleButton): class ToggleButton(buttons.GenBitmapToggleButton):
def __init__(self, parent, profileSetting, bitmapFilenameOn, bitmapFilenameOff, def __init__(self, parent, profileSetting, bitmapFilenameOn, bitmapFilenameOff,
helpText='', id=-1, callback=None, size=(20,20)): helpText='', id=-1, callback=None, size=(20, 20)):
self.bitmapOn = getBitmapImage(bitmapFilenameOn) self.bitmapOn = wx.Bitmap(getPathForImage(bitmapFilenameOn))
self.bitmapOff = getBitmapImage(bitmapFilenameOff) self.bitmapOff = wx.Bitmap(getPathForImage(bitmapFilenameOff))
super(ToggleButton, self).__init__(parent, id, self.bitmapOff, size=size) super(ToggleButton, self).__init__(parent, id, self.bitmapOff, size=size)
@ -75,7 +71,7 @@ class ToggleButton(buttons.GenBitmapToggleButton):
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
parent.AddControl(self) parent.AddControl(self)
def SetBitmap(self, boolValue): def SetBitmap(self, boolValue):
@ -117,11 +113,12 @@ class ToggleButton(buttons.GenBitmapToggleButton):
self.Refresh() self.Refresh()
event.Skip() event.Skip()
class RadioButton(buttons.GenBitmapButton): class RadioButton(buttons.GenBitmapButton):
def __init__(self, parent, group, bitmapFilenameOn, bitmapFilenameOff, def __init__(self, parent, group, bitmapFilenameOn, bitmapFilenameOff,
helpText='', id=-1, callback=None, size=(20,20)): helpText='', id=-1, callback=None, size=(20, 20)):
self.bitmapOn = getBitmapImage(bitmapFilenameOn) self.bitmapOn = wx.Bitmap(getPathForImage(bitmapFilenameOn))
self.bitmapOff = getBitmapImage(bitmapFilenameOff) self.bitmapOff = wx.Bitmap(getPathForImage(bitmapFilenameOff))
super(RadioButton, self).__init__(parent, id, self.bitmapOff, size=size) super(RadioButton, self).__init__(parent, id, self.bitmapOff, size=size)
@ -138,10 +135,10 @@ class RadioButton(buttons.GenBitmapButton):
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
if len(group) == 1: if len(group) == 1:
self.SetValue(True) self.SetValue(True)
parent.AddControl(self) parent.AddControl(self)
def SetBitmap(self, boolValue): def SetBitmap(self, boolValue):
@ -158,7 +155,7 @@ class RadioButton(buttons.GenBitmapButton):
for other in self.group: for other in self.group:
if other != self: if other != self:
other.SetValue(False) other.SetValue(False)
def GetValue(self): def GetValue(self):
return self._value return self._value
@ -179,10 +176,11 @@ class RadioButton(buttons.GenBitmapButton):
self.Refresh() self.Refresh()
event.Skip() event.Skip()
class NormalButton(buttons.GenBitmapButton): class NormalButton(buttons.GenBitmapButton):
def __init__(self, parent, callback, bitmapFilename, def __init__(self, parent, callback, bitmapFilename,
helpText='', id=-1, size=(20,20)): helpText='', id=-1, size=(20, 20)):
self.bitmap = getBitmapImage(bitmapFilename) self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
super(NormalButton, self).__init__(parent, id, self.bitmap, size=size) super(NormalButton, self).__init__(parent, id, self.bitmap, size=size)
self.helpText = helpText self.helpText = helpText
@ -193,9 +191,9 @@ class NormalButton(buttons.GenBitmapButton):
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
self.Bind(wx.EVT_BUTTON, self.OnButton) self.Bind(wx.EVT_BUTTON, self.OnButton)
parent.AddControl(self) parent.AddControl(self)
def OnButton(self, event): def OnButton(self, event):

View File

@ -1,141 +1,156 @@
import os, glob, subprocess, platform # coding=utf-8
import wx from __future__ import absolute_import
from util import profile import os
from gui import toolbarUtil import glob
import subprocess
try: import platform
#Try to find the OpenCV library for video capture.
from opencv import cv import wx
from opencv import highgui
except: from util import profile
cv = None from util.resources import getPathForImage
from gui import toolbarUtil
try:
#Use the vidcap library directly from the VideoCapture package. (Windows only) try:
# http://videocapture.sourceforge.net/ #Try to find the OpenCV library for video capture.
# We're using the binary interface, not the python interface, so we don't depend on PIL from opencv import cv
import vidcap as win32vidcap from opencv import highgui
except: except:
win32vidcap = None cv = None
def hasWebcamSupport(): try:
if cv == None and win32vidcap == None: #Use the vidcap library directly from the VideoCapture package. (Windows only)
return False # http://videocapture.sourceforge.net/
if not os.path.exists(getFFMPEGpath()): # We're using the binary interface, not the python interface, so we don't depend on PIL
return False import vidcap as win32vidcap
return True except:
win32vidcap = None
def getFFMPEGpath():
if platform.system() == "Windows": def hasWebcamSupport():
return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe")) if cv == None and win32vidcap == None:
elif os.path.exists('/usr/bin/ffmpeg'): return False
return '/usr/bin/ffmpeg' if not os.path.exists(getFFMPEGpath()):
return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg")) return False
return True
class webcam(object):
def __init__(self):
self._cam = None def getFFMPEGpath():
self._overlayImage = toolbarUtil.getBitmapImage("cura-overlay.png") if platform.system() == "Windows":
self._overlayUltimaker = toolbarUtil.getBitmapImage("ultimaker-overlay.png") return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe"))
if cv != None: elif os.path.exists('/usr/bin/ffmpeg'):
self._cam = highgui.cvCreateCameraCapture(-1) return '/usr/bin/ffmpeg'
elif win32vidcap != None: return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg"))
try:
self._cam = win32vidcap.new_Dev(0, False)
except: class webcam(object):
pass def __init__(self):
self._cam = None
self._doTimelaps = False self._overlayImage = wx.Bitmap(getPathForImage('cura-overlay.png'))
self._bitmap = None self._overlayUltimaker = wx.Bitmap(getPathForImage('ultimaker-overlay.png'))
if cv != None:
def hasCamera(self): self._cam = highgui.cvCreateCameraCapture(-1)
return self._cam != None elif win32vidcap != None:
try:
def propertyPages(self): self._cam = win32vidcap.new_Dev(0, False)
if self._cam == None: except:
return [] pass
if cv != None:
#TODO Make an OpenCV property page self._doTimelaps = False
return [] self._bitmap = None
elif win32vidcap != None:
return ['Image properties', 'Format properties'] def hasCamera(self):
return self._cam != None
def openPropertyPage(self, pageType = 0):
if self._cam == None: def propertyPages(self):
return if self._cam == None:
if cv != None: return []
pass if cv != None:
elif win32vidcap != None: #TODO Make an OpenCV property page
if pageType == 0: return []
self._cam.displaycapturefilterproperties() elif win32vidcap != None:
else: return ['Image properties', 'Format properties']
del self._cam
self._cam = None def openPropertyPage(self, pageType=0):
tmp = win32vidcap.new_Dev(0, False) if self._cam == None:
tmp.displaycapturepinproperties() return
self._cam = tmp if cv != None:
pass
def takeNewImage(self): elif win32vidcap != None:
if self._cam == None: if pageType == 0:
return self._cam.displaycapturefilterproperties()
if cv != None: else:
frame = cv.QueryFrame(self._cam) del self._cam
cv.CvtColor(frame, frame, cv.CV_BGR2RGB) self._cam = None
bitmap = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData) tmp = win32vidcap.new_Dev(0, False)
elif win32vidcap != None: tmp.displaycapturepinproperties()
buffer, width, height = self._cam.getbuffer() self._cam = tmp
try:
wxImage = wx.EmptyImage(width, height) def takeNewImage(self):
wxImage.SetData(buffer[::-1]) if self._cam == None:
if self._bitmap != None: return
del self._bitmap if cv != None:
bitmap = wxImage.ConvertToBitmap() frame = cv.QueryFrame(self._cam)
del wxImage cv.CvtColor(frame, frame, cv.CV_BGR2RGB)
del buffer bitmap = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData)
except: elif win32vidcap != None:
pass buffer, width, height = self._cam.getbuffer()
try:
dc = wx.MemoryDC() wxImage = wx.EmptyImage(width, height)
dc.SelectObject(bitmap) wxImage.SetData(buffer[::-1])
dc.DrawBitmap(self._overlayImage, bitmap.GetWidth() - self._overlayImage.GetWidth() - 5, 5, True) if self._bitmap != None:
if profile.getPreference('machine_type') == 'ultimaker': del self._bitmap
dc.DrawBitmap(self._overlayUltimaker, (bitmap.GetWidth() - self._overlayUltimaker.GetWidth()) / 2, bitmap.GetHeight() - self._overlayUltimaker.GetHeight() - 5, True) bitmap = wxImage.ConvertToBitmap()
dc.SelectObject(wx.NullBitmap) del wxImage
del buffer
self._bitmap = bitmap except:
pass
if self._doTimelaps:
filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg" % (self._snapshotCount))) dc = wx.MemoryDC()
self._snapshotCount += 1 dc.SelectObject(bitmap)
bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG) dc.DrawBitmap(self._overlayImage, bitmap.GetWidth() - self._overlayImage.GetWidth() - 5, 5, True)
if profile.getPreference('machine_type') == 'ultimaker':
return self._bitmap dc.DrawBitmap(self._overlayUltimaker, (bitmap.GetWidth() - self._overlayUltimaker.GetWidth()) / 2,
bitmap.GetHeight() - self._overlayUltimaker.GetHeight() - 5, True)
def getLastImage(self): dc.SelectObject(wx.NullBitmap)
return self._bitmap
self._bitmap = bitmap
def startTimelaps(self, filename):
if self._cam == None: if self._doTimelaps:
return filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap",
self._cleanTempDir() "__tmp_snap_%04d.jpg" % (self._snapshotCount)))
self._timelapsFilename = filename self._snapshotCount += 1
self._snapshotCount = 0 bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG)
self._doTimelaps = True
print "startTimelaps" return self._bitmap
def endTimelaps(self): def getLastImage(self):
if self._doTimelaps: return self._bitmap
ffmpeg = getFFMPEGpath()
basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg")) def startTimelaps(self, filename):
subprocess.call([ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', '1500k', '-f', 'vob', self._timelapsFilename]) if self._cam == None:
self._doTimelaps = False return
self._cleanTempDir()
def _cleanTempDir(self): self._timelapsFilename = filename
basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap")) self._snapshotCount = 0
try: self._doTimelaps = True
os.makedirs(basePath) print "startTimelaps"
except:
pass def endTimelaps(self):
for filename in glob.iglob(basePath + "/*.jpg"): if self._doTimelaps:
os.remove(filename) ffmpeg = getFFMPEGpath()
basePath = os.path.normpath(
os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg"))
subprocess.call(
[ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y',
'-b:v', '1500k', '-f', 'vob', self._timelapsFilename])
self._doTimelaps = False
def _cleanTempDir(self):
basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap"))
try:
os.makedirs(basePath)
except:
pass
for filename in glob.iglob(basePath + "/*.jpg"):
os.remove(filename)

View File

@ -1,61 +0,0 @@
import sys, os, zipfile
try:
import cx_Freeze
except:
print "ERROR: You need cx-Freeze installed to build this package"
sys.exit(1)
freezeVersion = map(int, cx_Freeze.version.split('.'))
if freezeVersion[0] < 4 or freezeVersion[0] == 4 and freezeVersion[1] < 2:
print "ERROR: Your cx-Freeze version is too old to use with Cura."
sys.exit(1)
sys.path.append(os.path.abspath('cura_sf'))
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {
"silent": True,
"packages": [
'encodings.utf_8',
"OpenGL", "OpenGL.arrays", "OpenGL.platform", "OpenGL.GLU",
], "excludes": [
'Tkinter', 'tcl', 'cura_sf', 'fabmetheus_utilities', 'skeinforge_application', 'numpy',
], "include_files": [
('images', 'images'),
], "build_exe": 'freeze_build'}
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
cx_Freeze.setup( name = "Cura",
version = "RC5",
description = "Cura",
options = {"build_exe": build_exe_options},
executables = [cx_Freeze.Executable("cura.py", base=base)])
m = cx_Freeze.ModuleFinder(excludes=["gui"])
m.IncludeFile(os.path.abspath("cura.py"))
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py"))
m.IncludeFile(os.path.abspath("cura_sf/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py"))
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py"))
for name in os.listdir("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins"):
if name.endswith('.py'):
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/" + name))
m.ReportMissingModules()
cwd = os.path.abspath(".")
z = zipfile.ZipFile("freeze_build/cura_sf.zip", "w", zipfile.ZIP_DEFLATED)
for mod in m.modules:
if mod.file != None and mod.file.startswith(cwd):
if mod.file[len(cwd)+1:] == "cura.py":
z.write(mod.file[len(cwd)+1:], "__main__.py")
else:
z.write(mod.file[len(cwd)+1:])
z.write('cura_sf/fabmetheus_utilities/templates/layer_template.svg')
z.write('cura_sf/fabmetheus_utilities/version.txt')
z.write('__init__.py')
z.close()

36
Cura/util/resources.py Normal file
View File

@ -0,0 +1,36 @@
# coding=utf-8
from __future__ import absolute_import
import os
import sys
__all__ = ['getPathForResource', 'getPathForImage', 'getPathForMesh']
if sys.platform.startswith('darwin'):
if hasattr(sys, 'frozen'):
from Foundation import *
imagesPath = os.path.join(NSBundle.mainBundle().resourcePath(), 'images')
meshesPath = os.path.join(NSBundle.mainBundle().resourcePath(), 'images')
else:
imagesPath = os.path.join(os.path.dirname(__file__), "../images")
meshesPath = os.path.join(os.path.dirname(__file__), "../images")
else:
if hasattr(sys, 'frozen'):
imagesPath = os.path.join(os.path.dirname(__file__), "../../images")
meshesPath = os.path.join(os.path.dirname(__file__), "../../images")
else:
imagesPath = os.path.join(os.path.dirname(__file__), "../images")
meshesPath = os.path.join(os.path.dirname(__file__), "../images")
def getPathForResource(dir, resource_name):
assert os.path.isdir(dir), "{p} is not a directory".format(p=dir)
path = os.path.normpath(os.path.join(dir, resource_name))
assert os.path.isfile(path), "{p} is not a file.".format(p=path)
return path
def getPathForImage(name):
return getPathForResource(imagesPath, name)
def getPathForMesh(name):
return getPathForResource(meshesPath, name)

View File

@ -9,9 +9,77 @@ Development
Cura is developed in Python. Getting Cura up and running for development is not very difficult. If you copy the python and pypy from a release into your Cura development checkout then you can use Cura right away, just like you would with a release. Cura is developed in Python. Getting Cura up and running for development is not very difficult. If you copy the python and pypy from a release into your Cura development checkout then you can use Cura right away, just like you would with a release.
For development with git, check the help on github. Pull requests is the fastest way to get changes into Cura. For development with git, check the help on github. Pull requests is the fastest way to get changes into Cura.
Packaging Packaging
--------- ---------
Cura development comes with a script "package.sh", this script has been designed to run under unix like OSes (Linux, MacOS). Running it from sygwin is not a priority. Cura development comes with a script "package.sh", this script has been designed to run under unix like OSes (Linux, MacOS). Running it from sygwin is not a priority.
The "package.sh" script generates a final release package. You should not need it during development, unless you are changing the release process. If you want to distribute your own version of Cura, then the package.sh script will allow you to do that. The "package.sh" script generates a final release package. You should not need it during development, unless you are changing the release process. If you want to distribute your own version of Cura, then the package.sh script will allow you to do that.
Mac OS X
--------
The following section describes how to prepare environment for developing and packaing for Mac OS X.
###Python
You'll need non-system, framework-based, universal with min deployment target set to 10.6 build of Python 2.7
**non-system**: it was not bundeled with distribution of Mac OS X. You can check this by `python -c "import sys; print sys.prefix"`. Output should *not* start with *"/System/Library/Frameworks/Python.framework/"*
**framework-based**: Output of `python -c "import distutils.sysconfig as c; print(c.get_config_var('PYTHONFRAMEWORK'))"` should be non-empty string
**universal**: output of ``lipo -info `which python` `` include both i386 and x86_64. E.g *"Architectures in the fat file: /usr/local/bin/python are: i386 x86_64"*
**deployment target set to 10.6**: Output of ``otool -l `which python` `` should contain *"cmd LC_VERSION_MIN_MACOSX ... version 10.6"*
The easiest way to install it is via [Homebrew](http://mxcl.github.com/homebrew/): `brew install --fresh osx_python_cura.rb --universal` (TODO: upload the formula). Note you'll need to uninstall Python if you already have it installed via Homebrew.
###Virtualenv
You may skip this step if you don't bother to use [virtualenv](http://pypi.python.org/pypi/virtualenv). It's not a requirement.
The main problem with virtualenv is that wxWidgets cannot be installed via pip. We'll have to build it manually from source by specifing prefix to our virtualenv.
Assuming you have virtualenv at *~/.virtualenvs/Cura*:
1. Download [wxPython sources](http://sourceforge.net/projects/wxpython/files/wxPython/2.9.4.0/wxPython-src-2.9.4.0.tar.bz2)
2. Configure project with the following flags: `./configure --prefix=$HOME/.virtualenvs/Cura/ --enable-optimise --with-libjpeg=builtin --with-libpng=builtin --with-libtiff=builtin --with-zlib=builtin --enable-monolithic --with-macosx-version-min=10.6 --disable-debug --enable-unicode --enable-std_string --enable-display --with-opengl --with-osx_cocoa --enable-dnd --enable-clipboard --enable-webkit --enable-svg --with-expat --enable-universal_binary=i386,x86_64`
3. `make install`
4. cd into the *wxPython* directory
5. Build wxPython modules: `python setup.py build_ext WXPORT=osx_cocoa WX_CONFIG=$HOME/.virtualenvs/Cura/bin/wx-config UNICODE=1 INSTALL_MULTIVERSION=0 BUILD_GLCANVAS=1 BUILD_GIZMOS=1 BUILD_STC=1` (Note that python is the python of your virtualenv)
6. Install wxPython and modules: `python setup.py install --prefix=$HOME/.virtualenvs/Cura/ WXPORT=osx_cocoa WX_CONFIG=$HOME/.virtualenvs/Cura/bin/wx-config UNICODE=1 INSTALL_MULTIVERSION=0 BUILD_GLCANVAS=1 BUILD_GIZMOS=1 BUILD_STC=1` (Note that python is the python of your virtualenv)
Another problem is that python in virtualenv is not suitable for running GUI code. Mac OS X requires python to be inside the bundle. To workaround this issue, we will add the following script to the ~/.virtualenvs/Cura/bin:
#!/bin/bash
ENV=`python -c "import sys; print sys.prefix"`
PYTHON=`python -c "import sys; print sys.real_prefix"`/bin/python
export PYTHONHOME=$ENV
exec $PYTHON "$@"
I typically name this script `pythonw`.
At this point virtualenv is configured for wxPython development. Remember to use `python` to pacakge the app and `pythonw` to run app without packaging (e.g. for debugging).
###Requirements
Following packages are required for development:
PyOpenGL>=3.0.2
numpy>=1.6.2
pyserial>=2.6
pyobjc>=2.5
Following packages are required for packaging Cura into app:
py2app>=0.7.2
The easiest way to install all this packages is to use virtualenv's pip: `pip install requirements_darwin.txt`
####PyObjC
At time of writing, pyobjc 2.5 is not available at pypi. You have to clone repo and install it manually:
hg clone https://bitbucket.org/ronaldoussoren/pyobjc
hg checkout c42c98d6e941 # last tested commit
python install.py
###Packaging
To package Cura into application bundle simply do `python setup.py py2app`. Resulting bundle is self-contained -- it includes Python and all needed packages.

View File

@ -1,17 +1,17 @@
#!/bin/bash #!/bin/bash
# This script is to package the Cura package for Windows/Linux and OSx # This script is to package the Cura package for Windows/Linux and Mac OS X
# This script should run under Linux and OSx, as well as Windows with Cygwin. # This script should run under Linux and Mac OS X, as well as Windows with Cygwin.
############################# #############################
# CONFIGURATION # CONFIGURATION
############################# #############################
##Select the build target ##Select the build target
BUILD_TARGET=${1:-all} # BUILD_TARGET=${1:-all}
#BUILD_TARGET=win32 #BUILD_TARGET=win32
#BUILD_TARGET=linux #BUILD_TARGET=linux
#BUILD_TARGET=osx64 BUILD_TARGET=darwin
##Do we need to create the final archive ##Do we need to create the final archive
ARCHIVE_FOR_DISTRIBUTION=1 ARCHIVE_FOR_DISTRIBUTION=1
@ -62,10 +62,50 @@ function extract
if [ "$BUILD_TARGET" = "all" ]; then if [ "$BUILD_TARGET" = "all" ]; then
$0 win32 $0 win32
$0 linux $0 linux
$0 osx64 $0 darwin
exit exit
fi fi
#############################
# Darwin
#############################
if [ "$BUILD_TARGET" = "darwin" ]; then
rm -rf scripts/darwin/build
rm -rf scripts/darwin/dist
python setup.py py2app
rc=$?
if [[ $rc != 0 ]]; then
echo "Cannot build app."
exit 1
fi
cd scripts/darwin
# Install QuickLook plugin
mkdir -p dist/Cura.app/Contents/Library/QuickLook
cp STLQuickLook.qlgenerator dist/Cura.app/Contents/Library/QuickLook/
# Archive app
$TAR cfp - dist/Cura.app | gzip --best -c > ../../${TARGET_DIR}.tar.gz
# Create sparse image for distribution
hdiutil detach /Volumes/Cura\ -\ Ultimaker/
rm -rf Cura.dmg.sparseimage
hdiutil convert DmgTemplateCompressed.dmg -format UDSP -o Cura.dmg
hdiutil resize -size 500m Cura.dmg.sparseimage
hdiutil attach Cura.dmg.sparseimage
cp -a dist/Cura.app /Volumes/Cura\ -\ Ultimaker/Cura/
hdiutil detach /Volumes/Cura\ -\ Ultimaker
hdiutil convert Cura.dmg.sparseimage -format UDZO -imagekey zlib-level=9 -ov -o ../../${TARGET_DIR}.dmg
exit
fi
#############################
# Rest
#############################
# Change working directory to the directory the script is in # Change working directory to the directory the script is in
# http://stackoverflow.com/a/246128 # http://stackoverflow.com/a/246128
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
@ -100,13 +140,6 @@ if [ $BUILD_TARGET = "win32" ]; then
downloadURL http://www.uwe-sieber.de/files/ejectmedia.zip downloadURL http://www.uwe-sieber.de/files/ejectmedia.zip
#Get pypy #Get pypy
downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-win32.zip downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-win32.zip
elif [ $BUILD_TARGET = "osx64" ]; then
downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-${BUILD_TARGET}.tar.bz2
downloadURL http://python.org/ftp/python/2.7.3/python-2.7.3-macosx10.6.dmg
downloadURL http://sourceforge.net/projects/numpy/files/NumPy/1.6.2/numpy-1.6.2-py2.7-python.org-macosx10.3.dmg
downloadURL http://pypi.python.org/packages/source/p/pyserial/pyserial-2.6.tar.gz
downloadURL http://pypi.python.org/packages/source/P/PyOpenGL/PyOpenGL-3.0.2.tar.gz
downloadURL http://downloads.sourceforge.net/wxpython/wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg
else else
downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-${BUILD_TARGET}.tar.bz2 downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-${BUILD_TARGET}.tar.bz2
fi fi
@ -131,7 +164,7 @@ if [ $BUILD_TARGET = "win32" ]; then
extract ffmpeg-20120927-git-13f0cd6-win32-static.7z ffmpeg-20120927-git-13f0cd6-win32-static/licenses extract ffmpeg-20120927-git-13f0cd6-win32-static.7z ffmpeg-20120927-git-13f0cd6-win32-static/licenses
extract comtypes-0.6.2.win32.exe extract comtypes-0.6.2.win32.exe
extract ejectmedia.zip Win32 extract ejectmedia.zip Win32
mkdir -p ${TARGET_DIR}/python mkdir -p ${TARGET_DIR}/python
mkdir -p ${TARGET_DIR}/Cura/ mkdir -p ${TARGET_DIR}/Cura/
mv \$_OUTDIR/App/* ${TARGET_DIR}/python mv \$_OUTDIR/App/* ${TARGET_DIR}/python
@ -150,7 +183,7 @@ if [ $BUILD_TARGET = "win32" ]; then
rm -rf VideoCapture-0.9-5 rm -rf VideoCapture-0.9-5
rm -rf numpy-1.6.2-sse2.exe rm -rf numpy-1.6.2-sse2.exe
rm -rf ffmpeg-20120927-git-13f0cd6-win32-static rm -rf ffmpeg-20120927-git-13f0cd6-win32-static
#Clean up portable python a bit, to keep the package size down. #Clean up portable python a bit, to keep the package size down.
rm -rf ${TARGET_DIR}/python/PyScripter.* rm -rf ${TARGET_DIR}/python/PyScripter.*
rm -rf ${TARGET_DIR}/python/Doc rm -rf ${TARGET_DIR}/python/Doc
@ -194,12 +227,12 @@ if (( ${ARCHIVE_FOR_DISTRIBUTION} )); then
#cd ${TARGET_DIR} #cd ${TARGET_DIR}
#7z a ../${TARGET_DIR}.zip * #7z a ../${TARGET_DIR}.zip *
#cd .. #cd ..
if [ ! -z `which wine` ]; then if [ ! -z `which wine` ]; then
#if we have wine, try to run our nsis script. #if we have wine, try to run our nsis script.
rm -rf scripts/win32/dist rm -rf scripts/win32/dist
ln -sf `pwd`/${TARGET_DIR} scripts/win32/dist ln -sf `pwd`/${TARGET_DIR} scripts/win32/dist
wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe /DVERSION=${BUILD_NAME} scripts/win32/installer.nsi wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe /DVERSION=${BUILD_NAME} scripts/win32/installer.nsi
mv scripts/win32/Cura_${BUILD_NAME}.exe ./ mv scripts/win32/Cura_${BUILD_NAME}.exe ./
fi fi
if [ -f '/c/Program Files (x86)/NSIS/makensis.exe' ]; then if [ -f '/c/Program Files (x86)/NSIS/makensis.exe' ]; then
@ -208,28 +241,6 @@ if (( ${ARCHIVE_FOR_DISTRIBUTION} )); then
'/c/Program Files (x86)/NSIS/makensis.exe' -DVERSION=${BUILD_NAME} 'scripts/win32/installer.nsi' >> log.txt '/c/Program Files (x86)/NSIS/makensis.exe' -DVERSION=${BUILD_NAME} 'scripts/win32/installer.nsi' >> log.txt
mv scripts/win32/Cura_${BUILD_NAME}.exe ./ mv scripts/win32/Cura_${BUILD_NAME}.exe ./
fi fi
elif [ $BUILD_TARGET = "osx64" ]; then
echo "Building osx app"
mkdir -p scripts/osx64/Cura.app/Contents/Resources
mkdir -p scripts/osx64/Cura.app/Contents/Pkgs
rm -rf scripts/osx64/Cura.app/Contents/Resources/Cura
rm -rf scripts/osx64/Cura.app/Contents/Resources/pypy
cp -a ${TARGET_DIR}/* scripts/osx64/Cura.app/Contents/Resources
cp python-2.7.3-macosx10.6.dmg scripts/osx64/Cura.app/Contents/Pkgs
cp numpy-1.6.2-py2.7-python.org-macosx10.3.dmg scripts/osx64/Cura.app/Contents/Pkgs
cp pyserial-2.6.tar.gz scripts/osx64/Cura.app/Contents/Pkgs
cp PyOpenGL-3.0.2.tar.gz scripts/osx64/Cura.app/Contents/Pkgs
cp wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg scripts/osx64/Cura.app/Contents/Pkgs
cd scripts/osx64
$TAR cfp - Cura.app | gzip --best -c > ../../${TARGET_DIR}.tar.gz
hdiutil detach /Volumes/Cura\ -\ Ultimaker/
rm -rf Cura.dmg.sparseimage
hdiutil convert DmgTemplateCompressed.dmg -format UDSP -o Cura.dmg
hdiutil resize -size 500m Cura.dmg.sparseimage
hdiutil attach Cura.dmg.sparseimage
cp -a Cura.app /Volumes/Cura\ -\ Ultimaker/Cura/
hdiutil detach /Volumes/Cura\ -\ Ultimaker
hdiutil convert Cura.dmg.sparseimage -format UDZO -imagekey zlib-level=9 -ov -o ../../${TARGET_DIR}.dmg
else else
echo "Archiving to ${TARGET_DIR}.tar.gz" echo "Archiving to ${TARGET_DIR}.tar.gz"
$TAR cfp - ${TARGET_DIR} | gzip --best -c > ${TARGET_DIR}.tar.gz $TAR cfp - ${TARGET_DIR} | gzip --best -c > ${TARGET_DIR}.tar.gz

4
requirements_darwin.txt Normal file
View File

@ -0,0 +1,4 @@
PyOpenGL>=3.0.2
numpy>=1.6.2
pyserial>=2.6
py2app>=0.7.2

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>12C60</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>QLGenerator</string>
<key>LSItemContentTypes</key>
<array>
<string>com.pleasantsoftware.uti.stl</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>STLQuickLook</string>
<key>CFBundleIdentifier</key>
<string>com.pleasantsoftware.qlgenerator.STLQuickLook</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>STLQuickLook</string>
<key>CFBundleShortVersionString</key>
<string>1.1</string>
<key>CFBundleVersion</key>
<string>2</string>
<key>CFPlugInDynamicRegisterFunction</key>
<string></string>
<key>CFPlugInDynamicRegistration</key>
<string>YES</string>
<key>CFPlugInFactories</key>
<dict>
<key>C02E94A7-9A81-43CB-9663-4C4F30F4D259</key>
<string>QuickLookGeneratorPluginFactory</string>
</dict>
<key>CFPlugInTypes</key>
<dict>
<key>5E2D9680-5022-40FA-B806-43349622E5B9</key>
<array>
<string>C02E94A7-9A81-43CB-9663-4C4F30F4D259</string>
</array>
</dict>
<key>CFPlugInUnloadFunction</key>
<string></string>
<key>DTCompiler</key>
<string></string>
<key>DTPlatformBuild</key>
<string>4G2008a</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>12C37</string>
<key>DTSDKName</key>
<string>macosx10.8</string>
<key>DTXcode</key>
<string>0452</string>
<key>DTXcodeBuild</key>
<string>4G2008a</string>
<key>QLNeedsToBeRunInMainThread</key>
<false/>
<key>QLPreviewHeight</key>
<real>600</real>
<key>QLPreviewWidth</key>
<real>800</real>
<key>QLSupportsConcurrentRequests</key>
<false/>
<key>QLThumbnailMinimumSize</key>
<real>48</real>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>Cura</string>
<key>CFBundleDisplayName</key>
<string>Cura 12.10</string>
<key>CFBundleIdentifer</key>
<string>daid.cura</string>
<key>CFBundleIconFile></key>
<string>Resources/Cura.icns</string>
<key>CFBundleVersion</key>
<string>12.10</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>Cura</string>
</dict>
</plist>

View File

@ -1,134 +0,0 @@
#!/bin/sh
SCRIPTDIR=`dirname "$0"`
RESDIR=${SCRIPTDIR}/../Resources/
PKGDIR=${SCRIPTDIR}/../Pkgs/
#run the path_helper to set the $PATH for accessing python
if [ -x /usr/libexec/path_helper ]; then
eval `/usr/libexec/path_helper -s`
fi
displayMessage()
{
/usr/bin/osascript > /dev/null << EOF
tell application "System Events"
activate
set question to display dialog "$@"
end tell
return button returned of question
EOF
if [ $? != 0 ]; then
/usr/bin/osascript > /dev/null << EOF
tell application "System Events"
activate
display dialog "User Cancelled Install" buttons {"Ok"}
end tell
EOF
exit 1
fi
}
#Testing for python2.7, which we need and is not always installed on MacOS 1.6
PY="python2.7"
$PY -c ''
if [ $? != 0 ]; then
displayMessage "Python 2.7 is missing from your system. Cura requires Python2.7.\nStarting the installer" $PATH
# Install python2.7
hdiutil attach $PKGDIR/python-2.7.3-macosx10.6.dmg
open -W /Volumes/Python\ 2.7.3/Python.mpkg
hdiutil detach /Volumes/Python\ 2.7.3
# Check the installation
$PY -c ''
if [ $? != 0 ]; then
displayMessage "Failed to install python2.7"
exit 1
fi
fi
#Next check for numpy, numpy does not always run under 64bit, so we need to check if we need to use "arch -i386"
$PY -c 'import numpy' 2> /dev/null
if [ $? != 0 ]; then
PY="arch -i386 python2.7"
$PY -c 'import numpy'
if [ $? != 0 ]; then
displayMessage "Numpy is missing from your system, this is required.\nStarting the installer"
# Install numpy
hdiutil attach $PKGDIR/numpy-1.6.2-py2.7-python.org-macosx10.3.dmg
open -W /Volumes/numpy/numpy-1.6.2-py2.7.mpkg
hdiutil detach /Volumes/numpy
#After installing numpy, we need to check if we need to use arch -386 again
PY="python2.7"
$PY -c 'import numpy'
if [ $? != 0 ]; then
PY="arch -i386 python2.7"
$PY -c 'import numpy'
if [ $? != 0 ]; then
displayMessage "Failed to install numpy."
exit 1
fi
fi
fi
fi
#Check for wxPython
$PY -c 'import wx'
if [ $? != 0 ]; then
displayMessage "wxPython is missing from your system. Cura requires wxPython.\nStarting the installer"
# Start wxPython installer
hdiutil attach $PKGDIR/wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg
open -W /Volumes/wxPython2.9-osx-2.9.4.0-cocoa-py2.7/wxPython2.9-osx-cocoa-py2.7.pkg
hdiutil detach /Volumes/wxPython2.9-osx-2.9.4.0-cocoa-py2.7
#Check if wxPython is installed correctly
$PY -c 'import wx'
if [ $? != 0 ]; then
displayMessage "Failed to properly install wxPython."
exit 1
fi
fi
#Check for PyOpenGL
$PY -c 'import OpenGL'
if [ $? != 0 ]; then
# Unpackage PyOpenGL
if [ ! -d "$PKGDIR/PyOpenGL-3.0.2/build/lib" ]; then
cd $PKGDIR
tar -xzf PyOpenGL-3.0.2.tar.gz
cd PyOpenGL-3.0.2
$PY setup.py build
fi
export PYTHONPATH="$PYTHONPATH:$PKGDIR/PyOpenGL-3.0.2/build/lib"
# Test if the installation was succesful
echo $PYTHONPATH
$PY -c 'import OpenGL'
if [ $? != 0 ]; then
displayMessage "Failed to properly use PyOpenGL."
exit 1
fi
fi
#Check for pyserial
$PY -c 'import serial'
if [ $? != 0 ]; then
#Unpackage PySerial
if [ ! -d "$PKGDIR/pyserial-2.6/build/lib" ]; then
cd $PKGDIR
tar -xzf pyserial-2.6.tar.gz
cd pyserial-2.6
$PY setup.py build
fi
export PYTHONPATH="$PYTHONPATH:$PKGDIR/pyserial-2.6/build/lib"
#Test if we have pyserial now
$PY -c 'import serial'
if [ $? != 0 ]; then
displayMessage "Failed to properly use PySerial."
exit 1
fi
fi
#All checks passed, start Cura
$PY "${RESDIR}Cura/cura.py" &
sleep 1
exit 0

View File

@ -1 +0,0 @@
APPL????

130
setup.py Normal file
View File

@ -0,0 +1,130 @@
# coding=utf-8
import sys
import os
if sys.platform.startswith('darwin'):
from setuptools import setup
APP = ['Cura/cura.py']
DATA_FILES = ['Cura/images', 'Cura/LICENSE', 'Cura/stl.ico']
PLIST = {
u'CFBundleName': u'Cura',
u'CFBundleShortVersionString': u'12.11',
u'CFBundleVersion': u'12.11',
u'CFBundleIdentifier': u'com.ultimaker.Cura',
u'LSMinimumSystemVersion': u'10.6',
u'LSApplicationCategoryType': u'public.app-category.graphics-design',
u'CFBundleDocumentTypes': [
{
u'CFBundleTypeRole': u'Viewer',
u'LSItemContentTypes': [u'com.pleasantsoftware.uti.stl'],
u'LSHandlerRank': u'Alternate',
},
{
u'CFBundleTypeRole': u'Viewer',
u'LSItemContentTypes': [u'org.khronos.collada.digital-asset-exchange'],
u'LSHandlerRank': u'Alternate'
},
{
u'CFBundleTypeName': u'Wavefront 3D Object',
u'CFBundleTypeExtensions': [u'obj'],
u'CFBundleTypeMIMETypes': [u'application/obj-3d'],
u'CFBundleTypeRole': u'Viewer',
u'LSHandlerRank': u'Alternate'
}
],
u'UTImportedTypeDeclarations': [
{
u'UTTypeIdentifier': u'com.pleasantsoftware.uti.stl',
u'UTTypeConformsTo': [u'public.data'],
u'UTTypeDescription': u'Stereo Lithography 3D object',
u'UTTypeReferenceURL': u'http://en.wikipedia.org/wiki/STL_(file_format)',
u'UTTypeTagSpecification': {u'public.filename-extension': [u'stl'], u'public.mime-type': [u'text/plain']}
},
{
u'UTTypeIdentifier': u'org.khronos.collada.digital-asset-exchange',
u'UTTypeConformsTo': [u'public.xml', u'public.audiovisual-content'],
u'UTTypeDescription': u'Digital Asset Exchange (DAE)',
u'UTTypeTagSpecification': {u'public.filename-extension': [u'dae'], u'public.mime-type': [u'model/vnd.collada+xml']}
}
]
}
OPTIONS = {
'argv_emulation': True,
'iconfile': 'Cura/Cura.icns',
'includes': ['objc', 'Foundation'],
'resources': DATA_FILES,
'optimize': '2',
'plist': PLIST,
'bdist_base': 'scripts/darwin/build',
'dist_dir': 'scripts/darwin/dist'
}
setup(
name="Cura",
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app']
)
else:
import zipfile
try:
import cx_Freeze
except:
print "ERROR: You need cx-Freeze installed to build this package"
sys.exit(1)
freezeVersion = map(int, cx_Freeze.version.split('.'))
if freezeVersion[0] < 4 or freezeVersion[0] == 4 and freezeVersion[1] < 2:
print "ERROR: Your cx-Freeze version is too old to use with Cura."
sys.exit(1)
sys.path.append(os.path.abspath('cura_sf'))
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {
"silent": True,
"packages": [
'encodings.utf_8',
"OpenGL", "OpenGL.arrays", "OpenGL.platform", "OpenGL.GLU",
], "excludes": [
'Tkinter', 'tcl', 'cura_sf', 'fabmetheus_utilities', 'skeinforge_application', 'numpy',
], "include_files": [
('images', 'images'),
], "build_exe": 'freeze_build'}
# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
base = "Win32GUI"
cx_Freeze.setup( name = "Cura",
version = "RC5",
description = "Cura",
options = {"build_exe": build_exe_options},
executables = [cx_Freeze.Executable("cura.py", base=base)])
m = cx_Freeze.ModuleFinder(excludes=["gui"])
m.IncludeFile(os.path.abspath("cura.py"))
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py"))
m.IncludeFile(os.path.abspath("cura_sf/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py"))
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py"))
for name in os.listdir("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins"):
if name.endswith('.py'):
m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/" + name))
m.ReportMissingModules()
cwd = os.path.abspath(".")
z = zipfile.ZipFile("freeze_build/cura_sf.zip", "w", zipfile.ZIP_DEFLATED)
for mod in m.modules:
if mod.file != None and mod.file.startswith(cwd):
if mod.file[len(cwd)+1:] == "cura.py":
z.write(mod.file[len(cwd)+1:], "__main__.py")
else:
z.write(mod.file[len(cwd)+1:])
z.write('cura_sf/fabmetheus_utilities/templates/layer_template.svg')
z.write('cura_sf/fabmetheus_utilities/version.txt')
z.write('__init__.py')
z.close()