refresh ListCtrl after drag and drop files - drag-and-drop

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()

Related

How to send signals/variables between a QDialog and Main Window

I am currently working on a project that involves graphing text file data into a pyqt graph and I have been running into problems with a subclass QDialog box. My goal is to have the QDialog box use a combox to choose between different data sets to graph (The code below shows the "steering angle" setting being chosen). The problem lies with how to make it so that when the Create Graph button is pressed (Found in the QDialog Class), it runs the createGraph(self): function in the main class. I dont know how to work classes that well so I dont know how to make this work.
If anyone has any pointers on either how to get this working, how to properly structure a PYQT Program or how to make it more efficient, I'm all ears.
Thank you for your time!
Main Window Code:
class MainWidget(QMainWindow):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.activateWindow()
self.raise_()
self.setupGraph()
self.dockcheck = 0
self.graphcheck = 0
self.setWindowTitle("Drag and Drop Test")
self.resize(1200, 800)
self.setAcceptDrops(True)
self.LBLDragAndDrop = QLabel("Drag And Drop Files Here")
self.LBLDragAndDrop.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
if self.graphcheck == 0:
self.setCentralWidget(self.LBLDragAndDrop)
self.path3 = "C:\\Users\\steph\\OneDrive\\Documents\\SAA Wing\\Coding\\Can Bus Data Reading\\Temporary Saves"
self.treeview = QTreeView()
self.treeview.setAnimated(True)
self.fileModel = QFileSystemModel()
self.fileModel.setRootPath(self.path3)
self.indexRoot = self.fileModel.index(self.fileModel.rootPath())
self.treeview.setModel(self.fileModel)
self.treeview.setRootIndex(self.fileModel.index(self.path3))
self.treeview.setColumnWidth(0, 250)
self.treeview.doubleClicked.connect(self.onSelectionChanged)
#self.treeview.doubleClicked.connect(self.openDialog)
####################################################################################################################
# Dialog Box
####################################################################################################################
def onSelectionChanged(self, index):
self.selectionPath = self.sender().model().filePath(index)
self.selectionFilename = (self.selectionPath.split("/")[-1])
IDList = ("ID 00d0","ID 00d1","ID 00d3","ID 00d4","ID 0140","ID 0141","ID 0360","ID 0361")
if self.selectionFilename in IDList:
if self.selectionFilename == "ID 00d0":
editDialog = Dialog00d0()
editDialog.exec_()
####################################################################################################################
# Graphing data
####################################################################################################################
def createGraph(self):
self.graphcheck = 1
if self.graphcheck == 1:
self.setCentralWidget(self.scroll_area)
################################################################################################################
# ID 00D0 Creating Graph
################################################################################################################
if self.selectionFilename == "ID 00d0":
self.df00d0 = pd.read_table(self.selectionPath, header=None , delim_whitespace=True, dtype=object)
self.df00d0.columns = ['Timestamp','ID',"B0","B1","B2","B3","B4","B5","B6","B7"]
self.df00d0.dropna(inplace=True)
self.SA = np.array([], dtype=float)
self.LatAcc = np.array([], dtype=float)
self.LonAcc = np.array([], dtype=float)
self.ComAcc = np.array([], dtype=float)
self.Time00d0 = np.array([], dtype=float)
self.Timestamp00d0 = np.array([], dtype=float)
############################################################################################################
# Getting Time Stamps
############################################################################################################
for item in self.df00d0['Timestamp']:
self.Time00d0 = np.append(self.Time00d0, datetime.fromtimestamp(float(item)).strftime("%H:%M:%S.%f")[:-4])
self.Timestamp00d0 = np.append(self.Timestamp00d0, float(item))
############################################################################################################
# Steering Angle Graph
############################################################################################################
if self.combobox00d0.currentText() == "Steering Angle":
SA_ = (((self.df00d0['B1']) + (self.df00d0['B0'])).apply(int, base=16) * 0.1)
for item in SA_:
if item > 6000:
self.SA = np.append(self.SA, round((item - 6553.6), 1))
else:
self.SA = np.append(self.SA, round(item))
y_value = self.SA
Here is the QDialog Box class code:
class Dialog00d0(QDialog):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
hlay = QHBoxLayout()
self.setLayout(self.layout)
self.setWindowTitle("Create Graph")
label = QLabel("Data Type")
self.combobox00d0 = QComboBox()
self.combobox00d0.addItem("Steering Angle")
self.combobox00d0.addItem("Latitudinal Acceleration")
self.combobox00d0.addItem("Longitudinal Acceleration")
self.combobox00d0.addItem("Combined Acceleration")
self.BTNCreateGraph = QPushButton("Create Graph")
self.BTNCancel = QPushButton("Cancel")
hlay.addWidget(self.BTNCreateGraph)
hlay.addWidget(self.BTNCancel)
self.layout.addWidget(label)
self.layout.addWidget(self.combobox00d0)
self.layout.addLayout(hlay)
self.BTNCreateGraph.clicked.connect("I need the self.creatGraph here")
self.BTNCancel.clicked.connect("self.close")
I imagine this will help you.
The pyqtSignal() argument tells you what information you want to carry.
In this case, I'm passing a text.
Good luck, I hope I helped.
import sys
from PyQt5.QtWidgets import QMainWindow, QDialog, QApplication
from PyQt5.QtWidgets import QPushButton, QVBoxLayout
from PyQt5 import QtCore, QtGui
class MainWidget(QMainWindow):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
button = QPushButton("Button to open dialog")
button.clicked.connect(self.button_clicked)
self.setCentralWidget(button)
self.show()
def button_clicked(self):
dlg = Dialog00d0()
dlg.signEmit.connect(self.createGraph)
dlg.exec()
def createGraph(self, _str):
print('Now Im here')
print(_str)
class Dialog00d0(QDialog):
signEmit = QtCore.pyqtSignal(str)
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.BTNCreateGraph = QPushButton("link to createGraph()")
self.layout.addWidget(self.BTNCreateGraph)
self.setLayout(self.layout)
self.BTNCreateGraph.clicked.connect(self.BTNCreateGraph_clicked)
def BTNCreateGraph_clicked(self):
self.signEmit.emit('But I passed by')
app = QApplication(sys.argv)
win = MainWidget()
app.exec()

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

Track progress of members of a chord

Is there a way to get the task_ids of all members of a chord? My goal is to track to the progress of each tasks of the chords. Here is my attempt so far:
import random
import time
from typing import List
import celery
from celery import Celery, chord
from celery.utils.log import get_task_logger
app = Celery(
"chaintasks",
backend="redis://localhost:6379",
broker="pyamqp://guest#localhost//",
)
app.conf.update(task_track_started=True, result_persistent=True)
#app.task(bind=True)
def process_item(self: celery.Task, item: str) -> str:
t = random.randint(0, 10)
for i in range(t):
chord_id = self.request.chord["options"]["task_id"]
self.update_state(
meta={"progress": i / t}, # How do I access this progress value?
)
self.update_state( # This way I write the group_id in the main task info,
task_id=chord_id, # but maybe I don't even need this?
meta={"group_id": self.request.group},
state="STARTED"
)
time.sleep(1)
return item + "_processed"
#app.task()
def group_items(items: List[str]) -> str:
return ":".join(items)
#app.task
def process_and_group(items: List[str]):
task = chord((process_item.s(i) for i in items), group_items.s())
res = task.delay()
return res.id
random.seed(42)
if __name__ == "__main__":
all_items = ["item1", "item2", "item3", "item4"]
res1 = process_and_group.delay(all_items)
while True:
subtask_id = res1.result
if subtask_id is None:
print("initial", res1.state)
else:
res2 = app.AsyncResult(subtask_id)
print("subtask state", res2.state)
info = res2.info
print("subtask info", res2.info)
if isinstance(res2.info, dict):
group_id = info.get("group_id")
if group_id is not None:
group_res = app.GroupResult(group_id)
# Ideally here, I would like to be able to track the progress all the chords tasks,
# something like an average of all progresses would be OK.
print("group results", group_res.results) # group_res.results is None, unfortunately
if res2.state == "SUCCESS":
break
time.sleep(0.5)
print("subtask result", res2.result)
How do I get something out of this self.request.group UUID? Is there a way to track the progress of chord subtasks? I'm open to any modification of this MWE to get it working.

python GTK entry: cursor to the left when the entry is empty

I am using a Gtk.Entry and writing from left to right (xalign==0.0).
When the entry is not empty, the cursor is to the right of the text (as expected):
But when the entry is empty, the cursor is on the right:
That is how I create the entry:
def CreateUserEntry(self):
self.user_entry = Gtk.Entry()
self.user_entry.set_width_chars(10)
user_label = Gtk.Label(label="User:")
self.user_entry.set_text(self.args.user)
self.user_entry.connect('activate', self.ActivateUserTextbox)
user_box = Gtk.HBox()
user_box.pack_start(user_label, expand=False, fill=False, padding=10)
user_box.pack_start(self.user_entry, expand=False, fill=False, padding=0)
return user_box
def ActivateUserTextbox(self, widget):
new_user = widget.get_text().strip()
self.args.user = new_user
How do I fix this?
thanks

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!