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.
Related
I have an application (actually a plugin for another application) that presents a GTK notebook. Each tab contains a technical drawing of an operation, with a set of SpinButtons that allow you to alter the dimensions of the operation.
If you need more context, it's here: https://forum.linuxcnc.org/41-guis/26550-lathe-macros?start=150#82743
As can be seen above, this all worked fine in GTK2. The widgets (first iteration in a GTK_Fixed, then moved to a GTK_Table) were pre-positioned and the image (a particular layer of a single SVG) was plonked in behind.
Then we updated to GTK3 (and Python 3) and it stopped working. The SVG image now appears on top of the input widgets, and they can no-longer be seen or operated.
I am perfectly happy to change the top level container[1], if that will help. But the code that used to work (and now doesn't) is:
def on_expose(self,nb,data=None):
tab_num = nb.get_current_page()
tab = nb.get_nth_page(tab_num)
cr = tab.get_property('window').cairo_create()
cr.set_operator(cairo.OPERATOR_OVER)
alloc = tab.get_allocation()
x, y, w, h = (alloc.x, alloc.y, alloc.width, alloc.height)
sw = self.svg.get_dimensions().width
sh = self.svg.get_dimensions().height
cr.translate(0, y)
cr.scale(1.0 *w / sw, 1.0*h/sh)
#TODO: gtk3 drawing works, but svg is drawn over the UI elements
self.svg.render_cairo_sub(cr = cr, id = '#layer%i' % tab_num)
[1] In fact I will probably go back to GTK_Fixed and move the elements about in the handler when the window resizes, scaled according to the original position. The GTK_Table (deprecated) version takes over 2 minutes to open in the Glade editor.
Unless there is a more elegant way to do this too?
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
I'm quite new to Python and have been unsuccessful in finding a way around this problem. I have a GUI using TKinter that displays an image using Label. I would like the user to be able to click on two places in the image and use those two pixel locations elsewhere.
Below is the basic code I'm using so far, but I'm unable to return the pixel locations. I believe bind is not what I want to use, is there another option?
px = []
py = []
def onmouse(event):
px.append(event.x)
py.append(event.y)
return px,py
self.ImgPanel.bind('<button-1>',onmouse)
If I try to use:
px,py = self.ImgPanel.bind('<button-1>',onmouse)
I get an error "Too many values to unpack"
bind is what you want, if you want to capture the x,y coordinate of the click. However, functions called from bindings don't "return". Technically they do, but they return a value to the internals of Tkinter.
What you need to do is set an instance or global variable within the bound function. In the code you included in your question, if you add global px,py, you can then use those values in other code.
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 glade GUI and i'm using dome gtk.MessageDialog widgets created with pygtk for user interaction. My problem is that whenever I throw a dialog message on the screen, they show up all over the place. One might show up on the top right corner, the next on the bottom left, top left, mid left etc...
Is there a way to force these things to show up in the center of the screen or at the position where the parent window is at?
Never mind. Found the solution.
For others who might wander about the same thing, the solution to this problem lies in specifying a parent value to the gtk.MessageDialog construct.
If you are using a glade gui, in your class, and your glade xml is loaded in to a variable named 'gui', it would look like this:
#!/usr/bin/env/python
par = self.gui.get_widget('your_parent_window')
msg = gtk.MessageDialog(type=gtk.MESSAGE_INFO, buttons = gtk.BUTTONS_OK, parent=par)
if msg.run():
msg.destroy()
return None
Check out the reference material at PyGTK 2.0 Reference Manual
I have not had a chance to try this but MessageDialog seems to be derived from Window which has a set_position method.
This method accepts one of the following:
# No influence is made on placement.
gtk.WIN_POS_NONE
# Windows should be placed in the center of the screen.
gtk.WIN_POS_CENTER
# Windows should be placed at the current mouse position.
gtk.WIN_POS_MOUSE
# Keep window centered as it changes size, etc.
gtk.WIN_POS_CENTER_ALWAYS
# Center the window on its transient parent
# (see the gtk.Window.set_transient_for()) method.
gtk.WIN_POS_CENTER_ON_PARENT
None of the provided solutions will work if your parent window is not yet shown, that is if the messagedialog is to be shown during the instantiation of a class (your class, not the "parent" window class). During this time Gtk has not yet placed the window, even if code for messagedialog is after the code that shows the window. Which means your dialog box will be somehow "parentless" and the message dialog will appear wherever it likes...
My naive solution for that problem...
GObject.timeout_add(interval=50, function=self.stupid_dialog_1)
and
def stupid_dialog_1(self):
par = self.gui.get_widget('your_parent_window')
msg = gtk.MessageDialog(type=gtk.MESSAGE_INFO, buttons = gtk.BUTTONS_OK, parent=par)
# do anything here...
return False #stop the timer...