Scrolling/Zooming within a Plotwidget - python

i am playing around with a Plotwidget and i cant figure out how i can find the min-max positions from my list when zooming in and out.
Lets say i have a list which has 1000 values. When im scrolling/zooming i can only figure out the viewbox range and not the positions i am in my list.
Example:
On init i am at x[0], x[1000]
Scrolling once im at x[?], x[?]
Is it possible to find out the min-max index (if i am not directly on an index , whats the closest one)? I also would like to know, if it is possible to change the zooming factor when scrolling and can i set the viewbox padding to 0? I only figured out that u can change this with setXRange(..., padding=0)
import sys
from random import randint
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt5.QtCore import Qt
import pyqtgraph as pg
class Win(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.graph = pg.PlotWidget()
self.setCentralWidget(self.graph)
self.graph.sigRangeChanged.connect(self.viewboxChanged)
x = list()
y = list()
pos = 0
for i in range(1000):
r = randint(0,5)
y.append(r)
x.append(pos)
pos +=r
self.graph.plot(x, y)
def viewboxChanged(self, view, range):
print(range)
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Win()
win.show()
sys.exit( app.exec_() )

Your question is more like how to interpret the values in your range variable. Note: Consider changing that variable name, because it can be confusing because there is a python's built-in-function called like that.
Your range variable has this structure:
[ [x_0, x_1], [y_0, y_1] ]
Where:
x_0 and x_1 are the minimum and maximum values that are visible in the x-axis.
y_0 and y_1 are the minimum and maximum values that are visible in the y-axis.
Now, what you want are the indexes, and that can be done using this answer. Finally, the implementation of all of the above in your code will look like this:
class Win(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.graph = pg.PlotWidget()
self.setCentralWidget(self.graph)
self.graph.sigRangeChanged.connect(self.viewboxChanged)
## Change the variables from local to an instance attribute
## to acces from the other functions
self.x = list()
self.y = list()
pos = 0
for i in range(1000):
r = randint(0,5)
self.y.append(r)
self.x.append(pos)
pos +=r
self.graph.plot(self.x, self.y)
def viewboxChanged(self, view, range_v):
## range_v[0] will return the list: [x_0, x_1]
x0 = range_v[0][0]
x1 = range_v[0][1]
x0_index = min(self.x, key = lambda x: abs(x-x0))
x1_index = min(self.x, key = lambda x: abs(x-x1))
print(x0_index, x1_index)
pass
There are comments inside explaining the changes I did.

Related

When I use "currentIndex()" on

I've been stuck on a school assignment for hours because of this one problem. I need to check the index number (AS AN INTEGER) for the currently selected item in a ListWidget.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QListView
from PyQt5.QtGui import QPixmap
import Ui_countries
class MyForm(QMainWindow, Ui_countries.Ui_mainWindow):
# DON'T TOUCH!
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
self.setupUi(self)
# END DON'T TOUCH
# EVENT HOOKS HERE
self.countryList.itemClicked.connect(self.CountrySelected)
self.actionLoad_Countries.triggered.connect(self.LoadCountries)
self.sqUnits.currentIndexChanged.connect(self.SqUnits)
#self.updatePopulation.itemClicked.connect(self.updateMemory)
# RESPONSES HERE
# def updateMemory(self):
def LoadCountries(self):
global namelist
global populationlist
global arealist
namelist = []
populationlist = []
arealist = []
objFile = open("GUI/countries.txt")
for line in objFile:
line = line.replace("\n","")
lineList = line.split(",")
self.countryList.addItem(lineList[0])
namelist.append(lineList[0])
populationlist.append(lineList[1])
arealist.append(lineList[2])
objFile.close()
def CountrySelected(self,selectedCountryIndex):
QMessageBox.information(self,"Country changed!",selectedCountryIndex.text())
strCountryName = selectedCountryIndex.text()
strCountryName = strCountryName.replace(" ", "_")
imagePixmap = QPixmap(f"GUI/Flags/{strCountryName}")
strCountryName = strCountryName.replace("_", " ")
self.lblCountryName.setText(strCountryName)
self.flag.setPixmap(imagePixmap)
self.flag.resize(imagePixmap.width(),imagePixmap.height())
idx = self.countryList.currentIndex()
# self.populationbox.setText(populationlist[idx])
# selectedCountryIndex.index()
#^^^^^^^^^^^ useful code
print(int(strCountryName))
def SqUnits(self):
QMessageBox.information(self,"Event Received","Please convert between different units.")
if self.sqUnits.currentText() == "Sq. Miles":
self.totalareabox.setText("YAAAAA")
else:
self.totalareabox.setText("YEEEE")
# DON'T TOUCH
if __name__ == "__main__":
app = QApplication(sys.argv)
the_form = MyForm()
the_form.show()
sys.exit(app.exec_())
The area to focus on would be the CountrySelected function. Whenever I try to run idx = self.countryList.currentIndex(), instead of an integer, I get 'PyQt5.QtCore.QModelIndex object at 0x051A6470' if I try to print idx. My instructor wants us to use pyqt, and I have no experience with it, so I'm kinda freaking out!
Figured it out, classmate sent me this: index = self.listWidget.currentRow()
No idea why index didn't work, but alas, tis the nature of doing your assignments one hour before they're due.

Python passing QtDesigner class as dynamic argument to class in external file

I want to be able to pass in any QT class object between two Python classes in separate *.py files. I researched this on-line and within SO before posting. I'm not that skilled in OO or Python.
First py file was created in QT_designer (5.11): rs485ConfigMenu with class Ui_rs485ConfigMenu(object), with from PyQt5 import QtCore, QtGui, QtWidgets. Not touching that code.
I wrote a second python (python 3.5) file with a class that should accept the Ui_rs485ConfigMenu. I know that the second argument for the class is wrong, but I don't know how to pass in the Ui class for any Ui object as needed (e.g., Ui_menu01 vs. Ui_menu02, etc.).
My code for the second, separate python file and its class is:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
widgetList = [ "QPushButton", "QCheckBox", "QDialogButtonBox", "QLineEdit", "QRadioButton", "QComboBox", "QLabel"]
class widgetListMethod( QMainWindow, Ui_menu ):
def __init__(self):
super( widgetListMethod, self ).__init__()
# Ui_mainMenu.__init__(self)
# self.setupUi(self)
def iterThroughWidgets(self):
menuWidgets = {}
i = 0
b = self.mainMenuFrame.children()
#need general menuFrame name for all menus
#need way of checking all instances in widgetList
for w in b:
flag1 = 0
string0 = str(type(w))
string1 = string0.split("PyQt5.QtWidgets.")
string2 = string1[1]
string3 = string2.split("'")
if not set(string3).isdisjoint(widgetList):
widgetText = w.text()
labelValue = widgetText.split("_value")
if isinstance( w, QLabel ) and len( labelValue ) > 1:
#keep only those QLabels that have .value in label
i = i + 1
flag1 = 1
elif not isinstance( w, QLabel ):
i = i + 1
flag1 = 1
if flag1 == 1:
x = w.geometry().x()
y = w.geometry().y()
menuWidgets[i] = [x, y, widgetText, w ]
return menuWidgets
Ui code from QtDesigner alone exceeds length for characters of 3000 by another 3000+. Cannot paste Ui and py code. Happy to send on or provide link from safe-site.

How to select cells in QtableWidget from list of indexes

In a QtableWidget, I would like to store the selected cells while I query a database and return the previously selected cells back to being selected. My refresh of items on the QtableWidget clears the selection. The user can select non-contiguous ranges of cells.
I have no problem getting the selected cells before I refresh the data with QtableWidget.selectedIndexes().
I have tried looping through the list of indexes and using setCurrentIndex but that only leaves me with the last index. I have run out of ideas. How can I restore the selected ranges of cells based on the stored indexes?
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from room_chart import *
from datetime import datetime, timedelta
class Guest_form(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self)
self.ui = Ui_rooms_chart()
self.ui.setupUi(self)
self.build_chart()
self.ui.book.clicked.connect(self.book)
def book(self):
self.indexes = self.ui.room_chart.selectedIndexes()
#Do stuff
self.build_chart()
#This has the right behaviour but only selects the last index
for x in range(len(self.indexes)):
self.ui.room_chart.setCurrentIndex(self.indexes[x])
self.ui.room_chart.setFocus()
def build_chart(self):
self.ui.room_chart.setRowCount(0)
self.ui.room_chart.setColumnCount(0)
col_labels = []
for x in range(8):
current_day = datetime.now() + timedelta(days=x)
col_labels.append(current_day.strftime('%a') + '\n' + current_day.strftime('%d/%m/%y'))
self.ui.room_chart.setColumnCount(len(col_labels))
self.ui.room_chart.setHorizontalHeaderLabels(col_labels)
row_labels = []
for x in range(8):
row_labels.append(str(x))
self.ui.room_chart.setRowCount(len(row_labels))
self.ui.room_chart.setVerticalHeaderLabels(row_labels)
self.button = QPushButton(self.ui.room_chart)
self.button.setText("Push me")
self.ui.room_chart.setCellWidget(0 , 0, self.button)
if __name__=="__main__":
app=QApplication(sys.argv)
myapp = Guest_form()
myapp.show()
sys.exit(app.exec_())
You have to use the select() method of QItemSelectionModel:
def book(self):
persistenIndex = map(QPersistentModelIndex, self.ui.room_chart.selectedIndexes())
#Do stuff
self.build_chart()
for pix in persistenIndex:
ix = QModelIndex(pix)
self.ui.room_chart.selectionModel().select(ix, QItemSelectionModel.Select)
self.ui.room_chart.setFocus()
Note: It converts the QModelIndex to QPersistentModelIndex to avoid problems since it is not known if build_chart() deletes, moves or performs any other action that changes the position of the items.

Qprogressbar with two values

I have some unusual question :
For visualization of packing progress i think about qprogressbar with two values in one bar - one showing bytes read, and another showing write-out bytes, which gives also imagine about compress ratio.
It is possible with QT4 ?
Also, I have very little experience with C++ coding, my current work is based on Python, PyQT4,
Yes it's possible, but you will have to implement your own "DualValueProgressbar" here you have an example, is not complete production code but it will point to you in the right direction.
A note before continue:
Will this you will be able to show two values in the bar, but show two colours in the same bar is a very different thing. So I'll recomend you to use two prograssbar for doing what you want, keep it simple.
Before see any code let me explain what I did.
Subclass QProgressBar
Add a variable member called self.__value_1. This will be the second value.
Override the method paintEvent in order to draw self.__value_1 inside the bar.
Recomendations:
Write code for establishing limits on the second value. (Minimun and maximun)
Write code for handle the format property.
Write code for habdle the aligment property.
This is the result:
Here is the code:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class DualValueProgressBar(QProgressBar):
def __init__(self, parent=None):
super(DualValueProgressBar, self).__init__(parent)
# The other value you want to show
self.__value_1 = 0
def paintEvent(self, event):
# Paint the parent.
super(DualValueProgressBar, self).paintEvent(event)
# In the future versions if your custom object you
# should use this to set the position of the value_1
# in the progressbar, right now I'm not using it.
aligment = self.alignment()
geometry = self.rect() # You use this to set the position of the text.
# Start to paint.
qp = QPainter()
qp.begin(self)
qp.drawText(geometry.center().x() + 20, geometry.center().y() + qp.fontMetrics().height()/2.0, "{0}%".format(str(self.value1)))
qp.end()
#property
def value1(self):
return self.__value_1
#pyqtSlot("int")
def setValue1(self, value):
self.__value_1 = value
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = QWidget()
hlayout = QHBoxLayout(window)
dpb = DualValueProgressBar(window)
dpb.setAlignment(Qt.AlignHCenter)
# This two lines are important.
dpb.setValue(20)
dpb.setValue1(10) # Look you can set another value.
hlayout.addWidget(dpb)
window.setLayout(hlayout)
window.show()
sys.exit(app.exec())
Finally the code sample:

How to interact with mayavi window while animating particles? (during Particle Swarm Optimization)

I am trying to do an animation of a Particle Swarm Optimization using Python and Mayavi2.
The animation is working fine, my problem is that it is not possible to interact with the plot while it is animating the movement. Specifically i would like to turn the graph and zoom. Maybe someone has experience doing animations?
The way i do it is first to calculate the positions of the particles and then to store them. After the calculation is finished i plot the positions of the particle at the first instace of time with point3d() and then i iterate through time updating the data using the set() method.
Is there a way to make it possible to turn the graph? I have heard about something with threads, disabeling the the rendering, but i could not figure out how to do it in my code. Besides lots of other stuff, I have read:
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/mlab_animating.html
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/tips.html#acceleration-mayavi-scripts
but it can't see how to use it.
Any suggestions?
Here is my code:
#!/usr/bin/env python
'''
#author rt
'''
import pylab as plt
from numpy import *
from mayavi import mlab
from threading import Thread # making plotting faster?
import ackley as ac
class Swarm(Thread, object):
'''
constructor for the swarm
initializes all instance variables
'''
def __init__(self,objective_function):
Thread.__init__(self)
# optimization options
self.omega = 0.9 # inertial constant
self.c1 = 0.06 # cognitive/private constant
self.c2 = 0.06 # social constant
self.objective = objective_function # function object
self.max_iteration = 100 # maximal number of iterations
# Swarm stuff
self.number = 0
self.best = [] # gbest; the global best position
self.particles = [] # empty list for particles
# temporary
self.min = self.objective.min
self.max = self.objective.max
self.best_evolution = []
# self.dimensions = 2 # dimensions NB!
'''
add particles to the swarm
find the best position of particle in swarm to set global best
'''
def add_particles(self, n):
for i in range(n):
particle = Particle(self)
if i == 0: # initialize self.best
self.best = particle.position
if particle.eval() < self._eval(): # check if there is a better and if, set it
self.best = copy(particle.position)
self.particles.append(particle) # append the particle to the swarm
def _eval(self):
return self.objective.evaluate(self.best)
def plot(self):
for i in range(self.max_iteration):
pos_x = []
pos_y = []
pos_z = []
#print pos_x
for particle in self.particles:
[x,y,z] = particle.trail[i]
pos_x.append(x)
pos_y.append(y)
pos_z.append(z)
#print pos_x
if i ==0:
g = mlab.points3d(pos_x, pos_y,pos_z, scale_factor=0.5)
ms =g.mlab_source
ms.anti_aliasing_frames = 0
ms.set(x=pos_x, y = pos_y, z = pos_z,scale_factor=0.5) #updating y value
#print pos_y
#ms.set(x=pos_x) # update x values
#ms.set(y=pos_y) #updating y value
#ms.set(z=pos_z) #updating y value
#for p in self.particles:
#p.plot()
def plot_objective(self):
delta = 0.1
v = mgrid[self.min:self.max:delta,self.min:self.max:delta]
z = self.objective.evaluate(v)
#mlab.mesh(v[0],v[1],z)
mlab.surf(v[0],v[1],z) # surf creates a more efficient data structure than mesh
mlab.xlabel('x-axis', object=None)
mlab.ylabel('y-axis', object=None)
mlab.zlabel('z-axis', object=None)
def _info(self):
self.plot()
print '----------------------------'
print 'The best result is:'
print 'Coordinates:', self.best
print 'Value: ', self._eval()
#print 'with ', nreval, 'evaluations'
print 'nr of particles: ', len(self.particles)
print '----------------------------'
def run(self):
self.plot_objective()
self.best = self.particles[0].get_position()
iteration = 0
while iteration < self.max_iteration:
#if iteration!= 0: obj.scene.disable_render = True
#disable_render = True
for particle in self.particles:
rnd_c1 = array([random.uniform(0,1),random.uniform(0,1)])
rnd_c2 = array([random.uniform(0,1),random.uniform(0,1)])
particle.velocity = self.omega * array(particle.velocity) + \
self.c1 * rnd_c1 * (array(particle.best) - array(particle.position)) + \
self.c2 * rnd_c2 * (array(self.best) - array(particle.position)) # TODO: change so independent rnd for components
particle.position = array(particle.position) + particle.velocity
if particle.eval() < particle.best_eval():
particle.best = copy(particle.position)
if particle.eval() < self._eval():
self.best = copy(particle.position)
particle.update() # add the point to the trail
iteration +=1
self.best_evolution.append(self._eval())
#obj.scene.disable_render = False
print 'finished: ', iteration
self._info()
'''
Class modeling particle
'''
class Particle():
def __init__(self, swarm):
self.swarm = swarm
x_rand = random.uniform(self.swarm.min,self.swarm.max)
y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.position = array([x_rand,y_rand])
v_x_rand = random.uniform(self.swarm.min,self.swarm.max)
v_y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.velocity = array([v_x_rand, v_y_rand])
self.size = 0.5
self.best = self.position
# visualization
self.trail = []
def plot(self):
[x,y] = self.position
z = self.eval()
mlab.points3d(x,y,z,scale_factor=self.size)
def eval(self):
return self.swarm.objective.evaluate(self.position)
def best_eval(self):
return self.swarm.objective.evaluate(self.best)
def get_position(self):
return self.position
def update(self):
[x,y] = self.position
z = self.eval()
#print [x,y,z]
self.trail.append([x,y,z])
def plot_trail(self,index):
[x,y,z] = self.trail[index]
mlab.points3d(x,y,z,scale_factor=self.size)
# Make the animation
mlab.figure(1, bgcolor=(0, 0, 0), size=(1300, 700)) # create a new figure with black background and size 1300x700
objective = ac.Ackley() # make an objective function
swarm = pso.Swarm(objective) # create a swarm
nr_of_particles = 25 # nr of particles in swarm
swarm.add_particles(nr_of_particles)
swarm.run()
#swarm.start()
mlab.show()
print '------------------------------------------------------'
print 'Particle Swarm Optimization'
#objective.info()
print 'Objective function to minimize has dimension = ', objective.get_dimension()
print '# of iterations = ', 1000
print '# of particles in swarm = ', nr_of_particles
print '------------------------------------------------------'
In my case, even though I was somewhat able to do what Brandon Rhodes suggested for a mock program (https://stackoverflow.com/questions/16617814/interacting-with-mlab-scene-while-it-is-being-drawn), I could not manage to convert my already existing larger program.
Then I found this link: http://wiki.wxpython.org/LongRunningTasks
So, I just sprinkled a lot of wx.Yield() s inside my loops. This way I did not need to change my program structure, and I am able to interact with the window. I think better ways are explained in the link.
Your problem is that the wx event loop, which runs the Mayavi GUI window and listens for mouse clicking and dragging and responds by moving the scene, is not getting any time to run during your animation because you are keeping Python captive in your loop without ever letting it return control.
Instead of keeping control of the program with a loop of your own, you need to create a wx.Timer class that advances the scene by one frame update, and that then returns control to the wx event loop after scheduling itself again. It will look something like this:
import wx
...
class Animator(wx.Timer):
def Notify(self):
"""When a wx.Timer goes off, it calls its Notify() method."""
if (...the animation is complete...):
return
# Otherwise, update all necessary data to advance one step
# in the animation; you might need to keep a counter or
# other state as an instance variable on `self`
# [DATA UPDATE GOES HERE]
# Schedule ourselves again, giving the wx event loop time to
# process any pending mouse motion.
self.Start(0, oneShot=True) # "in zero milliseconds, call me again!"
I played with slightly higher values like 1 for the number of milliseconds that wx gets to run the UI with, but could not really tell a difference between that and just choosing 0 and having control returned "immediately".

Categories