Index: src/sas/sasgui/perspectives/file_converter/converter_panel.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision fdbea3ce018551a044127bc34f8ecac9b274b219)
+++ src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision c9a519f7daec51667fa6d6607d49929fee6c8b03)
@@ -9,4 +9,5 @@
 from sas.sasgui.guiframe.panel_base import PanelBase
 from sas.sasgui.perspectives.calculator import calculator_widgets as widget
+from sas.sasgui.perspectives.file_converter.converter_widgets import VectorInput
 from sas.sasgui.guiframe.events import StatusEvent
 from sas.sasgui.guiframe.dataFitting import Data1D
@@ -155,12 +156,19 @@
 
         for ctrl in self.to_validate:
-            if ctrl.GetValue() == '': continue
-            ctrl_valid = check_float(ctrl)
+            ctrl_valid = True
+            invalid_control = None
+            if isinstance(ctrl, VectorInput):
+                ctrl_valid, invalid_control = ctrl.Validate()
+            else:
+                if ctrl.GetValue() == '': continue
+                ctrl_valid = check_float(ctrl)
+                invalid_control = ctrl
             if not ctrl_valid:
                 msg = "{} must be a valid float".format(
-                    ctrl.GetName().replace('_', ' '))
+                    invalid_control.GetName().replace('_', ' '))
                 wx.PostEvent(self.parent.manager.parent,
                     StatusEvent(status=msg, info='error'))
                 return False
+
         return True
 
@@ -293,23 +301,8 @@
         metadata_grid.Add(offset_label, (y,1), (1,1))
 
-        offset_sizer = wx.BoxSizer(wx.HORIZONTAL)
-        x_label = wx.StaticText(metadata_pane, -1, "x: ",
-            style=wx.ALIGN_CENTER_VERTICAL)
-        offset_sizer.Add(x_label, wx.ALIGN_CENTER_VERTICAL)
-        offset_x_input = wx.TextCtrl(metadata_pane, -1,
-            name="detector_offset_x", size=(50, -1))
-        offset_sizer.Add(offset_x_input)
-        self.to_validate.append(offset_x_input)
-        offset_x_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        offset_sizer.AddSpacer((15, -1))
-        y_label = wx.StaticText(metadata_pane, -1, "y: ",
-            style=wx.ALIGN_CENTER_VERTICAL)
-        offset_sizer.Add(y_label, wx.ALIGN_CENTER_VERTICAL)
-        offset_y_input = wx.TextCtrl(metadata_pane, -1,
-            name="detector_offset_y", size=(50, -1))
-        offset_sizer.Add(offset_y_input)
-        self.to_validate.append(offset_y_input)
-        offset_y_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        metadata_grid.Add(offset_sizer, (y,2), (1,1), wx.BOTTOM, 5)
+        offset_input = VectorInput(metadata_pane, "detector_offset",
+            callback=self.metadata_changed)
+        self.to_validate.append(offset_input)
+        metadata_grid.Add(offset_input.GetSizer(), (y,2), (1,1), wx.BOTTOM, 5)
         y += 1
 
@@ -318,30 +311,10 @@
         metadata_grid.Add(orientation_label, (y, 1), (1, 1))
 
-        orientation_sizer = wx.BoxSizer(wx.HORIZONTAL)
-        roll_label = wx.StaticText(metadata_pane, -1, "Roll: ")
-        orientation_sizer.Add(roll_label)
-        roll_input = wx.TextCtrl(metadata_pane, -1,
-            name="detector_orientation_x", size=(50, -1))
-        self.to_validate.append(roll_input)
-        roll_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        orientation_sizer.Add(roll_input)
-        orientation_sizer.AddSpacer((15, -1))
-        pitch_label = wx.StaticText(metadata_pane, -1, "Pitch: ")
-        orientation_sizer.Add(pitch_label)
-        pitch_input = wx.TextCtrl(metadata_pane, 1,
-            name="detector_orientation_y", size=(50,-1))
-        self.to_validate.append(pitch_input)
-        pitch_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        orientation_sizer.Add(pitch_input)
-        orientation_sizer.AddSpacer((15, -1))
-        yaw_label = wx.StaticText(metadata_pane, -1, "Yaw: ")
-        orientation_sizer.Add(yaw_label)
-        yaw_input = wx.TextCtrl(metadata_pane, 1,
-            name="detector_orientation_z", size=(50,-1))
-        yaw_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        self.to_validate.append(yaw_input)
-        orientation_sizer.Add(yaw_input)
-
-        metadata_grid.Add(orientation_sizer, (y,2), (1,1), wx.BOTTOM, 5)
+        orientation_input = VectorInput(metadata_pane, "detector_orientation",
+            callback=self.metadata_changed, z_enabled=True,
+            labels=["Roll: ", "Pitch: ", "Yaw: "])
+        self.to_validate.append(orientation_input)
+        metadata_grid.Add(orientation_input.GetSizer(),
+            (y,2), (1,1), wx.BOTTOM, 5)
         y += 1
 
@@ -349,22 +322,8 @@
         metadata_grid.Add(pixel_label, (y,1), (1,1))
 
-        pixel_sizer = wx.BoxSizer(wx.HORIZONTAL)
-        pixel_x_label = wx.StaticText(metadata_pane, -1, "x: ")
-        pixel_sizer.Add(pixel_x_label)
-        pixel_x_input = wx.TextCtrl(metadata_pane, -1,
-            name="detector_pixel_size_x", size=(50, -1))
-        self.to_validate.append(pixel_x_input)
-        pixel_x_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        pixel_sizer.Add(pixel_x_input)
-        pixel_sizer.AddSpacer((15, -1))
-        pixel_y_label = wx.StaticText(metadata_pane, -1, "y: ")
-        pixel_sizer.Add(pixel_y_label)
-        pixel_y_input = wx.TextCtrl(metadata_pane, 1,
-            name="detector_pixel_size_y", size=(50,-1))
-        self.to_validate.append(pixel_y_input)
-        pixel_y_input.Bind(wx.EVT_TEXT, self.metadata_changed)
-        pixel_sizer.Add(pixel_y_input)
-
-        metadata_grid.Add(pixel_sizer, (y,2), (1,1), wx.BOTTOM, 5)
+        pixel_input = VectorInput(metadata_pane, "detector_pixel_size",
+             callback=self.metadata_changed)
+        self.to_validate.append(pixel_input)
+        metadata_grid.Add(pixel_input.GetSizer(), (y,2), (1,1), wx.BOTTOM, 5)
 
         metadata_pane.SetSizer(metadata_grid)
Index: src/sas/sasgui/perspectives/file_converter/converter_widgets.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_widgets.py	(revision c9a519f7daec51667fa6d6607d49929fee6c8b03)
+++ src/sas/sasgui/perspectives/file_converter/converter_widgets.py	(revision c9a519f7daec51667fa6d6607d49929fee6c8b03)
@@ -0,0 +1,108 @@
+"""
+This module provides some custom wx widgets for the file converter perspective
+"""
+import wx
+from sas.sascalc.dataloader.data_info import Vector
+from sas.sasgui.guiframe.utils import check_float
+
+class VectorInput(object):
+    """
+    An input field for inputting 2 (or 3) components of a vector.
+    """
+
+    def __init__(self, parent, control_name, callback=None,
+        labels=["x: ", "y: ", "z: "], z_enabled=False):
+        """
+        Create the control
+
+        :param parent: The window to add the control to
+        :param control_name: All TextCtrl names will start with control_name
+        :param callback: The function to call when the text is changed
+        :param labels: An array of labels for the TextCtrls
+        :param z_enabled: Whether or not to show the z input field
+        """
+        self.parent = parent
+        self.control_name = control_name
+        self._callback = callback
+        self.labels = labels
+        self.z_enabled = z_enabled
+        self._sizer = None
+        self._inputs = {}
+
+        self._do_layout()
+
+    def GetSizer(self):
+        """
+        Get the control's sizer
+
+        :return sizer: a wx.BoxSizer object
+        """
+        return self._sizer
+
+    def GetValue(self):
+        """
+        Get the value of the vector input
+
+        :return v: A Vector object
+        """
+        v = Vector()
+        if not self.Validate(): return v
+        for direction, control in self._inputs:
+            try:
+                value = float(control.GetValue())
+                setattr(v, direction, value)
+            except: # Text field is empty
+                pass
+
+        return v
+
+    def Validate(self):
+        """
+        Validate the contents of the inputs
+
+        :return control_valid: Whether or not the inputs are valid
+        :return invalid_control: The control that is not valid
+            (or None if all are valid)
+        """
+        for control in self._inputs.values():
+            if control.GetValue() == '': continue
+            control.SetBackgroundColour(wx.WHITE)
+            control_valid = check_float(control)
+            if not control_valid:
+                return False, control
+        return True, None
+
+
+    def _do_layout(self):
+        self._sizer = wx.BoxSizer(wx.HORIZONTAL)
+        x_label = wx.StaticText(self.parent, -1, self.labels[0],
+            style=wx.ALIGN_CENTER_VERTICAL)
+        self._sizer.Add(x_label, wx.ALIGN_CENTER_VERTICAL)
+        x_input = wx.TextCtrl(self.parent, -1,
+            name="{}_x".format(self.control_name), size=(50, -1))
+        self._sizer.Add(x_input)
+        self._inputs['x'] = x_input
+        x_input.Bind(wx.EVT_TEXT, self._callback)
+
+        self._sizer.AddSpacer((15, -1))
+
+        y_label = wx.StaticText(self.parent, -1, self.labels[1],
+            style=wx.ALIGN_CENTER_VERTICAL)
+        self._sizer.Add(y_label, wx.ALIGN_CENTER_VERTICAL)
+        y_input = wx.TextCtrl(self.parent, -1,
+            name="{}_y".format(self.control_name), size=(50, -1))
+        self._sizer.Add(y_input)
+        self._inputs['y'] = y_input
+        y_input.Bind(wx.EVT_TEXT, self._callback)
+
+        if self.z_enabled:
+            self._sizer.AddSpacer((15, -1))
+
+            z_label = wx.StaticText(self.parent, -1, self.labels[2],
+                style=wx.ALIGN_CENTER_VERTICAL)
+            self._sizer.Add(z_label, wx.ALIGN_CENTER_VERTICAL)
+            z_input = wx.TextCtrl(self.parent, -1,
+                name="{}_z".format(self.control_name), size=(50, -1))
+            self._sizer.Add(z_input)
+            self._inputs['z'] = z_input
+            z_input.Bind(wx.EVT_TEXT, self._callback)
