Open subwindow in GTK3 - gtk3

When Canonical-Quickly sets up a new project it has the following line for the "About dialog":
self.AboutDialog = AboutNewAppDialog
I edited the menu item in glade and added the following code to the python code for the main window:
self.menuabout = self.builder.get_object("menuabout")
and
def on_menuabout_activate(self, menuitem, data=None):
print("About activated")
self.response = self.AboutDialog.run()
self.AboutDialog.hide()
But this produces the error:
self.response = self.AboutDialog.run()
TypeError: run() takes exactly 1 argument (0 given)
I am also working through this tutorial which is using a similar syntax: http://gnipsel.com/glade/glade02b.html
When I place Gtk.Dialog into the brackets the program crashes:
self.response = self.AboutDialog.run(Gtk.Dialog)
My second try:
#!/usr/bin/env python
from gi.repository import Gtk
class Handler:
def on_mainwindow_destroy(self, menuitem):
print("destroy window")
Gtk.main_quit()
def on_menuquit_activate(self, menuitem):
print("quit from menu")
Gtk.main_quit()
def on_menuabout_activate(self, menuitem, data=None):
print("menu about activated")
response = aboutdialog.run()
aboutdialog.hide()
builder = Gtk.Builder()
builder.add_from_file("psn.glade")
builder.connect_signals(Handler())
window = builder.get_object("mainwindow")
window.show_all()
Gtk.main()
Error:
"Traceback (most recent call last):
File "psn_main.py", line 21, in on_menuabout_activate
response = aboutdialog.run()
NameError: name 'aboutdialog' is not defined"

I got it to work using the following code. The function is activated by a menu item which calls "on_menuabout_activate". It prints a debug message to the console. Then it gets the aboutdialog-window from the glade file and runs it:
def on_menuabout_activate(self, menuitem, data=None):
print("menu about activated")
aboutdialog = builder.get_object("aboutdialog")
aboutdialog.run()

Related

Error when reloading new Plugin code in QGIS

I am nooby to Plugin Development but I'm trying to create a plugin in QGIS, for a Uni-subject, with its own graphical interface, which will receive a zipcode from the user and return the name of the corresponding location.
I already created the plugin skeleton, through the Plugin Builder and have designed the graphical interface with QtDesigner https://i.stack.imgur.com/h6k6Q.png . I also added the .txt file that contains the zipcodes database to the plugin folder as a resource.
From what I understand, the file to edit is the one that ends in dialog.py, through the init() method, in order to establish the connections between the signals emitted by the elements of the graphical interface and the corresponding callbacks.
However, when I change the code in the dialog.py and reload the plugin, it gives me an error, and when starting the QGIS it pop-ups an error message, and the plugin no longer appears. Error message after plugin reload
Could you give me some guidance here and maybe point me to where the problem could be? Thanks
The code is this one:
import os
import sys
import qgis.core
from qgis.PyQt import uic
from qgis.PyQt import (
QtCore,
QtWidgets
)
import geocoder
sys.path.append(os.path.dirname(__file__))
FORM_CLASS, _ = uic.loadUiType(
os.path.join(
os.path.dirname(__file__),
"example_dialog_base.ui"
),
resource_suffix=""
)
class ExampleDialog(QtWidgets.QDialog, FORM_CLASS):
POSTAL_CODES_PATH = ":/plugins/example/todos_cp.txt"
def __init__(self, parent=None):
"""Constructor."""
super(ExampleDialog, self).__init__(parent)
self.setupUi(self)
# connect signals
self.postal_code_le.textChanged.connect(self.toggle_find_button)
self.find_code_btn.clicked.connect(self.execute)
# set initial state
self.find_code_btn.setEnabled(False)
def toggle_find_button(self):
if self.postal_code_le.text() == "":
self.find_code_btn.setEnabled(False)
else:
self.find_code_btn.setEnabled(True)
def execute(self):
self.address_te.clear()
try:
raw_postal_code = self.postal_code_le.text()
main_code, extension = validate_postal_code(raw_postal_code)
record = self.find_record(main_code, extension)
place_name = record[3]
self.address_te.setPlainText(place_name)
if self.create_layer_chb.isChecked():
self.handle_layer_creation(record)
except (ValueError, RuntimeError) as err:
self.show_error(str(err))
def find_record(self, main_code, extension):
file_handler = QtCore.QFile(self.POSTAL_CODES_PATH)
file_handler.open(QtCore.QIODevice.ReadOnly)
stream = QtCore.QTextStream(file_handler)
while not stream.atEnd():
line = stream.readLine()
info = line.split(";")
code1 = info[-3]
code2 = info[-2]
if code1 == main_code and code2 == extension:
result = info
break
else:
raise RuntimeError("Sem resultados")
return result
def handle_layer_creation(self, record):
place_name = record[3]
point = geocode_place_name(place_name)
print("lon: {} - lat: {}".format(point.x(), point.y()))
layer = create_point_layer(
point,
f"found_location_for_{record[-3]}_{record[-2]}",
place_name
)
current_project = qgis.core.QgsProject.instance()
current_project.addMapLayer(layer)
def show_error(self, message):
message_bar = self.iface.messageBar()
message_bar.pushMessage("Error", message, level=message_bar.Critical)
def validate_postal_code(raw_postal_code):
code1, code2 = raw_postal_code.partition("-")[::2]
if code1 == "" or code2 == "":
raise ValueError(
"Incorrect postal code: {!r}".format(raw_postal_code))
return code1, code2
def geocode_place_name(place_name):
geocoder_object = geocoder.osm(place_name)
lon = geocoder_object.json.get("lng")
lat = geocoder_object.json.get("lat")
if lat is None or lon is None:
raise RuntimeError(
"Could not retrieve lon/lat for "
"place: {!r}".format(place_name)
)
point = qgis.core.QgsPointXY(lon, lat)
return point
def create_point_layer(point, layer_name, place_name):
layer = qgis.core.QgsVectorLayer(
"Point?crs=epsg:4326&field=address:string(100)",
layer_name,
"memory"
)
provider = layer.dataProvider()
geometry = qgis.core.QgsGeometry.fromPointXY(point)
feature = qgis.core.QgsFeature()
feature.setGeometry(geometry)
feature.setAttributes([place_name])
provider.addFeatures([feature])
layer.updateExtents()
return layer

Apscheduler runs once then throws TypeError

I'm trying to add a list of someone's soundcloud followers to a database every hour. I have the code working to pull their list of followers and add them to a db, but I run into errors when I use it with apscheduler.
Here's an example of the error:
Traceback (most recent call last):
File "desktop/SoundcloudProject/artistdailyfollowers.py", line 59, in <module>
scheduler.add_job(inserttodaysdata(), 'interval', hours=1)
File "//anaconda/lib/python3.5/site-packages/apscheduler/schedulers/base.py", line 425, in add_job
job = Job(self, **job_kwargs)
File "//anaconda/lib/python3.5/site-packages/apscheduler/job.py", line 44, in __init__
self._modify(id=id or uuid4().hex, **kwargs)
File "//anaconda/lib/python3.5/site-packages/apscheduler/job.py", line 165, in _modify
raise TypeError('func must be a callable or a textual reference to one')
TypeError: func must be a callable or a textual reference to one
Here's the code:
import soundcloud
import sqlite3
import datetime
import time
from apscheduler.schedulers.blocking import BlockingScheduler
client = soundcloud.Client(client_id='f3b669e6e4509690939aed943c56dc99')
conn = sqlite3.connect('desktop/SoundcloudProject/RageLogic.db')
c = conn.cursor()
writenow = datetime.datetime.now()
print("If this is printing that means it's running")
print("The time is now: \n" +str(writenow))
page ='https://soundcloud.com/ragelogic'
page_size = 200
def create_table():
c.execute('CREATE TABLE IF NOT EXISTS RageLogicFollowersByDay(day TEXT, list_of_followers TEXT)')
#add todays date and a list of followers to the db
def inserttodaysdata():
global page
full = False
user = client.get('/resolve', url=page)
ufollowing = []
ufirstfollowing = client.get('/users/'+str(user.id)+'/followers', order='id',limit=page_size, linked_partitioning=1)
for user in ufirstfollowing.collection:
ufollowing.append(user.id)
if hasattr(ufirstfollowing, "next_href"):
#print("MANYFOLLOWING")
newlink = ufirstfollowing.next_href
try:
while full == False:
newlist = client.get(newlink)
for user in newlist.collection:
ufollowing.append(user.id)
print(len(ufollowing))
if newlist.next_href == None:
print("full")
full = True
else:
newlink = newlist.next_href
except AttributeError:
None
#print(len(ufollowing))
wtl = []
wtl = repr(ufollowing)
writenow = datetime.datetime.now()
c.execute("INSERT INTO RageLogicFollowersByDay (day, list_of_followers) VALUES (?, ?)",(str(writenow), wtl))
conn.commit()
#create_table()
scheduler = BlockingScheduler()
scheduler.add_job(inserttodaysdata(), 'interval', hours=1)
scheduler.start()
I'm really new to this whole thing and any help anyone could give would be awesome, thanks!
See this line:
scheduler.add_job(inserttodaysdata(), 'interval', hours=1)
and it should be
scheduler.add_job(inserttodaysdata, 'interval', hours=1)
You're calling inserttodaysdata() and passing its return value to add_job(). Don't do that. Pass the function itself, not its call result.
I solved it with just adding time-zone:
scheduler = BackgroundScheduler(timezone="Europe/Berlin")

How to call a function on Tkinter class?

I'm trying to use a function which a placed inside the class of Tkinder but the function(aktodec) can't be found and a get an error. I don't wanna call the def as a command of a button but as a function that will give value to one of my variables
from Tkinter import *
class ADialog:
def __init__(self, parent):
top = self.top = Toplevel(parent)
Label(top, text="Number to convert").pack()
self.numb = Entry(top)
self.numb.pack(padx=15)
Label(top, text="Base of incered number").pack()
self.base = Entry(top)
self.base.pack(padx=15)
Label(top, text="Base you want to be converted").pack()
self.basemet=Entry(top)
self.basemet.pack(padx=15)
b = Button(top, text="OK", command=self.met)
b.pack(pady=5)
def aktodec(self,num,base): #####commands
dec=0
num=num[::-1]
num1=[]
for i in range(len(num)):
num1.append(num[i])
if base>9:
for i in range(len(num1)):
if str(num1[i])=='A':
num1.remove(num1[i])
num1.insert(i,'10')
if str(num1[i])=='B':
num1.remove(num1[i])
num1.insert(i,'11')
if str(num1[i])=='C':
num1.remove(num1[i])
num1.insert(i,'12')
if str(num1[i])=='D':
num1.remove(num1[i])
num1.insert(i,'13')
if str(num1[i])=='E':
num1.remove(num1[i])
num1.insert(i,'14')
if str(num1[i])=='F':
num1.remove(num1[i])
num1.insert(i,'15')
for i in range(len(num)):
s=int(num1[i])*(int(base)**i)
dec=dec+s
else:
for i in range(len(num1)):
s=int(num1[i])*(int(base)**i)
dec=dec+s
return dec
def met(self):
num=self.numb=str(self.numb.get())
base=self.base =int(self.base.get())
basemet=self.basemet=int(self.basemet.get())
if base==basemet:
Label(root,text="The number "+self.numb+"is converted to"+self.numb) ##why can't be print??
if base==10:
new=num
else:
new1=self.aktodec(num,base) ####why aktodec doesn't give value to "new"??
Label(root,text="Number is"+str(new))
self.top.destroy()
root = Tk()
def open_dialog():
dial = ADialog(root)
root.wait_window(dial.top)
root.wm_geometry("400x300+20+40")
message=StringVar()
message.set("Complete the form")
Label(root, textvariable=message).pack(padx=30)
root.update()
message.set("Form completed")
Button(root, text="Done", command=root.destroy).pack()
Button(root, text="new", command=open_dialog).pack()
root.update()
root.mainloop()
And also I have a problem whith the label
Label(root,text="The number "+self.numb+"is converted to"+self.numb
which (i don't know why) won't appear to the root even the base=basemet.
Help please!

refresh ListCtrl after drag and drop files

I'm relatively new to wxpython - really appreciate it any help you can offer me. Basically, I'm having trouble closing the loop between
1) filling a list called ListOfFiles in my OnDropFiles method below and
2) refreshing the FileList so that it displays the items in ListOfFiles.
I know that if you call
FileWindow(None, -1, 'List of Files and Actions')
right at the end of OnDropFiles, it inits a new frame and draws from ListOfFiles when populating the FileList listctrl... but I was hoping there would be a way to update in the same window. I've tried noodling around with Layout() and calling various methods on my FileWindowObject... but there's been no success.
Thanks so much for your help. I think the answer you give me might lead to a real breakthrough in my understanding of wxpython.
#!/usr/bin/env python
import wx
import sys
import traceback
import time
APP_EXIT = 1
ListOfFiles = []
class FileDrop(wx.FileDropTarget): #This is the file drop target
def __init__(self, window):
wx.FileDropTarget.__init__(self) #File Drop targets are subsets of windows
self.window = window
def OnDropFiles(self, x, y, filenames): #FileDropTarget now fills in the ListOfFiles
for DragAndDropFile in filenames:
ListOfFiles.append(DragAndDropFile) #We simply append to the bottom of our list of files.
class FileWindow(wx.Frame):
def __init__(self, parent, id, title): #This will initiate with an id and a title
wx.Frame.__init__(self, parent, id, title, size=(300, 300))
hbox = wx.BoxSizer(wx.HORIZONTAL) #These are layout items
panel = wx.Panel(self, -1) #These are layout items
self.FileList = wx.ListCtrl(panel, -1, style=wx.LC_REPORT) #This builds the list control box
DropTarget = FileDrop(self.FileList) #Establish the listctrl as a drop target
self.FileList.SetDropTarget(DropTarget) #Make drop target.
self.FileList.InsertColumn(0,'Filename',width=140) #Here we build the columns
for i in ListOfFiles: #Fill up listctrl starting with list of working files
InsertedItem = self.FileList.InsertStringItem(sys.maxint, i) #Here we insert an item at the bottom of the list
hbox.Add(self.FileList, 1, wx.EXPAND)
panel.SetSizer(hbox)
self.Show(True)
def main():
ex = wx.App(redirect = True, filename = time.strftime("%Y%m%d%H%M%S.txt"))
FileWindowObject = FileWindow(None, -1, 'List of Files and Actions')
ex.MainLoop()
if __name__ == '__main__':
main() #Execute function#!/usr/bin/env python
The problem is that all you're doing is adding items to a list, not to the ListCtrl itself. You need to subclass wx.ListCtrl and add an update method of some sort. Then you would call that update method instead of appending to a list you don't use anywhere. Here's one way to do it:
import wx
import time
########################################################################
class MyListCtrl(wx.ListCtrl):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT)
self.index = 0
#----------------------------------------------------------------------
def dropUpdate(self, path):
""""""
self.InsertStringItem(self.index, path)
self.index += 1
class FileDrop(wx.FileDropTarget): #This is the file drop target
def __init__(self, window):
wx.FileDropTarget.__init__(self) #File Drop targets are subsets of windows
self.window = window
def OnDropFiles(self, x, y, filenames): #FileDropTarget now fills in the ListOfFiles
for DragAndDropFile in filenames:
self.window.dropUpdate(DragAndDropFile) # update list control
class FileWindow(wx.Frame):
def __init__(self, parent, id, title): #This will initiate with an id and a title
wx.Frame.__init__(self, parent, id, title, size=(300, 300))
hbox = wx.BoxSizer(wx.HORIZONTAL) #These are layout items
panel = wx.Panel(self, -1) #These are layout items
self.FileList = MyListCtrl(panel) #This builds the list control box
DropTarget = FileDrop(self.FileList) #Establish the listctrl as a drop target
self.FileList.SetDropTarget(DropTarget) #Make drop target.
self.FileList.InsertColumn(0,'Filename',width=140) #Here we build the columns
hbox.Add(self.FileList, 1, wx.EXPAND)
panel.SetSizer(hbox)
self.Show(True)
def main():
ex = wx.App(redirect = True, filename = time.strftime("%Y%m%d%H%M%S.txt"))
FileWindowObject = FileWindow(None, -1, 'List of Files and Actions')
ex.MainLoop()
if __name__ == '__main__':
main()

Python/Gtk3 : How to add a Gtk.Entry to a Gtk.MessageDialog?

Good morning,
I'm trying to add a Gtk.Entry to a Gtk.MessageDialog. With the following code it seems that I added the Gtk.Entry but it's not visible on the dialog window (Python3/Gtk3):
#!/usr/bin/python3
from gi.repository import Gtk
def get_user_pw(parent, message, default=''):
dialogWindow = Gtk.MessageDialog(parent,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION,
Gtk.ButtonsType.OK_CANCEL,
message)
dialogBox = dialogWindow.get_content_area()
userEntry = Gtk.Entry()
userEntry.set_visibility(False)
userEntry.set_invisible_char("*")
userEntry.set_size_request(250,0)
userEntry.set_text("Test")
dialogBox.pack_end(userEntry, False, False, 0)
#dialogWindow.vbox.pack_start(userEntry, False, False, 0)
response = dialogWindow.run()
text = userEntry.get_text()
dialogWindow.destroy()
if response == Gtk.ResponseType.OK:
return text
else:
return None
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="MyWindowTitle")
userPassphrase = get_user_pw(self, "SSH key passphrase")
print("User passphrase: " + userPassphrase)
This code prints :
User passphrase: Test
I'm looking for clues about making the entry visible and editable, any help is welcome.
References:
http://python-gtk-3-tutorial.readthedocs.org/en/latest/dialogs.html
http://developer.gnome.org/gtk3/3.2/GtkDialog.html
Simple, versatile and re-usable entry dialog (sometimes referred to as input dialog) in PyGTK
Ok it works now, I needed to show_all() before run(). It took me some times to figure out this simple thing. Debugged code is :
def get_user_pw(parent, message, title=''):
# Returns user input as a string or None
# If user does not input text it returns None, NOT AN EMPTY STRING.
dialogWindow = Gtk.MessageDialog(parent,
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION,
Gtk.ButtonsType.OK_CANCEL,
message)
dialogWindow.set_title(title)
dialogBox = dialogWindow.get_content_area()
userEntry = Gtk.Entry()
userEntry.set_visibility(False)
userEntry.set_invisible_char("*")
userEntry.set_size_request(250,0)
dialogBox.pack_end(userEntry, False, False, 0)
dialogWindow.show_all()
response = dialogWindow.run()
text = userEntry.get_text()
dialogWindow.destroy()
if (response == Gtk.ResponseType.OK) and (text != ''):
return text
else:
return None
I use it like this :
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="MyWindowTitle")
userPassword = get_user_pw(self, "Please enter your password", "Password")
This may be going about it the hard way if this is just to run a sudo command - you could simply call
os.system('pkexec (yourcommand)')