I'm trying, like I said in the title, to get the parameters of a circle drawn with guiqwt.tools.CircleTool. I add that tool to the ImageWidget I create, and when you click over the widget, the circle it's drawn. But, as I want to get the coordinates, I have to extend the ImageWidget class so that when the left button it's released, a signal is emitted, in order to connect to a function where it's done all the stuff.
That's the extended class:
class ExtendedImageWidget(ImageWidget):
def mouseReleaseEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.emit(QtCore.SIGNAL('clicked()'))
The code to connect with the function:
QtCore.QObject.connect(self.imagewidget, QtCore.SIGNAL('clicked()'),self.selection_tools)
And finally the function:
def selection_tools(self):
print('inside selection_tools')
if self.selectCircle.action.isChecked()==True:
print('inside selectCircle')
tamListaItems=len(self.imagewidget.plot.items)
self.circulo=self.imagewidget.plot.items[tamListaItems-1]
#Remove any item other than an image or the grid
for i in range(1,tamListaItems)[::-1]:
if type(self.imagewidget.plot.items[i]) != type(self.imagewidget.plot.items[1]):
self.imagewidget.plot.del_item(self.imagewidget.plot.items[i])
#Add the last item drawn
self.imagewidget.plot.add_item(self.circulo, autoscale=False)
self.imagewidget.plot.replot()
#Get and print some parameters
circle=self.selectCircle.get_final_shape()
print('circle',circle)
self.center=circle.get_center()
ptsDiameter=circle.get_xdiameter()
print('center',self.center)
print('ptsDiameter',ptsDiameter)
self.radius=np.int(np.round((np.sqrt(np.square(ptsDiameter[0]-ptsDiameter[2]) + np.square(ptsDiameter[1]-ptsDiameter[3])))/2))
print('radius',self.radius)
The issue is that, the first circle I draw (It is drawn, and detected as an item in the ItemList), I got nothing at the output. The second I draw, I get the parameters of the previous circle. Here and example of the output I get:
inside selection_tools
inside selectCircle
('circle', <guiqwt.shapes.EllipseShape object at 0x0C6AA978>)
('center', (106.88683969465652, 148.08916030534348))
('ptsDiameter', (64.703511450381697, 101.00079389312975, 149.07016793893132, 195.17752671755721))
('radius', 63)
inside selection_tools
If I change:
circle=self.selectCircle.get_last_final_shape()
by:
circle=self.selectCircle.get_final_shape()
The signals seems to be working well, because when I draw a circle, I instantaneously get printed the notifications "inside selection_tools" and "inside selectCircle", but it doesn't return to any parameter.
If someone could help me it would be great.
Thanks in advance.
Related
I am facing some issue with the display style of Qcombobox items. Currently am hardcoding the data to be shown in the combobox.
here is the code :
self.Dummy = QComboBox(self)
self.Dummy.setGeometry(200,600, 350, 50)
self.Dummy.setStyleSheet("QComboBox {background-color: white;border-style: outset;" border-width: 2px;border-radius: 5px;border-color: #448aff; font: 12px; min-width: 10em; padding: 3px;}")
self.Dummy.addItems(["-Select-", "2", "3","4","5","6","7","8","9","0","11",])
The issue is that the dropdown "list" postion keeps changing after each selection. Here is the image of the issue am facing.
Below is my combobox
The list contains items <-Select->,2,3,4,5,6,7,8,9,0,11 , where <-Select-> will be the first element shown.
Now when I click the box, the box list "down" the elements and suppose I selected '2'. Then, if I try to select another item, the list will be dropped in a "downwards" direction. see below
Now, say if selected the last element from the items, '11'. Now if I try to select a new item by clicking on the box, the list will be popped "up" instead of down. see below
What should be done to fix it ? I don't think its an issue with stylesheet, without it also, this issue is happening. The reason I need this to be fixed is that when the list is popping up, its covering the label above it
What you see is a behavior that is OS and style dependent.
To avoid it, the best way is to subclass QComboBox and overwrite showPopup(), then we call the base class implementation (which is responsible of showing, resizing and positioning the popup view) and move it if necessary.
class Combo(QtWidgets.QComboBox):
def showPopup(self):
super().showPopup()
# find the widget that contains the list; note that this is *not* the view
# that QComboBox.view() returns, but what is used to show it.
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.mapToGlobal(self.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# move the popup at the bottom left of the combo
rect.moveTopLeft(self.mapToGlobal(self.rect().bottomLeft()))
# ensure that the popup is always inside the edges of the screen
# we use the center of the popup as a reference, since with multiple
# screens the combo might be between two screens, but that position
# could also be completely outside the screen, so the cursor position
# is used as a fallback to decide on what screen we'll show it
done = False
for i, pos in enumerate((rect.center(), QtGui.QCursor.pos())):
for screen in QtWidgets.QApplication.screens():
if pos in screen.geometry():
screen = screen.geometry()
if rect.x() < screen.x():
rect.moveLeft(screen.x())
elif rect.right() > screen.right():
rect.moveRight(screen.right())
if rect.y() < screen.y():
rect.moveTop(screen.y())
elif rect.bottom() > screen.bottom():
# if the popup goes below the screen, move its bottom
# *over* the combo, so that the its current selected
# item will always be visible
rect.moveBottom(self.mapToGlobal(QtCore.QPoint()).y())
done = True
break
if done:
break
popup.move(rect.topLeft())
This can also be done without subclassing (for example if you have many combos, you created the UI from Designer and don't want to use promoted widgets), but you'll have to remember to change all referencies to the combo.
class MyWindow(QtWidgets.QWidget):
def __init__(self):
# ...
self.combo = QtWidgets.QComboBox()
self.combo.showPopup = self.showPopupAndCheck
def showPopupAndCheck(self):
QtWidgets.QComboBox.showPopup(self.combo)
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.combo.mapToGlobal(self.combo.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# change everything from self to self.combo
Alternatively, if you want to keep this behavior consistent through all your program without always using the subclass, you can use some sort of monkey patching hack.
The advantage is that any QComboBox you create (even when loading UI files or creating a combo at runtime) will always use the new behavior.
Important: this MUST be at the very beginning of the main file of your program, possibly just after the import section.
from PyQt5 import QtCore, QtGui, QtWidgets
def customShowPopup(self):
# we can't use super(), because we're not in the class definition, but
# calling the class method with "self" as first argument is practically the
# same thing; note the underscore!
QtWidgets.QComboBox._showPopup(self)
popup = self.view().window()
# ... go on, exactly as above
# create a new reference to the showPopup method, which is the one we've used
# in the function above
QtWidgets.QComboBox._showPopup = QtWidgets.QComboBox.showPopup
# overwrite the original reference with the new function
QtWidgets.QComboBox.showPopup = customShowPopup
My environment is Python 2.7, running on Windows 7.
I'm trying get a Tkinter Listbox to trigger a callback in response to the user changing the 'active' item (i.e. the item with focus). I'm using a binding to the <<ListboxSelect>> event to make this happen, and it's working -- sort of.
The callback itself is supposed to check what the new active item is, and carry out some processing accordingly. This logic operates the way I expect when I change the active item via the up/down arrow keys. But when I point & click on a new item instead, the code mistakenly identifies the prior active item as the current one.
Here's a stripped-down code sample that illustrates the behavior I'm getting:
import Tkinter as tk
#Root window
root = tk.Tk()
#Callback to show focus change
def updateDisplay(*args):
focusIndex = str(lb.index(tk.ACTIVE))
ctrlFI.set('Focus is at index '+focusIndex)
#Control variables
ctrlLB = tk.StringVar()
ctrlFI = tk.StringVar()
#Widgets
lb = tk.Listbox(root,
width=20, height=10,
relief=tk.FLAT,highlightthickness=0,
selectmode=tk.EXTENDED,
activestyle='dotbox',
listvariable=ctrlLB)
lbl = tk.Label(root,
justify=tk.LEFT, anchor=tk.W,
textvariable=ctrlFI)
lb.grid(row=0,column=0,sticky=tk.NW,padx=(5,0),pady=5)
lbl.grid(row=1,column=0,columnspan=2,sticky=tk.NW,padx=5,pady=5)
#Listbox binding to trigger callback
lb.bind('<<ListboxSelect>>',updateDisplay)
#Initializations to prep GUI
ctrlLB.set('Index0-entry Index1-entry Index2-entry Index3-entry Index4-entry')
ctrlFI.set('Ready')
#Begin app
tk.mainloop()
Here are the results when you use the arrow keys:
But here's what you get when you click with the mouse:
The information 'lags' one behind, showing the prior selection instead. (If you click the same item a second time, it 'catches up.')
So my questions are:
What is causing the discrepancy?
How do I fix it so the mouse click gives the right result?
The active item is not necessarily the same as the selected item. When you press the mouse down it changes the selected value but it does not change the active item. The active item only changes once you release the mouse button.
You should be able to see this by clicking and holding the mouse button over an item that is not currently selected. When you do, you'll see something like this:
In the above image, the active item is the one surrounded by a dotted outline. The selected item is in blue. When your code displays the 'focus', it's displaying the active element rather than the selected element.
If you want the selected item, you need to use curselection to get the index of the selected item. It returns a tuple, so in extended mode you need to get the first element that is returned (eg: lb.curselection()[0]). Be sure to handle the case where curselection returns an empty string.
Relatively new to python (2.7) and trying to figure out wxpython (so I apologise in advance for any poor use of code). I've got a GUI of which I have multiple switchable panels on a frame. I need the frame to be scrollable, so I've used ScrolledWindow but now some of the of the GUI elements which are below the initial frame size do not show upon scrolling on.
I've found that changing my monitor resolution solves the problem, but I want to be able to have this working regardless of resolution.
Below is an example of the problem I'm having (doesn't display hi4 and cuts off hi4)
import wx
from apanel import apanel
class simpleapp_wx(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self,parent,id,title,size=(1000,1100))
self.parent=parent
self.scroll = wx.ScrolledWindow(self, -1)
self.scroll.SetScrollbars(1,1,1000,1100)
button0=wx.Button(self.scroll,-1,"hi0",(100,610))
self.panel=apanel(self.scroll)
self.CreateStatusBar()
self.sizer= wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.panel, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.Show(True)
app=wx.App(False)
frame=simpleapp_wx(None,-1,'Demo')
frame.Show()
app.MainLoop()
and panel is in another class (in a seperate file I called apanel.py)
import wx
class apanel(wx.Panel):
def __init__(self,parent):
wx.Panel.__init__(self,parent=parent)
button=wx.Button(self,-1,"hi",(800,60))
button2=wx.Button(self,-1,"hi2",(200,600))
button3=wx.Button(self,-1,"hi3",(800,800))
button4=wx.Button(self,-1,"hi4",(500,900))
button5=wx.Button(self,-1,'hi5',(10,100))
I've found some errors in your code, it's simple to solve. Look the working panel bellow:
class simpleapp_wx(wx.Frame):
def __init__(self,parent,id,title):
wx.Frame.__init__(self,parent,id,title,size=(1000,1100))
self.parent=parent
self.scroll = wx.ScrolledWindow(self, -1)
self.scroll.SetScrollbars(1,1,1000,1100)
self.CreateStatusBar()
sizer = wx.BoxSizer(wx.VERTICAL)
self.scroll.SetSizer(sizer) # The scrolledWindow sizer
self.panel = wx.Panel(self.scroll)
sizer.Add(self.panel, 0, wx.EXPAND)
button0=wx.Button(self.panel,-1,"hi0",(100,610))
Remarks:
If you use a scrolled window, create a sizer, and set the sizer in scrolled window.
The panel apanel need to be added on scrolled sizer created in line above.
The panel not resizing because simpleapp_wx (Frame) was set your size by the created BoxSizer, the order is inverse.
If you add some button after, put the apanel with parent, not scrolledwindow.
I suggest to you to use wxPython demo and docs: http://www.wxpython.org/download.php have a bunch of working examples.
Good luck in your wxpython studies!
I am displaying a message box on click of option menu when data is not available and is to be acquired. But somehow whenever I change content of the option menu the message box gets looped. Like- first time it vanishes when I press ok. when I change option again second time it displays message box two times and third time three times. Can someone tell me what to change in my code?
#the function where I change option menu value
def module_func(event):
#declarations
if(dateText.get()!="" and driveText.get()!=""):
global module_dir
global selected_module
modules['menu'].delete(0,END)
select="-----Select-----"
modules['menu'].add_command(label=select, command=tk._setit(module,select))
for module_dir in get_immediate_subdirectories(('%s\%s\%s')%(startPath,selected_shot,selected_fec)):
module_dropdown.append(module_dir)
modules['menu'].add_command(label=module_dir, command=tk._setit(module,module_dir))
module.trace('w',setEightText) #think problem is here
else:
errormsg("")
def setEightText(*args):
#declarations
if(os.path.exists(('%s\%s\%s\%s\%s')%(startPath,selected_shot,selected_fec,selected_module,chan))):
#some operations
else:
tkMessageBox.showinfo("Error","Data at given location not available.\n Please Wait until data is acquired.")
module=tk.StringVar(labelframe1)
module.set("-----Select-----")
modules = tk.OptionMenu(labelframe1, module, module_dropdown, command=module_func)
modules.config(width=20, background="SNOW")
modules.grid(row=8, column=2, sticky="W", padx=10, pady=5)
modules.bind('<Button-1>',module_func)
I think the problem is because of trace. But don't know what to add instead of that.
I'm trying to write a simple 'Simon' game, but I have hit a road block here, and honestly have no idea how to get around it.
So here, I made a class for the four buttons in the GUI:
class button:
def buttonclicked(self):
self.butclicked= True
def checkIfClicked(self):
if self.butclicked== True:
global pressed
pressed.append(self.color)
self.butclicked= False
def __init__(self, color1):
self.color= color1
self.button= tk.Button(root, text=' '*10, bg= self.color, command= self.buttonclicked)
self.button.pack(side='left')
self.butclicked=False
I then created four instances of this class in blue, red, yellow, and green as bb, rb, yb, and gb.
Once everything is packed into the Tk() module, it enters a while loop that appends a random color to a list activecolors. I try to use the following loop to wait until the list pressed is at least as long as the list activecolors before comparing the two to see if the user was correct:
while len(pressed)<len(activecolors):
sleep(.25)
print('In the check loop')
bb.checkIfClicked()
rb.checkIfClicked()
yb.checkIfClicked()
gb.checkIfClicked()
However, since it is stuck inside the while loop, the program can't tell that the button has been clicked. I thought adding the sleep method into the loop would allow the code to have time to do other things (such as process button clicks), but this is not the case. Any help is appreciated.
Here is the link to the full code, if you would like to see it. A warning though: it's not pretty.
Edit:
I ended up just changing the code to check the list only after a new button was clicked, telling the computer the code was ready. I've updated the Google Document if you'd like to see it.
You are making it too complicated. This program uses partial from functiontools to allow a variable to be passed to the function so one function handles all clicks (Python 2.7).
from Tkinter import *
from functools import partial
class ButtonsTest:
def __init__(self):
self.top = Tk()
self.top.title('Buttons Test')
self.top_frame = Frame(self.top, width =400, height=400)
self.colors = ("red", "green", "blue", "yellow")
self.colors_selected = []
self.num_clicks = 0
self.wait_for_number = 5
self.buttons()
self.top_frame.grid(row=0, column=1)
Button(self.top_frame, text='Exit',
command=self.top.quit).grid(row=2,column=1, columnspan=5)
self.top.mainloop()
##-------------------------------------------------------------------
def buttons(self):
for but_num in range(4):
b = Button(self.top_frame, text = str(but_num+1),
command=partial(self.cb_handler, but_num))
b.grid(row=1, column=but_num+1)
##----------------------------------------------------------------
def cb_handler( self, cb_number ):
print "\ncb_handler", cb_number
self.num_clicks += 1
this_color = self.colors[cb_number]
if (self.num_clicks > self.wait_for_number) \
and (this_color in self.colors_selected):
print "%s already selected" % (this_color)
self.colors_selected.append(this_color)
##===================================================================
BT=ButtonsTest()