AttributeError: type object 'QSizePolicy' has no attribute 'Expanding' in pyqt6 - pyqt6

button = QPushButton(value)
button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
layoutGrid.addWidget(button, *positions) # widget, position --> row index, column index

Related

Web Scraping: How do I return specific user input forms in python?

I'm having trouble with the forms returning an exact match for the user input.
Emphasoft developer challenge:
Taking a list of tax form names (ex: "Form W-2", "Form 1095-C"),
search the website and return some informational results.
Specifically, you must return the "Product Number", the "Title", and
the maximum and minimum years the form is available for download.
Taking a tax form name (ex: "Form W-2") and a range of years
(inclusive, 2018-2020 should fetch three years), download all PDFs
available within that range.
import json import os import sys import requests from bs4 import BeautifulSoup
URL = 'https://apps.irs.gov/app/picklist/list/priorFormPublication.html?resultsPerPage=200&sortColumn=sortOrder&indexOfFirstRow=0&{param.strip}&isDescending=false'
def get_forms(list_tax_form: list):
"""
function to get response from iris.gov with all forms content
:param list_tax_form: list of form names that we want to get info about
:return: dict with form name,form title
"""
response_list = [] # list for all responses of form names
with requests.session() as session:
for param in list_tax_form:
request_params = {'value': param,
'criteria': 'formNumber',
'submitSearch': 'Find',
}
res = session.get(URL, params=request_params).content
response_list.append(res)
return response_list
def parse_responses(list_tax_form: list):
"""
function to get all form names, titles years from previous func return
:param list_tax_form: list of form names that we want to get info about
:return: list of form names, titles, years
"""
responses = get_forms(list_tax_form)
# empty lists to fill them with the received information for all names, years, and titles
td_form_name, td_form_title, td_form_rev_year = [], [], []
for response in responses:
soup = BeautifulSoup(response, 'lxml')
td_name = soup.find_all('td', {'class': 'LeftCellSpacer'})
td_title = soup.find_all('td', {'class': 'MiddleCellSpacer'})
td_rev_year = soup.find_all('td', {'class': 'EndCellSpacer'})
td_form_name.extend(td_name)
td_form_title.extend(td_title)
td_form_rev_year.extend(td_rev_year)
return td_form_name, td_form_title, td_form_rev_year
def format_responses(list_tax_form: list):
"""
function to formate all responses for all forms we got!
1 Task
:param list_tax_form: list of form names that we want to get info about
:return: formated names,links,years
"""
td_names, td_titles, td_years = parse_responses(list_tax_form)
names = [name.text.strip() for name in td_names]
links = [link.find('a')['href'] for link in td_names]
titles = [title.text.strip() for title in td_titles]
years = [int(year.text.strip()) for year in td_years]
set_names = set(names)
final_dict = []
# loop to create dictionary of result information with years of tax form available to download
for name in set_names:
max_year = 0
min_year = max(years)
dict1 = {'form_number': name}
for index, p_name in enumerate(names):
if p_name == name:
if years[index] > max_year:
max_year = years[index]
elif years[index] < min_year:
min_year = years[index]
dict1['form_title'] = titles[index]
dict1['max_year'] = max_year
dict1['min_year'] = min_year
final_dict.append(dict1)
print(json.dumps(final_dict, indent=2))
return names, links, years
def download_files(list_tax_form):
"""
2 Task
Module to download pdf files of form_name that input from user.
:param list_tax_form: list of form names that we want to get info about
:return: message to user of successful create file or either
"""
names, links, years = format_responses(list_tax_form)
form_name = input('enter form name: ')
if form_name in names:
print('form exists. enter years range')
form_year1 = int(input('start year to analysis: '))
form_year2 = int(input('end year to analysis: '))
try:
os.mkdir(form_name)
except FileExistsError:
pass
# indecies to define names range in list of all tax form names
r_index = names.index(form_name) # index of first form_name mention on list
l_index = names.index(form_name) # index of last form_name mention on list
for name in names:
if name == form_name:
r_index += 1
years = years[l_index:r_index]
if form_year1 < form_year2:
range_years = range(form_year1, form_year2 + 1)
for year in range_years:
if year in years:
link = links[years.index(year)]
form_file = requests.get(link, allow_redirects=True)
open(f'{form_name}/{form_name}_{str(year)}.pdf', 'wb').write(form_file.content)
print(f'files saved to {form_name}/ directory!')
else:
print('input correct form name!')
if __name__ == '__main__':
tax_list = sys.argv[1:] # form names
download_files(tax_list)
(ex: "Form W-2" should not return "Form W-2 P")
When this file is ran, it is displaying other unrelated results.
How can I resolve this issue to display only specified user requests?

tkinter callback through an error: "Index 0 out of range"

I searched for hours what the heck is the reason for this error message:
I have a search entry, which update a listbox depending on my search with a callback function:
Listbox:
self.name_search=tk.StringVar()
self.name_search.trace_add('write', self.my_callback)
self.e_name_search_text = tk.Label(search_f, text="Name: ").grid(row=0, column=0, padx=10, pady=5, sticky='E')
self.e_name_search = ttk.Entry(search_f, width = 35, textvariable=self.name_search)
self.e_name_search.grid(row=0, column=1, padx=5, pady=5, sticky='W')
self.lbox = tk.Listbox(search_f, width=35, height=8)
self.lbox.bind("<Double-Button-1>", self.show_name_search)
self.lbox.bind('<Return>', self.show_name_search)
self.scrollbar = tk.Scrollbar(search_f)
self.lbox.grid(row=1, column=1, rowspan=3, padx=10, pady=1)
self.lbox.config(yscrollcommand = self.scrollbar.set)
self.scrollbar.grid(row=1, column=2, rowspan=3, padx=1, pady=1, sticky='ns')
self.scrollbar.config(command=self.lbox.yview)
So If I type my search, the listbox show me a reduced list of values out of my sqlite database, I am interessed in. If I select one with dobble click. Another sqlite query update my comboboxes.
If I select one I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "D:\... name.py", line 337, in show_name_search
self.e_fax.current(0)
File "C:\Python38-32\lib\tkinter\ttk.py", line 717, in current
return self.tk.call(self._w, "current", newindex)
_tkinter.TclError: Index 0 out of range
Line 337 comes from another function:
def show_name_search(self, event):
self.clear_field()
widget = event.widget
selection = widget.curselection()
indName = widget.get(selection[0])
print(indName)
print("selktierter Wert: {}".format(indName))
self.realName.set(indName)
connection = sqlite3.connect(select_connect_db)
print('Database connected.')
with connection:
cursor = connection.cursor()
cursor.execute("SELECT number, type, prio, id, uniqueid FROM numbers WHERE realName=?;",(indName,))
data = cursor.fetchall()
print(data)
for row in data:
if row[1] == 'home':
self.phone_home.append(row[0])
print('HOME:',self.phone_home)
if row[1] == 'mobile':
self.mobile.append(row[0])
print('Mobile:',self.mobile)
if row[1] == 'work':
self.business.append(row[0])
print(row[0])
print('WORK:',self.business)
if row[1] == 'fax_work':
self.fax.append(row[0])
print(row[0])
print('FAX_WORK:',self.fax)
self.uid_name.set(row[4])
if len(self.phone_home) != 0:
self.e_phone['values'] = self.phone_home
self.e_phone.current(0)
if len(self.mobile) != 0:
self.e_mobile['values'] = self.mobile
self.e_mobile.current(0)
if len(self.business) != 0:
self.e_business['values'] = self.business # Set the value to the new list
self.e_business.current(0) # Set the first item of the list as current item
if len(self.business) != 0:
self.e_fax['values'] = self.fax
self.e_fax.current(0) ### Line 337 - No entry for this value in my sqlite database
Any idea, what I can search for ?
So self.e_faxseems like a ttk.Combobox to me. Consider this code here:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
values = []
lb = ttk.Combobox(root,values=values)
lb.current(0)
lb.pack()
root.mainloop()
it throughs the same Error:
_tkinter.TclError: Index 0 out of range
and the reason is the list values is empty, insert any regular string in it and it works.
Make sure if you want to set default value that there is an value.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
values = ['see']
lb = ttk.Combobox(root,values=values)
lb.current(0)
lb.pack()
root.mainloop()

How to validate a field depending on the value of another field in Enaml?

Let's say I have a form with 2 IntFields. How can I validate IntField B depending on the input in IntField A?
e.g. if A == 1, B can only be within 0-30; if A == 2, B can only be within 0-50; else B can be any other numbers
From all I can google, I can only find IntValidator which validates the field only without the ability to link to another field. No examples that I can find that shows how I can update the min/max values in the IntValidator, nor any custom Validator which can take the value of another field such that validation can change according to change of value in another field...
enamldef IntFieldsWindow( Window ):
Container:
Form:
padding=0
Label:
text = 'Field A'
IntField: fld_a:
value = 0
Label:
text = 'Field B'
IntField: fld_b:
value = 0
After some tests, it is actually possible to pass fld_a to a custom validator, then get the fld_a.value in the validate function, finally set the custom validator to fld_b. Not sure if it is the way to do such validation though.
One way is to break the data out into a model and have it validate the members using an observer that is called when either member changes.
Then make the IntField bind to the model value (using <<) and update the model value using a notification handler (::) that catches and reports validation errors.
For example:
from atom.api import Atom, Int, observe
from enaml.stdlib.fields import IntField
from enaml.widgets.api import Window, Container, Label, Form
class Model(Atom):
a = Int()
b = Int()
#observe('a', 'b')
def _validate(self, change):
# When a or b is changed validate the model state
a, b = self.a, self.b
if a == 1:
if b < 0 or b > 30:
raise ValueError("B is out of range")
elif a == 2:
if b < 0 or b > 50:
raise ValueError("B is out of range")
enamldef Main(Window):
attr model = Model()
Container:
Form:
padding=0
Label:
text = 'Field A'
IntField: fld_a:
value << model.a
value ::
error.text = ''
try:
model.a = change['value']
except ValueError as e:
error.text = str(e)
Label:
text = 'Field B'
IntField: fld_b:
value << model.b
value ::
error.text = ''
try:
model.b = change['value']
except ValueError as e:
error.text = str(e)
Label:
text << 'A:{} B:{}'.format(model.a, model.b)
Label: error:
pass
Notice the that the label outputting the model values never has an invalid state! The validator on the model will ensure this never happens.

IndexError: list index out of range, scores.append( (fields[0], fields[1]))

I'm trying to read a file and put contents in a list. I have done this mnay times before and it has worked but this time it throws back the error "list index out of range".
the code is:
with open("File.txt") as f:
scores = []
for line in f:
fields = line.split()
scores.append( (fields[0], fields[1]))
print(scores)
The text file is in the format;
Alpha:[0, 1]
Bravo:[0, 0]
Charlie:[60, 8, 901]
Foxtrot:[0]
I cant see why it is giving me this problem. Is it because I have more than one value for each item? Or is it the fact that I have a colon in my text file?
How can I get around this problem?
Thanks
If I understand you well this code will print you desired result:
import re
with open("File.txt") as f:
# Let's make dictionary for scores {name:scores}.
scores = {}
# Define regular expressin to parse team name and team scores from line.
patternScore = '\[([^\]]+)\]'
patternName = '(.*):'
for line in f:
# Find value for team name and its scores.
fields = re.search(patternScore, line).groups()[0].split(', ')
name = re.search(patternName, line).groups()[0]
# Update dictionary with new value.
scores[name] = fields
# Print output first goes first element of keyValue in dict then goes keyName
for key in scores:
print (scores[key][0] + ':' + key)
You will recieve following output:
60:Charlie
0:Alpha
0:Bravo
0:Foxtrot

How to show a QDialog

I need to show a find dialog after pressing Ctrl+F in QWidget, which contains a QTableView. The find dialog will search in the first column of the table to find the matches.
I can show the QMessageBox after pressing Ctrl+F with the following code:
class Widget(QWidget):
def __init__(self,md,parent=None):
QWidget.__init__(self,parent)
layout=QVBoxLayout(self)
# initially construct the visible table
tv = QTableView()
# uncomment this if the last column shall cover the rest
tv.horizontalHeader().setStretchLastSection(True)
tv.show()
# set black grid lines
self.setStyleSheet("gridline-color: rgb(39, 42, 49)")
# construct the Qt model belonging to the visible table
model = NvmQtModel(md)
tv.setModel(model)
tv.resizeRowsToContents()
tv.resizeColumnsToContents()
# set the shortcut ctrl+F for find in menu
shortcut = QShortcut(QKeySequence('Ctrl+f'), self)
shortcut.activated.connect(self.handleFind)
# delegate for decimal
delegate = NvmDelegate()
tv.setItemDelegate(delegate)
self.setGeometry(200,200,600,600) # adjust this later
layout.addWidget(tv)
# set window title
self.setWindowTitle("TITLE")
# find function: search in the first column of the table
def handleFind(self):
reply = QMessageBox.question(
self, 'Find', 'Find Dialog',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
print('Yes')
else:
print('No')
Then I changed the QMessageBox to a QDialog, but now it does not work. I would appreciate if you could tell me where I am not doing it correctly:
class Widget(QWidget):
def __init__(self,md,parent=None):
QWidget.__init__(self,parent)
layout=QVBoxLayout(self)
# initially construct the visible table
tv = QTableView()
# uncomment this if the last column shall cover the rest
tv.horizontalHeader().setStretchLastSection(True)
tv.show()
# set black grid lines
self.setStyleSheet("gridline-color: rgb(39, 42, 49)")
# construct the Qt model belonging to the visible table
model = NvmQtModel(md)
tv.setModel(model)
tv.resizeRowsToContents()
tv.resizeColumnsToContents()
# set the shortcut ctrl+F for find in menu
shortcut = QShortcut(QKeySequence('Ctrl+f'), self)
shortcut.activated.connect(self.handleFind)
# delegate for decimal
delegate = NvmDelegate()
tv.setItemDelegate(delegate)
self.setGeometry(200,200,600,600) # adjust this later
layout.addWidget(tv)
# set window title
self.setWindowTitle("TITLE")
# find function: search in the first column of the table
def handleFind(self):
findDialog = QDialog()
findLabel = QLabel("Find what", findDialog)
findField = QLineEdit(findDialog)
findButton = QPushButton("Find", findDialog)
closeButton = QPushButton("Close", findDialog)
findDialog.show()
If you want the Dialog be a modal Dialog, call findDialog.exec_():
from PyQt4.QtGui import *
def handleFind():
findDialog = QDialog()
#findDialog.setModal(True)
findLabel = QLabel("Find what", findDialog)
findField = QLineEdit(findDialog)
findButton = QPushButton("Find", findDialog)
closeButton = QPushButton("Close", findDialog)
#findDialog.show()
findDialog.exec_()
app = QApplication([])
b = QPushButton("click me")
b.clicked.connect(handleFind)
b.show()
app.exec_()

Resources