#!/usr/bin/python
# -*- coding: utf-8 -*-
 
from PyQt4.QtGui import *
from PyQt4.QtCore import *
 
import PyQt4.Qwt5 as Qwt
import math
 
import sys
import sip
 
LOG2_MIN = 2.**(-20) # Mininum value for logarithmic scales.
LOG2_MAX = 2.**20 # Maximum value for logarithmic scales. 
 
class CustomScaleEngine(Qwt.QwtScaleEngine):
	def __init__(self):
		Qwt.QwtScaleEngine.__init__(self)
 
	def transformation(self):
		return CustomScaleTransformation()
 
	def autoScale(self, maxNumSteps, x1, x2):
		if x1 > x2:
			qswap(x1, x2)
 
		base = 2.
		interval = Qwt.QwtDoubleInterval(x1 / pow(base, self.lowerMargin()), x2 * pow(base, self.upperMargin()) )
 
		logRef = 1.
		if self.reference() > LOG2_MIN / 2:
			logRef = min(self.reference(), LOG2_MAX / 2)
 
		if self.testAttribute(Qwt.QwtScaleEngine.Symmetric):
			delta = max(interval.maxValue() / logRef, logRef / interval.minValue())
			interval.setInterval(logRef / delta, logRef * delta)
 
		if self.testAttribute(Qwt.QwtScaleEngine.IncludeReference):
			interval = interval.extend(logRef)
 
		interval = interval.limited(LOG2_MIN, LOG2_MAX)
 
		if interval.width() == 0.:
			interval = self.buildInterval(interval.minValue())
 
		stepSize = self.divideInterval(self.log2(interval).width(), max(maxNumSteps, 1))
		if stepSize < 1.:
			stepSize = 1.
 
		if not self.testAttribute(Qwt.QwtScaleEngine.Floating):
			interval = self.align(interval, stepSize)
 
		x1 = interval.minValue()
		x2 = interval.maxValue()
 
		if self.testAttribute(Qwt.QwtScaleEngine.Inverted):
			qSwap(x1, x2)
			stepSize = -stepSize
 
		return (x1, x2, stepSize)
 
	def divideScale(self, x1, x2, maxMajSteps, maxMinSteps, stepSize):
		interval = Qwt.QwtDoubleInterval(x1, x2).normalized()
		interval = interval.limited(LOG2_MIN, LOG2_MAX)
 
		if interval.width() <= 0:
			return Qwt.QwtScaleDiv()
 
		base = 2.
		if interval.maxValue() / interval.minValue() < base:
			# scale width is less than one octave -> build linear scale
 
			linearScaler = Qwt.QwtLinearScaleEngine()
			linearScaler.setAttributes(self.attributes())
			linearScaler.setReference(self.reference())
			linearScaler.setMargins(self.lowerMargin(), self.upperMargin())
 
			return linearScaler.divideScale(x1, x2, maxMajSteps, maxMinSteps, stepSize)
 
		stepSize = abs(stepSize)
 
		if stepSize == 0.:
			if maxMajSteps < 1:
				maxMajSteps = 1
 
			stepSize = self.divideInterval(self.log2(interval).width(), maxMajSteps)
 
			if stepSize < 1.:
				stepSize = 1. # major step must be >= 1 decade
 
		scaleDiv = Qwt.QwtScaleDiv()
 
		if stepSize <> 0.:
			ticks = self.buildTicks(interval, stepSize, maxMinSteps)
			scaleDiv = Qwt.QwtScaleDiv(interval, ticks[0], ticks[1], ticks[2])
 
		if x1 > x2:
			 scaleDiv.invert()
 
		return scaleDiv
 
	def buildTicks(self, interval, stepSize, maxMinSteps):
		boundingInterval = self.align(interval, stepSize)
 
		ticks = [[]]*Qwt.QwtScaleDiv.NTickTypes
 
		ticks[Qwt.QwtScaleDiv.MajorTick] = self.buildMajorTicks(boundingInterval, stepSize)
 
		if maxMinSteps > 0:
			ticks[Qwt.QwtScaleDiv.MinorTick] = self.buildMinorTicks(ticks[Qwt.QwtScaleDiv.MajorTick], maxMinSteps, stepSize)
 
		for i in range(0, Qwt.QwtScaleDiv.NTickTypes):
			ticks[i] = self.strip(ticks[i], interval)
 
		return ticks
 
	def buildMajorTicks(self, interval, stepSize):
		width = self.log2(interval).width()
 
		numTicks = int(round(width/stepSize)) + 1
 
		if numTicks > 10000:
			numTicks = 10000
 
		base = 2.
		lxmin = math.log(interval.minValue(), base)
		lxmax = math.log(interval.maxValue(), base)
		lstep = (lxmax - lxmin) / float(numTicks - 1)
 
		ticks = []
 
		ticks += [interval.minValue()]
 
		for i in range(1, numTicks-1):
			ticks += [math.pow(base, lxmin + float(i) * lstep)]
 
		ticks += [interval.maxValue()]
 
		return ticks
 
	def buildMinorTicks(self, majorTicks, maxMinSteps, stepSize):
		if stepSize < 1.1: # major step < one octave
			if (maxMinSteps < 1):
				return []
 
			if maxMinSteps >= 8:
				k0 = 2
				kmax = 9
				kstep = 1
			elif maxMinSteps >= 4:
				k0 = 2
				kmax = 8
				kstep = 2
			elif maxMinSteps >= 2:
				k0 = 2
				kmax = 5
				kstep = 3
			else:
				k0 = 5
				kmax = 5
				kstep = 1
 
			minorTicks = []
 
			for i in range(0, len(majorTicks)):
				v = majorTicks[i]
 
				for k in range (k0, kmax, kstep):
					minorTicks += [v * float(k)]
 
			return minorTicks
 
		else: # major step > one octave
			minStep = self.divideInterval(stepSize, maxMinSteps)
 
			if minStep == 0.:
				return []
 
			if minStep < 1.:
				minStep = 1.
 
			# number of subticks per interval
			nMin = int(round(stepSize / minStep)) - 1
 
			# Do the minor steps fit into the interval?
			if Qwt.QwtScaleArithmetic.compareEps((nMin +  1) * minStep, abs(stepSize), stepSize) > 0:
				nMin = 0
 
			if nMin < 1:
				return [] # no subticks
 
			# substep factor = 2^substeps
			base = 2.
			minFactor = max(pow(base, minStep), base)
 
			minorTicks = []
			for i in range(0, len(majorTicks)):
				val = majorTicks[i]
 
				for k in range(0, nMin):
					val *= minFactor
					minorTicks += [val]
 
			return minorTicks
 
	def align(self, interval, stepSize):
		intv = self.log2(interval)
 
		x1 = Qwt.QwtScaleArithmetic.floorEps(intv.minValue(), stepSize)
		x2 = Qwt.QwtScaleArithmetic.ceilEps(intv.maxValue(), stepSize)
 
		return self.pow2(Qwt.QwtDoubleInterval(x1, x2))
 
	def log2(self, interval):
		base = 2.
		return Qwt.QwtDoubleInterval(math.log(interval.minValue(), base), math.log(interval.maxValue(), base))
 
	def pow2(self, interval):
		base = 2.
		return Qwt.QwtDoubleInterval(math.pow(base, interval.minValue()), pow(base, interval.maxValue()))
 
 
class CustomScaleTransformation(Qwt.QwtScaleTransformation):
	def __init__(self):
		Qwt.QwtScaleTransformation.__init__(self, Qwt.QwtScaleTransformation.Other)
		self.base = 2.
 
	def copy(self):
		return CustomScaleTransformation()
 
	def xForm(self, s, s1, s2, p1, p2):
		s = max(s, LOG2_MIN)
		return p1 + (p2 - p1) / math.log(s2 / s1, self.base) * math.log(s / s1, self.base)
 
	def invXForm(self, p, p1, p2, s1, s2):
		return math.pow(self.base, (p - p1) / (p2 - p1) * math.log(s2 / s1, self.base)) * s1
 
#just some code to display window...
class window(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
 
        self.widget = QWidget()
        self.layout = QVBoxLayout()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)
 
        self.plot = Qwt.QwtPlot()
        self.layout.addWidget(self.plot)
 
        import numpy
        x = numpy.logspace(math.log(20., 2), math.log(20e3, 2), 100., base = 2.)
        y = numpy.random.rand(len(x)) + 1.
        curve = Qwt.QwtPlotCurve()
        curve.attach(self.plot)
        curve.setData(x,y)
        self.plot.setAxisScaleEngine(Qwt.QwtPlot.xBottom, CustomScaleEngine())
        #self.plot.setAxisScaleEngine(Qwt.QwtPlot.xBottom, Qwt.QwtLinearScaleEngine())
        self.plot.replot()
 
if __name__ == '__main__':
    #sip.settracemask(0x0007)
    app = QApplication(sys.argv)
    form = window()
    form.resize(800,600)
    form.show()
    app.exec_()