PyGTK FileChooserDialog Keep Getting Errors - image

Now I made WidgetArea originally for Windows, but being primarily a Linux user. I wanted to make it for Linux as well, but mainly to learn more about the file dialog in PyGTK. So I took a look at this tutorial to have a better understanding of it, while working on this simple, yet small application, as that's easier for me to learn, and understand by experimentation.
So here's my source code.
#!/usr/bin/env python
import sys, os
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst
class WidgetArea(gtk.Window):
def addwidget(self, w):
self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.set_title("Widget")
self.win.set_decorated(False)
self.win.set_has_frame(False)
self.win.set_resizable(False)
self.win.set_keep_above(True)
self.win.set_property('skip-taskbar-hint', True)
self.previewimage = gtk.Image()
self.win.add(self.previewimage)
self.win.show_all()
def pinning(self, checkbox):
if checkbox.get_active():
self.set_keep_above(True)
else:
self.set_keep_above(False)
def change_size(self, w):
width = int(self.entryw.get_text())
height = int(self.entryh.get_text())
self.win.set_size_request(width,height)
def __init__(self):
super(WidgetArea, self).__init__()
self.set_position(gtk.WIN_POS_CENTER)
self.set_title("WidgetArea")
self.set_resizable(False)
self.set_keep_above(True)
self.set_property('skip-taskbar-hint', True)
self.connect("destroy", gtk.main_quit, "WM destroy")
vbox = gtk.VBox(spacing=0)
hbox = gtk.HBox(spacing=0)
hbox2 = gtk.HBox(spacing=0)
hbox3 = gtk.HBox(spacing=0)
hbox4 = gtk.HBox(spacing=0)
self.widgetsize = gtk.Label("Widget Size:")
self.widgetsize.set_size_request(100, 30)
self.entryw = gtk.Entry()
self.entryh = gtk.Entry()
self.entryw.set_text("270")
self.entryw.set_size_request(75, 30)
labelcoma = gtk.Label(",")
labelcoma.set_size_request(10, 30)
self.entryh.set_text("221")
self.entryh.set_size_request(75, 30)
labelspac1 = gtk.Label(" ")
labelspac1.set_size_request(10, 30)
hbox.pack_start(self.widgetsize)
hbox.pack_start(self.entryw)
hbox.pack_start(labelcoma)
hbox.pack_start(self.entryh)
hbox.pack_start(labelspac1, 0, 0, 10)
check = gtk.CheckButton("Pin This Window")
check.set_active(True)
check.connect("clicked", self.pinning)
hbox.pack_start(check, 0, 0, 10)
labelspac2 = gtk.Label(" ")
labelspac2.set_size_request(250, 15)
hbox2.pack_start(labelspac2)
filefilter = gtk.FileFilter()
filefilter.set_name("Images")
filefilter.add_mime_type("image/png")
filefilter.add_mime_type("image/jpeg")
filefilter.add_mime_type("image/gif")
filefilter.add_mime_type("image/tiff")
filefilter.add_mime_type("image/svg+xml")
filefilter.add_pattern("*.jpg")
self.ref_file_button = gtk.FileChooserButton('Add Widget')
self.ref_file_button.set_current_folder("/".join([self.rootdir,"pics"]))
self.ref_file_button.set_filter(filefilter)
self.ref_file_button.connect("file-set", self.on_open_clicked)
hbox3.pack_start(self.ref_file_button, 150, 150, 10)
labelspac5 = gtk.Label(" ")
labelspac5.set_size_request(0, 10)
hbox4.pack_start(labelspac5)
vbox.pack_start(hbox)
vbox.pack_start(hbox2)
vbox.pack_start(hbox3)
vbox.pack_start(hbox4)
self.add(vbox)
self.show_all()
def on_open_clicked(self, widget, data=None):
ref_image_path = widget.get_filename()
self.previewimage.set_from_file(ref_image_path)
self.addwidg.connect("clicked", self.addwidget)
self.addwidg.connect("clicked", self.change_size)
ref_image_path.destroy()
WidgetArea()
gtk.gdk.threads_init()
gtk.main()
I removed the following code (1st), due to the following error (2nd).
self.ref_file_button.set_current_folder("/".join([self.rootdir,"pics"]))
Traceback (most recent call last):
File "./widgetarea.py", line 109, in <module>
WidgetArea()
File "./widgetarea.py", line 86, in __init__
self.ref_file_button.set_current_folder("/".join([self.rootdir,"pics"]))
AttributeError: 'WidgetArea' object has no attribute 'rootdir'
Now this isn't a big deal at this point. My main goal is to get the image displayed in a new window. So after I removed the code above, due to that error I got another one.
Traceback (most recent call last):
File "./widgetarea.py", line 103, in on_open_clicked
self.previewimage.set_from_file(ref_image_path)
AttributeError: 'WidgetArea' object has no attribute 'previewimage'
All I'm having problems with is when you browse to select an image I want the chosen image, when pressed OK to launch as a new window displaying the chosen image in that window, as stated above.

To correct the first error, use gtk.FILE_CHOOSER_ACTION_OPEN instead of gtk.FileChooserAction.OPEN.
The second problem is because there is no variable named image at that point in your code (line 116). Perhaps you are coming from a C++ or Java background, where a name like image can be resolved by looking at the attributes of the enclosing class, i.e. this.image?
In Python you can't do that. You have to assign explicitly to self.image in your addwidget() method. Otherwise the name image remains local to the addwidget() method and is not available outside of it.
This raises a different problem, what happens every time the button gets clicked and addwidget() is called? self.win and self.image are overwritten. That may be what you want, but I'm just calling it to your attention --- it seems a little odd to me.

I have used something like this in one of my projects. And it's working well for me in Linux.
def __init__(self):
# Define all the widgets
image_filter = gtk.FileFilter()
image_filter.set_name("Images")
image_filter.add_mime_type("image/png")
image_filter.add_mime_type("image/jpeg")
image_filter.add_mime_type("image/gif")
image_filter.add_mime_type("image/tiff")
image_filter.add_mime_type("image/svg+xml")
image_filter.add_pattern("*.jpg")
self.ref_file_button = gtk.FileChooserButton('Select Image')
self.ref_file_button.set_size_request(100,30)
self.ref_file_button.set_current_folder("/".join([self.rootdir,"pics"])) # set directory path
self.ref_file_button.set_filter(image_filter)
self.ref_file_button.set_tooltip_text('Select Image')
self.ref_file_button.connect("file-set", self.ref_image_selected)
def ref_image_selected(self,widget,data=None):
ref_image_path = widget.get_filename()
print ref_image_path
After getting the path of the image, you can load it using gtk.Image
EDIT:
Your code is a bit erroneous. You are never calling the function addwidget(), and hence self.previewimage is not defined. and so it gives AttributeError.
def __init__(self):
# your code
self.add(vbox)
self.addwidget(200) # I don't know what 'w' is. so I took a random number.
self.show_all()
def on_open_clicked(self, widget, data=None):
ref_image_path = widget.get_filename()
self.previewimage.set_from_file(ref_image_path)
self.addwidg.connect("clicked", self.addwidget)
self.addwidg.connect("clicked", self.change_size)
ref_image_path.destroy()
What is self.addwidg ?
And I am able to view the image now.

Related

Works with QGridLayout not with QVBoxLayout

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os, time
class SetName(QWidget):
def __init__(self):
QWidget.__init__(self)
self.show()
toplayout = QVBoxLayout()
self.setWindowTitle('Personal Info')
self.form_layout = QFormLayout()
self.setLayout(self.form_layout)
self.line_edit_param = QLineEdit(self)
self.line_edit_param.setPlaceholderText("Write Here")
self.form_layout.addRow('Write Name', self.line_edit_param)
toplayout.addLayout(self.form_layout)
self.setFocus()
class LearnApp(QDialog):
def __init__(self):
super(QDialog, self).__init__()
self.setWindowTitle("LearnApp")
self.active = False
close_button = QPushButton("Close")
close_button.clicked.connect(self.close)
self.check_button = QPushButton("Check")
self.check_button.clicked.connect(self.set_data)
self.tr = QTextEdit()
self.tr.setReadOnly(True)
# layout
layout = QHBoxLayout()
#layout.addWidget(self.button3)
sub_layout = QVBoxLayout()
sub_layout.addWidget(self.check_button)
sub_layout.addWidget(close_button)
layout.addLayout(sub_layout)
layout.addWidget(self.tr)
self.setLayout(layout)
self.setFocus()
def set_data(self):
print "in set_data"
SetName()
app = QApplication(sys.argv)
dialog = LearnApp()
dialog.show()
app.exec_()
This is the code I'm trying. If edit it with toplayout = QGridLayout(), program works fine but with toplayout = QVBoxLayout(), it gives message QLayout::addChildLayout: layout "" already has a parentand just flashes the new window. What could be the problem? How should I tackle this? I wanna use QVBoxLayout instead of QGridLayout
Firstly, the new window disappears straight away because you don't store a reference to it. You need to store a reference to the instance in your LearnApp class, or parent it to another Qt object outside of set_data() if you want it to stick around.
The error message regarding the layouts is not occurring because of your choice of layouts, but because you are calling
self.setLayout(self.form_layout)
and then
toplayout.addLayout(self.form_layout)
The first call assigns the layout to the instance of SetName, but in doing so also makes the instance the parent of self.form_layout. The second call is trying to add the same layout to toplayout and set it as the parent, but Qt sees that self.form_layout already has a parent (i.e. is being used elsewhere). This is what the error message is trying to tell you.
I suspect that instead of self.setLayout(self.form_layout), you intended to write something like
self.setLayout(toplayout)

Pyside - Change entire GUI when button is pressed

I'm totally new to pyside and I'm having a problem with my little program (and pyside layouts in general).
What I have is an UI with some QlineEdits, comboboxes and a button. After I have filled out the Qlines and press the button I want to either to open a new window with a completely new layout or preferably clear out the open window and fill it with a new layout based on the input from the qlines. Perhaps this is super basic but I can't get it to work. The reason is that I can't grasp how I would be able to replace or add new stuff to my gui when it's already set and shown.
Let's say I have a script like this:
import sys
import os
from PySide import QtCore, QtGui
class BasicGui(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.projectNameLbl1 = QtGui.QLabel('Label1')
self.projectNameLbl2 = QtGui.QLabel('Label2')
self.nextBtn = QtGui.QPushButton("Next")
self.projectNameEdit = QtGui.QLineEdit(self)
self.projectNameEdit2 = QtGui.QLineEdit(self)
grid = QtGui.QGridLayout()
grid.setSpacing(10)
grid.addWidget(self.projectNameLbl1, 2, 0)
grid.addWidget(self.projectNameEdit, 2, 1)
grid.addWidget(self.projectNameLbl2, 3, 0)
grid.addWidget(self.projectNameEdit2, 3, 1)
grid.addWidget(self.nextBtn, 4, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('projectCreator')
self.show()
self.nextBtn.clicked.connect(self.nextPressed)
def nextPressed(self):
self.msgBox = QtGui.QMessageBox()
self.msgBox.setText("When this button is pressed I want to generate a new layout")
self.msgBox.exec_()
def main():
app = QtGui.QApplication(sys.argv)
ex = BasicGui()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Say that I enter 10 in the line next to label1 and 2 in the other and press Next.Now I want to clear everything out and create 2 new columns with 10 qlines in each (or something like that).
Excuse me if I'm being either to vague or if I'm just repeating myself. I'm tired and irritated and English is not my first language.
I would deeply appreciate any help I could get or a push in the right direction.
Edit: If it's easier to accomplish this with some other widgetype with tabs or something that's fine. All i want to do is generate new widgets after i have recieved input from the user.
What you'll want to do is used a QStackedLayout[1].
Create a QWidget.
Create your layout.
Call setLayout() on the widget with your layout as the argument.
Push the new widget onto the QStackedLayout.
Use QStackedLayout's setCurrentIndex() or setCurrentWidget() functions to set the current layout.
I did something similar in a project of mine. See https://github.com/shanet/Cryptully/blob/master/cryptully/qt/qChatTab.py for a more complete example. Also see Pyside Changing Layouts for a similar problem.
[1] http://qt-project.org/doc/qt-4.8/qstackedlayout.html

wxpython using time.sleep() without blocking complete GUI

I want to change the text displayed in my GUI at specific time intervals. After a lot of approaches, I find that, specifically to my requirements, I must use time.sleep() instead of wx.Timer, but time.sleep() freeze the complete GUI. Here's an example of my code:
import wx
import time
DWELL_TIMES = [1, 2, 1, 3]
SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']
class DM1(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
panel = wx.Panel(self)
text_display = wx.StaticText(panel, pos = (400, 150))
for dwell_time in DWELL_TIMES:
text_display.SetLabel(SCREEN_STRINGS[dwell_time])
time.sleep(float(DWELL_TIMES[dwell_time]))
app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()
Does somebody know why this happen, and how to make the GUI doesn't block?
I guess that Threading could help me, doesn't it? If it does, which is the correct way to put threads inside this code? Is there an alternative to Threading?
Thanks a lot!
As mentioned by others, wx.CallAfter and wx.CallLater are your friends. Study them and learn them. Here is a complete, working example using wx.CallLater. I included other refactoring as I saw fit.
import wx
DATA = [
(1, 'nudge nudge'),
(2, 'wink wink'),
(1, 'I bet she does'),
(3, 'say no more!'),
]
class Frame(wx.Frame):
def __init__(self):
super(Frame, self).__init__(None)
panel = wx.Panel(self)
self.text = wx.StaticText(panel)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.AddStretchSpacer(1)
sizer.Add(self.text, 0, wx.ALIGN_CENTER)
sizer.AddStretchSpacer(1)
panel.SetSizer(sizer)
self.index = 0
self.update()
def update(self):
duration, label = DATA[self.index]
self.text.SetLabel(label)
self.index = (self.index + 1) % len(DATA)
wx.CallLater(int(duration * 1000), self.update)
if __name__ == '__main__':
app = wx.App(None)
frame = Frame()
frame.SetTitle('Example')
frame.SetSize((400, 300))
frame.Center()
frame.Show()
app.MainLoop()
If you look at the documentation for time.sleep(), you see that it basically blocks execution of that thread for the specified interval. The problem is that currently your GUI has only a single thread, so if you block the thread then you block ALL execution in that thread. This means, as you've experienced, that the GUI is unusable during the sleep.
Even using threading, the time.sleep() call can't be in the same thread as the GUI, thus trying to get your GUI to refresh after the sleep is over will be very complicated. Beyond that, it's basically reimplementing wx.Timer! No use redoing something that's already been done for you.
It seems to me that your question should be less "how do I make sleeps work?" and more "Why isn't wx.Timer working properly?" Please explain the problem you're having with wx.Timer in detail. Why won't it work? Maybe post some code. My guess is you probably aren't binding the wx.EVT_TIMER properly. Take a look at this tutorial.
Which is the correct way to put threads inside this code?
Although using wx.Timer is the correct solution to this simplified example, if your real goal is to know how to use a worker thread to do long tasks and give updates to your main GUI without freezing your whole application, here's how:
import wx
import threading
import time
class WorkerThread(threading.Thread):
DWELL_TIMES = [1, 2, 1, 3]
SCREEN_STRINGS = ['nudge nudge', 'wink wink', 'I bet she does', 'say no more!']
def __init__(self, window):
threading.Thread.__init__(self)
self.window = window
def run(self):
for i in range(len(WorkerThread.DWELL_TIMES)):
wx.CallAfter(self.window.set_text, WorkerThread.SCREEN_STRINGS[i])
time.sleep(float(WorkerThread.DWELL_TIMES[i]))
wx.CallAfter(self.window.close)
class DM1(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
panel = wx.Panel(self)
self.text_display = wx.StaticText(panel, pos = (400, 150))
self.kickoff_work()
def kickoff_work(self):
t = WorkerThread(self)
t.start()
def set_text(self, text):
self.text_display.SetLabel(text)
def close(self):
self.Close()
app = wx.App()
DM1Frame = DM1(None, size = (800, 600))
DM1Frame.Center()
DM1Frame.Show()
app.MainLoop()
You might try making a global variable that gets the time when it first starts, then having a second variable get the current time and see if the two times are far enough apart to work. Something like this:
When the text changes to something new,
global timestart
timestart = gettime()
Then, where you check if you are changing the code,
timestop = gettime()
if timestop - timestart >= timebetweenchanges:
change code
I don't understand why you can't use a timer for this. They seem to be made for the exact purpose you need them for. As acattle mentioned already, I wrote a tutorial on the subject.
He is completely right though. Using time.sleep() will freeze the GUI because it blocks wx's main event loop. If you absolutely HAVE to use time.sleep() (which I doubt), then you can use a thread. I wrote a tutorial on that subject too. In fact, I actually use time.sleep() in that example.
I might suggest you go use wx.CallLater. Refer to official doc: http://wxpython.org/docs/api/wx.CallLater-class.html
A convenience class for wx.Timer, that calls the given callable object
once after the given amount of milliseconds, passing any positional or
keyword args. The return value of the callable is availbale after it
has been run with the GetResult method.
If you don't need to get the return value or restart the timer then
there is no need to hold a reference to this object. It will hold a
reference to itself while the timer is running (the timer has a
reference to self.Notify) but the cycle will be broken when the timer
completes, automatically cleaning up the wx.CallLater object.
Possible further reference can be found in this question: Using wx.CallLater in wxPython

PyGTK Change Window Dimensions from Two Entry's

It's pretty simple what I'm trying to accomplish here.
Say you put down 320 in the left textbox. That means the width of the window will be 320px. Same applies for height, except for the right textbox.
However when I debug I get this error.
Traceback (most recent call last):
File "./app.py", line 37, in change_size
self.win.set_size_request(width,height)
TypeError: an integer is required
Here's the code.
#!/usr/bin/env python
import gtk
class app:
def __init__(self):
self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.win.set_title("Change Dimensions")
self.win.set_default_size(235, 60)
self.win.connect("destroy", gtk.main_quit)
vbox = gtk.VBox(spacing=4)
hbox = gtk.HBox(spacing=4)
self.entry = gtk.Entry()
self.entry2 = gtk.Entry()
hbox.pack_start(self.entry)
hbox.pack_start(self.entry2)
hbox2 = gtk.HBox(spacing=4)
ok = gtk.Button("OK")
ok.connect("clicked", self.change_size)
hbox2.pack_start(ok)
exit = gtk.Button("Exit")
exit.connect("clicked", gtk.main_quit)
hbox2.pack_start(exit)
vbox.pack_start(hbox)
vbox.pack_start(hbox2)
self.win.add(vbox)
self.win.show_all()
def change_size(self, w):
width = self.entry.get_text()
height = self.entry2.get_text()
self.win.set_size_request(width,height)
app()
gtk.main()
That's because get_text returns a string, but set_size_request requires integers. Simple fix:
width = int(self.entry.get_text())
height = int(self.entry2.get_text())

Error drawing text on NSImage in PyObjC

I'm trying to overlay an image with some text using PyObjC, while striving to answer my question, "Annotate images using tools built into OS X". By referencing CocoaMagic, a RubyObjC replacement for RMagick, I've come up with this:
#!/usr/bin/env python
from AppKit import *
source_image = "/Library/Desktop Pictures/Nature/Aurora.jpg"
final_image = "/Library/Desktop Pictures/.loginwindow.jpg"
font_name = "Arial"
font_size = 76
message = "My Message Here"
app = NSApplication.sharedApplication() # remove some warnings
# read in an image
image = NSImage.alloc().initWithContentsOfFile_(source_image)
image.lockFocus()
# prepare some text attributes
text_attributes = NSMutableDictionary.alloc().init()
font = NSFont.fontWithName_size_(font_name, font_size)
text_attributes.setObject_forKey_(font, NSFontAttributeName)
text_attributes.setObject_forKey_(NSColor.blackColor, NSForegroundColorAttributeName)
# output our message
message_string = NSString.stringWithString_(message)
size = message_string.sizeWithAttributes_(text_attributes)
point = NSMakePoint(400, 400)
message_string.drawAtPoint_withAttributes_(point, text_attributes)
# write the file
image.unlockFocus()
bits = NSBitmapImageRep.alloc().initWithData_(image.TIFFRepresentation)
data = bits.representationUsingType_properties_(NSJPGFileType, nil)
data.writeToFile_atomically_(final_image, false)
When I run it, I get this:
Traceback (most recent call last):
File "/Users/clinton/Work/Problems/TellAtAGlance/ObviouslyTouched.py", line 24, in <module>
message_string.drawAtPoint_withAttributes_(point, text_attributes)
ValueError: NSInvalidArgumentException - Class OC_PythonObject: no such selector: set
Looking in the docs for drawAtPoint:withAttributes:, it says, "You should only invoke this method when an NSView has focus." NSImage is not a subclass of NSView, but I would hope this would work, and something very similar seems to work in the Ruby example.
What do I need to change to make this work?
I rewrote the code, converting it faithfully, line for line, into an Objective-C Foundation tool. It works, without problems. [I would be happy to post if here if there is a reason to do so.]
The question then becomes, how does:
[message_string drawAtPoint:point withAttributes:text_attributes];
differ from
message_string.drawAtPoint_withAttributes_(point, text_attributes)
? Is there a way to tell which "OC_PythonObject" is raising the NSInvalidArgumentException?
Here are the problems in the above code:
text_attributes.setObject_forKey_(NSColor.blackColor, NSForegroundColorAttributeName)
->
text_attributes.setObject_forKey_(NSColor.blackColor(), NSForegroundColorAttributeName)
bits = NSBitmapImageRep.alloc().initWithData_(image.TIFFRepresentation)
data = bits.representationUsingType_properties_(NSJPGFileType, nil)
->
bits = NSBitmapImageRep.imageRepWithData_(image.TIFFRepresentation())
data = bits.representationUsingType_properties_(NSJPEGFileType, None)
Minor typos indeed.
Note that the middle portion of the code can be replaced with this more-readable variant:
# prepare some text attributes
text_attributes = {
NSFontAttributeName : NSFont.fontWithName_size_(font_name, font_size),
NSForegroundColorAttributeName : NSColor.blackColor()
}
# output our message
NSString.drawAtPoint_withAttributes_(message, (400, 400), text_attributes)
I learned this by looking at the source code to NodeBox, the twelve lines of psyphography.py and cocoa.py, particularly the save and _getImageData methods.

Resources