I'm new to python/pyside/pyqtgraph and I'm kind of stuck in my program.
So, I have an numpy.ndarray representing 10000 values, and I plot it in a PlotWidget using the plot method.
The result is ok but now I want to allow the user to select points of the curve so I can save the X axis of the point and use it later on.
What I would like to do is creating a QPushButton which when clicked it waits for the user to select two points on the curve by left-clicking and then save the X axis. Seems pretty simple conceptually but I don't find the good way of doing it.
I would be really pleased if you could give me an example or something, I'm also open to any suggestion that deviate from this use case.
I can resume the code by this lines :
self.myWidget = pyqtgraph.PlotWidget()
self.myWidget.plot(myValues) # myValues is the numpy array
self.select2PointsButton = QtGui.QPushButton()
self.select2PointsButton.clicked.connect(self.waitForSelection)
def waitForSelection(self):
# Wait for a click left on the curve to select first point then save the X-axis
# Do it again to select the second point
Thanks,
Morgan
Edit after Zet4 answer :
Thank you for your answer it helped me get started.
In the end, I made a subclass of PlotWidget :
class PltWidget(pg.PlotWidget):
def __init__(self, parent=None):
super(PltWidget, self).__init__(parent)
self.selectionMode = False
def mousePressEvent(self, ev):
if self.selectionMode:
if ev.button() == QtCore.Qt.LeftButton:
# How do I get the X axis ?
else:
super(PltWidget, self).mousePressEvent(ev)
Then I use it in my window, connecting the button signal with the slot changing the boolean of my PltWidget :
..... # Other attributes and connections of my Window
self.T0Button = QtGui.QPushButton()
self.graphicsLeft = PltWidget()
self.T0Button.clicked.connect(self.selectT0)
def selectT0(self):
self.graphicsLeft.selectionMode = not(self.graphicsLeft.selectionMode)
I'll probably use your buffer strategy to command two selections from the user.
However, I still need to know how do I get the X axis of the PlotWidget from where I clicked. If anyone using pyqtgraph know the answer, please let me know.
Thanks.
My apologize, i'm not a pyqt expert, but your problem seems too be more conceptual than technical.
You can use your QPushButton.clicked (in your code, the waitForSelection function) to change the functional state of your object (allow or disable point selection).
So you need to :
create a function that intercept the click on your pushButton (your waitForSelection function)
create a function that intercept left click on your graphical object (i'll assume you name it onLeftClick)
a functional state handler : a boolean is the easiest way for it ( isSelectedMode ).
a buffer that represent a point. ( buffer here, it can be your X-axis as you say'ed )
Your waitForSelection function will only inverse the state of isSelectedMode. It will also clear the buffer, before you don't need it anymore. pseudocode :
if isSelectedMode == true
buffer = null;
isSelectedMode = !isSelectedMode;
The onLeftClick will do the harder of the work, see this pseudocode :
if isSelectedMode == true
if buffer != null // No point save, so this click is the first one
//Here you save your data in the buffer
else
// One point is saved so that's the second one.
// Do what you want with it and buffer
else
// The selection mode is not active.
This only thing that missing is the way of getting your X-axis from your left click.
I hope this can help you.
Best regards
Related
I try to get window origin in a mate panel applet.
To be more precise I want to know the position of my applet (x and y) on the screen because I have a button which show/hide a Gtk.window but I need to move that window next to my button (above, below, right, left depending on where the mate panel is)
The only way that I found is to call get_origin but there is a problem. It should return a tuple x,y but instead like the c function it require two integers and since python use pass by value of course it doesn't work.
This code is valid but useless:
window = self.get_window()
x = 0
y = 0
window.get_origin(x, y)
All other "way to use" get_origin (that you can found in any doc) does not work because it require 3 args (I don't know why)
So I'm looking for a way to get the position of my applet (even if it's not accurate) or to move my window next to my button.
I found an alternative get_root_coords
window = self.get_window()
x,y = window.get_root_coords(0, 0)
It works even with multiple screens and I'm able to move my Gtk.Window next to my button with it.
I've been trying to set a fixed size padding (in pixels) on my matplotlib figure. I've never been able to find a solution that suits my needs on the internet so I had to make a little workaround for this, which is done with the following code :
def resizeEvent(self,e):
windowWidth=self.geometry().width()
windowHeight=self.geometry().height()
#print(windowWidth,windowHeight)
figure.subplots_adjust(left=100.0/windowWidth,right=1-100.0/windowWidth, top=1-100.0/windowHeight,bottom=100.0/windowHeight)
It works fine when manually resizing the window (we have a padding of 100px on every side).
Unfortunatly, when clicking Maximize, the padding (in 0 to 1) seems to be equal to it's previous value, even if the print returns the correct window size (1920px).
A second click to Restore Down will then set the padding to the value we should have when we maximized it.
I don't really get what's happening here, I must be missing something...
Tell me if you need more information such as more code.
Thank you for your kind help :)
I've had similar issues in the past (the figure not resizing at start up) and moved the recalculation of the figure subplots to the draw method. Something like this (I use tight_layout but you get the idea):
class PlotWidget(FigureCanvas):
def __init__(self):
self.__resizing_needed = True
def draw(self):
if self.__resizing_needed:
self.__resizing_needed = False
try:
self.figure.tight_layout()
except ValueError:
# Set the minimumSize of this widget to prevent this
print('Plot too small.')
super(PlotWidget, self).draw()
def resizeEvent(self, event):
self.__resizing_needed = True
super(PlotWidget, self).resizeEvent(event)
I want both view below are blue, how to set it? please help me! when i forcus to the second line i want it highlight both of object are blue, not one blue and one grey as below.
Code like this:
ui = twin_gtk_builder('twin.ui', ['dia_support', 'liststore7'])
win = ui.get_object('dia_support')
##### Begin function tree view
liststore = gtk.ListStore(int, int, int)
liststore.append([1,2,3])
liststore.append([2,2,2])
liststore.append([4,4,4])
win.sw = gtk.ScrolledWindow()
win.sm = gtk.TreeModelSort(liststore)
##### Set sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.vbox.pack_start(win.sw)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
##### End function tree view
win.show_all()
and how it work
Tried one more time with #PM 2Ring help, Thanks so much for your help!
Somebody did it like this, but i can't find his contact...
I had to do a bit of work to get that code to run, Sunshine jp. In future, please try to post code that others can run & test, especially if it's GUI code. Otherwise it can be very hard to work out what the problem is and how to fix it.
I'm not familiar with twin_gtk1_builder(). Is it a GTK1 function?
Anyway, I've modified your code to run on GTK2+. I'm not quite sure what you want your code to do. So I've given row 2 a background color of cyan. Also, I've added the ability to make multiple selections, either using Ctrl or Shift on the keyboard when you select with the mouse; you can also do multiple selection with the keyboard with shift up and down arrows.
When the window loses focus the selected row(s) stays blue on my system. Maybe that's a feature of GTK2 that GTK1 doesn't have. (Or maybe it's due to my window manager - I'm using KDE 4.5.3 on Mepis Linux).
#!/usr/bin/env python
'''
TreeView test
From http://stackoverflow.com/questions/25840091/how-to-make-forcus-highlight-for-2-objects-at-the-same-time
'''
import pygtk
#pygtk.require('2.0')
import gtk
def TreeViewTest():
def delete_event(widget, event, data=None):
gtk.main_quit()
return False
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("TreeView Test")
win.set_size_request(320, 160)
win.connect("delete_event", delete_event)
win.vbox = gtk.VBox()
win.add(win.vbox)
win.sw = gtk.ScrolledWindow()
win.vbox.pack_start(win.sw)
##### Begin function tree view
#Set up liststore data. Column 3 controls whether
# background color of the TreeView is default or special.
liststore = gtk.ListStore(int, int, int, bool)
liststore.append([1, 2, 3, False])
#Tell row 2 to use the special color
liststore.append([2, 2, 2, True])
liststore.append([4, 4, 4, False])
win.sm = gtk.TreeModelSort(liststore)
##### Set initial sort column
n = 1
win.sm.set_sort_column_id(n, gtk.SORT_ASCENDING)
win.tv = gtk.TreeView(win.sm)
win.sw.add(win.tv)
win.tv.column = [None] * 3
win.tv.column[0] = gtk.TreeViewColumn('0-1000')
win.tv.column[1] = gtk.TreeViewColumn('0-1000000')
win.tv.column[2] = gtk.TreeViewColumn('-10000-10000')
#Set up cell renderers
win.tv.cell = [None] * 3
for i in range(3):
win.tv.cell[i] = gtk.CellRendererText()
win.tv.cell[i].set_property('cell-background', 'cyan')
win.tv.append_column(win.tv.column[i])
win.tv.column[i].set_sort_column_id(i)
win.tv.column[i].pack_start(win.tv.cell[i], True)
#win.tv.column[i].set_attributes(win.tv.cell[i], text=i)
win.tv.column[i].set_attributes(win.tv.cell[i], text=i,
cell_background_set=3)
#Allow multiple selection
treeselection = win.tv.get_selection()
treeselection.set_mode(gtk.SELECTION_MULTIPLE)
##### End function tree view
win.show_all()
def main():
TreeViewTest()
gtk.main()
if __name__ == "__main__":
main()
Note that this is NOT a good way to make a GUI. You should be creating a proper class, not adding everything as an attribute to win. Please see the PyGTK 2.0 Tutorial for plenty of code examples.
Edit
Ok. Sorry about my earlier confusion over what your problem is. At least we've now got a nice simple example of a PyGTK program that creates a TreeView. :)
Anyway, it turns out that I was right when I guessed that the blue color of the selection turning to grey when the window loses focus on your computer is due to the behaviour of the window manager. I suppose there may be a way to block that in the application, by playing with Widget attributes, but I'm not sure how to do that. And besides, it's considered rude for programs to ignore the settings in the users' window theme.
So the most appropriate solution to your problem is to make the appropriate change in your window manager's appearance settings.
In KDE the relevant property is called "Inactive selection changes color", as described in Color Scheme Options:
Inactive selection changes color — If checked, the current selection in elements which do not have input focus will be drawn using a different color. This can assist visual identification of the element with input focus in some applications, especially those which simultaneously display several lists.
To change this, open up system settings (ALT+F2 → "systemsettings", or the [K] menu → system settings), then go to "Application appearance" and select "Colors". In the "Options" tab, uncheck the "Inactive selection changes color" setting, and click apply.
... ... ...
If you're not using KDE you'll have to figure out for yourself how to change it; hopefully, other window manager settings interfaces and documentation refer to this property with the same name or a similar name.
I'm working on a window manager written using python's xlib bindings and I'm (initially) attempting to mimic dwm's behavior in a more pythonic way. I've gotten much of what I need, but I'm having trouble using X's built in window border functionality to indicate window focus.
Assuming I've got an instance of Xlib's window class and that I'm reading the documentation correctly, this should do what I want to do (at least for now) - set the window border of a preexisting window to a garish color and set the border width to 2px.
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(\
"#ff00ff").pixel
window.change_attributes(None,border_pixel=border_color,
border_width = 2 )
self.dpy.sync()
However, I get nothing from this - I can add print statements to prove that my program is indeed running the callback function that I associated with the event, but I get absolutely no color change on the border. Can anyone identify what exactly I'm missing here? I can pastebin a more complete example, if it will help. I'm not exactly sure it will though as this is the only bit that handles the border.
Looks like this was complete PEBKAC. I've found an answer. Basically, I was doing this:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color,
border_width=2)
self.dpy.sync()
Apparently this was confusing X enough that it was doing nothing. The solution that I've stumbled upon was to remove the border_width portion from the window.change_attributes() call, like so:
def set_active_border(self, window):
border_color = self.colormap.alloc_named_color(
"#ff00ff"
).pixel
window.configure(border_width=2)
window.change_attributes(
None,
border_pixel=border_color
)
self.dpy.sync()
I hope this helps someone later on down the road!
I have a treeview-widget inside a ScrolledWindow, which is populated during runtime. I want the ScrolledWindow to auto-scroll to the end of the list. I "solved" the problem, by adjusting the vadjustment of the ScrolledWindow, everytime a row is inserted into the treeview. e.g:
if new_line_in_row:
adj = self.scrolled_window.get_vadjustment()
adj.set_value( adj.upper - adj.page_size )
If i run the code in an interactive ipython session and set the value by myself, everything works as expected.
If i run the code with the default python interpreter, the auto-scroll doesn't work all the time. I debugged the code and the problem seems be, that the adjustment values have some kind of "lag" and are only changed after some period of time.
My question is: how do I scroll, reliably, to maximum position of the ScrolledWindow? is a special signal generated which i can use? or is there a better way to set the adjustment-value?
After widening my search-radius, i found a ruby-related answer. since the problem is gtk-related, it should be able to be solved in any language like this:
you connect the widget which changes, in my case the treeview, with gtk.widget's 'size-allocate' signal and set the gtk.scrolledwindow value to "upper - page_size". example:
self.treeview.connect('size-allocate', self.treeview_changed)
...
def treeview_changed(self, widget, event, data=None):
adj = self.scrolled_window.get_vadjustment()
adj.set_value( adj.upper - adj.page_size )
link to the original post at ruby-forum.com:
hint hint
fookatchu's answer can be improved so that the callback could be used by multiple widgets:
def treeview_changed( self, widget, event, data=None ):
adj = widget.get_vadjustment()
adj.set_value( adj.upper - adj.pagesize )
Python Gtk 3 version:
adj.set_value(adj.get_upper() - adj.get_page_size())
The accepted answer has helped me figure out a Rust solution in gtk-rs to the auto-scroll to end of content issue.
Here's a Rust snippet that might help others:
// Imports used for reference
use gtk::{TextBuffer, TextView, TextBufferBuilder, ScrolledWindow, ScrolledWindowBuilder};
// The text buffer where the text is appended later
let text_buffer: TextBuffer = TextBufferBuilder::new().build();
// The containing text view that holds the text buffer
let text_view: TextView = TextView::new_with_buffer(&text_buffer);
// The scrolled window container with fixed height/width that holds the text view
let scrolled_window: ScrolledWindow = ScrolledWindowBuilder::new()
.min_content_height(400)
.min_content_width(600)
.child(&text_view)
.build();
// Have the text view connect to signal "size-allocate"
text_view.connect_size_allocate(clone!(#weak scrolled_window => move |_,_| {
let adj = scrolled_window.get_vadjustment().unwrap();
adj.set_value(adj.get_upper() - adj.get_page_size());
}));
// ...
// Later on, fill buffer with some text.
text_buffer.insert(&mut text_buffer.get_end_iter(), "This is my text I'm adding");
None of the suggested solutions worked for me, but I was able to get the desired behavior doing this (using GTk4 bindings for Go):
adj := scrolledWindow.VAdjustment()
adj.SetUpper(adj.Upper() + adj.PageSize())
adj.SetValue(adj.Upper())
The other solutions would move the scrolled window (mine contained a list box) down to the 2nd-to-last item, but not show the last item. The way it was acting made me think that the upper limit needed to be increased, so I tried increasing it by page-size then setting the scroll value to the new upper limit.
Still need the callback (I used a textView rather than treeView):
textView = Gtk.TextView()
scrolledWindow = Gtk.ScrolledWindow()
def textViewChanged( self, widget ):
adjustment = scrolledWindow.get_vadjustment()
adjustment.set_value( adjustment.get_upper() - adjustment.get_page_size() )
textView.connect( "size-allocate", textViewChanged )