How do I (quickly) thumbnail 300 images with PyQT4? - python

I'm working (still) on a book binding application, and to make it aesthetically pleasing, I've added a thumbnail to every page you drag in. It works just fine, but the only problem is that when I drag in a whole book (i.e. 400 images), it freezes completely until it's done.
Here's my simple drop code:
def fileDropped(self, file):
f = str(file[-1])
if os.path.splitext(f)[1][1:] != 'tif':
reply = QtGui.QMessageBox.question(self, 'Message', 'All files must be TIF images. Would you like me to convert a copy of your file to the TIF format?', QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
if not os.path.exists('./djvu_backup/'): os.mkdir('./djvu_backup/')
if f not in self.getChildren(self.ui.pageList): # It's a custom method. It does what it looks like it does.
icon = QtGui.QIcon(f)
pixmap = icon.pixmap(72, 72)
icon = QtGui.QIcon(pixmap)
item = QtGui.QListWidgetItem(f, self.ui.pageList)
item.setIcon(icon)
item.setStatusTip(f)
return True
Also, just as a side question, as you can see in the code, f = str(file[-1]). I have to select the last element from my dropped files array every time the method is called, as it is called for every file dropped, even if they are dropped all at once. Is there a reason/workaround for this?
Thank you!

You could try to make the thumbnailing faster, but that would only increase the size of the book that you could drop before you'd notice the problem. The answers are to either only thumbnail a page as it's displayed, or relegate the thumbnailing to a background thread and update the display as each is finished.

I know this is really simple but have you considered using a QProgressBar just so the users can see that the program is still processing when it appears to be frozen?

Related

Tkinter Canvas text substitution intermittent

I have a method that is called from a button and it allows the user to select a file and pump out an analysis on the other side. There is a canvas outside the class and method that has a text widget that tells the user what is happening, with an original text='Ready'. This is the method that gets it done:
def process_solution(self):
canvas.itemconfig(infobox_text, text='Please Select a File.')
self.import_file_path = filedialog.askopenfilename()
canvas.itemconfig(infobox_text, text='Analysing Solution. Please Wait.')
if self.import_file_path:
# UnGzip
with gzip.open(self.import_file_path, 'rt') as decompressed_buffer:
decompressed_data = decompressed_buffer.read()
# Parse to XML
parsed_data = ElementTree.fromstring(decompressed_data)
# Now extract metrics
self.analysis_results = ExtractMetrics.start(parsed_data)
# Display Parsed Results in Window
canvas.itemconfig(infobox_text, text=self.analysis_results)
else:
canvas.itemconfig(infobox_text, text='Upload Cancelled.')
It changes from the original 'Ready' message to 'Please Select a File' with no problem, then it seems to skip 'Analysing Solution. Please Wait.' (even when I put the thread to sleep for a few seconds). And finally it displays self.analysis_results. Even if the file selection is cancelled it displays the right message, but not when a file is selected.
My question is why, if in all cases I'm using canvas.itemconfig(infobox_text, text=...) the first, second and fourth changes to the text work, but it skips the third. No errors anywhere either.

Is it possible to ignore dialogue boxes when checking size of focused window?

I noticed that if using windows focus detection, the rect value will change depending on what part of the program you are in. I want the size of the main window only if possible.
I've managed to get around some of these issues by checking the window title. If the title is empty, it's a dropdown menu, so ignore. If it is titled "Open", "Save As", etc, then it's obviously a dialogue box and ignore. However, the message "Do you want to quit without saving?" seems to have only the title of the program itself, so that slips through the check.
I just tested the heights of those boxes to see if there was a constant value to ignore, but each program seems to have different heights. I could possibly just ignore anything under a certain resolution, but I'd prefer not to as it's not actually solving the issue, and there may be some rare cases where it needs to use that particular resolution.
Here's the bits of code I currently use from pywin32 to do the detection. I also have the ctypes alternatives, but it's basically the same thing.
import win32gui
import win32process
import psutil
hwnd = win32gui.GetForegroundWindow()
print 'pid:', win32process.GetWindowThreadProcessId(hwnd)[1]
print 'rect:', win32gui.GetWindowRect(hwnd)
print 'name:', win32gui.GetWindowText(hwnd)
print 'exe:', psutil.Process(win32process.GetWindowThreadProcessId(hwnd)[1]).name()
Basically I want the size of the main window at all times, no matter which other windows are loaded on top of it. I've only found GetWindowRect and GetClientRect which both do the same thing, just wondering if I'm missing something?
Managed to get to the solution by realising it was basically the last parent window I needed:
def _get_parent(self):
while True:
try:
parent = win32gui.GetParent(hwnd)
except UnboundLocalError:
hwnd = win32gui.GetForegroundWindow()
else:
if parent:
hwnd = parent
else:
break
return hwnd
Basically it just keeps checking if the current window has a parent until there are no more parents. Not super efficient but does the job. I've only really tested it on paint > save as > are you sure you want to replace?, but it gets the correct hwnd value for the main window and returns a constant resolution.

Psychopy builder expt. - how to add live updating text on screen

I am creating an experiment using Psychopy builder.
The participant is presented with an image containing numbers, e.g. 10 and 20.
They enter what they think is the mean of the numbers, 15 in this case, and then press the spacebar to move on to the next image.
I am trying to have it so there is a display/box on screen that shows them their entry, as with larger numbers in the hundreds of thousands and millions I think they might lose track of what they have pressed.
The ability to change their entry would be excellent also, but really I am most interested in them being able to see it on screen.
In builder I can't find a way to do this, and the ratings scale is not appropriate for huge numbers.
I found these solutions in code to do something that sounds like it:
http://www.psychopy.org/wiki/home.php/Snippets/LiveUpdatingText
However when I try to add them using the code insert function , or just adding them to the compiled script the screen locks up when I try to run the experiment. I am a novice at python, and am not sure where to start fixing this. Is what I'm trying to do possible?
I'm happy to provide some example code from the compiled builder experiment.
Thanks in advance!
Those code snippets are designed for Coder, where you control everything that is happening and when. The same thing can be done in Builder, but you will have to amend the code to fit in with Builder's event loop cycle. i.e. Builder does certain things at the start of an experiment, on every trial, on every screen refresh and so on. So you can't just insert this sort of code without modification, because, for example, it attempts to wait indefinitely for a keypress. Builder meanwhile, is checking the keyboard every screen refresh (typically at 60 Hz), so if you try to wait indefinitely for a keypress in code, you'll be halting Builder from doing everything else it needs to do.
In essence, you just need to break up the code into snippets that go in the appropriate tab in a Builder Code Component (for code to be executed at experiment start, on each frame, and so on), and avoid indefinite functions like event.waitKeys() in favour of instantaneous checking via event.getKeys()
e.g. to adapt the second example from Jonas Lindeløv, in the "Begin Routine" tab, put:
chars = list('0123456789.') # the valid characters
meanText = '' # start with an empty answer on each trial
In the "Each Frame" tab, put something like:
response = event.getKeys() # get a list of keys pressed at this instant
if len(response) > 0: # if there was one,
key = response[0] # just convenient shorthand
if key in chars:
meanText = meanText + response[0]
elif key == 'space':
meanText = meanText + ' '
elif key == 'backspace' and len(meanText) > 0:
meanText = meanText[:-1]
elif key == 'return':
thisExp.addData('Answer', meanText) # save the response
continueRoutine = False # finish this trial
# update the appropriate text stimulus with the current response value:
insertNameOfYourTextStimulusComponent.text = meanText

What is the simplest way of monitoring when a wxPython frame has been resized?

I want to know when a frame has been resized, so I can save the size and remember it the next time the application launches. Here is my on_resize method:
def on_resize(self, event):
logic.config_set('main_frame_size',
(event.Size.width, event.Size.height))
event.Skip()
And it's bound like this:
self.Bind(wx.EVT_SIZE, self.on_resize)
The problem is performance. For safety, my logic module saves the config file every time a setting changes, and writing the config file every time the resize event fires is way too performance taxing.
What would be the best/easiest way of monitoring for when the user is done resizing the frame?
Update
My config_set function:
def config_set(key, value):
"""Set a value to the config file."""
vprint(2, 'Setting config value: "{}": "{}"'.format(key, value))
config[key] = value
# Save the config file.
with open(config_file_path, 'w') as f:
pickle.dump(config, f)
You could handle EVT_IDLE which is triggered when the event queue is empty:
wx.IdleEvent: This class is used for EVT_IDLE events, which are generated and sent when the application becomes idle. In other words, the when the event queue becomes empty then idle events are sent to all windows (by default) and as long as none of them call RequestMore then there are no more idle events until after the system event queue has some normal events and then becomes empty again.
The process of resizing or moving a window should keep the event queue jammed so it won't become empty (and trigger the idle event) until the resizing/moving is done.
Set a dirty flag in EVT_SIZE and check it in the EVT_IDLE handler. If the flag is set, save the new size and reset the flag:
import wx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.resized = False # the dirty flag
self.Bind(wx.EVT_SIZE,self.OnSize)
self.Bind(wx.EVT_IDLE,self.OnIdle)
def OnSize(self,event):
self.resized = True # set dirty
def OnIdle(self,event):
if self.resized:
# take action if the dirty flag is set
print "New size:", self.GetSize()
self.resized = False # reset the flag
app = wx.PySimpleApp()
frame = Frame().Show()
app.MainLoop()
EVT_SIZE may also be triggered when restoring a minimized window (the window size remains the same). If you want to cut down on unnecessary saves, you may want to check if the size is actually different before you save it to the config (you could keep track of it in a variable containing the last saved size).
You may want to add EVT_MOVE to keep track of the window position.
You could start a timer and have it check for changes every so often, kind of like the auto-save in Microsoft Word. Or you could set some kind of flag when EVT_SIZING or EVT_SIZE occurs and then bind to EVT_LEAVE_WINDOW as you'll almost certainly leave the window when you're done resizing. Thus when that event fires, you check the flag that was set and if it is set, you save and reset the flag.
On windows, you can save the configuration in the registry, which results in no performance hit when the window is resized.
On other OS's, where there is no registry, I guess you need to use a file. However, I am surprised that even this gives the kind of performance penalty that you would notice.
Are you sure that whatever poor performance you are seeing is due to this? ( Perhaps your redrawing code is slow? )
I would think that any modern OS would look after such a small file write without getting in your way. Perhaps it is Python problem?
I urge you to look into the above questions first. However, to answer your actual question:
The way to do this is to save the window size in a variable, and only write it to a file when your application quits.
Took a look at the code you just posted. I am not a python expert, but it looks like you are re-opening the file on every update. If so, no wonder it is slow!
Keep the file open all the time.
Only write the file when your app quits.
You might also take a look at the wxWidget wxConfig class.
You definitely shouldn't be saving the window geometry on every resize, it should be normally only done when the frame is closed. If you want extra safety (but, honestly, how do you manage to crash in Python?), you can also call the same function you call on frame close from a EVT_TIMER handler. But window geometry is hardly a critical resource so I don't think there is ever any real need to do this.

Saving wx widget contents to a file

I have created different shapes like circle/rect etc in my program using BufferedPaintDC on event. Now i want to save the file as I click the saveas button in the menu option. For that I am using memoryDC and save the contents as bmp file.
def Saveas(self,event):
dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", \
wx.SAVE | wx.OVERWRITE_PROMPT)
if dlg.ShowModal() == wx.ID_OK: # user enters filename as something.bmp
self.show_bmp = wx.StaticBitmap(self)
w, h = self.GetClientSize()
draw_bmp = wx.EmptyBitmap(w, h)
c = wx.MemoryDC(draw_bmp)
c.SetBrush(wx.Brush('white'))
c.Clear()
c.SetPen(wx.Pen("purple", 15))
c.DrawRectangle(30,30,60,60) ### ??????####
myimage = self.show_bmp.GetBitmap()
self.filename=dlg.GetFilename()
name = os.path.join('C:\mad', self.filename)
myimage.SaveFile(name, wx.BITMAP_TYPE_JPEG)
self.filename=name
dlg.Destroy()
Now my problem is how do I get the things drawn by the buffered dc on the " c ", so that they can be then converted to image?? I hope my question is clear.As u can see I am drawing the rectangle on the "c" and that is being converted to an image. But I want to get the shapes created on ONPaint . How do I extract that?
Thanks
What you're asking is very close to saying you want to take a screenshot. Although technically grabbing a copy of what the window currently looks like is not the same as cloning what the OnPaint does, it's possible it would do the job for you.
If it doesn't work, take note of the technique, including the use of DC.Blit(), as those would be the tools you'd use.
See this wxpython-users mailing list post by Andrea Gavana for image-grabbing code.
Edit: if the problem is that because you're doing all your drawing inside the EVT_PAINT handler, then you probably need a different technique. Draw everything from a different routine to a pre-allocated buffer bitmap. Inside the OnPaint, which is just how the image actually gets to the screen, you don't draw, you just copy the already-drawn bitmap. The buffer bitmap persists between OnPaint calls (and in fact is basically independent of OnPaint), so you can add a Save() routine that works rather simply as well. See the wxPyWiki DoubleBuffererDrawing page for various snippets that will show you how to do that. (Also note, this will be a bit of a learning curve, so ask for more help if it's not enough.)

Categories

Resources