diff --git a/doc/gnofract4d-manual/C/gnofract4d-manual.xml b/doc/gnofract4d-manual/C/gnofract4d-manual.xml
index 9ef62a964..2f957ef11 100644
--- a/doc/gnofract4d-manual/C/gnofract4d-manual.xml
+++ b/doc/gnofract4d-manual/C/gnofract4d-manual.xml
@@ -345,17 +345,16 @@ keyframe will stay in video (stopped for) and
interpolation type between several possibilities. When you hit
Render button, Director will render all frames
and put them in directory you selected and then it will try to create
-video using transcode
-tool.
+a video using
+FFmpeg.
Tips:
-In order to end up with video file, not just a bunch of images, you need to have
-transcode tool compiled with support for ImageMagick.
+In order to end up with a video file, not just a bunch of images, you need to have
+ffmpeg compiled with support for zlib and libvpx.
diff --git a/fract4d/animation.py b/fract4d/animation.py
index 7c21721c2..bc6641315 100644
--- a/fract4d/animation.py
+++ b/fract4d/animation.py
@@ -1,48 +1,48 @@
#!/usr/bin/python
-import os, sys, copy, math
+import os
+import math
from xml.sax import make_parser
from xml.sax.handler import ContentHandler
-from . import fracttypes
-from . import fractal
from . import fractconfig
-#interpolation type constants
-INT_LINEAR= 0
-INT_LOG= 1
-INT_INVLOG= 2
-INT_COS= 3
+# interpolation type constants
+INT_LINEAR = 0
+INT_LOG = 1
+INT_INVLOG = 2
+INT_COS = 3
def getAttrOrDefault(attrs, name, default):
x = attrs.get(name)
- if x == None:
+ if x is None:
x = default
return x
def getAttrOrElse(attrs, name):
x = attrs.get(name)
- if x == None:
+ if x is None:
raise ValueError(
"Invalid file: Cannot find required attribute '%s'" % name)
return x
class KeyFrame:
- def __init__(self,filename,duration,stop,int_type,flags=(0,0,0,0,0,0)):
+ def __init__(self, filename, duration, stop, int_type, flags=(0,0,0,0,0,0)):
self.filename = filename
self.duration = duration
self.stop = stop
self.int_type = int_type
self.flags = flags
- def save(self,fh):
+ def save(self, fh):
fh.write(
'\t\t\n')
+ @staticmethod
def load_from_xml(attrs):
kf = KeyFrame(
getAttrOrElse(attrs,"filename"),
@@ -56,8 +56,6 @@ def load_from_xml(attrs):
int(getAttrOrDefault(attrs,"yw",0)),
int(getAttrOrDefault(attrs,"zw",0))))
return kf
-
- load_from_xml=staticmethod(load_from_xml)
class T:
def __init__(self, compiler):
@@ -65,13 +63,13 @@ def __init__(self, compiler):
self.reset()
def reset(self):
- self.avi_file=""
- self.width=640
- self.height=480
- self.framerate=25
- self.redblue=True
- #keyframes is a list of KeyFrame objects
- self.keyframes=[]
+ self.avi_file = ""
+ self.width = 640
+ self.height = 480
+ self.framerate = 25
+ self.redblue = False
+ # keyframes is a list of KeyFrame objects
+ self.keyframes = []
def get_fct_enabled(self):
return fractconfig.instance.getboolean("director","fct_enabled")
@@ -98,63 +96,63 @@ def get_avi_file(self):
return self.avi_file
def set_avi_file(self,file):
- if file!=None:
- self.avi_file=file
+ if file is not None:
+ self.avi_file = file
else:
- self.avi_file=""
+ self.avi_file = ""
def get_width(self):
return self.width
def set_width(self,width):
- if width!=None:
- self.width=int(width)
+ if width is not None:
+ self.width = int(width)
else:
- self.width=640
+ self.width = 640
def get_height(self):
return self.height
def set_height(self,height):
- if height!=None:
- self.height=int(height)
+ if height is not None:
+ self.height = int(height)
else:
- self.height=480
+ self.height = 480
def get_framerate(self):
return self.framerate
def set_framerate(self,fr):
- if fr!=None:
- self.framerate=int(fr)
+ if fr is not None:
+ self.framerate = int(fr)
else:
- self.framerate=25
+ self.framerate = 25
def get_redblue(self):
return self.redblue
def set_redblue(self,rb):
- if rb!=None:
- if rb==1:
- self.redblue=True
- elif rb==0:
- self.redblue=False
- self.redblue=rb
+ if rb is not None:
+ if rb == 1:
+ self.redblue = True
+ elif rb == 0:
+ self.redblue = False
+ self.redblue = rb
else:
- self.redblue=True
+ self.redblue = True
def add_keyframe(self,filename,duration,stop,int_type,index=None):
kf = KeyFrame(filename,duration,stop,int_type)
- if index==None:
+ if index is None:
self.keyframes.append(kf)
else:
self.keyframes.insert(index, kf)
def remove_keyframe(self,index):
- self.keyframes[index:index+1]=[]
+ del self.keyframes[index:index+1]
def change_keyframe(self,index,duration,stop,int_type):
- if index\n')
- fh.write("\n")
- fh.write('\t\n')
- for kf in self.keyframes:
- kf.save(fh)
- fh.write('\t\n')
- fh.write('\t\n'%
- (self.avi_file,self.framerate,self.width,self.height,self.redblue))
- fh.write("\n")
- fh.close()
-
- #leftover from debugging purposes
+ with open(file, "w") as fh:
+ fh.write('\n')
+ fh.write("\n")
+ fh.write('\t\n')
+ for kf in self.keyframes:
+ kf.save(fh)
+ fh.write('\t\n')
+ fh.write('\t\n' %
+ (self.avi_file,self.framerate,self.width,self.height,self.redblue))
+ fh.write("\n")
+
+ # leftover from debugging purposes
def pr(self):
print(self.__dict__)
def get_image_filename(self,n):
"The filename of the image containing the Nth frame"
- return os.path.join(self.get_png_dir(),"image_%07d.png" %n)
+ return os.path.join(self.get_png_dir(), "image_%07d.png" % n)
def get_fractal_filename(self,n):
"The filename of the .fct file which generates the Nth frame"
return os.path.join(self.get_fct_dir(),"file_%07d.fct" % n)
def get_mu(self, int_type, x):
- if int_type==INT_LINEAR:
- mu=x
- elif int_type==INT_LOG:
- mu=math.log(x+1,2)
- elif int_type==INT_INVLOG:
- mu=(math.exp(x)-1)/(math.e-1)
- elif int_type==INT_COS:
- mu=(1-math.cos(x*math.pi))/2
+ if int_type == INT_LINEAR:
+ mu = x
+ elif int_type == INT_LOG:
+ mu = math.log(x+1,2)
+ elif int_type == INT_INVLOG:
+ mu = (math.exp(x)-1) / (math.e-1)
+ elif int_type == INT_COS:
+ mu = (1-math.cos(x*math.pi)) / 2
else:
raise ValueError("Unknown interpolation type %d" % int_type)
return mu
@@ -262,18 +259,16 @@ def get_mu(self, int_type, x):
# create a list containing all the filenames of the frames
def create_list(self):
framelist = []
- folder_png=self.get_png_dir()
-
- current=1
+ current = 1
for i in range(self.keyframes_count()):
- for j in range(self.get_keyframe_stop(i)): #output keyframe 'stop' times
+ for j in range(self.get_keyframe_stop(i)): # output keyframe 'stop' times
framelist.append(self.get_image_filename(current-1))
if i < self.keyframes_count()-1:
# final frame has no transitions following it
- for j in range(self.get_keyframe_duration(i)): #output all transition files
+ for j in range(self.get_keyframe_duration(i)): # output all transition files
framelist.append(self.get_image_filename(current))
- current=current+1
+ current += 1
return framelist
@@ -296,17 +291,16 @@ def get_total_frames(self):
class AnimationHandler(ContentHandler):
def __init__(self,animation):
- self.animation=animation
+ self.animation = animation
def startElement(self, name, attrs):
- if name=="output":
+ if name == "output":
self.animation.set_avi_file(attrs.get("filename"))
self.animation.set_framerate(attrs.get("framerate"))
self.animation.set_width(attrs.get("width"))
self.animation.set_height(attrs.get("height"))
self.animation.set_redblue(int(attrs.get("swap")))
- elif name=="keyframe":
- kf= KeyFrame.load_from_xml(attrs)
+ elif name == "keyframe":
+ kf = KeyFrame.load_from_xml(attrs)
self.animation.keyframes.append(kf)
return
-
diff --git a/fract4d/fractconfig.py b/fract4d/fractconfig.py
index 31b073d85..f43c35464 100644
--- a/fract4d/fractconfig.py
+++ b/fract4d/fractconfig.py
@@ -27,8 +27,7 @@ def __init__(self, file):
"helpers" : {
"editor" : self.get_default_editor(),
"mailer" : self.get_default_mailer(),
- "browser" : self.get_default_browser(),
- "video_encoder" : "transcode"
+ "browser" : self.get_default_browser()
},
"general" : {
"threads" : "1",
diff --git a/fract4d/test_animation.py b/fract4d/test_animation.py
index f30a9fd6a..860ceddf2 100755
--- a/fract4d/test_animation.py
+++ b/fract4d/test_animation.py
@@ -29,7 +29,7 @@ def testDefault(self):
self.assertEqual(self.anim.get_width(),640)
self.assertEqual(self.anim.get_height(),480)
self.assertEqual(self.anim.get_framerate(),25)
- self.assertEqual(self.anim.get_redblue(),True)
+ self.assertEqual(self.anim.get_redblue(),False)
self.assertEqual(self.anim.keyframes_count(),0)
def testChangeOptions(self):
diff --git a/fract4dgui/AVIGen.py b/fract4dgui/AVIGen.py
index ca1292110..22301d5ba 100644
--- a/fract4dgui/AVIGen.py
+++ b/fract4dgui/AVIGen.py
@@ -1,155 +1,134 @@
-#UI and logic for generation of AVI file from bunch of images
-#it knows from director bean class where images are stored (there is also a list file
-#containing list of images that will be frames - needed for transcode) and it create
-#thread to call transcode.
+# UI and logic for generation of a video file from a bunch of images.
+# Uses a list file containing the images that will be frames
+# and creates a subprocess to execute ffmpeg.
-#Limitations: user can destroy dialog, but it will not destroy transcode process!?
-
-
-
-from gi.repository import Gdk, Gtk, GObject, GLib
import os
-import re
-from threading import *
+import signal
-from fract4d import animation, fractconfig
+from gi.repository import Gdk, Gtk, GLib
class AVIGeneration:
- def __init__(self,animation):
- self.dialog=Gtk.Dialog(
- title="Generating AVI file...",
+ def __init__(self, animation, parent):
+ self.animation = animation
+ self.converterpath = parent.converterpath
+ self.fh_err = None
+ self.pid = None
+
+ self.dialog = Gtk.Dialog(
+ transient_for=parent,
+ title="Generating video file",
modal=True,
- destroy_with_parent=True)
-
+ destroy_with_parent=True
+ )
self.dialog.add_buttons(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL)
- self.pbar = Gtk.ProgressBar()
- self.pbar.set_text("Please wait...")
- self.dialog.vbox.pack_start(self.pbar,True,True,0)
+ self.spinner = Gtk.Spinner()
+ label = Gtk.Label.new("Please wait...")
+ self.dialog.vbox.pack_start(self.spinner, True, True, 10)
+ self.dialog.vbox.pack_start(label, True, True, 10)
geometry = Gdk.Geometry()
geometry.min_aspect = 3.5
geometry.max_aspect = 3.5
self.dialog.set_geometry_hints(None, geometry, Gdk.WindowHints.ASPECT)
- self.animation=animation
- self.delete_them=-1
def generate_avi(self):
- #-------getting all needed information------------------------------
- folder_png=self.animation.get_png_dir()
- if folder_png[-1]!="/":
- folder_png=folder_png+"/"
-
- avi_file=self.animation.get_avi_file()
- width=self.animation.get_width()
- height=self.animation.get_height()
- framerate=self.animation.get_framerate()
- yield True
- #------------------------------------------------------------------
-
- try:
- if self.running==False:
- yield False
- return
-
- if not(os.path.exists(folder_png+"list")):#check if image listing already exist
- Gdk.threads_enter()
- error_dlg = Gtk.MessageDialog(
- self.dialog,
- Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
- "In directory: %s there is no listing file. Cannot continue" %(folder_png))
- response=error_dlg.run()
- error_dlg.destroy()
- Gdk.threads_leave()
- event = Gdk.Event(Gdk.DELETE)
- self.dialog.emit('delete_event', event)
- yield False
- return
-
- #--------calculating total number of frames------------
- count=self.animation.get_total_frames()
- #------------------------------------------------------
- #calling transcode
- swap=""
- if self.animation.get_redblue():
- swap="-k"
-
- call="transcode -z -i %slist -x imlist,null -g %dx%d -y ffmpeg,null -F mpeg4 -f %d -o %s -H 0 --use_rgb %s 2>/dev/null"%(folder_png,width,height,framerate,avi_file,swap)
- dt=DummyThread(call,self.pbar,float(count))
- dt.start()
-
- working=True
- while(working):
- dt.join(0.5) #refresh gtk every 0.5 seconds
- working=dt.isAlive()
- yield True
-
- if self.running==False:
- yield False
- return
- yield True
- except Exception as err:
- self.running=False
- self.error=True
- Gdk.threads_enter()
- error_dlg = Gtk.MessageDialog(self.dialog,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
- _("Error during generation of avi file: %s" % err))
+ folder_png = self.animation.get_png_dir()
+ list_file = os.path.join(folder_png, "list")
+ video_file = self.animation.get_avi_file()
+ framerate = self.animation.get_framerate()
+
+ if not os.path.exists(list_file):
+ error_dlg = Gtk.MessageDialog(
+ transient_for=self.dialog,
+ title="Cannot continue",
+ modal=True,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.ERROR,
+ buttons=Gtk.ButtonsType.OK,
+ text="In directory: %s there is no listing file" % (folder_png)
+ )
error_dlg.run()
error_dlg.destroy()
- Gdk.threads_leave()
- event = Gdk.Event(Gdk.DELETE)
- self.dialog.emit('delete_event', event)
- yield False
return
- if self.running==False:
- yield False
- return
- self.running=False
- self.dialog.destroy()
- yield False
-
+
+ self.spinner.start()
+
+ # calling ffmpeg
+ # https://trac.ffmpeg.org/wiki/Concatenate
+ # https://trac.ffmpeg.org/wiki/Encode/VP9
+ call = [
+ self.converterpath, "-nostdin", "-y",
+ "-loglevel", "error", "-hide_banner",
+ "-r", str(framerate), "-f", "concat", "-safe", "0", "-i", list_file]
+ if self.animation.get_redblue():
+ call.extend(["-vf", "colorchannelmixer=rr=0:rb=1:br=1:bb=0"])
+ call.extend([
+ "-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0",
+ "-r", str(framerate), video_file])
+ self.pid, fd_in, fd_out, fd_err = GLib.spawn_async(call,
+ flags=GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+ standard_output=False, standard_error=True)
+ self.fh_err = os.fdopen(fd_err, "r")
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, self.pid, self.video_complete)
+ GLib.io_add_watch(self.fh_err, GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, self.video_error)
+
+ def video_error(self, source, condition):
+ error_text = source.read()
+ print(error_text)
+ error_dlg = Gtk.MessageDialog(
+ transient_for=self.dialog,
+ title=_("Error generating video file"),
+ modal=True,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.ERROR,
+ buttons=Gtk.ButtonsType.OK,
+ text=error_text)
+ error_dlg.run()
+ error_dlg.destroy()
+
+ def video_complete(self, pid, status):
+ self.spinner.stop()
+ self.running = False
+ if status == 0:
+ self.error = False
+ else:
+ self.error = True
+ self.dialog.emit('delete_event', Gdk.Event())
def show(self):
- #------------------------------------------------------------
self.dialog.show_all()
- self.running=True
- self.error=False
- task=self.generate_avi()
- GLib.idle_add(task.__next__)
+ self.running = True
+ self.error = False
+
+ try:
+ self.generate_avi()
+ except GLib.GError as err:
+ error_dlg = Gtk.MessageDialog(
+ transient_for=self.dialog,
+ title=_("Error executing video converter"),
+ modal=True,
+ destroy_with_parent=True,
+ message_type=Gtk.MessageType.ERROR,
+ buttons=Gtk.ButtonsType.OK,
+ text=str(err))
+ error_dlg.run()
+ error_dlg.destroy()
+
+ return -1
+
+ result = 0
response = self.dialog.run()
- if response != Gtk.ResponseType.CANCEL:
- if self.running==True: #destroy by user
- self.running=False
- self.dialog.destroy()
- return 1
- else:
- if self.error==True: #error
- self.dialog.destroy()
- return -1
- else: #everything ok
- self.dialog.destroy()
- return 0
- else: #cancel pressed
- self.running=False
- self.dialog.destroy()
- return 1
-
-#thread for calling transcode
-class DummyThread(Thread):
- def __init__(self,s,pbar,count):
- Thread.__init__(self)
- self.s=s
- self.pbar=pbar
- self.count=count
-
- def run(self):
- #os.system(self.s) <----this is little faster, but user can't see any progress
- reg=re.compile("\[.*-.*\]")
- pipe=os.popen(self.s)
- for line in pipe:
- m=reg.search(line)
- if m!=None:
- cur=re.split("-",m.group())[1][0:-1]
- self.pbar.set_fraction(float(cur)/self.count)
- pipe.close()
- return
+ if response == Gtk.ResponseType.CANCEL:
+ # cancel pressed
+ result = 1
+ else:
+ if self.running is True: # destroy by user
+ GLib.spawn_close_pid(self.pid)
+ os.kill(self.pid, signal.SIGTERM)
+ result = 1
+ elif self.error is True: # error
+ result = -1
+
+ if self.fh_err:
+ self.fh_err.close()
+ self.dialog.destroy()
+ return result
diff --git a/fract4dgui/DlgAdvOpt.py b/fract4dgui/DlgAdvOpt.py
index 3b227b419..083925a20 100644
--- a/fract4dgui/DlgAdvOpt.py
+++ b/fract4dgui/DlgAdvOpt.py
@@ -5,19 +5,20 @@
# DlgAdvOpt.py: dialog for advanced interpolation options for director
#
-from gi.repository import Gtk
-from gi.repository import GObject
-import os
-import re
from threading import *
+from gi.repository import Gtk
class DlgAdvOptions:
-
- def __init__(self,current_kf,animation):
- self.dialog=Gtk.Dialog("Keyframe advanced options...",None,
- Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- (Gtk.STOCK_OK,Gtk.ResponseType.OK,Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL))
+ def __init__(self,current_kf,animation,parent):
+ self.dialog=Gtk.Dialog(
+ transient_for=parent,
+ title="Keyframe advanced options",
+ modal=True,
+ destroy_with_parent=True
+ )
+ self.dialog.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK,
+ Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
self.current_kf=current_kf
self.animation=animation
diff --git a/fract4dgui/FCTGen.py b/fract4dgui/FCTGen.py
index a6a7e0594..193d9e047 100644
--- a/fract4dgui/FCTGen.py
+++ b/fract4dgui/FCTGen.py
@@ -9,29 +9,26 @@
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
-#
+#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
-#
+#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
-
-from gi.repository import Gtk
-from gi.repository import GObject
-import re
import math
-import sys
import os
-import math
-from fract4d import animation, fractal, fc, fractconfig
-class FCTGeneration:
+from gi.repository import Gtk, GObject
+
+from fract4d import fractal, fc, fractconfig
+
+class FCTGeneration:
def __init__(self,dir_bean,parent):
self.dialog=Gtk.Dialog(
"Generating .fct files...",parent,
@@ -52,7 +49,7 @@ def generate_fct(self):
self.show_error("Could not find gnofract4d version. Can not continue")
yield False
return
- #-------------loads gnofract4d libs----------------------------
+ # -------------loads gnofract4d libs----------------------------
try:
self.fractal=fractal
self.compiler=fc.Compiler()
@@ -64,8 +61,8 @@ def generate_fct(self):
self.show_error("Gnofract4d libs could not be found")
yield False
return
- #--------------------------------------------------------------
- #find values from base keyframe first
+ # --------------------------------------------------------------
+ # find values from base keyframe first
try:
ret=self.find_values(self.dir_bean.get_base_keyframe())
if len(ret)==11:
@@ -79,7 +76,7 @@ def generate_fct(self):
yield False
return
try:
- #find values and duration from all keyframes
+ # find values and duration from all keyframes
for i in range(self.dir_bean.keyframes_count()):
ret=self.find_values(self.dir_bean.get_keyframe_filename(i))
if len(ret)==11:
@@ -93,14 +90,14 @@ def generate_fct(self):
yield False
return
self.durations.append(self.dir_bean.get_keyframe_duration(i))
- #interpolate and write .fct files
+ # interpolate and write .fct files
for i in range(self.dir_bean.keyframes_count()):
- if self.running==False:
+ if self.running is False:
yield False
break
self.write_fct_file(i)
percent=float(i+1)/float(self.dir_bean.keyframes_count())
- if self.running==False:
+ if self.running is False:
break
self.pbar.set_fraction(percent)
self.pbar.set_text(str(percent*100)+"%")
@@ -110,10 +107,10 @@ def generate_fct(self):
yield False
return
- if self.running==False:
+ if self.running is False:
yield False
return
- self.running=False
+ self.running = False
self.dialog.destroy()
yield False
@@ -130,7 +127,7 @@ def find_version(self):
pipe.close()
return version
- #finding x,y,z,w,size values from argument file and returns them as tuple
+ # finding x,y,z,w,size values from argument file and returns them as tuple
def find_values(self,file):
f=self.fractal.T(self.compiler)
f.loadFctFile(open(file))
@@ -147,18 +144,18 @@ def find_values(self,file):
zw=f.params[f.ZWANGLE]
return (x,y,z,w,size,xy,xz,xw,yz,yw,zw)
- #interpolate values and writes .fct files
+ # interpolate values and writes .fct files
def write_fct_file(self,iteration):
- #sum of all frames, needed for padding output files
+ # sum of all frames, needed for padding output files
sumN=sum(self.durations)
lenN=len(str(sumN))
sumBefore=sum(self.durations[0:iteration])
- #current duration
+ # current duration
N=self.durations[iteration]
- #get content of file
+ # get content of file
f=self.fractal.T(self.compiler)
f.loadFctFile(open(self.dir_bean.get_base_keyframe()))
- #get all values
+ # get all values
x1=float(self.values[iteration][f.XCENTER])
x2=float(self.values[iteration+1][f.XCENTER])
y1=float(self.values[iteration][f.YCENTER])
@@ -181,9 +178,9 @@ def write_fct_file(self,iteration):
yw2=float(self.values[iteration+1][f.YWANGLE])
zw1=float(self.values[iteration][f.ZWANGLE])
zw2=float(self.values[iteration+1][f.ZWANGLE])
- #------------find direction for angles----------------------
+ # ------------find direction for angles----------------------
to_right=[False]*6
- #----------xy--------------
+ # ----------xy--------------
dir_xy=self.dir_bean.get_directions(iteration)[0]
if dir_xy==0:
if abs(xy2-xy1)xy1:
+ if to_right[0] is False and xy2>xy1:
xy1=xy1+2*math.pi
- #--------------------------
- #----------xz--------------
+ # --------------------------
+ # ----------xz--------------
dir_xz=self.dir_bean.get_directions(iteration)[1]
if dir_xz==0:
if abs(xz2-xz1)xz1:
+ if to_right[1] is False and xz2>xz1:
xz1=xz1+2*math.pi
- #--------------------------
- #----------xw--------------
+ # --------------------------
+ # ----------xw--------------
dir_xw=self.dir_bean.get_directions(iteration)[2]
if dir_xw==0:
if abs(xw2-xw1)xw1:
+ if to_right[2] is False and xw2>xw1:
xw1=xw1+2*math.pi
- #--------------------------
- #----------yz--------------
+ # --------------------------
+ # ----------yz--------------
dir_yz=self.dir_bean.get_directions(iteration)[3]
if dir_yz==0:
if abs(yz2-yz1)yz1:
+ if to_right[3] is False and yz2>yz1:
yz1=yz1+2*math.pi
- #--------------------------
- #----------yw--------------
+ # --------------------------
+ # ----------yw--------------
dir_yw=self.dir_bean.get_directions(iteration)[4]
if dir_yw==0:
if abs(yw2-yw1)yw1:
+ if to_right[4] is False and yw2>yw1:
yw1=yw1+2*math.pi
- #--------------------------
- #----------zw--------------
+ # --------------------------
+ # ----------zw--------------
dir_zw=self.dir_bean.get_directions(iteration)[5]
if dir_zw==0:
if abs(zw2-zw1)zw1:
+ if to_right[5] is False and zw2>zw1:
zw1=zw1+2*math.pi
- #--------------------------
- #------------------------------------------------------------
+ # --------------------------
+ # ------------------------------------------------------------
for i in range(N+1):
- #depending on interpolation type, mu constant get different values from 0 to 1
+ # depending on interpolation type, mu constant get different values from 0 to 1
int_type=self.dir_bean.get_keyframe_int(iteration)
mu=float(i)/float(N)
if int_type==INT_LINEAR:
@@ -310,7 +307,7 @@ def write_fct_file(self,iteration):
mu=(math.exp(mu)-1)/(math.e-1)
else:
mu=(1-math.cos(mu*math.pi))/2
- #calculating new values
+ # calculating new values
new_x=x1*(1-mu)+x2*mu
new_y=y1*(1-mu)+y2*mu
new_z=z1*(1-mu)+z2*mu
@@ -334,7 +331,7 @@ def write_fct_file(self,iteration):
new_zw=zw1*(1-mu)+zw2*mu
while new_zw>math.pi:
new_zw=new_zw-2*math.pi
- #replacing them in fractal
+ # replacing them in fractal
f.params[f.XCENTER]=new_x
f.params[f.YCENTER]=new_y
f.params[f.ZCENTER]=new_z
@@ -346,7 +343,7 @@ def write_fct_file(self,iteration):
f.params[f.YZANGLE]=new_yz
f.params[f.YWANGLE]=new_yw
f.params[f.ZWANGLE]=new_zw
- #writes .fct file
+ # writes .fct file
folder=self.dir_bean.get_fct_dir()
if folder[-1]!="/":
folder=folder+"/"
@@ -356,15 +353,15 @@ def write_fct_file(self,iteration):
def show_error(self,s):
self.running=False
self.error=True
- Gtk.threads_enter()
+ Gdk.threads_enter()
error_dlg = Gtk.MessageDialog(
self.dialog,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,s)
error_dlg.run()
error_dlg.destroy()
- Gtk.threads_leave()
- event = Gdk.Event(Gdk.DELETE)
+ Gdk.threads_leave()
+ event = Gdk.Event(Gdk.EventType.DELETE)
self.dialog.emit('delete_event', event)
def show(self):
@@ -375,18 +372,18 @@ def show(self):
GObject.idle_add(task.__next__)
response = self.dialog.run()
if response != Gtk.ResponseType.CANCEL:
- if self.running==True: #destroy by user
+ if self.running is True: # destroy by user
self.running=False
self.dialog.destroy()
return 1
else:
- if self.error==True: #error
+ if self.error is True: # error
self.dialog.destroy()
return -1
- else: #everything ok
+ else: # everything ok
self.dialog.destroy()
return 0
- else: #cancel pressed
+ else: # cancel pressed
self.running=False
self.dialog.destroy()
return 1
diff --git a/fract4dgui/PNGGen.py b/fract4dgui/PNGGen.py
index 7611db76d..9982f2496 100644
--- a/fract4dgui/PNGGen.py
+++ b/fract4dgui/PNGGen.py
@@ -1,26 +1,24 @@
-#UI and logic for generation PNG images
-#It gets all information from director bean class, gets all values, and,
-#in special thread, while it finds in-between values it call gtkfractal.HighResolution
-#to create images
+# UI and logic for generation PNG images
+# It gets all information from director bean class, gets all values, and,
+# in special thread, while it finds in-between values it call gtkfractal.HighResolution
+# to create images
-import re
-import math
-import sys
import os
from threading import *
-from gi.repository import Gdk, Gtk, GObject, GLib
+from gi.repository import Gdk, Gtk, GLib
from . import gtkfractal, hig
-from fract4d import fractal,fracttypes, animation
+from fract4d import fractal
running=False
thread_error=False
class PNGGeneration(Gtk.Dialog,hig.MessagePopper):
- def __init__(self,animation,compiler):
+ def __init__(self, animation, compiler, parent):
Gtk.Dialog.__init__(
self,
+ transient_for=parent,
title="Generating images...",
modal=True, destroy_with_parent=True)
@@ -40,14 +38,14 @@ def __init__(self,animation,compiler):
self.set_geometry_hints(None, geometry, Gdk.WindowHints.ASPECT)
self.anim=animation
- #-------------loads compiler----------------------------
+ # -------------loads compiler----------------------------
self.compiler=compiler
def generate_png(self):
global running
durations=[]
- #--------find values and duration from all keyframes------------
+ # --------find values and duration from all keyframes------------
try:
durations = self.anim.get_keyframe_durations()
except Exception as err:
@@ -55,7 +53,7 @@ def generate_png(self):
yield False
return
- #---------------------------------------------------------------
+ # ---------------------------------------------------------------
create_all_images=self.to_create_images_again()
gt=GenerationThread(
durations,self.anim,
@@ -68,13 +66,12 @@ def generate_png(self):
working=gt.isAlive()
yield True
- if thread_error==True:
+ if thread_error is True:
self.show_error("Error during image generation", "Unknown")
yield False
return
-
- if running==False:
+ if running is False:
yield False
return
running=False
@@ -86,7 +83,7 @@ def to_create_images_again(self):
filelist = self.anim.create_list()
for f in filelist:
if os.path.exists(f):
- Gtk.threads_enter()
+ Gdk.threads_enter()
try:
folder_png = self.anim.get_png_dir()
response = self.ask_question(
@@ -95,10 +92,10 @@ def to_create_images_again(self):
except Exception as err:
print(err)
- Gtk.threads_leave()
+ Gdk.threads_leave()
raise
- Gtk.threads_leave()
+ Gdk.threads_leave()
if response==Gtk.ResponseType.ACCEPT:
create=False
@@ -109,7 +106,6 @@ def to_create_images_again(self):
return create
def show_error(self,message,secondary):
- running=False
self.error=True
Gdk.threads_enter()
error_dlg = hig.ErrorAlert(
@@ -119,7 +115,7 @@ def show_error(self,message,secondary):
error_dlg.run()
error_dlg.destroy()
Gdk.threads_leave()
- event = Gdk.Event(Gdk.DELETE)
+ event = Gdk.Event(Gdk.EventType.DELETE)
self.emit('delete_event', event)
def show(self):
@@ -131,34 +127,34 @@ def show(self):
GLib.idle_add(task.__next__)
response = self.run()
if response != Gtk.ResponseType.CANCEL:
- if running==True: #destroy by user
+ if running is True: # destroy by user
running=False
self.destroy()
return 1
else:
- if self.error==True: #error
+ if self.error is True: # error
self.destroy()
return -1
- else: #everything ok
+ else: # everything ok
self.destroy()
return 0
- else: #cancel pressed
+ else: # cancel pressed
running=False
self.destroy()
return 1
-#thread to interpolate values and calls generation of .png files
+# thread to interpolate values and calls generation of .png files
class GenerationThread(Thread):
def __init__(
- self,durations,animation,compiler,
- create_all_images,pbar_image,pbar_overall):
+ self,durations,animation,compiler,
+ create_all_images,pbar_image,pbar_overall):
Thread.__init__(self)
self.durations=durations
self.anim=animation
self.create_all_images=create_all_images
self.pbar_image=pbar_image
self.pbar_overall=pbar_overall
- #initializing progress bars
+ # initializing progress bars
self.pbar_image.set_fraction(0)
self.pbar_overall.set_fraction(0)
self.pbar_overall.set_text("0/"+str(sum(self.durations)+1))
@@ -171,7 +167,7 @@ def __init__(
self.current.connect('status-changed', self.onStatusChanged)
self.current.connect('progress-changed', self.onProgressChanged)
- #semaphore to signalize that image generation is finished
+ # semaphore to signalize that image generation is finished
self.next_image=Semaphore(1)
def onProgressChanged(self,f,progress):
@@ -179,31 +175,30 @@ def onProgressChanged(self,f,progress):
if running:
self.pbar_image.set_fraction(progress/100.0)
- #one image generation complete - tell (with "semaphore" self.next_image) we can continue
+ # one image generation complete - tell (with "semaphore" self.next_image) we can continue
def onStatusChanged(self,f,status_val):
if status_val == 0:
- #release semaphore
+ # release semaphore
self.next_image.release()
def run(self):
global thread_error,running
import traceback
try:
- #first generates image from base keyframe
+ # first generates image from base keyframe
self.generate_base_keyframe()
- #pass through all keyframes and generates inter images
+ # pass through all keyframes and generates inter images
for i in range(self.anim.keyframes_count()-1):
self.generate_images(i)
- if running==False:
+ if running is False:
return
- #wait for last image to finish rendering
+ # wait for last image to finish rendering
self.next_image.acquire()
- #generate list file
- list = self.anim.create_list()
+ # generate list file
lfilename = os.path.join(self.anim.get_png_dir(), "list")
- lfile = open(lfilename,"w")
- print("\n".join(list), file=lfile)
- lfile.close()
+ with open(lfilename, "w") as lfile:
+ for imagefile in self.anim.create_list():
+ print("file '%s'" % imagefile, file=lfile)
except:
traceback.print_exc()
@@ -211,7 +206,6 @@ def run(self):
running=False
return
-
def generate_base_keyframe(self):
f=fractal.T(self.compiler)
fh = open(self.anim.get_keyframe_filename(0))
@@ -221,33 +215,32 @@ def generate_base_keyframe(self):
fh.close()
self.next_image.acquire()
- #writes .fct file if user wanted that
+ # writes .fct file if user wanted that
if self.anim.get_fct_enabled():
f.save(open(self.anim.get_fractal_filename(0),"w"))
- #check if image already exist and user wants to leave it or not
- if not(os.path.exists(self.anim.get_image_filename(0)) and self.create_all_images==False): #check if image already exist
+ # check if image already exist and user wants to leave it or not
+ if not(os.path.exists(self.anim.get_image_filename(0)) and self.create_all_images is False): # check if image already exist
self.current.set_fractal(f)
self.current.reset_render()
self.current.draw_image(self.anim.get_image_filename(0))
else:
- #just release semaphore
+ # just release semaphore
self.next_image.release()
return
- #main method for generating images
- #it generates images between iteration-th-1 and iteration-th keyframe
- #first, it gets border values (keyframe values)
- #(values - x,y,z,w,size,angles,formula parameters)
- #then, in a loop, it generates inter values, fill fractal class with it and
- #calls gtkfractal.HighResolution to generate images
+ # main method for generating images
+ # it generates images between iteration-th-1 and iteration-th keyframe
+ # first, it gets border values (keyframe values)
+ # (values - x,y,z,w,size,angles,formula parameters)
+ # then, in a loop, it generates inter values, fill fractal class with it and
+ # calls gtkfractal.HighResolution to generate images
def generate_images(self,iteration):
global running
- #sum of all frames, needed for padding output files
+ # sum of all frames, needed for padding output files
sumN=sum(self.durations)
- lenN=len(str(sumN))
- #number of images already generated
+ # number of images already generated
sumBefore=sum(self.durations[0:iteration])
- #current duration
+ # current duration
N=self.durations[iteration]
f_prev=fractal.T(self.compiler)
@@ -264,15 +257,15 @@ def generate_images(self,iteration):
finally:
fh.close()
- #------------------------------------------------------------
- #loop to generate images between current (iteration-th) and previous keyframe
+ # ------------------------------------------------------------
+ # loop to generate images between current (iteration-th) and previous keyframe
for i in range(1,N+1):
- #but, first, wait for previous image to finish rendering
+ # but, first, wait for previous image to finish rendering
self.next_image.acquire()
- #check if user canceled us
- if running==False:
+ # check if user canceled us
+ if running is False:
return
- #update progress bar
+ # update progress bar
percent=float((sumBefore+i))/(sumN+1)
self.pbar_overall.set_fraction(percent)
self.pbar_overall.set_text(str(sumBefore+i)+"/"+str(sumN+1))
@@ -282,16 +275,16 @@ def generate_images(self,iteration):
mu=self.anim.get_mu(int_type, float(i)/float(N))
f_frame = f_prev.blend(f_next,mu)
- #writes .fct file if user wanted that
+ # writes .fct file if user wanted that
if self.anim.get_fct_enabled():
f_frame.save(open(self.anim.get_fractal_filename(sumBefore+i),"w"))
- #check if image already exist and user wants to leave it or not
- if not(os.path.exists(self.anim.get_image_filename(sumBefore+i)) and self.create_all_images==False): #check if image already exist
+ # check if image already exist and user wants to leave it or not
+ if not(os.path.exists(self.anim.get_image_filename(sumBefore+i)) and self.create_all_images is False): # check if image already exist
self.current.set_fractal(f_frame)
self.current.reset_render()
self.current.draw_image(self.anim.get_image_filename(sumBefore+i))
else:
- #just release semaphore
+ # just release semaphore
self.next_image.release()
return
diff --git a/fract4dgui/director.py b/fract4dgui/director.py
index cb376be12..fe43eefd2 100644
--- a/fract4dgui/director.py
+++ b/fract4dgui/director.py
@@ -1,27 +1,22 @@
-#UI for gathering needed data and storing them in director bean class
-#then it calls PNGGeneration, and (if everything was OK) AVIGeneration
+# UI for gathering needed data and storing them in director bean class
+# then it calls PNGGeneration, and (if everything was OK) AVIGeneration
-#TODO: change default directory when selecting new according to already set item
-#(for temp dirs, avi and fct files selections)
-
-from gi.repository import Gdk
-from gi.repository import Gtk
-from gi.repository import GObject
-from gi.repository import GLib
+# TODO: change default directory when selecting new according to already set item
+# (for temp dirs, avi and fct files selections)
import os
import fnmatch
-import pickle
-import sys
import tempfile
+from gi.repository import Gdk, Gtk, GObject
+
from . import dialog, hig
from fract4d import animation, fractconfig
from . import PNGGen,AVIGen,DlgAdvOpt,director_prefs
class UserCancelledError(Exception):
- pass
+ pass
class SanityCheckError(Exception):
"The type of exception which is thrown when animation sanity checks fail"
@@ -43,35 +38,35 @@ def check_for_keyframe_clash(self,keyframe,fct_dir):
_("Keyframe %s is in the temporary .fct directory and could be overwritten. Please change temp directory." % keyframe))
def check_fct_file_sanity(self):
- #things to check with fct temp dir
+ # things to check with fct temp dir
if not self.animation.get_fct_enabled():
# we're not generating .fct files, so this is superfluous
return
- #check fct temp dir is set
+ # check fct temp dir is set
if self.animation.get_fct_dir()=="":
raise SanityCheckError(
_("Directory for temporary .fct files not set"))
- #check if it is directory
+ # check if it is directory
if not os.path.isdir(self.animation.get_fct_dir()):
raise SanityCheckError(
_("Path for temporary .fct files is not a directory"))
fct_path=self.animation.get_fct_dir()
- #check if any keyframe fct files are in temp fct dir
+ # heck if any keyframe fct files are in temp fct dir
for i in range(self.animation.keyframes_count()):
keyframe = self.animation.get_keyframe_filename(i)
self.check_for_keyframe_clash(keyframe,fct_path)
- #check if there are any .fct files in temp fct dir
+ # check if there are any .fct files in temp fct dir
has_any=False
for file in os.listdir(fct_path):
if fnmatch.fnmatch(file,"*.fct"):
has_any=True
- if has_any==True:
+ if has_any is True:
response = self.ask_question(
_("Directory for temporary .fct files contains other .fct files"),
_("These may be overwritten. Proceed?"))
@@ -79,36 +74,36 @@ def check_fct_file_sanity(self):
raise UserCancelledError()
return
- #throws SanityCheckError if there was a problem
+ # throws SanityCheckError if there was a problem
def check_sanity(self):
- #check if at least two keyframes exist
+ # check if at least two keyframes exist
if self.animation.keyframes_count()<2:
raise SanityCheckError(_("There must be at least two keyframes"))
- #check png temp dir is set
+ # check png temp dir is set
if self.animation.get_png_dir()=="":
raise SanityCheckError(
_("Directory for temporary .png files not set"))
- #check if it is directory
+ # check if it is directory
if not os.path.isdir(self.animation.get_png_dir()):
raise SanityCheckError(
_("Path for temporary .png files is not a directory"))
- #check avi file is set
+ # check avi file is set
if self.animation.get_avi_file()=="":
raise SanityCheckError(_("Output AVI file name not set"))
self.check_fct_file_sanity()
- #wrapper to show dialog for selecting .fct file
- #returns selected file or empty string
+ # wrapper to show dialog for selecting .fct file
+ # returns selected file or empty string
def get_fct_file(self):
temp_file=""
- dialog = Gtk.FileChooserDialog("Choose keyframe...",None,Gtk.FileChooserAction.OPEN,
+ dialog = Gtk.FileChooserDialog("Choose keyframe...",self,Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.OK)
- #----setting filters---------
+ # ----setting filters---------
filter = Gtk.FileFilter()
filter.set_name("gnofract4d files (*.fct)")
filter.add_pattern("*.fct")
@@ -117,35 +112,36 @@ def get_fct_file(self):
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
- #----------------------------
+ # ----------------------------
response = dialog.run()
if response == Gtk.ResponseType.OK:
temp_file=dialog.get_filename()
dialog.destroy()
return temp_file
- #wrapper to show dialog for selecting .avi file
- #returns selected file or empty string
+ # wrapper to show dialog for selecting .avi file
+ # returns selected file or empty string
def get_avi_file(self):
temp_file=""
- dialog = Gtk.FileChooserDialog("Save AVI file...",None,Gtk.FileChooserAction.SAVE,
+ dialog = Gtk.FileChooserDialog("Save AVI file...",self,Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.OK)
+ dialog.set_filename(self.txt_temp_avi.get_text())
response = dialog.run()
if response == Gtk.ResponseType.OK:
temp_file=dialog.get_filename()
dialog.destroy()
return temp_file
- #wrapper to show dialog for selecting .cfg file
- #returns selected file or empty string
+ # wrapper to show dialog for selecting .cfg file
+ # returns selected file or empty string
def get_cfg_file_save(self):
temp_file=""
- dialog = Gtk.FileChooserDialog("Save animation...",None,Gtk.FileChooserAction.SAVE,
+ dialog = Gtk.FileChooserDialog("Save animation...",self,Gtk.FileChooserAction.SAVE,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.OK)
dialog.set_current_name("animation.fcta")
- #----setting filters---------
+ # ----setting filters---------
filter = Gtk.FileFilter()
filter.set_name("gnofract4d animation files (*.fcta)")
filter.add_pattern("*.fcta")
@@ -154,21 +150,21 @@ def get_cfg_file_save(self):
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
- #----------------------------
+ # ----------------------------
response = dialog.run()
if response == Gtk.ResponseType.OK:
temp_file=dialog.get_filename()
dialog.destroy()
return temp_file
- #wrapper to show dialog for selecting .fct file
- #returns selected file or empty string
+ # wrapper to show dialog for selecting .fct file
+ # returns selected file or empty string
def get_cfg_file_open(self):
temp_file=""
- dialog = Gtk.FileChooserDialog("Choose animation...",None,Gtk.FileChooserAction.OPEN,
+ dialog = Gtk.FileChooserDialog("Choose animation...",self,Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.OK)
- #----setting filters---------
+ # ----setting filters---------
filter = Gtk.FileFilter()
filter.set_name("gnofract4d animation files (*.fcta)")
filter.add_pattern("*.fcta")
@@ -177,7 +173,7 @@ def get_cfg_file_open(self):
filter.set_name("All files")
filter.add_pattern("*")
dialog.add_filter(filter)
- #----------------------------
+ # ----------------------------
response = dialog.run()
if response == Gtk.ResponseType.OK:
temp_file=dialog.get_filename()
@@ -188,9 +184,8 @@ def get_cfg_file_open(self):
def temp_avi_clicked(self,widget, data=None):
avi=self.get_avi_file()
- if avi!="":
+ if avi:
self.txt_temp_avi.set_text(avi)
- self.animation.set_avi_file(avi)
def output_width_changed(self,widget,data=None):
self.animation.set_width(self.spin_width.get_value())
@@ -219,18 +214,16 @@ def interpolation_type_changed(self,widget,data=None):
self.animation.set_keyframe_int(self.current_select,int(self.cmb_interpolation_type.get_active()))
self.update_model()
- #point of whole program:)
- #first we generate png files and list, then .avi
+ # point of whole program:)
+ # first we generate png files and list, then .avi
def generate(self,create_avi=True):
try:
self.check_sanity()
except SanityCheckError as exn:
self.show_error(_("Cannot Generate Animation"), str(exn))
- return
- except UserCancelledError:
- return
+ raise
- png_gen=PNGGen.PNGGeneration(self.animation, self.compiler)
+ png_gen=PNGGen.PNGGeneration(self.animation, self.compiler, self)
res=png_gen.show()
if res==1:
# user cancelled, but they know that. Stop silently
@@ -242,7 +235,7 @@ def generate(self,create_avi=True):
if not create_avi:
return
- avi_gen=AVIGen.AVIGeneration(self.animation)
+ avi_gen=AVIGen.AVIGeneration(self.animation, self)
res=avi_gen.show()
if res==1:
# user cancelled, but they know that. Stop silently
@@ -259,23 +252,23 @@ def generate_clicked(self, widget, data=None):
def adv_opt_clicked(self,widget,data=None):
if self.current_select==-1:
return
- dlg=DlgAdvOpt.DlgAdvOptions(self.current_select,self.animation)
- res=dlg.show()
+ dlg=DlgAdvOpt.DlgAdvOptions(self.current_select,self.animation,self)
+ dlg.show()
- #before selecting keyframes in list box we must update values of spin boxes in case user typed something in there
+ # before selecting keyframes in list box we must update values of spin boxes in case user typed something in there
def before_selection(self, selection, data=None, *kwargs):
self.spin_duration.update()
self.spin_kf_stop.update()
return True
- #update right box (duration, stop, interpolation type) when keyframe is selected from list
+ # update right box (duration, stop, interpolation type) when keyframe is selected from list
def selection_changed(self,widget, data=None):
(model,it)=self.tv_keyframes.get_selection().get_selected()
- if it!=None:
- #------getting index of selected row-----------
+ if it is not None:
+ # ------getting index of selected row-----------
index=0
it=model.get_iter_first()
- while it!=None:
+ while it is not None:
if self.tv_keyframes.get_selection().iter_is_selected(it):
break
it=model.iter_next(it)
@@ -293,11 +286,11 @@ def selection_changed(self,widget, data=None):
def update_model(self):
(model,it)=self.tv_keyframes.get_selection().get_selected()
- if it!=None:
- #------getting index of selected row-----------
+ if it is not None:
+ # ------getting index of selected row-----------
index=0
it=model.get_iter_first()
- while it!=None:
+ while it is not None:
if self.tv_keyframes.get_selection().iter_is_selected(it):
break
it=model.iter_next(it)
@@ -332,25 +325,25 @@ def add_from_current(self,widget,data=None):
def add_keyframe(self,file):
if file!="":
- #get current seletion
+ # get current seletion
(model,it)=self.tv_keyframes.get_selection().get_selected()
- if it==None: #if it's none, just append at the end of the list
+ if it is None: # if it's none, just append at the end of the list
it=model.append([file,25,1,"Linear"])
- else: #append after currently selected
+ else: # append after currently selected
it=model.insert_after(it,[file,25,1,"Linear"])
- #add to bean with default parameters
+ # add to bean with default parameters
if self.current_select!=-1:
self.animation.add_keyframe(file,25,1,animation.INT_LINEAR,self.current_select+1)
else:
self.animation.add_keyframe(file,25,1,animation.INT_LINEAR)
- #and select newly item
+ # and select newly item
self.tv_keyframes.get_selection().select_iter(it)
- #set default duration
+ # set default duration
self.spin_duration.set_value(25)
- #set default stop
+ # set default stop
self.spin_kf_stop.set_value(1)
- #set default interpolation type
+ # set default interpolation type
self.cmb_interpolation_type.set_active(animation.INT_LINEAR)
def add_keyframe_clicked(self,widget, event):
@@ -363,15 +356,15 @@ def add_keyframe_clicked(self,widget, event):
return False
def remove_keyframe_clicked(self,widget,data=None):
- #is anything selected
+ # is anything selected
(model,it)=self.tv_keyframes.get_selection().get_selected()
- if it!=None:
+ if it is not None:
temp_curr=self.current_select
model.remove(it)
self.animation.remove_keyframe(temp_curr)
def updateGUI(self):
- #keyframes
+ # keyframes
(model,it)=self.tv_keyframes.get_selection().get_selected()
model.clear()
for i in range(self.animation.keyframes_count()-1,-1,-1):
@@ -388,14 +381,14 @@ def updateGUI(self):
model.set(it,3,"Inverse logarithmic")
else:
model.set(it,3,"Cosine")
- #output part
+ # output part
self.txt_temp_avi.set_text(self.animation.get_avi_file())
self.spin_width.set_value(self.animation.get_width())
self.spin_height.set_value(self.animation.get_height())
self.spin_framerate.set_value(self.animation.get_framerate())
self.chk_swapRB.set_active(self.animation.get_redblue())
- #loads configuration file, returns 0 on ok, -1 on error (and displays error message)
+ # loads configuration file, returns 0 on ok, -1 on error (and displays error message)
def load_configuration(self,file):
if file=="":
return -1
@@ -406,37 +399,37 @@ def load_configuration(self,file):
_("Cannot load animation"),
str(err))
return -1
- #set GUI to reflect changes
+ # set GUI to reflect changes
self.updateGUI()
return 0
- #loads configuration from pickled file
+ # loads configuration from pickled file
def load_configuration_clicked(self,widget,data=None):
cfg=self.get_cfg_file_open()
if cfg!="":
self.load_configuration(cfg)
- #reset all field to defaults
+ # reset all field to defaults
def new_configuration_clicked(self,widget,data=None):
self.animation.reset()
self.updateGUI()
- #save configuration in file
+ # save configuration in file
def save_configuration_clicked(self,widget,data=None):
cfg=self.get_cfg_file_save()
if cfg!="":
try:
- result=self.animation.save_animation(cfg)
+ self.animation.save_animation(cfg)
except Exception as err:
self.show_error(
_("Error saving animation"),
str(err))
def preferences_clicked(self,widget,data=None):
- dlg=director_prefs.DirectorPrefs(self.animation)
- res=dlg.show()
+ dlg=director_prefs.DirectorPrefs(self.animation, self)
+ dlg.show()
- #creating window...
+ # creating window...
def __init__(self, main_window, f, conf_file=""):
dialog.T.__init__(
self,
@@ -451,14 +444,15 @@ def __init__(self, main_window, f, conf_file=""):
self.f=f
self.compiler = f.compiler
- #main VBox
- self.box_main=Gtk.VBox(False,0)
- #--------------------menu-------------------------------
+ # main VBox
+ self.box_main=Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
+ self.box_main.set_homogeneous(False)
+ # --------------------menu-------------------------------
self.manager = Gtk.UIManager()
accelgroup = self.manager.get_accel_group()
self.add_accel_group(accelgroup)
- actiongroup = Gtk.ActionGroup("Director")
+ actiongroup = Gtk.ActionGroup.new("Director")
actiongroup.add_actions([
('DirectorMenuAction', None, _('_Director')),
('DirectorEditAction', None, _('_Edit')),
@@ -494,30 +488,27 @@ def __init__(self, main_window, f, conf_file=""):
self.menubar = self.manager.get_widget('/menubar')
self.box_main.pack_start(self.menubar, False, True, 0)
-
- #-----------creating popup menu-------------------------------
- #popup menu for keyframes
+ # -----------creating popup menu-------------------------------
+ # popup menu for keyframes
self.popup_menu=Gtk.Menu()
- self.mnu_pop_add_file=Gtk.MenuItem("From file")
+ self.mnu_pop_add_file=Gtk.MenuItem.new_with_label("From file")
self.popup_menu.append(self.mnu_pop_add_file)
self.mnu_pop_add_file.connect("activate", self.add_from_file, None)
self.mnu_pop_add_file.show()
- self.mnu_pop_add_current=Gtk.MenuItem("From current fractal")
+ self.mnu_pop_add_current=Gtk.MenuItem.new_with_label("From current fractal")
self.popup_menu.append(self.mnu_pop_add_current)
self.mnu_pop_add_current.connect("activate", self.add_from_current, None)
self.mnu_pop_add_current.show()
- #--------------Keyframes box-----------------------------------
+ # --------------Keyframes box-----------------------------------
self.frm_kf = Gtk.Frame.new("Keyframes")
self.frm_kf.set_border_width(10)
- self.hbox_kfs=Gtk.HBox(homogeneous=False,spacing=0)
- self.tbl_keyframes_left=Gtk.Table(n_rows=2,n_columns=2,homogeneous=False)
- self.tbl_keyframes_left.set_row_spacings(10)
- self.tbl_keyframes_left.set_col_spacings(10)
- self.tbl_keyframes_left.set_border_width(10)
-
+ vbox_kfs = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8)
+ button_box_kfs = Gtk.ButtonBox()
+ button_box_kfs.set_layout(Gtk.ButtonBoxStyle.SPREAD)
self.sw=Gtk.ScrolledWindow()
self.sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
+ self.sw.set_size_request(-1, 100)
# filenames=Gtk.ListStore(GObject.TYPE_STRING,
# GObject.TYPE_STRING)
# self.tv_keyframes=Gtk.TreeView(filenames)
@@ -533,7 +524,8 @@ def __init__(self, main_window, f, conf_file=""):
# self.tv_column_name.add_attribute(self.cell_duration, 'text', 0)
filenames=Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_UINT,
GObject.TYPE_UINT, GObject.TYPE_STRING)
- self.tv_keyframes = Gtk.TreeView(filenames)
+ self.tv_keyframes = Gtk.TreeView()
+ self.tv_keyframes.set_model(filenames)
column = Gtk.TreeViewColumn('Keyframes', Gtk.CellRendererText(),text=0)
self.tv_keyframes.append_column(column)
@@ -546,23 +538,25 @@ def __init__(self, main_window, f, conf_file=""):
column = Gtk.TreeViewColumn('Interpolation type', Gtk.CellRendererText(),text=3)
self.tv_keyframes.append_column(column)
- self.sw.add_with_viewport(self.tv_keyframes)
+ self.sw.add(self.tv_keyframes)
self.tv_keyframes.get_selection().connect("changed",self.selection_changed,None)
self.tv_keyframes.get_selection().set_select_function(self.before_selection,None)
self.current_select=-1
- self.tbl_keyframes_left.attach(self.sw,0,2,0,1)
self.btn_add_keyframe=Gtk.Button.new_from_stock(Gtk.STOCK_ADD)
#self.btn_add_keyframe.connect("clicked",self.add_keyframe_clicked,None)
self.btn_add_keyframe.connect_object("event",self.add_keyframe_clicked,self.popup_menu)
- self.tbl_keyframes_left.attach(self.btn_add_keyframe,0,1,1,2,0,0)
self.btn_remove_keyframe=Gtk.Button.new_from_stock(Gtk.STOCK_REMOVE)
self.btn_remove_keyframe.connect("clicked",self.remove_keyframe_clicked,None)
- self.tbl_keyframes_left.attach(self.btn_remove_keyframe,1,2,1,2,0,0)
- self.hbox_kfs.pack_start(self.tbl_keyframes_left,True,True,10)
- self.frm_kf.add(self.hbox_kfs)
+ button_box_kfs.pack_start(self.btn_add_keyframe, True, True, 0)
+ button_box_kfs.pack_start(self.btn_remove_keyframe, True, True, 0)
+
+ vbox_kfs.pack_start(self.sw, True, True, 0)
+ vbox_kfs.pack_start(button_box_kfs, True, True, 10)
+
+ self.frm_kf.add(vbox_kfs)
self.box_main.pack_start(self.frm_kf,True,True,0)
# current keyframe box
@@ -571,31 +565,33 @@ def __init__(self, main_window, f, conf_file=""):
self.box_main.pack_start(self.current_kf,True,True,0)
- self.tbl_keyframes_right=Gtk.Table(n_rows=4,n_columns=2,homogeneous=True)
- self.tbl_keyframes_right.set_row_spacings(10)
- self.tbl_keyframes_right.set_col_spacings(10)
+ self.tbl_keyframes_right = Gtk.Grid()
+ self.tbl_keyframes_right.set_column_homogeneous(True)
+ self.tbl_keyframes_right.set_row_homogeneous(True)
+ self.tbl_keyframes_right.set_row_spacing(10)
+ self.tbl_keyframes_right.set_column_spacing(10)
self.tbl_keyframes_right.set_border_width(10)
- self.lbl_duration=Gtk.Label(label="Duration")
- self.tbl_keyframes_right.attach(self.lbl_duration,0,1,0,1)
+ self.lbl_duration = Gtk.Label(label="Duration:")
+ self.tbl_keyframes_right.attach(self.lbl_duration,0,0,1,1)
- adj_duration = Gtk.Adjustment(25,1,10000,1,10)
+ adj_duration = Gtk.Adjustment.new(25,1,10000,1,10,0)
self.spin_duration = Gtk.SpinButton()
self.spin_duration.set_adjustment(adj_duration)
self.spin_duration.connect("output",self.duration_changed,None)
- self.tbl_keyframes_right.attach(self.spin_duration,1,2,0,1)
+ self.tbl_keyframes_right.attach(self.spin_duration,1,0,1,1)
self.lbl_kf_stop=Gtk.Label(label="Keyframe stopped for:")
- self.tbl_keyframes_right.attach(self.lbl_kf_stop,0,1,1,2)
+ self.tbl_keyframes_right.attach(self.lbl_kf_stop,0,1,1,1)
- adj_kf_stop = Gtk.Adjustment(1,1,10000,1,10)
+ adj_kf_stop = Gtk.Adjustment.new(1,1,10000,1,10,0)
self.spin_kf_stop = Gtk.SpinButton()
self.spin_duration.set_adjustment(adj_kf_stop)
self.spin_kf_stop.connect("output",self.stop_changed,None)
- self.tbl_keyframes_right.attach(self.spin_kf_stop,1,2,1,2)
+ self.tbl_keyframes_right.attach(self.spin_kf_stop,1,1,1,1)
self.lbl_int_type=Gtk.Label(label="Interpolation type:")
- self.tbl_keyframes_right.attach(self.lbl_int_type,0,1,2,3)
+ self.tbl_keyframes_right.attach(self.lbl_int_type,0,2,1,1)
self.cmb_interpolation_type=Gtk.ComboBoxText() #Gtk.ComboBox()
self.cmb_interpolation_type.append_text("Linear")
@@ -604,26 +600,28 @@ def __init__(self, main_window, f, conf_file=""):
self.cmb_interpolation_type.append_text("Cosine")
self.cmb_interpolation_type.set_active(0)
self.cmb_interpolation_type.connect("changed",self.interpolation_type_changed,None)
- self.tbl_keyframes_right.attach(self.cmb_interpolation_type,1,2,2,3)
+ self.tbl_keyframes_right.attach(self.cmb_interpolation_type,1,2,1,1)
self.btn_adv_opt=Gtk.Button(label="Advanced options")
self.btn_adv_opt.connect("clicked",self.adv_opt_clicked,None)
- self.tbl_keyframes_right.attach(self.btn_adv_opt,0,2,3,4)
+ self.tbl_keyframes_right.attach(self.btn_adv_opt,0,3,2,1)
- self.current_kf.add(self.tbl_keyframes_right) #,False,False,10)
- #-------------------------------------------------------------------
- #----------------------output box-----------------------------------
+ self.current_kf.add(self.tbl_keyframes_right)
+ # -------------------------------------------------------------------
+ # ----------------------output box-----------------------------------
self.frm_output = Gtk.Frame.new("Output options")
self.frm_output.set_border_width(10)
- self.box_output_main=Gtk.VBox(homogeneous=True,spacing=10)
- self.box_output_file=Gtk.HBox(homogeneous=False,spacing=10)
+ self.box_output_main = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
+ homogeneous=True, spacing=10)
+ self.box_output_main.set_border_width(10)
+ self.box_output_file = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
+ homogeneous=False, spacing=10)
self.lbl_temp_avi=Gtk.Label(label="Resulting video file:")
self.box_output_file.pack_start(self.lbl_temp_avi,False,False,10)
self.txt_temp_avi = Gtk.Entry()
- self.txt_temp_avi.set_editable(False)
self.box_output_file.pack_start(self.txt_temp_avi,True,True,10)
self.btn_temp_avi=Gtk.Button(label="Browse")
@@ -637,19 +635,17 @@ def __init__(self, main_window, f, conf_file=""):
self.lbl_res=Gtk.Label(label="Resolution:")
self.box_output_res.pack_start(self.lbl_res,False,False,10)
- adj_width = Gtk.Adjustment(640,320,2048,10,100,0)
+ adj_width = Gtk.Adjustment.new(640,320,2048,10,100,0)
self.spin_width = Gtk.SpinButton()
self.spin_width.set_adjustment(adj_width)
- self.spin_width.connect("output",self.output_width_changed,None)
self.box_output_res.pack_start(self.spin_width,False,False,10)
self.lbl_x=Gtk.Label(label="x")
self.box_output_res.pack_start(self.lbl_x,False,False,10)
- adj_height=Gtk.Adjustment(480,240,1536,10,100,0)
+ adj_height=Gtk.Adjustment.new(480,240,1536,10,100,0)
self.spin_height = Gtk.SpinButton()
self.spin_height.set_adjustment(adj_height)
- self.spin_height.connect("output",self.output_height_changed,None)
self.box_output_res.pack_start(self.spin_height,False,False,10)
self.box_output_main.pack_start(self.box_output_res,True,True,0)
@@ -659,15 +655,12 @@ def __init__(self, main_window, f, conf_file=""):
self.lbl_framerate=Gtk.Label(label="Frame rate:")
self.box_output_framerate.pack_start(self.lbl_framerate,False,False,10)
- adj_framerate = Gtk.Adjustment(25,5,100,1,5,0)
+ adj_framerate = Gtk.Adjustment.new(25,5,100,1,5,0)
self.spin_framerate = Gtk.SpinButton()
self.spin_framerate.set_adjustment(adj_framerate)
- self.spin_framerate.connect("output",self.output_framerate_changed,None)
self.box_output_framerate.pack_start(self.spin_framerate,False,False,10)
self.chk_swapRB=Gtk.CheckButton(label="Swap red and blue component")
- self.chk_swapRB.set_active(True)
- self.chk_swapRB.connect("toggled",self.swap_redblue_clicked,None)
self.box_output_framerate.pack_start(self.chk_swapRB,False,False,50)
self.box_output_main.pack_start(self.box_output_framerate,True,True,0)
@@ -675,10 +668,9 @@ def __init__(self, main_window, f, conf_file=""):
self.frm_output.add(self.box_output_main)
self.box_main.pack_start(self.frm_output,False,False,0)
-
- # check if transcode can be found
- self.transpath = fractconfig.instance.find_on_path("transcode")
- if not self.transpath:
+ # check if video converter can be found
+ self.converterpath = fractconfig.instance.find_on_path("ffmpeg")
+ if not self.converterpath:
# put a message at the bottom to warn user
warning_box = Gtk.HBox()
image = Gtk.Image.new_from_stock(
@@ -687,18 +679,28 @@ def __init__(self, main_window, f, conf_file=""):
warning_box.pack_start(image, True, True, 0)
message = Gtk.Label(label=
- _("Transcode utility not found. Without it we can't generate any video but can still save sequences of still images."))
+ _("ffmpeg utility not found. Without it we can't generate any video but can still save sequences of still images."))
message.set_line_wrap(True)
warning_box.pack_start(message, True, True, 0)
self.box_main.pack_end(warning_box, True, True, 0)
- #--------------showing all-------------------------------
+ # initialise default settings
+ if conf_file:
+ self.load_configuration(conf_file)
+ else:
+ self.updateGUI()
+
+ # don't connect signals until after settings initialised
+ self.spin_height.connect("value-changed",self.output_height_changed,None)
+ self.spin_width.connect("value-changed",self.output_width_changed,None)
+ self.spin_framerate.connect("value-changed",self.output_framerate_changed,None)
+ self.chk_swapRB.connect("toggled",self.swap_redblue_clicked,None)
+
+ # --------------showing all-------------------------------
self.vbox.add(self.box_main)
self.vbox.show_all()
self.controls = self.vbox
- if conf_file!="":
- self.load_configuration(conf_file)
def onResponse(self,widget,id):
if id == Gtk.ResponseType.CLOSE or \
@@ -706,7 +708,12 @@ def onResponse(self,widget,id):
id == Gtk.ResponseType.DELETE_EVENT:
self.hide()
elif id == DirectorDialog.RESPONSE_RENDER:
- self.generate(self.transpath != None)
+ self.animation.set_avi_file(self.txt_temp_avi.get_text())
+ try:
+ self.generate(self.converterpath is not None)
+ except (SanityCheckError, UserCancelledError):
+ # prevent dialog closing if being run
+ GObject.signal_stop_emission_by_name(self, "response")
def main(self):
Gtk.main()
diff --git a/fract4dgui/director_prefs.py b/fract4dgui/director_prefs.py
index d840fa61b..6a0fefcc3 100644
--- a/fract4dgui/director_prefs.py
+++ b/fract4dgui/director_prefs.py
@@ -1,127 +1,124 @@
-from gi.repository import Gtk
-from gi.repository import GObject
import os
-from fract4d import animation
+from gi.repository import Gtk
class DirectorPrefs:
-
- #wrapper to show dialog for selecting folder
- #returns selected folder or empty string
- def get_folder(self):
- temp_folder=""
- dialog = Gtk.FileChooserDialog("Choose directory...",None,Gtk.FileChooserAction.SELECT_FOLDER,
- (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
- dialog.set_default_response(Gtk.ResponseType.OK)
- response = dialog.run()
- if response == Gtk.ResponseType.OK:
- temp_folder=dialog.get_filename()
- dialog.destroy()
- return temp_folder
-
- def create_fct_toggled(self,widget,data=None):
- self.btn_temp_fct.set_sensitive(self.chk_create_fct.get_active())
- self.txt_temp_fct.set_sensitive(self.chk_create_fct.get_active())
-
- def temp_fct_clicked(self,widget, data=None):
- fold=self.get_folder()
- if fold!="":
- self.txt_temp_fct.set_text(fold)
-
- def temp_png_clicked(self,widget, data=None):
- fold=self.get_folder()
- if fold!="":
- self.txt_temp_png.set_text(fold)
-
- def __init__(self,animation):
- self.dialog=Gtk.Dialog("Director preferences...",None,
- Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- (Gtk.STOCK_OK,Gtk.ResponseType.OK,Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL))
-
- self.animation=animation
- #-----------Temporary directories---------------------
- #self.frm_dirs=Gtk.Frame("Temporary directories selection")
- #self.frm_dirs.set_border_width(10)
- self.tbl_dirs=Gtk.Table(n_rows=3,n_columns=3,homogeneous=False)
- self.tbl_dirs.set_row_spacings(10)
- self.tbl_dirs.set_col_spacings(10)
- self.tbl_dirs.set_border_width(10)
-
- self.lbl_temp_fct=Gtk.Label(label="Temporary directory for .fct files:")
- self.tbl_dirs.attach(self.lbl_temp_fct,0,1,1,2)
-
- self.txt_temp_fct = Gtk.Entry()
- self.txt_temp_fct.set_text(self.animation.get_fct_dir())
- self.txt_temp_fct.set_sensitive(False)
- self.tbl_dirs.attach(self.txt_temp_fct,1,2,1,2)
-
- self.btn_temp_fct=Gtk.Button(label="Browse")
- self.btn_temp_fct.connect("clicked",self.temp_fct_clicked,None)
- self.btn_temp_fct.set_sensitive(False)
- self.tbl_dirs.attach(self.btn_temp_fct,2,3,1,2)
-
- #this check box goes after (even if it's above above widgets because
- #we connect and change its state here and it change those buttons, so they wouldn't exist
- self.chk_create_fct=Gtk.CheckButton(label="Create temporary .fct files")
- self.chk_create_fct.connect("toggled",self.create_fct_toggled,None)
- self.chk_create_fct.set_active(self.animation.get_fct_enabled())
- self.tbl_dirs.attach(self.chk_create_fct,0,1,0,1)
-
- self.lbl_temp_png=Gtk.Label(label="Temporary directory for .png files:")
- self.tbl_dirs.attach(self.lbl_temp_png,0,1,2,3)
-
- self.txt_temp_png = Gtk.Entry()
- self.txt_temp_png.set_text(self.animation.get_png_dir())
- self.tbl_dirs.attach(self.txt_temp_png,1,2,2,3)
-
- self.btn_temp_png=Gtk.Button(label="Browse")
- self.btn_temp_png.connect("clicked",self.temp_png_clicked,None)
- self.tbl_dirs.attach(self.btn_temp_png,2,3,2,3)
-
- #self.frm_dirs.add(self.tbl_dirs)
- self.dialog.vbox.pack_start(self.tbl_dirs,False,False,0)
- #self.dialog.vbox.pack_start(self.tbl_main,True,True,0)
-
- #checking is txt fields valid dirs
- def check_fields(self):
- if self.chk_create_fct.get_active():
- #checking fct dir
- if not os.path.isdir(self.txt_temp_fct.get_text()):
- error_dlg = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
- "Directory for temporary .fct files is not directory")
- error_dlg.run()
- error_dlg.destroy()
- return False
- if not os.path.isdir(self.txt_temp_png.get_text()):
- error_dlg = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
- Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
- "Directory for temporary .png files is not directory")
- error_dlg.run()
- error_dlg.destroy()
- return False
- return True
-
- def show(self):
- self.dialog.show_all()
- #nasty, nasty hack to keep dialog running while either it is canceled or
- #valid dirs are entered
- is_ok=False
- write=False
- while is_ok==False:
- response = self.dialog.run()
- if response == Gtk.ResponseType.OK:
- is_ok=self.check_fields()
- if is_ok:
- write=True
- else:
- is_ok=True
-
- if write:
- self.animation.set_fct_enabled(self.chk_create_fct.get_active())
- if self.chk_create_fct.get_active():
- self.animation.set_fct_dir(self.txt_temp_fct.get_text())
- self.animation.set_png_dir(self.txt_temp_png.get_text())
-
- self.dialog.destroy()
- return
+ # wrapper to show dialog for selecting folder
+ # returns selected folder or empty string
+ def get_folder(self):
+ temp_folder = ""
+ dialog = Gtk.FileChooserDialog("Choose directory...",None,Gtk.FileChooserAction.SELECT_FOLDER,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+ dialog.set_default_response(Gtk.ResponseType.OK)
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ temp_folder = dialog.get_filename()
+ dialog.destroy()
+ return temp_folder
+
+ def create_fct_toggled(self,widget,data=None):
+ self.btn_temp_fct.set_sensitive(self.chk_create_fct.get_active())
+ self.txt_temp_fct.set_sensitive(self.chk_create_fct.get_active())
+
+ def temp_fct_clicked(self,widget, data=None):
+ fold = self.get_folder()
+ if fold != "":
+ self.txt_temp_fct.set_text(fold)
+
+ def temp_png_clicked(self,widget, data=None):
+ fold = self.get_folder()
+ if fold != "":
+ self.txt_temp_png.set_text(fold)
+
+ def __init__(self,animation,parent):
+ self.dialog = Gtk.Dialog("Director preferences...",parent,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ (Gtk.STOCK_OK,Gtk.ResponseType.OK,Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL))
+
+ self.animation = animation
+ # -----------Temporary directories---------------------
+ #self.frm_dirs=Gtk.Frame("Temporary directories selection")
+ #self.frm_dirs.set_border_width(10)
+ self.tbl_dirs = Gtk.Table(n_rows=3,n_columns=3,homogeneous=False)
+ self.tbl_dirs.set_row_spacings(10)
+ self.tbl_dirs.set_col_spacings(10)
+ self.tbl_dirs.set_border_width(10)
+
+ self.lbl_temp_fct = Gtk.Label(label="Temporary directory for .fct files:")
+ self.tbl_dirs.attach(self.lbl_temp_fct,0,1,1,2)
+
+ self.txt_temp_fct = Gtk.Entry()
+ self.txt_temp_fct.set_text(self.animation.get_fct_dir())
+ self.txt_temp_fct.set_sensitive(False)
+ self.tbl_dirs.attach(self.txt_temp_fct,1,2,1,2)
+
+ self.btn_temp_fct = Gtk.Button(label="Browse")
+ self.btn_temp_fct.connect("clicked",self.temp_fct_clicked,None)
+ self.btn_temp_fct.set_sensitive(False)
+ self.tbl_dirs.attach(self.btn_temp_fct,2,3,1,2)
+
+ # this check box goes after (even if it's above above widgets because
+ # we connect and change its state here and it change those buttons, so they wouldn't exist
+ self.chk_create_fct = Gtk.CheckButton(label="Create temporary .fct files")
+ self.chk_create_fct.connect("toggled",self.create_fct_toggled,None)
+ self.chk_create_fct.set_active(self.animation.get_fct_enabled())
+ self.tbl_dirs.attach(self.chk_create_fct,0,1,0,1)
+
+ self.lbl_temp_png = Gtk.Label(label="Temporary directory for .png files:")
+ self.tbl_dirs.attach(self.lbl_temp_png,0,1,2,3)
+
+ self.txt_temp_png = Gtk.Entry()
+ self.txt_temp_png.set_text(self.animation.get_png_dir())
+ self.tbl_dirs.attach(self.txt_temp_png,1,2,2,3)
+
+ self.btn_temp_png = Gtk.Button(label="Browse")
+ self.btn_temp_png.connect("clicked",self.temp_png_clicked,None)
+ self.tbl_dirs.attach(self.btn_temp_png,2,3,2,3)
+
+ #self.frm_dirs.add(self.tbl_dirs)
+ self.dialog.vbox.pack_start(self.tbl_dirs,False,False,0)
+ #self.dialog.vbox.pack_start(self.tbl_main,True,True,0)
+
+ # checking is txt fields valid dirs
+ def check_fields(self):
+ if self.chk_create_fct.get_active():
+ # checking fct dir
+ if not os.path.isdir(self.txt_temp_fct.get_text()):
+ error_dlg = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
+ "Directory for temporary .fct files is not directory")
+ error_dlg.run()
+ error_dlg.destroy()
+ return False
+ if not os.path.isdir(self.txt_temp_png.get_text()):
+ error_dlg = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
+ "Directory for temporary .png files is not directory")
+ error_dlg.run()
+ error_dlg.destroy()
+ return False
+ return True
+
+ def show(self):
+ self.dialog.show_all()
+ # nasty, nasty hack to keep dialog running while either it is canceled or
+ # valid dirs are entered
+ is_ok = False
+ write = False
+ while is_ok is False:
+ response = self.dialog.run()
+ if response == Gtk.ResponseType.OK:
+ is_ok = self.check_fields()
+ if is_ok:
+ write = True
+ else:
+ is_ok = True
+
+ if write:
+ self.animation.set_fct_enabled(self.chk_create_fct.get_active())
+ if self.chk_create_fct.get_active():
+ self.animation.set_fct_dir(self.txt_temp_fct.get_text())
+ self.animation.set_png_dir(self.txt_temp_png.get_text())
+
+ self.dialog.destroy()
+ return
diff --git a/fract4dgui/test_director.py b/fract4dgui/test_director.py
index 0a90606a2..3ebd6cfc6 100755
--- a/fract4dgui/test_director.py
+++ b/fract4dgui/test_director.py
@@ -2,10 +2,9 @@
# unit tests for renderqueue module
-import unittest
-import sys
import os
-import subprocess
+import sys
+import unittest
import gi
gi.require_version('Gtk', '3.0')
@@ -17,8 +16,7 @@
if sys.path[1] != "..": sys.path.insert(1, "..")
from fract4dgui import director, PNGGen, hig
-
-from fract4d import fractal, image, fc, animation
+from fract4d import fractal, fc, animation
g_comp = fc.Compiler()
g_comp.add_func_path("../fract4d")
@@ -62,8 +60,8 @@ def testDirectorDialog(self):
self.assertEqual(True,os.path.exists("./image_0000000.png"))
self.assertEqual(True,os.path.exists("./image_0000001.png"))
- if dd.transpath != None:
- # only check for video if transcode is installed
+ if dd.converterpath:
+ # only check for video if video converter is installed
self.assertEqual(True,os.path.exists("video.avi"))
dd.destroy()
@@ -137,11 +135,11 @@ def testKeyframeClash(self):
def testPNGGen(self):
f = fractal.T(g_comp)
dd= director.DirectorDialog(None,f,"")
- pg = PNGGen.PNGGeneration(dd.animation,g_comp)
+ pg = PNGGen.PNGGeneration(dd.animation,g_comp,dd)
pg.generate_png()
dd.destroy()
-
+
def suite():
return unittest.makeSuite(Test,'test')