Repository: Qiskit/qiskit

Issue:
Text-based circuit drawer output is scrambled in notebook unless print is called.
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit Terra version**:
- **Python version**:
- **Operating system**:

### What is the current behavior?
```python 
from qiskit import *
from qiskit.tools.visualization import circuit_drawer

q = QuantumRegister(5)
qc = QuantumCircuit(q)
qc.ccx(q[1], q[2], q[3])
qc.ccx(q[0], q[2], q[4])
qc.x(q[4])
qc.x(q[3])

circuit_drawer(qc, output='text')
```

'                             \nq1_0: |0>────────────■───────\n                     │       \nq1_1: |0>──■─────────┼───────\n           │         │       \nq1_2: |0>──■─────────■───────\n         ┌─┴─┐┌───┐  │       \nq1_3: |0>┤ X ├┤ X ├──┼───────\n         └───┘└───┘┌─┴─┐┌───┐\nq1_4: |0>──────────┤ X ├┤ X ├\n                   └───┘└───┘'




### Steps to reproduce the problem
The output is messed up unless calling `print(circuit_drawer(qc, output='text'))`.  Therefore it is not a drop in replacement for the other methods which work fine in a notebook.


### What is the expected behavior?



### Suggested solutions




Discussion:

Resolving patch:
diff --git a/qiskit/tools/visualization/_circuit_visualization.py b/qiskit/tools/visualization/_circuit_visualization.py
--- a/qiskit/tools/visualization/_circuit_visualization.py
+++ b/qiskit/tools/visualization/_circuit_visualization.py
@@ -60,7 +60,7 @@ def circuit_drawer(circuit,
                    plot_barriers=True,
                    reverse_bits=False):
     """Draw a quantum circuit to different formats (set by output parameter):
-    0. text: ASCII art string
+    0. text: ASCII art TextDrawing that can be printed in the console.
     1. latex: high-quality images, but heavy external software dependencies
     2. matplotlib: purely in Python with no external dependencies
 
@@ -79,7 +79,7 @@ def circuit_drawer(circuit,
             output types. If a str is passed in that is the path to a json
             file which contains that will be open, parsed, and then used just
             as the input dict.
-        output (str): Select the output method to use for drawing the circuit.
+        output (TextDrawing): Select the output method to use for drawing the circuit.
             Valid choices are `text`, `latex`, `latex_source`, `mpl`. Note if
             one is not specified it will use latex and if that fails fallback
             to mpl. However this behavior is deprecated and in a future release
@@ -100,8 +100,8 @@ def circuit_drawer(circuit,
     Returns:
         PIL.Image: (outputs `latex` and `python`) an in-memory representation of
                    the circuit diagram.
-        String: (outputs `text` and `latex_source`). The ascii art or the LaTeX
-                source code.
+        String: (output `latex_source`). The LaTeX source code.
+        TextDrawing: (output `text`). A drawing that can be printed as ascii art
     Raises:
         VisualizationError: when an invalid output method is selected
 
@@ -342,19 +342,17 @@ def _text_circuit_drawer(circuit, filename=None,
         reversebits (bool): Rearrange the bits in reverse order.
         plotbarriers (bool): Draws the barriers when they are there.
     Returns:
-        String: The drawing in a loooong string.
+        TextDrawing: An instances that, when printed, draws the circuit in ascii art.
     """
     dag_circuit = DAGCircuit.fromQuantumCircuit(circuit, expand_gates=False)
     json_circuit = transpile_dag(dag_circuit, basis_gates=basis, format='json')
-
-    text = "\n".join(
-        _text.TextDrawing(json_circuit, reversebits=reversebits, plotbarriers=plotbarriers).lines(
-            line_length))
+    text_drawing = _text.TextDrawing(json_circuit, reversebits=reversebits)
+    text_drawing.plotbarriers = plotbarriers
+    text_drawing.line_length = line_length
 
     if filename:
-        with open(filename, mode='w', encoding="utf8") as text_file:
-            text_file.write(text)
-    return text
+        text_drawing.dump(filename)
+    return text_drawing
 
 
 # -----------------------------------------------------------------------------
diff --git a/qiskit/tools/visualization/_text.py b/qiskit/tools/visualization/_text.py
--- a/qiskit/tools/visualization/_text.py
+++ b/qiskit/tools/visualization/_text.py
@@ -401,13 +401,20 @@ def fillup_layer(names):  # pylint: disable=arguments-differ
 class TextDrawing():
     """ The text drawing"""
 
-    def __init__(self, json_circuit, reversebits=False, plotbarriers=True):
+    def __init__(self, json_circuit, reversebits=False, plotbarriers=True, line_length=None):
         self.json_circuit = json_circuit
         self.reversebits = reversebits
         self.plotbarriers = plotbarriers
+        self.line_length = line_length
         self.qubitorder = self._get_qubitorder()
         self.clbitorder = self._get_clbitorder()
 
+    def __str__(self):
+        return self.single_string()
+
+    def _repr_html_(self):
+        return '<pre style="line-height: 15px;">%s</pre>' % self.single_string()
+
     def _get_qubit_labels(self):
         qubits = []
         for qubit in self.json_circuit['header']['qubit_labels']:
@@ -449,6 +456,24 @@ def _get_clbitorder(self):
                 creg_dest_order.append("%s_%s" % (creg[0], clbit))
         return [creg_dest_order.index(ind) for ind in self._get_clbit_labels()]
 
+    def single_string(self):
+        """
+        Creates a loong string with the ascii art
+        Returns:
+            str: The lines joined by '\n'
+        """
+        return "\n".join(self.lines())
+
+    def dump(self, filename, encoding="utf8"):
+        """
+        Dumps the ascii art in the file.
+        Args:
+            filename (str): File to dump the ascii art.
+            encoding (str): Optional. Default "utf-8".
+        """
+        with open(filename, mode='w', encoding=encoding) as text_file:
+            text_file.write(self.single_string())
+
     def lines(self, line_length=None):
         """
         Generates a list with lines. These lines form the text drawing.
@@ -462,6 +487,8 @@ def lines(self, line_length=None):
         Returns:
             list: A list of lines with the text drawing.
         """
+        if line_length is None:
+            line_length = self.line_length
         if line_length is None:
             line_length, _ = get_terminal_size()
 
@@ -475,6 +502,9 @@ def lines(self, line_length=None):
         # -| H |---
         # -| H |---
 
+        if not line_length:
+            line_length = self.line_length
+
         layer_groups = [[]]
         rest_of_the_line = line_length
         for layerno, layer in enumerate(layers):


Test patch:
diff --git a/test/python/test_circuit_text_drawer.py b/test/python/test_circuit_text_drawer.py
--- a/test/python/test_circuit_text_drawer.py
+++ b/test/python/test_circuit_text_drawer.py
@@ -16,8 +16,8 @@
 from .common import QiskitTestCase
 
 try:
-    from qiskit.tools.visualization import _text_circuit_drawer
     from qiskit.tools.visualization import _text as elements
+    from qiskit.tools.visualization import _text_circuit_drawer
 
     VALID_MATPLOTLIB = True
 except (RuntimeError, ImportError):
@@ -98,7 +98,7 @@ def test_text_pager(self):
         circuit.measure(qr[0], cr[0])
         circuit.cx(qr[1], qr[0])
         circuit.cx(qr[0], qr[1])
-        self.assertEqual(_text_circuit_drawer(circuit, line_length=20), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, line_length=20)), expected)
 
     def test_text_no_pager(self):
         """ The pager can be disable."""
@@ -106,7 +106,7 @@ def test_text_no_pager(self):
         circuit = QuantumCircuit(qr)
         for _ in range(100):
             circuit.h(qr[0])
-        amount_of_lines = _text_circuit_drawer(circuit, line_length=-1).count('\n')
+        amount_of_lines = str(_text_circuit_drawer(circuit, line_length=-1)).count('\n')
         self.assertEqual(amount_of_lines, 2)
 
 
@@ -133,7 +133,7 @@ def test_text_measure_1(self):
         cr = ClassicalRegister(3, 'c')
         circuit = QuantumCircuit(qr, cr)
         circuit.measure(qr, cr)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_measure_1_reversebits(self):
         """ The measure operator, using 3-bit-length registers, with reversebits """
@@ -154,7 +154,7 @@ def test_text_measure_1_reversebits(self):
         cr = ClassicalRegister(3, 'c')
         circuit = QuantumCircuit(qr, cr)
         circuit.measure(qr, cr)
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_measure_2(self):
         """ The measure operator, using some registers. """
@@ -181,7 +181,7 @@ def test_text_measure_2(self):
         cr2 = ClassicalRegister(2, 'c2')
         circuit = QuantumCircuit(qr1, qr2, cr1, cr2)
         circuit.measure(qr2, cr2)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_measure_2_reversebits(self):
         """ The measure operator, using some registers, with reversebits """
@@ -208,7 +208,7 @@ def test_text_measure_2_reversebits(self):
         cr2 = ClassicalRegister(2, 'c2')
         circuit = QuantumCircuit(qr1, qr2, cr1, cr2)
         circuit.measure(qr2, cr2)
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_swap(self):
         """ Swap drawing. """
@@ -225,7 +225,7 @@ def test_text_swap(self):
         qr2 = QuantumRegister(2, 'q2')
         circuit = QuantumCircuit(qr1, qr2)
         circuit.swap(qr1, qr2)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_swap_reversebits(self):
         """ Swap drawing with reversebits. """
@@ -242,7 +242,7 @@ def test_text_swap_reversebits(self):
         qr2 = QuantumRegister(2, 'q2')
         circuit = QuantumCircuit(qr1, qr2)
         circuit.swap(qr1, qr2)
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_cswap(self):
         """ CSwap drawing. """
@@ -258,7 +258,7 @@ def test_text_cswap(self):
         circuit.cswap(qr[0], qr[1], qr[2])
         circuit.cswap(qr[1], qr[0], qr[2])
         circuit.cswap(qr[2], qr[1], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cswap_reversebits(self):
         """ CSwap drawing with reversebits. """
@@ -274,7 +274,7 @@ def test_text_cswap_reversebits(self):
         circuit.cswap(qr[0], qr[1], qr[2])
         circuit.cswap(qr[1], qr[0], qr[2])
         circuit.cswap(qr[2], qr[1], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_cu3(self):
         """ cu3 drawing. """
@@ -289,7 +289,7 @@ def test_text_cu3(self):
         circuit = QuantumCircuit(qr)
         circuit.cu3(pi / 2, pi / 2, pi / 2, qr[0], qr[1])
         circuit.cu3(pi / 2, pi / 2, pi / 2, qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cu3_reversebits(self):
         """ cu3 drawing with reversebits"""
@@ -304,7 +304,7 @@ def test_text_cu3_reversebits(self):
         circuit = QuantumCircuit(qr)
         circuit.cu3(pi / 2, pi / 2, pi / 2, qr[0], qr[1])
         circuit.cu3(pi / 2, pi / 2, pi / 2, qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_crz(self):
         """ crz drawing. """
@@ -319,7 +319,7 @@ def test_text_crz(self):
         circuit = QuantumCircuit(qr)
         circuit.crz(pi / 2, qr[0], qr[1])
         circuit.crz(pi / 2, qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cx(self):
         """ cx drawing. """
@@ -334,7 +334,7 @@ def test_text_cx(self):
         circuit = QuantumCircuit(qr)
         circuit.cx(qr[0], qr[1])
         circuit.cx(qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cy(self):
         """ cy drawing. """
@@ -349,7 +349,7 @@ def test_text_cy(self):
         circuit = QuantumCircuit(qr)
         circuit.cy(qr[0], qr[1])
         circuit.cy(qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cz(self):
         """ cz drawing. """
@@ -364,7 +364,7 @@ def test_text_cz(self):
         circuit = QuantumCircuit(qr)
         circuit.cz(qr[0], qr[1])
         circuit.cz(qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_ch(self):
         """ ch drawing. """
@@ -379,7 +379,7 @@ def test_text_ch(self):
         circuit = QuantumCircuit(qr)
         circuit.ch(qr[0], qr[1])
         circuit.ch(qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cu1(self):
         """ cu1 drawing. """
@@ -394,7 +394,7 @@ def test_text_cu1(self):
         circuit = QuantumCircuit(qr)
         circuit.cu1(pi / 2, qr[0], qr[1])
         circuit.cu1(pi / 2, qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_cu1_reversebits(self):
         """ cu1 drawing with reversebits"""
@@ -409,7 +409,7 @@ def test_text_cu1_reversebits(self):
         circuit = QuantumCircuit(qr)
         circuit.cu1(pi / 2, qr[0], qr[1])
         circuit.cu1(pi / 2, qr[2], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit, reversebits=True), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, reversebits=True)), expected)
 
     def test_text_ccx(self):
         """ cx drawing. """
@@ -425,7 +425,7 @@ def test_text_ccx(self):
         circuit.ccx(qr[0], qr[1], qr[2])
         circuit.ccx(qr[2], qr[0], qr[1])
         circuit.ccx(qr[2], qr[1], qr[0])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_reset(self):
         """ Reset drawing. """
@@ -443,7 +443,7 @@ def test_text_reset(self):
         circuit = QuantumCircuit(qr1, qr2)
         circuit.reset(qr1)
         circuit.reset(qr2[1])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_single_gate(self):
         """ Single Qbit gate drawing. """
@@ -461,7 +461,7 @@ def test_text_single_gate(self):
         circuit = QuantumCircuit(qr1, qr2)
         circuit.h(qr1)
         circuit.h(qr2[1])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_barrier(self):
         """ Barrier drawing. """
@@ -479,7 +479,7 @@ def test_text_barrier(self):
         circuit = QuantumCircuit(qr1, qr2)
         circuit.barrier(qr1)
         circuit.barrier(qr2[1])
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_plotbarriers(self):
         """ Drawing without plotbarriers. """
@@ -499,7 +499,7 @@ def test_text_plotbarriers(self):
         circuit.barrier(qr1)
         circuit.barrier(qr2[1])
         circuit.h(qr2)
-        self.assertEqual(_text_circuit_drawer(circuit, plotbarriers=False), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit, plotbarriers=False)), expected)
 
     def test_text_conditional_1(self):
         """ Conditional drawing with 1-bit-length regs."""
@@ -520,7 +520,7 @@ def test_text_conditional_1(self):
                               "c1_0: 0 ═════════╡ = 0x1 ╞",
                               "                 └───────┘"])
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_conditional_2(self):
         """ Conditional drawing with 2-bit-length regs."""
@@ -545,7 +545,7 @@ def test_text_conditional_2(self):
                               "c1_1: 0 ═════════╡       ╞",
                               "                 └───────┘"])
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_conditional_3(self):
         """ Conditional drawing with 3-bit-length regs."""
@@ -574,7 +574,7 @@ def test_text_conditional_3(self):
                               "c1_2: 0 ═════════╡       ╞",
                               "                 └───────┘"])
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_conditional_4(self):
         """ Conditional drawing with 4-bit-length regs."""
@@ -607,7 +607,7 @@ def test_text_conditional_4(self):
                               "c1_3: 0 ═════════╡       ╞",
                               "                 └───────┘"])
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
 
     def test_text_conditional_5(self):
         """ Conditional drawing with 5-bit-length regs."""
@@ -644,7 +644,20 @@ def test_text_conditional_5(self):
                               "c1_4: 0 ═════════╡       ╞",
                               "                 └───────┘"])
         circuit = QuantumCircuit.from_qasm_str(qasm_string)
-        self.assertEqual(_text_circuit_drawer(circuit), expected)
+        self.assertEqual(str(_text_circuit_drawer(circuit)), expected)
+
+    def test_text_measure_html(self):
+        """ The measure operator. HTML representation. """
+        expected = '\n'.join(["<pre style=\"line-height: 15px;\">        ┌─┐",
+                              "q_0: |0>┤M├",
+                              "        └╥┘",
+                              " c_0: 0 ═╩═",
+                              "           </pre>"])
+        qr = QuantumRegister(1, 'q')
+        cr = ClassicalRegister(1, 'c')
+        circuit = QuantumCircuit(qr, cr)
+        circuit.measure(qr, cr)
+        self.assertEqual(_text_circuit_drawer(circuit)._repr_html_(), expected)
 
 
 if __name__ == '__main__':
diff --git a/test/python/test_visualization_output.py b/test/python/test_visualization_output.py
--- a/test/python/test_visualization_output.py
+++ b/test/python/test_visualization_output.py
@@ -115,7 +115,7 @@ def test_text_drawer(self):
         self.assertFilesAreEqual(filename, self.text_reference)
         os.remove(filename)
         try:
-            encode(output, encoding='cp437')
+            encode(str(output), encoding='cp437')
         except UnicodeEncodeError:
             self.fail("_text_circuit_drawer() should only use extended ascii (aka code page 437).")