summaryrefslogtreecommitdiff
path: root/cad/src/commands/PovraySceneProperties/PovraySceneProp.py
blob: 4d58d0407ef405d4108d95bb2cc6ef2c293c9cc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# 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("""<b>NanoEngineer-1 Sponsor</b>
        <p>Click on the logo to learn more
        about this NanoEngineer-1 sponsor.</p>""")
        self.name_linedit.setWhatsThis("""<b>Node Name</b>
        <p>The POV-Ray Scene file node name as it appears
        in the Model Tree.</p>""")
        self.output_type_combox.setWhatsThis("""<b>Image Format </b>- 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 <pov> 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
        # <node> 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)