summaryrefslogtreecommitdiff
path: root/cad/src/commands/Extrude/ExtrudePropertyManager.py
blob: c7e98329514264d871f3bbed7a820be347afd16b (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
# Copyright 2004-2008 Nanorex, Inc.  See LICENSE file for details.
"""
ExtrudePropertyManager.py - Property Manager for
B{Extrude mode}.  The UI is defined in L{Ui_ExtrudePropertyManager}

@version: $Id$
@copyight: 2004-2008 Nanorex, Inc.  See LICENSE file for details.

History:

ninad 2007-01-10: Split the ui code out of extrudeMode while converting
extrude dashboard to extrude property manager.
ninad 2007-07-25: code cleanup to create a propMgr object for extrude mode. Also
moved many ui helper methods defined globally in extrudeMode.py to this class.
"""

import math
from PyQt4.Qt import SIGNAL
from PyQt4.Qt import Qt
from commands.Extrude.Ui_ExtrudePropertyManager import Ui_ExtrudePropertyManager

_superclass = Ui_ExtrudePropertyManager
class ExtrudePropertyManager(Ui_ExtrudePropertyManager):
    """
    The ExtrudePropertyManager class provides the Property Manager for the
    B{Extrude mode}.  The UI is defined in L{Ui_ExtrudePropertyManager}
    """

    def __init__(self, command):
        """
        Constructor for the B{Extrude} property manager.

        @param command: The parent mode where this Property Manager is used
        @type  command: L{ExtrudeMode}
        """
        self.suppress_valuechanged = False

        _superclass.__init__(self, command)

    def show(self):
        """
        Extends superclass method.
        """
        _superclass.show(self)

        self.updateMessage()


    def connect_or_disconnect_signals(self, connect):
        """
        Connect or disconnect widget signals sent to their slot methods.
        @param isConnect: If True the widget will send the signals to the slot
                          method.
        @type  isConnect: boolean
        @see: L{extrudeMode.connect_or_disconnect_signals} where this is called
        """
        if connect:
            change_connect = self.w.connect
        else:
            change_connect = self.w.disconnect

        # Connect or disconnect widget signals to slots

        for toggle in self.extrude_pref_toggles:
            change_connect(toggle,
                           SIGNAL("stateChanged(int)"),
                           self.command.toggle_value_changed)

        change_connect(self.extrudeSpinBox_n,
                       SIGNAL("valueChanged(int)"),
                       self.command.spinbox_value_changed)

        change_connect(self.extrudeSpinBox_x,
                       SIGNAL("valueChanged(double)"),
                       self.command.spinbox_value_changed)

        change_connect(self.extrudeSpinBox_y,
                       SIGNAL("valueChanged(double)"),
                       self.command.spinbox_value_changed)

        change_connect(self.extrudeSpinBox_z,
                       SIGNAL("valueChanged(double)"),
                       self.command.spinbox_value_changed)

        change_connect(self.extrudeSpinBox_length,
                       SIGNAL("valueChanged(double)"),
                       self.command.length_value_changed)

        slider = self.extrudeBondCriterionSlider

        change_connect(slider,
                       SIGNAL("valueChanged(int)"),
                       self.command.slider_value_changed)

        change_connect(self.extrude_productTypeComboBox,
                       SIGNAL("activated(int)"),
                       self.command.ptype_value_changed)

    def keyPressEvent(self, event):
        """
        Extends superclass method. Provides a way to update 3D workspace
        when user hits Enter key.
        @see: self.preview_btn_clicked()
        """
        #The following implements a NFR Mark needs. While in extrude mode,
        #if user changes values in the spinboxes, don't immediatey update
        #it on the 3D workspace -- because it takes long time to do so
        #on a big model. Instead provide a way to update , when, for example,
        #user hits 'Enter' after changing a spinbox value or hits preview
        #button.  -- Ninad 2008-10-30

        if event.key() == Qt.Key_Return:
            self.command.update_from_controls()

        _superclass.keyPressEvent(self, event)

    def preview_btn_clicked(self):
        """
        Provides a way to update 3D workspace when user hits Preview button
        @see: a comment in self.keyPressEvent()
        @see: extrudeMode.update_from_controls()
        """
        self.command.update_from_controls()

    def set_extrude_controls_xyz(self, (x, y, z) ):
        self.set_extrude_controls_xyz_nolength((x, y, z))
        self.update_length_control_from_xyz()

    def set_extrude_controls_xyz_nolength(self, (x, y, z) ):
        self.extrudeSpinBox_x.setValue(x)
        self.extrudeSpinBox_y.setValue(y)
        self.extrudeSpinBox_z.setValue(z)

    def set_controls_minimal(self):
        #e would be better to try harder to preserve xyz ratio
        ll = 0.1 # kluge, but prevents ZeroDivisionError
        x = y = 0.0
        z = ll
        self.call_while_suppressing_valuechanged(
            lambda: self.set_extrude_controls_xyz_nolength((x, y, z) ) )

        self.call_while_suppressing_valuechanged(
            lambda: self.extrudeSpinBox_length.setValue(ll) )

        # ZeroDivisionError after user sets xyz each to 0 by typing in them


    def get_extrude_controls_xyz(self):
        x = self.extrudeSpinBox_x.value()
        y = self.extrudeSpinBox_y.value()
        z = self.extrudeSpinBox_z.value()
        return (x,y,z)

    def update_length_control_from_xyz(self):
        x, y, z = self.get_extrude_controls_xyz()
        ll = math.sqrt(x*x + y*y + z*z)
        if ll < 0.1: # prevent ZeroDivisionError
            self.set_controls_minimal()
            return

        self.call_while_suppressing_valuechanged(
            lambda: self.extrudeSpinBox_length.setValue(ll) )

    def update_xyz_controls_from_length(self):
        x, y, z = self.get_extrude_controls_xyz()
        ll = math.sqrt(x*x + y*y + z*z)
        if ll < 0.1: # prevent ZeroDivisionError
            self.set_controls_minimal()
            return
        length = self.extrudeSpinBox_length.value()
        rr = float(length) / ll
        self.call_while_suppressing_valuechanged(
            lambda: self.set_extrude_controls_xyz_nolength(
                (x*rr, y*rr, z*rr) ) )

    def call_while_suppressing_valuechanged(self, func):
        old_suppress_valuechanged = self.suppress_valuechanged
        self.suppress_valuechanged = 1
        try:
            res = func()
        finally:
            self.suppress_valuechanged = old_suppress_valuechanged
        return res

    def set_bond_tolerance_and_number_display(self, tol, nbonds = -1):
        #e -1 indicates not yet known ###e '?' would look nicer
        self.extrudeBondCriterionLabel.setText(\
            self.lambda_tol_nbonds(tol,nbonds))

    def set_bond_tolerance_slider(self, tol):
        # this will send signals!
        self.extrudeBondCriterionSlider.setValue(int(tol * 100))

    def get_bond_tolerance_slider_val(self):
        ival = self.extrudeBondCriterionSlider.value()
        return ival / 100.0

    def lambda_tol_nbonds(self, tol, nbonds):
        if nbonds == -1:
            nbonds_str = "?"
        else:
            nbonds_str = "%d" % (nbonds,)
        tol_str = ("      %d" % int(tol * 100.0))[-3:]
        # fixed-width (3 digits) but using initial spaces
        # (doesn't have all of desired effect, due to non-fixed-width font)
        tol_str = tol_str + "%"
        return "Tolerance: %s => %s bonds" % (tol_str, nbonds_str)

    def updateMessage(self, msg = ''):
        """
        Updates the message box with an informative message.
        """

        if not msg:
            numCopies = self.extrudeSpinBox_n.value() - 1
            if self.command.product_type == "straight rod":
                msg = "Drag one of the " + str(numCopies) + " copies on the right \
                    to position them. Bondpoints will highlight in blue and green \
                    pairs whenever bonds can be formed between them."
            else:
                msg = "Use the spinboxes below to position the copies. \
                    Bondpoints will highlight in blue and green pairs \
                    whenever bonds can be formed between them."

        self.MessageGroupBox.insertHtmlMessage( msg, setAsDefault  =  True )