Refer to views, models and widgets using variables in PyQt - pyqt

I am using Pyqt5 and I have a tab widget which has 4 tabs which contain exactly the same layout of widgets - a table view and a few buttons. The difference is that each tab displays a slightly different view of the data based on one database field which is an integer of 1 to 4. Below is a snippet of code which sets up the model and view:
def setup_folio1(self):
self.folio1_model = QSqlQueryModel(self)
self.folio1_model.setQuery("SELECT trans_timestamp, person, charge_code, description, dr, cr, id from "
" transactions where folio = 1 and account = '{0}' ORDER BY trans_timestamp".format(myapp.res_id))
self.folio1_model.setHeaderData(0, Qt.Horizontal, "Time stamp")
self.folio1_model.setHeaderData(1, Qt.Horizontal, "User")
self.folio1_model.setHeaderData(2, Qt.Horizontal, "Charge code")
self.folio1_model.setHeaderData(3, Qt.Horizontal, "Description")
self.folio1_model.setHeaderData(4, Qt.Horizontal, "Debit")
self.folio1_model.setHeaderData(5, Qt.Horizontal, "Credit")
self.folio1_view = self.ui.folio1_1
self.folio1_view.setModel(self.folio1_model)
self.folio1_view.setColumnWidth(0, 120)
self.folio1_view.setColumnWidth(1, 80)
self.folio1_view.setColumnWidth(2, 80)
self.folio1_view.setColumnWidth(3, 395)
self.folio1_view.setColumnWidth(4, 50)
self.folio1_view.setColumnWidth(5, 50)
self.folio1_view.setColumnHidden(6, True)
self.folio1_view.setSelectionBehavior(QTableView.SelectRows)
self.folio1_view.setDragEnabled(True)
cr_total = dr_total = 0
for i in range(self.folio1_model.rowCount()):
dr_total = dr_total + self.folio1_model.index(i,4).data()
cr_total = cr_total + self.folio1_model.index(i,5).data()
self.ui.folio1_balance.setText(str(dr_total - cr_total))
def setup_folio2(self):
self.folio2_model = QSqlQueryModel(self)
As you can see, I am repeating much of the same code to set up what is essentially different by one digit - whether it is a parameter on the SQL query or more importantly the names of widgets.
As being new to PyQT I haven't been able to figure out how I can refer to these models, views and widgets variably. I would like to have a method like
def setup_folios(self, folio_num=0):
and either loop to call the method or loop inside the method. I would like to vary the number of tabs based on the data but that is phase II. I have found tabwidgets.count() but I would like to refer to the models, views and widgets in the code using an integer.

Related

Display first ten cols of Altair chart if no user selection?

My graph contains 90 rows of different companies. It's controlled by a dropdown menu that allows users to select an individual company. However, until a user selects a value from the dropdown, ALL the rows are displayed all my graph, which looks messy. Is there anyway to show only the first company by default? Or the first ten companies? Thanks.
Current Output.
Desired Output
I haven't found any solutions thus far. Here is my current code.
dropdown_list = df["Name"].sort_values().unique().tolist()
dropdown = alt.binding_select(options=[None] + dropdown_list, labels = ['All'] + dropdown_list, name = "Operator")
selection = alt.selection_single(fields=["Name"], bind=dropdown)
`(alt.Chart(df).mark_circle(opacity=1, size = 150).transform_window(id='rank()',groupby=['Variable']).encode(alt.X('Percentage:O', sort='ascending', axis=alt.Axis(ticks=False, grid=False)),
alt.Y('Name:N'),
color=alt.Color("Variable:N", scale=alt.Scale(range=cp.CALITP_CATEGORY_BRIGHT_COLORS), legend=None),
tooltip = ['Name', 'Variable'])
.properties(title = "Title Here").add_selection(selection).transform_filter(selection))`

Interactive labeling of images in jupyter notebook

I have a list of pictures:
pictures = {im1,im2,im3,im4,im5,im6}
Where
im1:
im2:
im3:
im4:
im5:
im6:
I want to assign the pictures to labels (1,2,3,4 etc.)
For instance, here pictures 1 to 3 belong to label 1, picture 4 belongs to label 2, picture 5 to label 3, and picture 6 to label 4.
-> label = {1,1,1,2,3,4}
Since I need to see the images when I label them, I need a method to do that while labeling them. I was thinking of creating an array of images:
And then I define the ranges by clicking on the first and last picture belonging to the same labels, so for example:
What do you think ? Is this somehow possible ?
I would like to assign different labels to different ranges of pictures.
For instance: When one has finished selecting the first label one could indicate it by a Double-click and then do the selection of the second label range, then Double-click, then do the selection of the third label range, then Double-click, then do the selection of the fourth label range, etc.
It does not have to be Double-clicking to change the selection of the labels, it could also just be a buttom or any other idea that you might have.
In the end one should have the list of labels.
Essentially, most of the interaction you are looking for boils down to being able to display images, and detect clicks on them in real time. As that is the case, you can use the jupyter widgets (aka ipywidgets) module to achieve most (if not all) of what you are looking for.
Take a look at the button widget which is described here with explanation on how to register to its click event. The problem - we can't display an image on a button, and I didn't find any way to do this within the ipywidgets documentation. There is an image widget, but it does not provide an on_click event. So construct a custom layout, with a button underneath each image:
COLS = 4
ROWS = 2
IMAGES = ...
IMG_WIDTH = 200
IMG_HEIGHT = 200
def on_click(index):
print('Image %d clicked' % index)
import ipywidgets as widgets
import functools
rows = []
for row in range(ROWS):
cols = []
for col in range(COLS):
index = row * COLS + col
image = widgets.Image(
value=IMAGES[index], width=IMG_WIDTH, height=IMG_HEIGHT
)
button = widgets.Button(description='Image %d' % index)
# Bind the click event to the on_click function, with our index as argument
button.on_click(functools.partial(on_click, index))
# Create a vertical layout box, image above the button
box = widgets.VBox([image, button])
cols.append(box)
# Create a horizontal layout box, grouping all the columns together
rows.append(widgets.HBox(cols))
# Create a vertical layout box, grouping all the rows together
result = widgets.VBox(rows)
You can technically also write a custom widget to display an image and listen for a click, but I simply don't believe it's worth your time and effort.
Good luck!
The qsl package provides widgets that do this. For your case, the following code would allow you to label images in batches. Full disclosure, qsl is a project I started because I, like you, wanted to label images from inside Jupyter notebooks.
import qsl
from IPython.display import display
labeler = qsl.MediaLabeler(
items=[
{"target": "https://i.stack.imgur.com/cML6z.jpg"},
{"target": "https://i.stack.imgur.com/6EVAP.jpg"},
{"target": "https://i.stack.imgur.com/CAxUw.jpg"},
{"target": "https://i.stack.imgur.com/8fhan.jpg"},
{"target": "https://i.stack.imgur.com/eMXn5.jpg"},
{"target": "https://i.stack.imgur.com/YFBfM.jpg"}
],
# Optional, you can also configure the labeler from
# the UI.
config={
"image": [
{
"name": "Type",
"options": [
{"name": "Foo"},
{"name": "Bar"}
]
}
]
},
# Optional, set to 1 if you want to label
# one image at a time.
batch_size=4,
# Optionally, save labels to JSON. You
# can also get the labels using `labeler.items`.
jsonpath="labels.json"
)
display(labeler)
This generates a UI that looks like this.
Here is a Google Colab notebook that shows how to do this in Google Colab.

Combobox values appearing as single string characters

I am new posting to the forums & utilizing python/tkinter in order to create an electronic form to gather issues from manufacturing floor.
I have a (2) comboboxes.
The 1st combobox gains its "values" from a given dictionary.keys() which updates the "values" of a 2nd combobox. However, when selecting the values for the 2nd combobox after selecting for the 1st combobox, the values only show singular characters from the dictionary.values().
Here is the list from the txt file. The first item on each line is the key, while the rest of the items on the same line are its values.
Bondshop, Plies: Layup Difficulty, Plies: Cutting Edge
IB Postbond, POU, Equipment, Inspection, Available Work Space, Big Paint, 1575, QN
General Postbond, Inspection
Seal Area, Inspection
Big Paint, Inspection
Deflash, Inspection
Why does my 2nd combobox appear as so? See below.
"...show singular characters"...
===snip===
def loadcategory(self):
# Reads production areas & cateogries from dictCategory.txt file
self.textfileCategory = open('dictCategory.txt', 'r')
self.lines_category = self.textfileCategory.readlines()
for line in self.lines_category:
splitLine = line.split(',')
dict_category[str(splitLine[0])] = ",".join(splitLine[1:])
self.textfileCategory.close()
===snip===
def UpdateData(self, event):
# Updates Category Combobox based on Production Area selection
self.menu_category.set('---')
category = self.menu_production_area.get()
self.menu_category['values'] = sorted(dict_category[category])
===snip===
self.menu_production_area = ttk.Combobox(Main_Frame, state='readonly', values = sorted(list(dict_category.keys())), width=25)
self.menu_production_area.bind('<<ComboboxSelected>>', self.UpdateData)
self.menu_production_area.grid(row=2, column=1, sticky='w')

How can I detect uiview is an activated viewport

I need to detect whether a Uiview is a standard opened view or if it is an activated viewport on a sheet. Querying the uiview’s view Id returns the Id of the activated viewport's view. I have found no direct way to detect that a uiview is actually a sheet with an activated viewport.
I am already tracking opened views in the view activated event for another purpose. So I considered storing the view Id with the uiview hashcode for later checking that it was indeed a sheetview prior to becoming an activated view. Unfortunately, and I think in opposition to standard use, the uiview hashcode is not stable. Multiple hashcode requests from the uiview object return different values.
Does anyone have a way to detect this condition? I need to be able to use the the methods on the uiview still. So any help to find the actual child windows I would like to relate to the uiview object. The view still says "Sheet: ..." in the title when a view is activated.
TaskDialog mainDialog = new TaskDialog("Hello, viewport check!");
mainDialog.MainInstruction = "Hello, viewport check!";
mainDialog.MainContent =
"Sadly Revit API doesn't automatically know if the user is in an active viewport. "
+ "Please click 'Yes' if your are, or 'No' if your not.";
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1,
"Yes, I am in an active viewport on a sheet.");
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2,
"No, I am just in an ordinary view.");
mainDialog.CommonButtons = TaskDialogCommonButtons.Close;
mainDialog.DefaultButton = TaskDialogResult.Close;
TaskDialogResult tResult = mainDialog.Show();
bool YesOrNo = true;
if (TaskDialogResult.CommandLink1 == tResult)
{
YesOrNo = true;
}
else if (TaskDialogResult.CommandLink2 == tResult)
{
YesOrNo = false;
}
else{
return;
}
You can use the ViewSheet GetAllViewports method to determine all the viewports on a given sheet. Using that, you could put together a bi-directional dictionary lookup system map any sheet to all the viewports it hosts and vice versa. That should help solve your task. Here is some example usage:
http://thebuildingcoder.typepad.com/blog/2014/04/determining-the-size-and-location-of-viewports-on-a-sheet.html
Im late to the party - but another way to sense if the user is in a viewport is to investigate the Process.MainWindow title. Something like this (in RevitPythonShell):
import threading, clr
from System.Diagnostics import Process
# need winform libraries for feedback form only
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Label
app = __revit__.Application
doc = __revit__.ActiveUIDocument.Document
ui = __revit__.ActiveUIDocument
def lookAtWindow(activeView):
# Looking for one of three conditions:
# 1. User is on a sheet (ActiveView will be DrawingSheet)
# 2. User is in an active ViewPort on a sheet (ActiveView will NOT be be DrawingSheet, but MainWindowTitle will contain " - [Sheet: " string)
# 3. User is on a View (neither of the previous two conditions)
result = False
if str(activeView.ViewType) == 'DrawingSheet':
result = 'Youre on a sheet'
else:
processes = list(Process.GetProcesses())
for process in processes:
window = process.MainWindowTitle
if window and 'Autodesk Revit '+app.VersionName[-4:] in window and ' - [Sheet: ' in window and ' - '+doc.Title+']' in window:
result = 'I reckon youre in a Viewport'
if not result:
result = 'so you must be in a '+str(activeView.ViewType)
form = Form()
form.Width = 300
form.Height = 100
label = Label()
label.Width = 280
label.Height = 70
label.Text = result
label.Parent = form
form.ShowDialog()
# need to close RevitPythonShell console before checking MainWindowTitle, so run on timer
threading.Timer(1, lookAtWindow, [ui.ActiveView]).start()
__window__.Close()

Tkinter insert a Combobox inside a Treeview widget

For example, lets create a Treeview widget using a class as follows:
class FiltersTree:
def __init__(self, master, filters):
self.master = master
self.filters = filters
self.treeFrame = Frame(self.master)
self.treeFrame.pack()
self._create_treeview()
self._populate_root()
def _create_treeview(self):
self.dataCols = ['filter', 'attribute']
self.tree = ttk.Treeview(self.master, columns = self.dataCols, displaycolumns = '#all')
Populate root, insert children as usual. At the end of the codeblock, you can see where I want to put a Combobox in the tree, using a Combo object:
def _populate_root(self):
# a Filter object
for filter in self.filters:
top_node = self.tree.insert('', 'end', text=filter.name)
# a Field object
for field in filter.fields:
mid_node = self.tree.insert(top_node, 'end', text = field.name)
# insert field attributes
self.insert_children(mid_node, field)
def insert_children(self, parent, field):
name = self.tree.insert(parent, 'end', text = 'Field name:',
values = [field.name])
self.tree.insert(parent, 'end', text = 'Velocity: ',
values = [Combo(self)]) # <--- Combo object
...
Next the class definition of Combo follows. The way I understand it, the combobox widget inherits from and must be placed inside the Labelframe widget from ttk:
class Combo(ttk.Frame):
def __init__(self, master):
self.opts = ('opt1', 'opt2', 'etc')
self.comboFrame = ttk.Labelframe(master, text = 'Choose option')
self.comboFrame.pack()
self.combo = ttk.Combobox(comboFrame, values=self.opts, state='readonly')
self.combo.current(1)
self.combo.pack()
So is this completely wrong? I want to have the ability to change between units (eg m/s, ft/s, etc) from within the Treeview widget.
Any suggestions, plz?
The treeview widget doesn't support embedded widgets. The values for the values attribute are treated as strings.
By default, a Treeview is a static display of a forest of lists of strings. However, with work, after carefully reading Treeview references, one can make a Treeview fairly interactive. For this question, I would bind left click to an event handler that compares the mouse x,y to the bounding box (.bbox) for the units attribute cell. If in the box, display a Combobox, initialized with the current value (such as 'flops'), directly on top of the units attribute cell.
Tkinter.ttk Treeview reference and Tcl/tk treeview reference
Of course, it might be easier to put the Treeview in a frame with with a separate Combobox.

Resources