Repository: pyqtgraph/pyqtgraph

Issue:
Adding an AxisItem to a PlotItem causes an exception
### Code to reproduce
<!-- Please provide a minimal working example that reproduces the issue in the code block below.
     Ideally, this should be a full example someone else could run without additional setup. -->
```python
import pyqtgraph as pg

pg.mkQApp()

p1 = pg.plot([1,2])

ax2 = pg.AxisItem("right")
p1.addItem(ax2)

pg.QAPP.exec_()
```

### Expected behavior
You should be able to add an `AxisItem` to a `PlotItem`
### Real behavior
```
Traceback (most recent call last):
  File "/home/vieler/issue.py", line 8, in <module>
    p1.addItem(ax2)
  File "/home/vieler/.virtualenvs/general/lib/python3.7/site-packages/pyqtgraph/graphicsItems/PlotItem/PlotItem.py", line 526, in addItem
    item.setLogMode(self.ctrl.logXCheck.isChecked(), self.ctrl.logYCheck.isChecked())
TypeError: setLogMode() takes 2 positional arguments but 3 were given
```

### Tested environment(s)

 * PyQtGraph version: 0.10.0
 * Qt Python binding: PyQt5 5.12.1 Qt 5.12.2
 * Python version: 3.7.3
 * NumPy version: 1.16.4
 * Operating system: Arch Linux x64
 * Installation method: pip


Discussion:
Can reproduce this from the current develop branch.
Ran this through the debugger, this is really weird, put the breakpoint right before the call, verified I'm getting booleans from the `self.ctrl.log[X|Y]Check.isChecked()` but it's still saying it's getting 3 positional arguments.

Then I called it myself with

```
(Pdb) item.setLogMode(False, False)
*** TypeError: setLogMode() takes 2 positional arguments but 3 were given
```

As soon as I typed that issue, I figured out the problem

`AxisItem.setLogMode` takes in arguments of `self` and `log`, whereas `PlotItem.setLogMode`, takes arguments of `self`, `x` and `y`.

Fix likely would involve making `setLogMode` for axisItem take the same arguments as PlotItem...but changes in the rest of the code base will likely need to take place to make sure things are compatible.
I'm curious about use cases for adding additional `AxisItem`s to a `PlotItem`'s `ViewBox`. The `PlotItem` is basically a grid layout with four `AxisItem` objects bounding a central `ViewBox`, so there are already top and right `AxisItem`s which you can show with e.g. `plotItem.showAxis('right')`. Also see [examples/MultiplePlotAxes.py](https://github.com/pyqtgraph/pyqtgraph/blob/develop/examples/MultiplePlotAxes.py) for an approach to making plots with separate left/right axes.

Though I suppose just from an API standpoint it makes sense to be able to add any of the `*Item` objects to a plot without raising an exception. The simplest thing to do is just add  `and not isinstance(item, AxisItem)` here:

https://github.com/pyqtgraph/pyqtgraph/blob/ad7453c7818eddb3b18354c65370f416cb668b29/pyqtgraph/graphicsItems/PlotItem/PlotItem.py#L498-L499

And on a related note: I think it might make sense to make it possible to pass the `axisItems` kwarg through `pg.plot`.
First, I didn't really know that the right AxisItem was already there, but hidden. I thought that the layout could accommodate one but you had to create it yourself (well, my fault :smiley: ).

Second, I still think that there are two reason to fix this problem:

1. In the [docs](http://pyqtgraph.org/documentation/graphicsItems/plotitem.html), it says
   > Use addItem() to add any QGraphicsItem to the view.

    So either this documentation part gets corrected or the code is.
2. @ixjlyons rightly asks for a use case. I agree that it might be a bit specific but, for example, you would want to display the same data in two different ways. The simplest example I can think of is showing the "lossess" of system (an optical component, a transmission line...) in both dB and %. Or even, in spectroscopy, you might want to display your X axis in both nanometers and cm<sup>-1</sup>

Resolving patch:
diff --git a/pyqtgraph/graphicsItems/AxisItem.py b/pyqtgraph/graphicsItems/AxisItem.py
--- a/pyqtgraph/graphicsItems/AxisItem.py
+++ b/pyqtgraph/graphicsItems/AxisItem.py
@@ -205,20 +205,42 @@ def setGrid(self, grid):
         self.prepareGeometryChange()
         self.update()
 
-    def setLogMode(self, log):
+    def setLogMode(self, *args, **kwargs):
         """
-        If *log* is True, then ticks are displayed on a logarithmic scale and values
-        are adjusted accordingly. (This is usually accessed by changing the log mode
-        of a :func:`PlotItem <pyqtgraph.PlotItem.setLogMode>`)
-        The linked ViewBox will be informed of the change.
+        Set log scaling for x and/or y axes.
+
+        If two positional arguments are provided, the first will set log scaling
+        for the x axis and the second for the y axis. If a single positional
+        argument is provided, it will set the log scaling along the direction of
+        the AxisItem. Alternatively, x and y can be passed as keyword arguments.
+
+        If an axis is set to log scale, ticks are displayed on a logarithmic scale
+        and values are adjusted accordingly. (This is usually accessed by changing
+        the log mode of a :func:`PlotItem <pyqtgraph.PlotItem.setLogMode>`.) The 
+        linked ViewBox will be informed of the change.
         """
-        self.logMode = log
-        self.picture = None
+        if len(args) == 1:
+            self.logMode = args[0]
+        else:
+            if len(args) == 2:
+                x, y = args
+            else:
+                x = kwargs.get('x')
+                y = kwargs.get('y')
+
+            if x is not None and self.orientation in ('top', 'bottom'):
+                self.logMode = x
+            if y is not None and self.orientation in ('left', 'right'):
+                self.logMode = y
+        
         if self._linkedView is not None:
-            if self.orientation in ('top', 'bottom'):
-                self._linkedView().setLogMode('x', log)
+            if self.orientation in ('top', 'bottom'):           
+                self._linkedView().setLogMode('x', self.logMode)    
             elif self.orientation in ('left', 'right'):
-                self._linkedView().setLogMode('y', log)
+                self._linkedView().setLogMode('y', self.logMode)    
+
+        self.picture = None
+
         self.update()
 
     def setTickFont(self, font):
diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
--- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
+++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
@@ -907,10 +907,10 @@ def updateLogMode(self):
         for i in self.items:
             if hasattr(i, 'setLogMode'):
                 i.setLogMode(x,y)
-        self.getAxis('bottom').setLogMode(x)
-        self.getAxis('top').setLogMode(x)
-        self.getAxis('left').setLogMode(y)
-        self.getAxis('right').setLogMode(y)
+        self.getAxis('bottom').setLogMode(x, y)
+        self.getAxis('top').setLogMode(x, y)
+        self.getAxis('left').setLogMode(x, y)
+        self.getAxis('right').setLogMode(x, y)
         self.enableAutoRange()
         self.recomputeAverages()
     


Test patch:
diff --git a/tests/graphicsItems/test_AxisItem.py b/tests/graphicsItems/test_AxisItem.py
--- a/tests/graphicsItems/test_AxisItem.py
+++ b/tests/graphicsItems/test_AxisItem.py
@@ -1,4 +1,5 @@
 from math import isclose
+import pytest
 
 import pyqtgraph as pg
 
@@ -146,3 +147,31 @@ def test_AxisItem_label_visibility():
     assert axis.labelText == ''
     assert axis.labelUnits == 'V'
     assert axis.label.isVisible()
+
+@pytest.mark.parametrize(
+    "orientation,x,y,expected",
+    [
+       ('top', False, True, False),
+       ('top', True, False, True),
+       ('left', False, True, True),
+       ('left', True, False, False),
+    ],
+)
+def test_AxisItem_setLogMode_two_args(orientation, x, y, expected):
+    axis = pg.AxisItem(orientation)
+    axis.setLogMode(x, y)
+    assert axis.logMode == expected
+
+@pytest.mark.parametrize(
+    "orientation,log,expected",
+    [
+       ('top', True, True),
+       ('left', True, True),
+       ('top', False, False),
+       ('left', False, False),
+    ],
+)
+def test_AxisItem_setLogMode_one_arg(orientation, log, expected):
+    axis = pg.AxisItem(orientation)
+    axis.setLogMode(log)
+    assert axis.logMode == expected