I'm trying to make an Application with PyQt5, Python 3.7.3 using a Raspberry pi4B and a 5 inch touch screen.
The thing is that I need to make a QDial, but I want it to make more than one revolution if it goes from min range to max range. For example, if the Qdial has range from 0 to 500, I want it to make 100 points per revolution, so you have to do a full rotation 5 times to go from the min value to the max value.
This is what I've tried:
`
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = QDial()
self.dial.setMinimum(0)
self.dial.setMaximum(100)
self.dial.setValue(40)
self.dial.valueChanged.connect(self.sliderMoved)
self.dial.setWrapping(True)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
self.isHigher=False
def sliderMoved(self):
print("Dial value = %i" % (self.dial.value()))
self.text.setText(str(self.dial.value()))
if(self.dial.value()==100 and self.isHigher==False):
self.higher_range()
self.isHigher=True
if(self.dial.value()==100 and self.isHigher==True):
self.lower_range()
self.isHigher=False
def higher_range(self):
self.dial.setRange(100,200)
self.dial.setValue(105)
def lower_range(self):
self.dial.setRange(0,100)
self.dial.setValue(95)
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
`
But this doesn't work, It keeps changing from 95 to 105 and viceversa.
QDial is a pretty peculiar control. While it's still supported, it's poorly implemented, and I believe it's by choice: due to its nature, it's really hard to add more features. I had quite an amount of experience with it, and I know it's not an easy element to deal with.
One of its issues is that it represents a monodimensional range but, visually and UI speaking, it is a bidimensional object.
What you're trying to achieve is possible, but consider that an UI element should always display its state in a clear way and have a corresponding proper behavior; that's the only way UI can tell the user the state. Physical dials don't have this issue: you also have a tactile response that tells you when the gear reaches its end.
From my experience I could tell you that you should avoid it as much as possible: it seems a nice and intuitive widget, but in reality it's very difficult to get a proper result that is actually intuitive to the user. There are some instances for which it makes sense to use it (in my case, representation of a physical knob of an electronic musical instrument). I suggest you to do some research on skeumorphism and UX aspects.
That said, this is a possible raw implementation. I've overridden some aspects (most importantly, the valueChanged signal, for naming consistency), but for a proper implementation you should do much more work (and testing).
The trick is to set the range based on the number of "revolutions": if the maximum is 500 and 5 revolutions are chosen, then the dial will have an actual maximum of 100. Then, whenever the value changes, we check whether previous value was below or above the minimum/maximum of the actual range, and change the revolution count accordingly.
Two important notes:
since QDial inherits from QAbstractSlider, it has a range(minimum, maximum + 1), and since the division could have some rest, the "last" revolution will have a different range;
I didn't implement the wheel event, as that requires further inspection and choosing the appropriate behavior depending on the "previous" value and revolution;
class SpecialDial(QDial):
_cycleValueChange = pyqtSignal(int)
def __init__(self, minimum=0, maximum=100, cycleCount=2):
super().__init__()
assert cycleCount > 1, 'cycles must be 2 or more'
self.setWrapping(True)
self.cycle = 0
self.cycleCount = cycleCount
self._minimum = minimum
self._maximum = maximum
self._normalMaximum = (maximum - minimum) // cycleCount
self._lastMaximum = self._normalMaximum + (maximum - minimum) % self._normalMaximum
self._previousValue = super().value()
self._valueChanged = self.valueChanged
self.valueChanged = self._cycleValueChange
self._valueChanged.connect(self.adjustValueChanged)
self.setRange(0, self._normalMaximum)
def value(self):
return super().value() + self._normalMaximum * self.cycle
def minimum(self):
return self._minimum
def maximum(self):
return self._maximum()
def dialMinimum(self):
return super().minimum()
def dialMaximum(self):
return super().maximum()
def adjustValueChanged(self, value):
if value < self._previousValue:
if (value < self.dialMaximum() * .3 and self._previousValue > self.dialMaximum() * .6 and
self.cycle + 1 < self.cycleCount):
self.cycle += 1
if self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif (value > self.dialMaximum() * .6 and self._previousValue < self.dialMaximum() * .3 and
self.cycle > 0):
self.cycle -= 1
if self.cycle == 0:
self.setMaximum(self._normalMaximum)
new = self.value()
if self._previousValue != new:
self._previousValue = value
self.valueChanged.emit(self.value())
def setValue(self, value):
value = max(self._minimum, min(self._maximum, value))
if value == self.value():
return
block = self.blockSignals(True)
self.cycle, value = divmod(value, self._normalMaximum)
if self.dialMaximum() == self._normalMaximum and self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif self.dialMaximum() == self._lastMaximum and self.cycle < self.cycleCount - 1:
self.setMaximum(self._normalMaximum)
super().setValue(value)
self.blockSignals(block)
self._previousValue = self.value()
self.valueChanged.emit(self._previousValue)
def keyPressEvent(self, event):
key = event.key()
if key in (Qt.Key_Right, Qt.Key_Up):
step = self.singleStep()
elif key in (Qt.Key_Left, Qt.Key_Down):
step = -self.singleStep()
elif key == Qt.Key_PageUp:
step = self.pageStep()
elif key == Qt.Key_PageDown:
step = -self.pageStep()
elif key in (Qt.Key_Home, Qt.Key_End):
if key == Qt.Key_Home or self.invertedControls():
if super().value() > 0:
self.cycle = 0
block = self.blockSignals(True)
super().setValue(0)
self.blockSignals(block)
self.valueChanged.emit(self.value())
else:
if self.cycle != self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
self.cycle = self.cycleCount - 1
if super().value() != self._lastMaximum:
block = self.blockSignals(True)
super().setValue(self._lastMaximum)
self.blockSignals(block)
self.valueChanged.emit(self.value())
return
else:
super().keyPressEvent(event)
return
if self.invertedControls():
step *= -1
current = self.value()
new = max(self._minimum, min(self._maximum, current + step))
if current != new:
super().setValue(super().value() + (new - current))
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = SpecialDial()
self.dial.valueChanged.connect(self.sliderMoved)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
def sliderMoved(self):
self.text.setText(str(self.dial.value()))
I strongly suggest you to take your time to:
consider is this is really what you want, since, as said, this kind of control can be very tricky from the UX perspective;
carefully read the code and understand its logics;
Related
I am currently creating my genetic algorithm and want to print the number of generations at the very end of the program when it finishes. However I am unsure how to access the counter variable that is the number of generations when it is outside of the class and method. So for example, at the end it would be like
Generation 100, average fit 18966, best fit 18947
Your best chromosone at generation 100
'\x06pzÂ\x8cYÆr¯n0q\x07l¿M8\x93Þ\x19\x87"\x01\x85\x1er\x89[F_VyER\x9b\x0bm=)\x9a\x9a¿¥\x10F\x12A\x84\x0fZ^\x14\x99\x8a4®\x9f¿*\\\xa0yi\x19E\x8aÇ+6(_<¾£cO~\x9c\x99\x932\x06\x0f\x82\x7f¤\x808xǸñA\x13\x0e<%\x06ÿ#í\x91Pô\x98 ®\r\x1b}\x89y¦\x0cqAK\tp\x95\x99ÔNj=Wn\x16\x94\x0cu!¯ñ\x13Qü[e8_ÂóU\x10\x1av_+%Q_¡ù\x87=\x08~ciÎ_Ï[\x8f#AëT\x14©qG\x89#Z«L\x9b¢\x94WL\x1dV¶R03\x84æ^ßr\x1fÃÈ\x1d\x8e Læª&®x\x94?TAÒD\x14£i\x82J\x15=w~\x03\x0c\xa0¾5\x02f5T\x91ol¢bIÞfk¬¡27W16(}6\x92\x87\n®xm0\x1a\n<8(à}ñ\x88̾\x17g\x9bj6\x8fI&\x12\x9aÂ\x9a_F\x1a\r[\x1dK\x15<.±DjcIy`98d>\x197Z\x91£%tIJ\x820\x93|\x07\x8dnÚ QÂ!Pf\x1d\nåòf\x91\x1d#S¾|\x9ff[d>O=T$ݶI\x9e»QÛÂ\x1d"¿U=û´F÷\x83C}wA\xa0É\x8aD\x93x»\x85\x7f\x14^\x0eL'
done:
100 generations
How do I exactly access the 100 from the method in the class?
import random
class GeneticAlgorithm(object):
def __init__(self, genetics):
self.genetics = genetics
pass
def run(self):
population = self.genetics.initial()
while True:
fits_pops = [(self.genetics.fitness(ch), ch) for ch in population]
if self.genetics.check_stop(fits_pops): break
population = self.next(fits_pops)
pass
return population
def next(self, fits):
parents_generator = self.genetics.parents(fits)
size = len(fits)
nexts = []
while len(nexts) < size:
parents = next(parents_generator)
cross = random.random() < self.genetics.probability_crossover()
children = self.genetics.crossover(parents) if cross else parents
for ch in children:
mutate = random.random() < self.genetics.probability_mutation()
nexts.append(self.genetics.mutation(ch) if mutate else ch)
pass
pass
return nexts[0:size]
pass
class GeneticFunctions(object):
def probability_crossover(self):
r"""returns rate of occur crossover(0.0-1.0)"""
return 1.0
def probability_mutation(self):
r"""returns rate of occur mutation(0.0-1.0)"""
return 0.0
def initial(self):
r"""returns list of initial population
"""
return []
def fitness(self, chromosome):
r"""returns domain fitness value of chromosome
"""
return len(chromosome)
def check_stop(self, fits_populations):
r"""stop run if returns True
- fits_populations: list of (fitness_value, chromosome)
"""
return False
def parents(self, fits_populations):
r"""generator of selected parents
"""
gen = iter(sorted(fits_populations))
while True:
f1, ch1 = next(gen)
f2, ch2 = next(gen)
yield (ch1, ch2)
pass
return
def crossover(self, parents):
r"""breed children
"""
return parents
def mutation(self, chromosome):
r"""mutate chromosome
"""
return chromosome
pass
if __name__ == "__main__":
"""
example: Mapped guess prepared Text
"""
class GuessText(GeneticFunctions):
def __init__(self, target_text,
limit=100, size=100,
prob_crossover=0.9, prob_mutation=0.2):
self.target = self.text2chromo(target_text)
self.counter = 0
self.limit = limit
self.size = size
self.prob_crossover = prob_crossover
self.prob_mutation = prob_mutation
pass
# GeneticFunctions interface impls
def probability_crossover(self):
return self.prob_crossover
def probability_mutation(self):
return self.prob_mutation
def initial(self):
return [self.random_chromo() for j in range(self.size)]
def fitness(self, chromo):
# larger is better, matched == 0
return -sum(abs(c - t) for c, t in zip(chromo, self.target))
def check_stop(self, fits_populations):
self.counter += 1
if self.counter % 100 == 0:
best_match = list(sorted(fits_populations))[-1][1]
fits = [f for f, ch in fits_populations]
best = -(max(fits))
ave = -(sum(fits) / len(fits))
print(
"Generation %3d, average fit %4d, best fit %4d" %
(self.counter, ave, best,
))
print("Your best chromosone at generation %3d" % self.counter)
print("%r" % self.chromo2text(best_match))
pass
return self.counter >= self.limit
def parents(self, fits_populations):
while True:
father = self.tournament(fits_populations)
mother = self.tournament(fits_populations)
yield (father, mother)
pass
pass
def crossover(self, parents):
father, mother = parents
index1 = random.randint(1, len(self.target) - 2)
index2 = random.randint(1, len(self.target) - 2)
if index1 > index2: index1, index2 = index2, index1
child1 = father[:index1] + mother[index1:index2] + father[index2:]
child2 = mother[:index1] + father[index1:index2] + mother[index2:]
return (child1, child2)
def mutation(self, chromosome):
index = random.randint(0, len(self.target) - 1)
vary = random.randint(-5, 5)
mutated = list(chromosome)
mutated[index] += vary
return mutated
# internals
def tournament(self, fits_populations):
alicef, alice = self.select_random(fits_populations)
bobf, bob = self.select_random(fits_populations)
return alice if alicef > bobf else bob
def select_random(self, fits_populations):
return fits_populations[random.randint(0, len(fits_populations)-1)]
def text2chromo(self, text):
return [ord(ch) for ch in text]
def chromo2text(self, chromo):
return "".join(chr(max(1, min(ch, 255))) for ch in chromo)
def random_chromo(self):
return [random.randint(1, 255) for i in range(len(self.target))]
pass
GeneticAlgorithm(GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")).run()
print('done:')
print("%3d " 'generations' % counter)
pass
Define the GuessText first. Then access the counter.
gt = GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")
GeneticAlgorithm(gt).run()
print('done:')
print("%3d " 'generations' % gt.counter)
Consider a right-angle triangle, which has the properties
Hypotenuse (side)
Adjacent (side)
Opposite (side)
Area
Given any 2 of these properties, it is always possible to calculate the value of the other 2. My question relates to what the most efficient/elegant way of doing this is.
At present, the only way of doing this that I can think of is to use (4C2)*2 = 12 case statements, each relating to a possible combination of inputsa that may be provided.
For example, using python you might have something like
class RightAngleTriangle():
def __init__(this, propertyType1, propertyValue1, propertyType2, propertyValue2):
this.adjacent = 0
this.opposite = 0
this.hypotenuse = 0
this.area = 0
if (propertyType1 == "adjacent" and propertyType2 == "opposite"):
this.adjacent = propertyValue1
this.opposite = propertyValue2
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "opposite" and propertyType2 == "adjacent"):
this.adjacent = propertyValue2
this.opposite = propertyValue1
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "adjacent" and propertyType2 == "hypotenuse"):
this.adjacent = propertyValue1
this.hypotenuse = propertyValue2
this.opposite = (propertyValue2**2 + propertyValue1**2)**0.5
this.area = (this.opposite * this.adjacent)/2
...and so on...
You could then create your triangle object, and print its four properties, using code (in this case python) like the below.
t1 = RightAngleTriangle("adjacent", 10, "opposite", 12)
print(t1.adjacent)
print(t1.opposite)
print(t1.hypotenuse)
print(t1.area)
This is hideous. Is there a more eligant solution to this problem?
Yes, at least two - one using args and one using key word args. So:
class RightAngleTriangle():
def __init__(self, *args):
self.adjacent = 0
self.opposite = 0
self.hypotenuse = 0
self.area = 0
for property_type, property_value in zip(args[::2], args[1::2]):
setattr(self, property_type, property_value)
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
This would work with your current input, but let's agree - it's still not very elegant solution. So, let's use kwargs:
class RightAngleTriangle():
def __init__(self, adjacent=0, opposite=0, hypotenuse=0):
self.adjacent = adjacent
self.opposite = opposite
self.hypotenuse = hypotenuse
self.area = 0
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
And now you can simply call this code as:
t1 = RightAngleTriangle(adjacent=10, opposite=12)
Here is the code from one python file that I import into another file...
class Crop():
def water(self):
print('not')
def harvest(self):
print('not')
def __init__(self):
self.height = 0
class Corn(Crop):
def water(self):
self.height = self.height + 2
def harvest(self):
if self.height >= 9:
return 1
else:
return 0
class Wheat(Crop):
def water(self):
self.height = self.height + 1
def harvest(self):
if self.height >= 5:
return 1
else:
return 0
class Irrigator():
def __init__(self, load):
self.load = load
def irrigate(self, field):
while self.load > 0:
self.load = self.load - 1
field.rain()
I take the above code and then I import it into another python file...
from farmhelper import *
from random import *
# Field for holding the crops.
class Field():
def rain(self):
for i in range(len(self.plants)):
self.plants[i].water()
def __init__(self, size, crop):
self.plants = [0] * size
for i in range(size):
self.plants[i] = crop()
class Combine():
def harvest(self, field):
quantity = 0
for i in range(len(field.plants)):
quantity += field.plants[i].harvest()
return quantity
# Create fields with 10,000 of each crop
cornField = Field(10000, Corn)
wheatField = Field(10000, Wheat)
# Create irrigators for each field
cornIrrigator = Irrigator(20000)
wheatIrrigator = Irrigator(500)
# Create a combine for harvesting
combine = Combine()
# 90 days ~3 months of growth
for i in range(90):
# Low chance of rain
if randint(0, 100) > 95:
print("It rained")
cornField.rain()
wheatField.rain()
# Always run the irrigators. Since they are never
# refilled they will quickly run out
cornIrrigator.irrigate(cornField)
wheatIrrigator.irrigate(wheatField)
# Gather the crops - DONE
earsOfCorn = combine.harvest(cornField)
headsOfWheat = combine.harvest(wheatField)
# Print the result - DONE
print("Grew", earsOfCorn, "ears of corn")
print("and", headsOfWheat, "heads of wheat")
But for some reason it takes around 2 to 3 minutes for the code to run. I believe there is a problem with the latter code posted. If anyone has a solution, lemme know!
A more efficient design would be to not model every single plant as a separate instance. As it stands you perform the exact same operation on each plant of a field. Just give the Field a size attribute and a crop attribute thus modelling just that one plant per field and multiply any size related outputs by size.
Something along the lines of this:
class Field():
def rain(self):
self.crop.water()
def __init__(self, size, crop):
self.size = size
self.crop = crop()
class Combine():
def harvest(self, field):
quantity = field.crop.harvest() * field.size
return quantity
I am new to oop in python.
Below is a class for a mathod that is similar to range() except that it is inclusive for the range boundary.
I am trying to create an indexing method inside the class so that when a specific index is called the element with that index is returned. I read that __getitem__ can perform indexing yet I have not been successful in implementing it correctly. If there is a more efficient way not necessarily using __getitem__ please advise.
Please take a look at the code below, this is a simple code aimed at learning how create classes.
the method starting at def __getitem__(self,index) is the one that does not work and this corresponds to calling the index at the end o[4] which is what I would like to achieve.
class inclusive_range:
def __init__(self, *args):
numargs = len(args)
if numargs < 1: raise TypeError('requires at least one argument')
elif numargs == 1:
self.stop = args[0]
self.start = 0
self.step = 1
elif numargs == 2:
(self.start,self.stop) = args
self.step = 1
elif numargs == 3:
(self.start,self.stop,self.step) = args
else:
raise TypeError('three arguments required at most, got {}'.format(numargs))
def __iter__(self): # this makes the function (the method) and iterable function
i = self.start
while i <= self.stop:
yield i
i += self.step
def __getitem__(self,index):
return self[index]
print(self[index])
def main():
o = inclusive_range(5, 10, 1)
for i in o: print(i, end=' ')
o[2]
if __name__ == "__main__": main()
Thank you
You can just calculate the number based on self.start, the index and the step size. For your object to be a proper sequence you also need a length, which comes in handy when testing for the boundaries:
def __len__(self):
start, stop, step = self.start, self.stop, self.step
if step < 0:
lo, hi = stop, start
else:
lo, hi = start, stop
return ((hi - lo) // abs(step)) + 1
def __getitem__(self, i):
length = len(self)
if i < 0:
i += length
if 0 <= i < length:
return self.start + i * self.step
raise IndexError('Index out of range: {}'.format(i))
The above is based on my own translation of the range() source code to Python, with a small adjustment to account for the end being inclusive.
I'd cache the __len__ result in __init__, to avoid having to re-calculate it each time you want to know the length.
I have a python script that reads the input signal of a joystick via pygame and passes that input to a motor controller.
Now i would like to add a ramping function. let's say to ramp up to the input value with a given delay. if the input of the joystick is '100' it should only pass that value to the motor controller after the delay has passed and 'fade' in between.
Also, it would be a plus to define the ramp. (linear or logarithmic curve)
Is there a simple way of doing that?
thanks for your help!
A simple class could look like this:
class RampUp:
def __init__(self):
self.value = 0
self.target = 0
self.next = self.__next__
self.calc = None
def __iter__(self):
return self
def __next__(self):
if self.value == self.target:
return self.value
else:
self.value = self.calc(self.value, self.target)
return self.value
this class simply holds a state, is iterable and calls a function that calculates a new value in every iteration.
A simple "ramp" function that simply increases the output value by a given delta could be:
def simple(value, target):
if value < target:
value = min(value + 1, target)
else:
value = max(value - 1, target)
return value
Example of usage:
gen = RampUp()
gen.calc = simple
gen.target = 20
for _ in xrange(100):
# that's the value you pass to your motor controller
print next(gen)
Another example ramp function that is more complex:
def smooth(value, target):
# maximum acceleration is 2
delta_max = 2
# minimun acceleration is 0.5
delta = max(0.5, (abs(target) - abs(target-value)) / 10.)
delta = min(delta, delta_max)
if value < target:
value = min(value + delta, target)
else:
value = max(value - delta, target)
return value
In response to your comment:
def with_step(step):
def simple(value, target):
if value < target:
value = min(value + step, target)
else:
value = max(value - step, target)
return value
return simple
gen = RampUp()
gen.target = 20
# you could move this line into the class
# reaches the target value in 12 steps
gen.calc = with_step((gen.target - gen.value)/12.)