I have 3 panels and I want to make drags on them.
The problem is that when I do a drag on one this happens:
http://img41.yfrog.com/img41/9043/soundlog.png http://img41.yfrog.com/img41/9043/soundlog.png
How can I refresh the frame to happear its color when the panel is no longer there?
This is the code that I have to make the drag:
def onMouseMove(self, event):
(self.pointWidth, self.pointHeight) = event.GetPosition()
(self.width, self.height) = self.GetSizeTuple()
if (self.pointWidth>100 and self.pointWidth<(self.width-100) and self.pointHeight < 15) or self.parent.dragging:
self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING))
"""implement dragging"""
if not event.Dragging():
self.w = 0
self.h = 0
return
self.CaptureMouse()
if self.w == 0 and self.h == 0:
(self.w, self.h) = event.GetPosition()
else:
(posw, posh) = event.GetPosition()
displacement = self.h - posh
self.SetPosition( self.GetPosition() - (0, displacement))
else:
self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
def onDraggingDown(self, event):
if self.pointWidth>100 and self.pointWidth<(self.width-100) and self.pointHeight < 15:
self.parent.dragging = 1
self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
self.SetBackgroundColour('BLUE')
self.parent.SetTransparent(220)
self.Refresh()
def onDraggingUp(self, event):
self.parent.dragging = 0
self.parent.SetTransparent(255)
self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
and this are the binds for this events:
self.Bind(wx.EVT_MOTION, self.onMouseMove)
self.Bind(wx.EVT_LEFT_DOWN, self.onDraggingDown)
self.Bind(wx.EVT_LEFT_UP, self.onDraggingUp)
With this, if I click on the top of the panel, and move down or up, the panel position changes (I drag the panel) to the position of the mouse.
To refresh the parent on every repositioning of self, you could add
self.parent.Refresh()
right after your existing call to self.SetPosition in your def onMouseMove method. Right now you're refreshing the frame only in the def onDraggingDown method, i.e., the first time the mouse left button is clicked and held down, not every time the mouse is moved while said button is held down (i.e., the "dragging" action itself).
I was unable to download your code for testing purposes, due to the rather "spammy peculiar" site you chose to upload it to -- the site keeps bombarding me with ads, no clear way for me to just do the download, occasionally complaining that it doesn't support my machine (I use a Mac and Google Chrome, the site at some spots insists on Windows with IE or Firefox...), etc etc. I'm sure you can find other sites, more usable than that one, for people who are trying to help you out!-)
Related
I want to create a draggable and resizable frameless window in Pyside6 by rewritting the mouseEvent and resizeEvent.And try to use QSizeGrip to control the shape of window.
Drag and resize, I can implement both functions alone, but there is a problem when they are combined.
when I resize the window after dragging, the position will be wrong. I want to know what's the wrong in this codeļ¼
import sys
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtGui import *
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.text = QLabel("Hello World",alignment=Qt.AlignCenter)
self.layout =QVBoxLayout(self)
self.layout.addWidget(self.text)
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
self.gripSize = 16
self.grips = []
for i in range(4):
grip = QSizeGrip(self)
grip.resize(self.gripSize, self.gripSize)
self.grips.append(grip)
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
rect = self.rect()
# top left grip doesn't need to be moved...
# top right
self.grips[1].move(rect.right() - self.gripSize, 0)
# bottom right
self.grips[2].move(
rect.right() - self.gripSize, rect.bottom() - self.gripSize)
# bottom left
self.grips[3].move(0, rect.bottom() - self.gripSize)
def mousePressEvent(self, event):
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
delta = QPoint(event.globalPos() - self.oldPos)
self.move(self.x() + delta.x(), self.y() + delta.y())
self.oldPos = event.globalPos()
if __name__ == "__main__":
app =QApplication([])
By default, QSizeGrip interfaces with the OS for the actual resizing as soon as it's activated (by pressing the left mouse button on it).
The result is that, after that, all mouse move events are intercepted by the system until the button is released. Since the button release is also intercepted by the system (to know that the resizing has been completed), QSizeGrip will be able to handle again mouse events only after the button release; since the previous condition was the mouse button press, it will receive a MouseMove event, and, by defaults, those events are ignored by widgets if they don't handle it.
If a mouse event is ignored, it is propagated to its parent(s), which in this case is your MyWidget.
Unfortunately, your assumption is that you only get mouse move events only after a button press, but, due to what explained above, this is not the case: you will not receive a mouse button press (it was handled by the size grip), but only a mouse move (since it's been ignored by the size grip).
Now, there are two cases:
you previously moved the window, so there is an oldPos based on the previous start mouse position, and the window will be moved using the wrong parameters;
you only resized the window since startup, and the program will crash because there was no oldPos attribute;
There are various possible solutions, but the simple one is to create a default oldPos attribute having a None value, set it in the mouse press, check if self.oldPos is not None in the mouse move (and eventually move) and, most importantly, restore self.oldPos = None in the mouse release.
Note that it's usually better to move the window only using a single button (the convention is the left one, but the middle one is not uncommon)
class MyWidget(QWidget):
oldPos = None
# ...
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.oldPos = event.globalPos()
def mouseMoveEvent(self, event):
if self.oldPos is not None:
delta = event.globalPos() - self.oldPos
self.move(self.pos() + delta)
self.oldPos = event.globalPos()
def mouseReleaseEvent(self, event):
self.oldPos = None
Note: QPoint + QPoint is already a QPoint, and move() accepts a QPoint by default, so there's no need to sum x and y coordinates individually.
This question already has answers here:
Moving QSlider to Mouse Click Position
(4 answers)
Closed 1 year ago.
For example, we have a QSlider instance of PYQT5, from left to right, 0 to 100%
When I clicked the 50% location, the handle won't move to 50% directly but just move a constant stride.
What should I do?
What you're referring to is the "absolute set" option, which completely relies on the current style. That behavior is checked by the slider by querying the QStyle styleHint() about the SH_Slider_AbsoluteSetButtons, which replies with a (possibly empty) Qt.MouseButton mask.
By default, pressing outside the handle with the left button just repeatedly scrolls from the current slider position toward the mouse cursor, while pressing with the middle button it places the slider exactly where the cursor is (depending on the OS and QStyle).
If you want to override this there are two possibilities.
Proxy style override
This works by using a QProxyStyle and overriding the aforementioned styleHint. Unfortunately, QSlider just queries the style about the hint without providing the widget argument, so there's no way to know which slider sent the request. The result is that the behavior will become global for all QSliders in the application, unless you apply the style only to those slider for which you want this behavior, but this can result in some issues and inconsistencies, especially if you already need to use a proxy style.
class ProxyStyle(QtWidgets.QProxyStyle):
def styleHint(self, hint, opt=None, widget=None, returnData=None):
res = super().styleHint(hint, opt, widget, returnData)
if hint == self.SH_Slider_AbsoluteSetButtons:
res |= QtCore.Qt.LeftButton
return res
app = QtWidgets.QApplication(sys.argv)
# set the style globally for the application
app.setStyle(ProxyStyle())
slider = QtWidgets.QSlider()
# or just for the slider
slider.setStyle(ProxyStyle())
Override mouseButtonPress
This option relies on subclassing and partially overriding the mouse button press event. The trick is to check the pressed button and if the slider is not currently pressed (to avoid unexpected behavior when pressing multiple buttons), then move the slider at the mouse position and finally call the base mouseButtonPress implementation: since at that point the handle will be under the mouse, the slider will "believe" that the handle was already there, thus beginning an actual slider move.
class SliderCustom(QtWidgets.QSlider):
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton and not self.isSliderDown():
opt = QtWidgets.QStyleOptionSlider()
self.initStyleOption(opt)
sliderRect = self.style().subControlRect(
QtWidgets.QStyle.CC_Slider, opt,
QtWidgets.QStyle.SC_SliderHandle, self)
if event.pos() not in sliderRect:
# the mouse is not over the handle, let's move it; this is based
# on the original C++ code that moves the handle when the
# "absolute button" is pressed
grooveRect = self.style().subControlRect(
QtWidgets.QStyle.CC_Slider, opt,
QtWidgets.QStyle.SC_SliderGroove, self)
center = sliderRect.center() - sliderRect.topLeft()
pos = event.pos() - center
if self.orientation() == QtCore.Qt.Horizontal:
sliderLength = sliderRect.width()
sliderMin = grooveRect.x()
sliderMax = grooveRect.right() - sliderLength + 1
pos = pos.x()
else:
sliderLength = sliderRect.height()
sliderMin = grooveRect.y()
sliderMax = grooveRect.bottom() - sliderLength + 1
pos = pos.y()
value = self.style().sliderValueFromPosition(
self.minimum(), self.maximum(), pos - sliderMin,
sliderMax - sliderMin, opt.upsideDown
)
self.setSliderPosition(value)
super().mousePressEvent(event)
I have a base program I was playing with this morning I wrote up a few years back. It only had horizontal scrolling and zooming. I managed to get it so I could scroll(one axis at a time) and zoom on both axis. I'm wanting to create a program that will allow me to move around the screen in both x and y directions at the same time(like Google Earth where you can hold down the down and left key at the same time to move to lower left). It would be nice to do it without having the scroll bars on the screen as well. I don't want to have to go back and forth and click on the appropriate scroll bar to be able to scroll in that axis. Currently to change which axis I'm scrolling in I have to click on the opposite axis.
I tried the program at [http://www.tkdocs.com/tutorial/canvas.html#scrolling][1] but I already have the capability of doing that. It doesn't allow me to scroll both directions simultaneously and if I want to change which direction I'm scrolling without having to click on the opposite axis.
Is there a way of doing what I'm trying to do with Tkinter or should I look elsewhere and if so, where?
Thanks.
edit:
With the code Bryan posted below I added in the following code to try to get it two work with the keyboard versus only the mouse. I would like the be able to use the cursor keys to move the image around versus the mouse. I have a nastily touch sensitive mouse on this computer, that has a mind of its own and as a result I would like to stick with the keyboard. Plus, given the naturedness of this darn project I have to leave all option open or else I know I will regret it sometime before this entire project gets finished.
self.canvas.bind("<Left>", self.on_press)
self.canvas.bind("<Right>", self.on_press)
I also tried directing it to self.on_motion and neither one accepted the cursor keys.
Yes, this is possible. There's nothing preventing you from directly calling the canvas xview and yview methods with any arguments you want.
You first need to create bindings that tracks the clicking and the motion of the mouse. In the bound function you can compute the direction that the mouse moved, then use the results to call both the xview and yview methods of the widget at the same time.
Here's an example:
import tkinter as tk
import random
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self, background="bisque", width=400, height=400)
self.canvas.pack(fill="both", expand=True)
self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))
self.canvas.bind("<ButtonPress-1>", self.on_press)
self.canvas.bind("<B1-Motion>", self.on_motion)
# the following two values cause the canvas to scroll
# one pixel at a time
self.canvas.configure(xscrollincrement=1, yscrollincrement=1)
# finally, draw something on the canvas so we can watch it move
for i in range(1000):
x = random.randint(-1000, 1000)
y = random.randint(-1000, 1000)
color = random.choice(("red", "orange", "green", "blue", "violet"))
self.canvas.create_oval(x, y, x+20, y+20, fill=color)
def on_press(self, event):
self.last_x = event.x
self.last_y = event.y
def on_motion(self, event):
delta_x = event.x - self.last_x
delta_y = event.y - self.last_y
self.last_x = event.x
self.last_y = event.y
self.canvas.xview_scroll(-delta_x, "units")
self.canvas.yview_scroll(-delta_y, "units")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
Hi Iam using Python and GTK+. In my GUI I have 2 toolbars I want show first toolbar only if user moves mouse than hide it again after few seconds as for second toolbar I want to show it when user is on particular x,y coordinates.How can I achieve it ?
EDIT:
Iam creating some kind of media player so I want toolbars to disapear while user is not using mouse in case of playerMenu toolbar or if user doesn't move it to specific location in case of ribbonBar toolbar .Iam using GTK+ here is my code for toolbars:
class Player(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
def build_UI(self):
container=Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
ribbonBar=Gtk.Toolbar()
playerMenu=Gtk.Toolbar()
def mouse_moved(self):
#TO-DO here I should check cordinates for example I want to see if mouse.y=window.height-50px and I would like to show ribbonaBar
#after that Gdk.threads_add_timeout(1000,4000,ribbonBar.hide)
#TO-DO here I show playerMenu toolbar if mouse is moved
# smt like playerMenu.show()
#after that I would call Gdk.threads_add_timeout(1000,4000,playerMenu.hide)
# to hide it again after 4 seconds
I should connect my window to some mouse event but I don't know the event name and how can I get mouse.x and mouse.y?
Why do you want to do this? Trying to use widgets that disappear when you're not moving the mouse is rather annoying, IMHO.
But anyway...
To toggle the visibility of a widget use the show() and hide() methods, or map() and unmap() if you don't want the other widgets in your window to move around. To handle timing, use gobject.timeout_add(), and you'll need to connect() your window to "motion_notify_event" and set the appropriate event masks: gtk.gdk.POINTER_MOTION_MASK and probably gtk.gdk.POINTER_MOTION_HINT_MASK. The Event object that your motion_notify callback receives will contain x,y mouse coordinates.
At least, that's how I'd do it in GTK2; I don't know GTK3.
If you want more specific help you need to post some code.
I see that you've posted some code, but it doesn't have a lot of detail... But I understand that GTK can be a bit overwhelming. I haven't used it much in the last 5 years, so I'm a bit rusty, but I just started getting into it again a couple of months ago and thought your question would give me some good practice. :)
I won't claim that the code below is the best way to do this, but it works. And hopefully someone who is a GTK expert will come along with some improvements.
This program builds a simple Toolbar with a few buttons. It puts the Toolbar into a Frame to make it look nicer, and it puts the Frame into an Eventbox so we can receive events for everything in the Frame, i.e., the Toolbar and its ToolItems. The Toolbar only appears when the mouse pointer isn't moving and disappears after a few seconds, unless the pointer is hovering over the Toolbar.
This code also shows you how to get and process mouse x,y coordinates.
#!/usr/bin/env python
''' A framed toolbar that disappears when the pointer isn't moving
or hovering in the toolbar.
A response to the question at
http://stackoverflow.com/questions/26272684/how-can-i-show-hide-toolbar-depending-on-mouse-movements-and-mouse-position-insi
Written by PM 2Ring 2014.10.09
'''
import pygtk
pygtk.require('2.0')
import gtk
import gobject
if gtk.pygtk_version < (2, 4, 0):
print 'pygtk 2.4 or better required, aborting.'
exit(1)
class ToolbarDemo(object):
def button_cb(self, widget, data=None):
#print "Button '%s' %s clicked" % (data, widget)
print "Button '%s' clicked" % data
return True
def show_toolbar(self, show):
if show:
#self.frame.show()
self.frame.map()
else:
#self.frame.hide()
self.frame.unmap()
def timeout_cb(self):
self.show_toolbar(self.in_toolbar)
if not self.in_toolbar:
self.timer = False
return self.in_toolbar
def start_timer(self, interval):
self.timer = True
#Timer will restart if callback returns True
gobject.timeout_add(interval, self.timeout_cb)
def motion_notify_cb(self, widget, event):
if not self.timer:
#print (event.x, event.y)
self.show_toolbar(True)
self.start_timer(self.time_interval)
return True
def eventbox_cb(self, widget, event):
in_toolbar = event.type == gtk.gdk.ENTER_NOTIFY
#print event, in_toolbar
self.in_toolbar = in_toolbar
#### self.show_toolbar(in_toolbar) does BAD things :)
if in_toolbar:
self.show_toolbar(True)
return True
def quit(self, widget): gtk.main_quit()
def __init__(self):
#Is pointer over the toolbar Event box?
self.in_toolbar = False
#Is pointer motion timer running?
self.timer = False
#Time in milliseconds after point stops before toolbar is hidden
self.time_interval = 3000
self.window = win = gtk.Window(gtk.WINDOW_TOPLEVEL)
width = gtk.gdk.screen_width() // 2
height = gtk.gdk.screen_height() // 5
win.set_size_request(width, height)
win.set_title("Magic Toolbar demo")
win.set_border_width(10)
win.connect("destroy", self.quit)
#self.motion_handler = win.connect("motion_notify_event", self.motion_notify_cb)
win.connect("motion_notify_event", self.motion_notify_cb)
win.add_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.POINTER_MOTION_HINT_MASK)
box = gtk.VBox()
box.show()
win.add(box)
#An EventBox to capture events inside Frame,
# i.e., for the Toolbar and its child widgets.
ebox = gtk.EventBox()
ebox.show()
ebox.set_above_child(True)
ebox.connect("enter_notify_event", self.eventbox_cb)
ebox.connect("leave_notify_event", self.eventbox_cb)
box.pack_start(ebox, expand=False)
self.frame = frame = gtk.Frame()
frame.show()
ebox.add(frame)
toolbar = gtk.Toolbar()
#toolbar.set_border_width(5)
toolbar.show()
frame.add(toolbar)
def make_toolbutton(text):
button = gtk.ToolButton(None, label=text)
#button.set_expand(True)
button.connect('clicked', self.button_cb, text)
button.show()
return button
def make_toolsep():
sep = gtk.SeparatorToolItem()
sep.set_expand(True)
#sep.set_draw(False)
sep.show()
return sep
for i in xrange(5):
button = make_toolbutton('ToolButton%s' % (chr(65+i)))
toolbar.insert(button, -1)
#toolbar.insert(make_toolsep(), -1)
for i in xrange(1, 9, 2):
toolbar.insert(make_toolsep(), i)
button = gtk.Button('_Quit')
button.show()
box.pack_end(button, False)
button.connect("clicked", self.quit)
win.show()
frame.unmap()
def main():
ToolbarDemo()
gtk.main()
if __name__ == "__main__":
main()
I need to allow the users to type text into the Canvas Widget, making the canvas update as the user types new text.
Here's what I have tried so far, but am not getting it to work.
First I have a mouseDown method which is bound to Button-1 event
widget.bind(self.canvas, "<Button-1>", self.mouseDown)
This mouseDown method returns the startx, starty positions to my method drawText
def drawText(self, x, y, fg):
self.currentObject = self.canvas.create_text(x,y,fill=fg,text=self.typedtext)
I also have a global binding on the canvas widget to capture any key press like this:
Widget.bind(self.canvas, "<Any KeyPress>", self.currentTypedText)
def currentTypedText(self, event):
self.typedtext = str(event.keysym)
self.drawText(self, self.startx, self.starty,self.foreground)
However there's no error and nothing gets printed on the canvas.
What you want to do is pretty complex and will require quite a bit of code to get working nicely. You will need to handle click events, keypress events, special keypress events (such as "Shift" and "Ctrl"), "Backspace" and delete events, and a lot more.
Nevertheless, first is first and that is getting text to appear in the canvas as a user types. Now, since I don't have your full script, I can't really work with your stuff as is. However, I went and made my own little app that does exactly what you want. Hopefully, it will shine some light on where to go:
from Tkinter import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
# self.x and self.y are the current mouse position
# They are set to None here because nobody has clicked anywhere yet.
self.x = None
self.y = None
self.makeCanvas()
self.bind("<Any KeyPress>", lambda event: self.drawText(event.keysym))
def makeCanvas(self):
self.canvas = Canvas(self)
self.canvas.pack()
self.canvas.bind("<Button-1>", self.mouseDown)
def mouseDown(self, event):
# Set self.x and self.y to the current mouse position
self.x = event.x
self.y = event.y
def drawText(self, newkey):
# The if statement makes sure we have clicked somewhere.
if None not in {self.x, self.y}:
self.canvas.create_text(self.x, self.y, text=newkey)
# I set x to increase by 5 each time (it looked the nicest).
# 4 smashed the letters and 6 left gaps.
self.x += 5
App().mainloop()
Once you click somewhere in the canvas and start typing, you will see text appear. Note however that I have not enabled this to handle deletion of text (that is a little tricky and beyond the scope of your question).