Once I have created my rating scales and flipped the screen, they do not appear. What am I doing wrong? - window

Once I have created the ratingscales in PsychoPy, I cannot get them to show after showing my stimuli. What am I doing wrong?
Preparing experiment
Define window:
win = visual.Window(fullscr=True, color = 'Black')
Preparing stimuli
Pictures
Make list of images in my folder:
images = glob.glob("Yellow/*.jpg")
Randomize the order for pictures:
random.shuffle(images)
Prepare Fixation cross
stim_fix_left = visual.TextStim(win, '+') # Fixation cross is just the character "+". Units are inherited from Window when not explicitly specified.
stim_fix_left.pos = (0.5,0)
stim_fix_right = visual.TextStim(win, '+') # Fixation cross is just the character "+". Units are inherited from Window when not explicitly specified.
stim_fix_right.pos = (-0.5, 0)
Ratings
# the item to-be-rated or respond to:
whichred_left = visual.TextStim(win, text="Does left or right picture contain most red?", height=.04, units='norm')
whichred_left.pos = (0.5,0)
whichred_right = visual.TextStim(win, text="Does left or right picture contain most red?", height=.04, units='norm')
whichred_right.pos = (-0.5, 0)
Create stimuli
Show introduction message:
msg(intro, "white")
Show introduction to block:
msg(block_red, "red")
for i in images:
##Pictures
stim_fix_right.draw()
stim_fix_left.draw()
win.flip()#flip screen
core.wait(1.0)
#Picture 1
img1 = visual.ImageStim(win, image = i)#create visual stimuli
img1.pos = (0.4, 0)
img1.size = (0.5)
#Picture 2
img2 = visual.ImageStim(win, image = images[2])#create visual stimuli
img2.pos = (0.7, 0)
img2.size = (0.5)
#Picture 3
img3 = visual.ImageStim(win, image = i)#create visual stimuli
img3.pos = (-0.4, 0)
img3.size = (0.5)
#Picture 4
img4 = visual.ImageStim(win, image = images[2])#create visual stimuli
img4.pos = (-0.7, 0)
img4.size = (0.5)
#Drawing picures
img1.draw()#draw picture
img2.draw()#draw picture
img3.draw()#draw picture
img4.draw()#draw picture
win.flip()#flip screen
stopwatch.reset() #set clock
core.wait(1.0)
#Ratings
event.clearEvents()
# define window
winrating = visual.Window(size = (1280, 720), color = 'black', units='pix')
x, y = winrating.size # for converting norm units to pix
leftward = -0.35 * x / 2 # use pix units, because the drawing window's units are pix
rightward = -1 * leftward
# create a RatingScale object:
RatingLeft = visual.RatingScale(winrating, choices=map(str, range(1, 8)), mouseOnly=True, pos=(leftward, -y/6),
marker='circle', size=0.85, name='left')
RatingRight = visual.RatingScale(winrating, choices=map(str, range(1, 8)), low=0, high=8, markerStart=4,
leftKeys='left', rightKeys = 'right', acceptKeys='down', pos=(rightward, -y/6),
marker='circle', size=0.85, name='right')
while RatingLeft.noResponse or RatingRight.noResponse:
whichred_left.draw
whichred_right.draw
RatingLeft.draw()
RatingRight.draw()
winrating.flip()
if event.getKeys(['escape']):
core.quit()

There is no need to define a second window. Do all of your drawing into your originally defined window, win. This is created as a full screen window, so I'd assume that the second window, winrating is hidden behind it and any drawing wouldn't be visible.

Related

pygame does not access sprite functions after putting the dot "."

I am following a tutorial for a game of ships, and when creating the Player class in the init function I have a strange problem when wanting to access the functions of the Sprite module: when I write the name of the variable and put the point to access its functions remain in white, instead of being colored yellow indicating that it is a reserved word (I am using visual studio)
(Sorry if I do not express myself correctly I am using google translator.)
so the strange is this:
code screenshot
what is indicated in the red boxes should be in yellow (like what I underlined in yellow), since theoretically it would be accessing the functions of a Sprite object therefore it should be highlighted in yellow ...
the problem is that it appears white, as if it did not recognize the variable as a Sprite object
Here is where i create the image directory.
import pygame, random, os, sys
from os import path
img_dir = path.join(path.dirname(__file__), 'img')
And here is the Meteor class:
class Meteor(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image_orig = random.choice(meteor_images)
self.image_orig.set_colorkey("black")
self.image_orig.set_clip()
self.image = self.image_orig.copy()
self.rect = self.image.get_rect()
self.radius = int(self.rect.width*0.85/2) # *0.85 <- se leeria como (el 85% del width) / 2
pygame.draw.circle(self.image, "red", self.rect.center, self.radius)
self.rect.x = random.randrange(0, width)
self.rect.bottom = random.randrange(-100, -50)
self.speedx = random.randrange(-4, 4)
self.speedy = random.randrange(2, 8)
self.rot = 0
self.rot_speed = random.randrange(-8, 8)
self.last_update = pygame.time.get_ticks()
and then here is where create the meteors list:
meteor_images = []
meteor_list = ['meteorBrown_big1.png',
'meteorBrown_big2.png',
'meteorBrown_big3.png',
'meteorBrown_big4.png']
for img in meteor_list:
meteor_images.append(pygame.image.load(path.join(img_dir, img)).convert())
I am not very expert on the subject, what could be the problem??

Interacting with sg.image on a clic or a mouse fly over

I made a code using pysimplegui. it basically shows some images from a database based on a scanned number. it works but sometimes it could be useful to be able to increase the size of the image + it would make my user interface a bit more interactive
i want to have the possibility to either:
when i fly over the image with the mouse, i want the image to increase in size
have the possibility to clic on the image and have a pop-up of the image showing up (in a bigger size)
i am not sure on how to interact with a sg.image()
Below you will find a trunkated part of my code where i show my way of getting the image to show up.
layout = [
[
sg.Text("Numéro de boîte"),
sg.Input(size=(25, 1), key="-FILE-"),
sg.Button("Load Image"),
sg.Button("Update DATA"),
sg.Text("<- useless text ")
],
[sg.Text("Indicateur au max" , size = (120, 1),font = ("Arial", 18), justification = "center")],
[sg.Image(key="-ALV1-"),sg.Image(key="-ALV2-"), sg.Image(key="-ALV3-"), sg.Image(key="-ALV4-"), sg.Image(key="-ALV5-")],
[sg.Image(key="-ALV6-"),sg.Image(key="-ALV7-"), sg.Image(key="-ALV8-"), sg.Image(key="-ALV9-"), sg.Image(key="-ALV10-")],
[sg.Text("_" * 350, size = (120, 1), justification = "center")],
[sg.Text("Indicateur au milieu" , size = (120, 1),font = ("Arial", 18), justification = "center")],
[sg.Image(key="-ALV11-"),sg.Image(key="-ALV12-"), sg.Image(key="-ALV13-"), sg.Image(key="-ALV14-"), sg.Image(key="-ALV15-")],
[sg.Image(key="-ALV16-"),sg.Image(key="-ALV17-"), sg.Image(key="-ALV18-"), sg.Image(key="-ALV19-"), sg.Image(key="-ALV20-")],
[sg.Text("↓↓↓ ↓↓↓" , size = (120, 1),font = ("Arial", 18), justification = "center")],
]
ImageAlv1 = Image.open(PathAlv1)
ImageAlv1.thumbnail((250, 250))
bio1 = io.BytesIO()
ImageAlv1.save(bio1, format="PNG")
window["-ALV1-"].update(data=bio1.getvalue())
Using bind method for events, like
"<Enter>", the user moved the mouse pointer into a visible part of an element.
"<Double-1>", specifies two click events happening close together in time.
Using PIL.Image to resize image and io.BytesIO as buffer.
import base64
from io import BytesIO
from PIL import Image
import PySimpleGUI as sg
def resize(image, size=(256, 256)):
imgdata = base64.b64decode(image)
im = Image.open(BytesIO(imgdata))
width, height = size
w, h = im.size
scale = min(width/w, height/h)
new_size = (int(w*scale+0.5), int(h*scale+0.5))
new_im = im.resize(new_size, resample=Image.LANCZOS)
buffer = BytesIO()
new_im.save(buffer, format="PNG")
return buffer.getvalue()
sg.theme('DarkBlue3')
number = 4
column_layout, line = [], []
limit = len(sg.EMOJI_BASE64_HAPPY_LIST) - 1
for i, image in enumerate(sg.EMOJI_BASE64_HAPPY_LIST):
line.append(sg.Image(data=image, size=(64, 64), pad=(1, 1), background_color='#10C000', expand_y=True, key=f'IMAGE {i}'))
if i % number == number-1 or i == limit:
column_layout.append(line)
line = []
layout = [
[sg.Image(size=(256, 256), pad=(0, 0), expand_x=True, background_color='green', key='-IMAGE-'),
sg.Column(column_layout, expand_y=True, pad=(0, 0))],
]
window = sg.Window("Title", layout, margins=(0, 0), finalize=True)
for i in range(limit+1):
window[f'IMAGE {i}'].bind("<Enter>", "") # Binding for Mouse enter sg.Image
#window[f'IMAGE {i}'].bind("<Double-1>", "") # Binding for Mouse double click on sg.Image
element = window['-IMAGE-']
now = None
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event.startswith("IMAGE"):
index = int(event.split()[-1])
if index != now:
element.update(data=resize(sg.EMOJI_BASE64_HAPPY_LIST[index]))
now = index
window.close()

Tkinter: Widget not being applied to the top right hand corner of the screen(grid)

I want to make a GUI for a voice assistant and for that when I try to use the grid option the label's get aligned at the centre of the screen when specified as column 2/3/4/5.
import tkinter
from tkinter import Canvas, Frame, Image, Label, StringVar, Tk, font
from tkinter.constants import ANCHOR, BOTTOM, E, END, GROOVE, RAISED, RIDGE, RIGHT, SUNKEN, TOP, Y
from typing import Text
window = tkinter.Tk()
window.title("App name")
window.geometry("320x640")
f = Frame(window)
x = f.grid_size()
# Add image file
bg = tkinter.PhotoImage(file = "Greybg.png")
# Show image using label
label1 = tkinter.Label( window, image = bg)
label1.place(x = 0, y = 0)
userimg = tkinter.PhotoImage(file = 'user1.png')
userlabel = tkinter.Label(window, image = userimg, bg = '#3D4154')
userlabel.place(relx = 1.0, rely = 0.01, anchor = 'ne')
def clicked():
print("Wow no error")
# Creating a photoimage object to use image
photo = tkinter.PhotoImage(file = "mic.png")
# here, image option is used to
# set image on button
micbtn = tkinter.Button(window, text = 'Click Me !', image = photo, bg = '#808588', border = 0, command = clicked).place(x = 142,y=580)
string_variable = tkinter.StringVar()
string_variable.set("User Text Here")
text = tkinter.Label(window, textvariable = string_variable, bg = "#80EAF7", wraplength= 250, pady = 1, padx = 1, fg = '#020402')
text.grid(row = 0, column=1, sticky=E,)
string_variable1 = tkinter.StringVar()
string_variable1.set("Assistant Reply here")
text1 = tkinter.Label(window, textvariable = string_variable1, wraplength= 250, pady = 1, padx = 1, fg = '#020402', font = ('Helvetica',8,'bold'))
text1.grid(row = 1, column=0)
window.resizable(0, 0)
window.mainloop()
In the above code, the icons get aligned like this, whereas I want the "User Text Here" to be at the top right-hand corner of the screen, how do I do that?
You can make columns 0 and 1 to use all the horizontal available space using
window.columnconfigure((0,1), weight=1)
Credit to https://stackoverflow.com/users/5317403/acw1668

GUI plot refreshing incorrectly on wxpython panel

Ok, so I've been tasked with creating a VERY simple GUI at work (I'm an intern). The task is to eventually connect to a machine and process real data, but right now I'm working on randomly generated sine data with noise. I've chose to work in Python 3.0, and use wxpython to create my GUI components.
As I want everything to appear on the same window, I'm using panels (hence wx.lib.plot.PlotCanvas rather than something like matplotlib.pyplot)
The problem that I have is that over time, the plot seems to 'expand' off of the panel. This is temporarily solved when I manually resize the window, but resumes again immediately after (you need to run the code to see what I mean).
Expansion over time in panel
Another problem (that has bugged me since I have started writing the code) is that sometimes when I resize the window (manually) or minimize it and then maximize it again, the timer randomly starts and stops.
I have tried all sorts of things (changing padding in sizers, extra arguments, changing time between refreshes GetBestSize()) but I believe that I simply don't understand wxpython well enough to identify where the problem is
I would really appreciate any help you can shed on either of these problems (I don't know, they might even be linked to each other).
FYI: I am not an experienced coder, and my code is not finished (I have more functions to code, but I feel like I should resolve this first). I have constructed this code by looking at different techniques from various tutorials and websites like stackoverflow, so I know it's not formatted well and could definitely be made more efficient. Also, I have removed some parts just to be safe about confidentiality - nothing important, just strings in messages.
PS: If you do have an easier way to do the whole plot/update thing that doesn't have this problem (preferably still in wx) I would be thrilled to hear that as well
And here's my code:
EDIT: Solved the expanding problem by using self.p2.SetSize((W+0,L+0)) instead of (self.p2.GetBestSize())
EDIT: Made transitions much smoother by just regenerating data and redrawing it on existing canvas in the evt_timer function (instead of recreating the whole canvas, which gave a blink-y appearance if you know what I mean)
import wx
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import leastsq
import wx.lib.plot as plot
import time
import os
wildcard = "Text File (*.txt)|*.txt|"\
"Picture (*.png)|*.png|"\
"All files (*.*)|*.*"#This wildcard shows the options for file endings in the "SAVE" tab - see OnSave(self,event)
wildcard2 = "Picture (*.png)|*.png|"\
"Text File (*.txt)|*.txt|"\
"All files (*.*)|*.*"
class PlotCanvas(plot.PlotCanvas):
def __init__(self,parent,id,size,accepted):
"""
This randomly generates sine data (with noise) and plots it to a panel.
Incorporated as a separate class instead of instatiating it as a plot.PlotCanvas object
to overcome an issue of the size of the plot in the panel.
"""
plot.PlotCanvas.__init__(self,parent,id,style=wx.BORDER_SUNKEN,size = size)
N = 100 # number of data points
self.t = np.linspace(0, 4*np.pi, N)
f = 1.15247 # Optional!! Advised not to use
self.data = 3.0*np.sin(f*self.t+0.001) + 0.5 + np.random.randn(N) # create artificial data with noise
guess_mean = np.mean(self.data)
guess_phase = 0
guess_freq = 1
guess_amp = 1
optimize_func = lambda x: x[0]*np.sin(x[1]*self.t+x[2]) + x[3] - self.data
est_amp, est_freq, est_phase, est_mean = leastsq(optimize_func, [guess_amp, guess_freq, guess_phase, guess_mean])[0]
fine_t = np.arange(0,max(self.t),0.1)
data_fit=est_amp*np.sin(est_freq*fine_t+est_phase)+est_mean
multiplier = 1
dataset1 = [(x,[d for d in self.data][[td for td in self.t].index(x)])for x in [td for td in self.t]]
fitdata1 = [(x,[df for df in data_fit][[tf for tf in fine_t].index(x)]) for x in [tf for tf in fine_t]]
dataset =[(x,y*multiplier) for (x,y) in dataset1]
fitdata = [(x,y*multiplier) for (x,y) in fitdata1]
self.data = dataset
self.data2 = fitdata
line = plot.PolyLine(self.data,legend = 'random',colour = 'light blue', width =2)
line2 = plot.PolyLine(self.data2,legend = 'sineline',colour ='black',width =2)
a = []
if "D" in accepted:
a.append(line)
if "S" in accepted:
a.append(line2)
if "G" in accepted:
pass
if "L" in accepted:
pass
gc = plot.PlotGraphics(a,'Line Graph','X','Y')
xmin = self.t[0]-0.01*(self.t[-1]-self.t[0])
xmax = self.t[-1]+0.01*(self.t[-1]-self.t[0])
self.Draw(gc,xAxis=(xmin,xmax),yAxis=(min([x[1] for x in dataset])-0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset])),
max([x[1] for x in dataset])+0.01*(max([x[1] for x in dataset])-min([x[1] for x in dataset]))))
#self.showLegend = True
#self.enableZoom = True
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
class Frame(wx.Frame):
"""
This is the main class. In it, we declare the separate panels, canvas, menubar, buttons and sizers.
"""
def __init__(self,parent,id,title):
wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition)
self.CurrentDirectory = os.getcwd()
self.timer=wx.Timer(self)#Instantiating the timer
self.count=0
self.Bind(wx.EVT_TIMER,self.evt_timer)#Binding it to itself so that it is always triggered
self.Bind(wx.EVT_PAINT,self.paint)
menubar = wx.MenuBar()
fileMenu = wx.Menu() #Creating the Menubar at the top
#Creating 3 menus: fileMenu,fit,and help
save = wx.Menu()
z = wx.MenuItem(save,wx.ID_ANY,'Save Raw Data\tCtrl+D')
self.Bind(wx.EVT_MENU,self.OnSave,z)
save.Append(z)
z= wx.MenuItem(save,wx.ID_ANY,'Save Image\tCtrl+I')
self.Bind(wx.EVT_MENU,self.OnSaveImage,z)
save.Append(z)
fileMenu.AppendSubMenu(save,'&Save')
fileMenu.AppendSeparator()
z = wx.MenuItem(fileMenu, wx.ID_EXIT, '&Quit\tCtrl+W')
self.Bind(wx.EVT_MENU, self.OnQuit, z)
fileMenu.Append(z)
fit = wx.Menu()#Making a check menu
self.gaussian = fit.Append(wx.ID_ANY,'Gaussian',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleGaussian,self.gaussian)
fit.Check(self.gaussian.GetId(),False)
self.sine = fit.Append(wx.ID_ANY,'Sine',kind = wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleSine,self.sine)
fit.Check(self.sine.GetId(),False)
self.linear = fit.Append(wx.ID_ANY,'Linear',kind=wx.ITEM_CHECK)
#self.Bind(wx.EVT_MENU,self.ToggleLinear,self.linear)
fit.Check(self.linear.GetId(),False)
help = wx.Menu()
z = wx.MenuItem(help,wx.ID_ANY,'&About\tCtrl+H')
self.Bind(wx.EVT_MENU,self.OnHelp,z)
help.Append(z)
menubar.Append(fileMenu, '&File')
menubar.Append(fit, '&Fit')
menubar.Append(help, '&Help')#adding menus to menubar
self.SetMenuBar(menubar)#formatting the frame with menubar
self.sp = wx.SplitterWindow(self)#Splitting the window into 2 panels
self.p1 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For buttons and user events
self.p2 = wx.Panel(self.sp,style = wx.SUNKEN_BORDER)#For display of the plot
self.sp.SplitVertically(self.p1,self.p2,300)
sizer = wx.GridBagSizer(3, 3)#Versatile sizer for layout of first panel self.p1
bitmappath = self.CurrentDirectory + "\\BITMAPS"
bmp = wx.Bitmap(bitmappath+"\\SAVE.BMP",wx.BITMAP_TYPE_BMP)
self.saveBtn = wx.BitmapButton(self.p1,wx.ID_ANY,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))
self.Bind(wx.EVT_BUTTON,self.OnSave,self.saveBtn)
sizer.Add(self.saveBtn, (0, 0), wx.DefaultSpan, wx.ALL,5)
bmp = wx.Bitmap(bitmappath +"\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn = wx.BitmapButton(self.p1,-1,bitmap = bmp,size =(bmp.GetWidth()+10,bmp.GetHeight()+10))# A button that starts and stops the plotting
self.startBtn.startval = "START"
self.Bind(wx.EVT_BUTTON,self.paint,self.startBtn)
sizer.Add(self.startBtn, (0, 1), wx.DefaultSpan,wx.ALL,5)
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),["D"])
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p1.SetSizerAndFit(sizer)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(W,L)
self.Maximize(True)
self.Centre()
self.Show()
############### event methods ###########
def paint(self,event):
"""
Updates the canvas based on the value of the startbtn(not the image). Bound to self.timer.
"""
bitmappath = self.CurrentDirectory + "\\BITMAPS"
if self.startBtn.startval == "START":
self.timer.Start(1)# increase the value for more time
bmp = wx.Bitmap(bitmappath + "\\STOP.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "STOP"
elif self.startBtn.startval == "STOP":
self.timer.Stop()
bmp = wx.Bitmap(bitmappath+ "\\START.BMP",wx.BITMAP_TYPE_BMP)
self.startBtn.SetBitmap(bmp)
self.startBtn.startval = "START"
def evt_timer(self,event):
self.count +=1
if self.count== 10:# By increasing count (or the number in self.timer.Start()) you can increase the interval between updates
#self.p2.canvas.Clear()
sizer1 = wx.BoxSizer(wx.VERTICAL)
W,L = self.p2.GetSize()
a = ["D"]
if self.sine.IsChecked():
a.append("S")
elif self.linear.IsChecked():
a.append("L")
elif self.gaussian.IsChecked():
a.append("G")
self.p2.canvas = PlotCanvas(self.p2,wx.ID_ANY,(W,L),a)
sizer1.Add(self.p2.canvas,1,wx.ALL,0,0)
self.p2.SetSizerAndFit(sizer1)
self.p2.SetSize(self.p2.GetBestSize())
self.count=0 # reset the count
def Dialog(self, parent, message, c):# Will be used to notify the user of errors/processes
if c == "W":
caption = "Warning!"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_WARNING)
elif c == "I":
caption = "Information"
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()#Destroys dialog on close
def OnSave(self,event):#Triggered by menubar and button
try:
rawdata = self.p2.canvas.data
raw_X =[x[0] for x in rawdata]
raw_Y =[x[1] for x in rawdata]
dlg = wx.FileDialog(#Code for this from http://www.blog.pythonlibrary.org
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
f = open(path+".txt","w+")
for i in range(len(raw_X)):
f.write(str(raw_X[i])+"\t"+str(raw_Y[i])+"\n")
f.close()
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:#Catch error when user closes save window without selecting any directory or filename
pass
def OnSaveImage(self,event):
try:
rawdata = self.p2.canvas.data
raw_X = [x[0] for x in rawdata]
raw_Y = [x[1] for x in rawdata]
dlg = wx.FileDialog(
self, message="Save file as ...",
defaultDir=self.CurrentDirectory,
defaultFile=str(time.ctime()), wildcard=wildcard2, style=wx.FD_SAVE
)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
fig1 = plt.figure()
plt.plot(raw_X,raw_Y)
plt.title("Raw Data")
fig1.savefig(path+".png")
self.Dialog(None,"File successfully saved","I")
except UnboundLocalError:
pass
def OnMultiply(self,e):
try:
factor = self.x.GetValue()
factor = float(factor)
self.IntegrationTime = factor
except ValueError as e:
self.Dialog(None,str(e),"W")
def OnQuit(self, e):
self.Close()
def OnHelp(self,e):
self.Dialog(None,"N/A","I")
def ToggleSine(self,e):
pass
def ToggleLinear(self,e):
self.Dialog(None,"Not added yet","W")
def ToggleGaussian(self,e):
self.Dialog(None,"Not added yet","W")
if __name__ =="__main__":
app=wx.App()
Frame(None,-1,"N/A")
app.MainLoop()

pyqt4 pixel information on rotated image

I have adapted an image viewer (see code below) to allow me to get pixel information from a loaded image. You load an image using the 'Load image' button, then you can zoom in and out using the scroll wheel, and pan using mouse left click and drag. When you press the button 'Enter pixel info mode', the dragging is disabled (you can still zoom) and clicking on the image will give the pixel coordinate (integer pixel indices) and grayscale value of the pixel.
The problem is that if you rotate the image, by pressing the 'Rotate image' button, using the pixel info button no longer gives the correct pixel info. I imagine that the mapToScene method is not the right thing to use on a rotated image but can find no other way to do it. I have tried various things, such as using toImage() on the rotated pixmap and then replacing the original image with this, but nothing seems to work. What would be the best way to resolve this?
The code:
from PyQt4 import QtCore, QtGui
class PhotoViewer(QtGui.QGraphicsView):
photoClicked = QtCore.pyqtSignal(QtCore.QPoint)
def __init__(self, parent):
super(PhotoViewer, self).__init__(parent)
self._zoom = 0
self._empty = True
self._scene = QtGui.QGraphicsScene(self)
self._photo = QtGui.QGraphicsPixmapItem()
self._scene.addItem(self._photo)
self.setScene(self._scene)
self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setBackgroundBrush(QtGui.QBrush(QtGui.QColor(30, 30, 30)))
self.setFrameShape(QtGui.QFrame.NoFrame)
def fitInView(self):
rect = QtCore.QRectF(self._photo.pixmap().rect())
if not rect.isNull():
unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self.centerOn(rect.center())
self._zoom = 0
def hasPhoto(self):
return not self._empty
def toggleDragMode(self):
if self.dragMode() == QtGui.QGraphicsView.ScrollHandDrag:
self.setDragMode(QtGui.QGraphicsView.NoDrag)
elif self.hasPhoto():
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
def setPhoto(self, pixmap=None):
self._zoom = 0
if pixmap and not pixmap.isNull():
self._empty = False
self.setDragMode(QtGui.QGraphicsView.ScrollHandDrag)
self._photo.setPixmap(pixmap)
self.fitInView()
else:
self._empty = True
self.setDragMode(QtGui.QGraphicsView.NoDrag)
self._photo.setPixmap(QtGui.QPixmap())
def wheelEvent(self, event):
if not self._photo.pixmap().isNull():
if event.delta() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def mousePressEvent(self, event):
if (self.hasPhoto() and
self.dragMode() == QtGui.QGraphicsView.NoDrag and
self._photo.isUnderMouse()):
self.photoClicked.emit(QtCore.QPoint(event.pos()))
super(PhotoViewer, self).mousePressEvent(event)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = PhotoViewer(self)
# 'Load image' button
self.btnLoad = QtGui.QToolButton(self)
self.btnLoad.setText('Load image')
self.btnLoad.clicked.connect(self.loadImage)
# Button to change from drag/pan to getting pixel info
self.btnPixInfo = QtGui.QToolButton(self)
self.btnPixInfo.setText('Enter pixel info mode')
self.btnPixInfo.clicked.connect(self.pixInfo)
self.editPixInfo = QtGui.QLineEdit(self)
self.editPixInfo.setReadOnly(True)
# Button to rotate image by 10 degrees
self.btnRotate = QtGui.QToolButton(self)
self.btnRotate.setText('Rotate image')
self.btnRotate.clicked.connect(self.rotateImage)
self.viewer.photoClicked.connect(self.photoClicked)
# Arrange layout
VBlayout = QtGui.QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QtGui.QHBoxLayout()
HBlayout.setAlignment(QtCore.Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
HBlayout.addWidget(self.btnRotate)
HBlayout.addWidget(self.btnPixInfo)
HBlayout.addWidget(self.editPixInfo)
VBlayout.addLayout(HBlayout)
def loadImage(self):
self.viewer.setPhoto(QtGui.QPixmap('pic.jpg'))
def pixInfo(self):
self.viewer.toggleDragMode()
def rotateImage(self):
self.viewer._photo.setRotation(10)
def photoClicked(self, pos):
pos = self.viewer.mapToScene(pos)
# p.s. I realise the following lines are probably a very convoluted way of getting
# a grayscale value from RGB, but I couldn't make it work any other way I tried
rot_image = self.viewer._photo.pixmap().toImage().pixel(pos.x(), pos.y())
colour = QtGui.QColor.fromRgb(rot_image)
gsval = QtGui.qGray(colour.red(), colour.green(), colour.blue())
self.editPixInfo.setText('X:%d, Y:%d Grayscale: %d' % (pos.x(), pos.y(), gsval))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
You need to map the scene coordinates to item coordinates:
pos = self.viewer._photo.mapFromScene(self.viewer.mapToScene(pos))

Resources