# Copyright 2004-2009 Nanorex, Inc. See LICENSE file for details. """ PovraySceneProp.py - the PovraySceneProp class, including all methods needed by the POV-Ray Scene dialog. @author: Mark @version: $Id$ @copyright: 2004-2009 Nanorex, Inc. See LICENSE file for details. History: mark 060602 - Created for NFR: "Insert > POV-Ray Scene". """ from PyQt4.Qt import SIGNAL, QDialog, QWhatsThis, QDialog from commands.PovraySceneProperties.PovrayScenePropDialog import Ui_PovrayScenePropDialog import foundation.env as env, os from utilities.Log import redmsg, greenmsg from PM.GroupButtonMixin import GroupButtonMixin from sponsors.Sponsors import SponsorableMixin from utilities.Comparison import same_vals class PovraySceneProp(QDialog, SponsorableMixin, GroupButtonMixin, Ui_PovrayScenePropDialog): cmdname = greenmsg("Insert POV-Ray Scene: ") prefix = 'POVRayScene' extension = ".pov" def __init__(self, win): QDialog.__init__(self, win) # win is parent. self.setupUi(self) self.connect(self.cancel_btn,SIGNAL("clicked()"),self.cancel_btn_clicked) self.connect(self.done_btn,SIGNAL("clicked()"),self.ok_btn_clicked) self.connect(self.height_spinbox,SIGNAL("valueChanged(int)"),self.change_height) self.connect(self.ok_btn,SIGNAL("clicked()"),self.ok_btn_clicked) self.connect(self.preview_btn,SIGNAL("clicked()"),self.preview_btn_clicked) self.connect(self.restore_btn,SIGNAL("clicked()"),self.restore_defaults_btn_clicked) self.connect(self.sponsor_btn,SIGNAL("clicked()"),self.open_sponsor_homepage) self.connect(self.whatsthis_btn,SIGNAL("clicked()"),self.whatsthis_btn_clicked) self.connect(self.width_spinbox,SIGNAL("valueChanged(int)"),self.change_width) self.connect(self.abort_btn,SIGNAL("clicked()"),self.cancel_btn_clicked) self.connect(self.grpbtn_1,SIGNAL("clicked()"),self.toggle_grpbtn_1) self.connect(self.grpbtn_2,SIGNAL("clicked()"),self.toggle_grpbtn_2) self.win = win self.glpane = self.win.glpane self.node = None self.previousParams = None self.sponsor_btn.setWhatsThis("""NanoEngineer-1 Sponsor

Click on the logo to learn more about this NanoEngineer-1 sponsor.

""") self.name_linedit.setWhatsThis("""Node Name

The POV-Ray Scene file node name as it appears in the Model Tree.

""") self.output_type_combox.setWhatsThis("""Image Format - the output image format when rendering an image from this POV-Ray Scene file.""") def setup(self, pov=None): """ Show the Properties Manager dialog. If is supplied, get the parameters from it and load the dialog widgets. """ if not self.win.assy.filename: env.history.message( self.cmdname + redmsg("Can't insert POV-Ray Scene until the current part has been saved.") ) return if not pov: self.node_is_new = True from model.PovrayScene import PovrayScene self.node = PovrayScene(self.win.assy, None) else: self.node_is_new = False self.node = pov self.name = self.originalName = self.node.name ini, self.originalPov, out = self.node.get_povfile_trio() self.width, self.height, self.output_type = self.node.get_parameters() self.update_widgets() self.previousParams = params = self.gather_parameters() self.show() def gather_parameters(self): """ Returns a tuple with the current parameter values from the widgets. """ self.node.try_rename(self.name_linedit.text()) # Next three lines fix bug 2026. Mark 060702. self.name_linedit.setText(self.node.name) # In case the name was illegal and "Preview" was pressed. name = self.node.name width = self.width_spinbox.value() height = self.height_spinbox.value() output_type = str(self.output_type_combox.currentText()).lower() return (name, width, height, output_type) def update_widgets(self): """ Update the widgets using the current attr values. """ self.name_linedit.setText(self.name) self.output_type_combox.setItemText(self.output_type_combox.currentIndex(), self.output_type.upper()) # This must be called before setting the values of the width and height spinboxes. Mark 060621. self.aspect_ratio = float(self.width) / float(self.height) aspect_ratio_str = "%.3f to 1" % self.aspect_ratio self.aspect_ratio_value_label.setText(aspect_ratio_str) self.width_spinbox.setValue(self.width) # Generates signal. self.height_spinbox.setValue(self.height) # Generates signal. def update_node(self, ok_pressed=False): """ Update the POV-Ray Scene node. """ self.set_params( self.node, self.gather_parameters()) ini, pov, out = self.node.get_povfile_trio() # If the node was renamed, rename the POV-Ray Scene file name, too. # Don't do this if the "Preview" button was pressed since the user could later # hit "Cancel". In that case we'd loose the original .pov file, which would not be good. # Mark 060702. if ok_pressed and self.originalName != self.node.name: if os.path.isfile(self.originalPov): if os.path.isfile(pov): # Normally, I'd never allow the user to delete an existing POV-Ray Scene file without asking. # For A8 I'll allow it since I've run out of time. # This will be fixed when Bruce implements the new File class in A9 (or later). Mark 060702. os.remove(pov) os.rename(self.originalPov, pov) # Write the POV-Ray Scene (.pov) file if this is a new node or if the node's ".pov" file doesn't exist. # Possible ways the ".pov" file could be missing from an existing node: # 1. the user renamed the node in the Model Tree, or # 2. the POV-Ray Scene node was deleted, which deletes the file in self.kill(), and then Undo was pressed, or # 3. the ".pov" file was deleted by the user another way (via OS). # In the future, the POV-Ray Scene should save the view quat in the MMP (info) record. Then it # would always be possible to regenerate the POV-Ray Scene file from the MMP record, even if # the node's .pov file didn't exist on disk anymore. Mark 060701. if self.node_is_new or not os.path.exists(pov): errorcode, filename_or_errortext = self.node.write_povrayscene_file() if errorcode: # The Pov-Ray Scene file could not be written, so remove the node. self.remove_node() env.history.message( self.cmdname + redmsg(filename_or_errortext) ) return def set_params(self, node, params): #bruce 060620, since pov params don't include name, but our params do # should be a PovrayScene node name = params[0] pov_params = params[1:] node.name = name # this ought to be checked for being a legal name; maybe we should use try_rename ###e node.set_parameters(pov_params) # Warning: code in this class assumes a specific order and set of params must be used here # (e.g. in gather_parameters), so it might be clearer if set_parameters was just spelled out here # as three assignments to attrs of struct. On the other hand, these three parameters (in that order) # are also known to the node itself, for use in its mmp record format. Probably that's irrelevant # and we should still make this change during the next cleanup of these files. ###@@@ [bruce 060620 comment] return def remove_node(self): """ Delete this POV-Ray Scene node. """ if self.node != None: #&&& self.node.kill(require_confirmation = False) # This version prompts the user to confirm deleting the node if its file exists (usually). self.node.kill() # Assume the user wants to delete the node's POV-Ray Scene file. self.node = None self.win.mt.mt_update() # Property manager standard button slots. def ok_btn_clicked(self): """ Slot for the OK button """ self.win.assy.current_command_info(cmdname = self.cmdname) self.update_node(ok_pressed = True) if self.node_is_new: self.win.assy.addnode(self.node) self.node.update_icon() # In case we rewrote a lost POV-Ray Scene file in update_node(). self.win.mt.mt_update() # Update model tree regardless whether it is a new node or not, # since the user may have changed the name of an existing POV-Ray Scene node. env.history.message(self.cmdname + self.done_msg()) self.node = None QDialog.accept(self) def done_msg(self): """ Returns the message to print after the OK button has been pressed. """ if self.node_is_new: return "%s created." % self.name else: if not same_vals( self.previousParams, self.gather_parameters()): return "%s updated." % self.name else: return "%s unchanged." % self.name def cancel_btn_clicked(self): """ Slot for Cancel button. """ self.win.assy.current_command_info(cmdname = self.cmdname + " (Cancel)") if self.node_is_new: self.remove_node() else: self.set_params(self.node, self.previousParams) QDialog.accept(self) def restore_defaults_btn_clicked(self): """ Slot for Restore Defaults button. """ self.name, self.width, self.height, self.output_type = self.previousParams self.update_widgets() def preview_btn_clicked(self): """ Slot for Preview button. """ self.update_node() self.node.raytrace_scene() #&&& Should we print history message in this method or return the errorcode and text so the caller #&&& can decide what to do? I think it would be better to display the history msg in raytrace_scene. Mark 060701. #&&&errorcode, errortext = self.node.raytrace_scene() #&&&if errorcode: #&&& env.history.message( self.cmdname + redmsg(errortext) ) #&&&else: #&&& env.history.message( self.cmdname + errortext ) # "Rendering finished" message. def whatsthis_btn_clicked(self): """ Slot for the What's This button """ QWhatsThis.enterWhatsThisMode() # Property manager widget slots. def change_width(self, width): """ Slot for Width spinbox """ self.width = width self.update_height() def update_height(self): """ Updates height when width changes """ self.height = int (self.width / self.aspect_ratio) self.disconnect(self.height_spinbox,SIGNAL("valueChanged(int)"),self.change_height) self.height_spinbox.setValue(self.height) self.connect(self.height_spinbox,SIGNAL("valueChanged(int)"),self.change_height) def change_height(self, height): """ Slot for Height spinbox """ self.height = height self.update_width() def update_width(self): """ Updates width when height changes """ self.width = int (self.height * self.aspect_ratio) self.disconnect(self.width_spinbox,SIGNAL("valueChanged(int)"),self.change_width) self.width_spinbox.setValue(self.width) self.connect(self.width_spinbox,SIGNAL("valueChanged(int)"),self.change_width) # Property Manager groupbox button slots def toggle_grpbtn_1(self): """ Slot for first groupbox toggle button """ self.toggle_groupbox_in_dialogs(self.grpbtn_1, self.line2, self.name_linedit) def toggle_grpbtn_2(self): """ Slot for second groupbox toggle button """ self.toggle_groupbox_in_dialogs(self.grpbtn_2, self.line3, self.output_type_label, self.output_type_combox, self.width_label, self.width_spinbox, self.height_label, self.height_spinbox, self.aspect_ratio_label, self.aspect_ratio_value_label)